From c1243a9d6ddd75921b446e3d184dee32fd3280c2 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sun, 22 Feb 2015 14:34:35 +0100 Subject: [PATCH] Fixed a bug in mob movement that had it ignore its own check for traversal-permission --- evennia/contrib/tutorial_world/build.ev | 57 +++- evennia/contrib/tutorial_world/mob.py | 382 +----------------------- evennia/locks/lockfuncs.py | 1 - evennia/scripts/tickerhandler.py | 2 +- 4 files changed, 52 insertions(+), 390 deletions(-) diff --git a/evennia/contrib/tutorial_world/build.ev b/evennia/contrib/tutorial_world/build.ev index 587beb9ed6..5ee23c5547 100644 --- a/evennia/contrib/tutorial_world/build.ev +++ b/evennia/contrib/tutorial_world/build.ev @@ -1026,17 +1026,22 @@ stairs down # except one which is the one decided by the scene shown by the # Obelisk last we looked. # -@dig/tel Blue bird tomb +@dig Blue bird tomb : tutorial_world.rooms.TeleportRoom = Tomb with stone bird;bird;blue;stone # +@desc Blue bird tomb = +The entrance to this tomb is decorated with a very lifelike blue bird. +# +Blue bird tomb +# @set here/puzzle_value = 0 # @set here/failure_teleport_to = tut#15 # @set here/success_teleport_to = tut#16 # -@set here/failure_teleport_to = +@set here/failure_teleport_msg = The tomb is dark. You fumble your way through it. You think you can make out a coffin in front of you in the gloom. @@ -1065,17 +1070,25 @@ stairs down # @tel tut#14 # -@dig/teleport Tomb of woman on horse +@dig Tomb of woman on horse : tutorial_world.rooms.TeleportRoom = Tomb with statue of riding woman;horse;riding; # +@desc Tomb of woman on horse = +The entrance to this tomb depicts a scene of a strong +warrior woman on a black horse. She shouts and brandishes +a glowing weapon as she charges down a hill towards +some enemy not depicted. +# +Tomb of woman on horse +# @set here/puzzle_value = 1 # @set here/failure_teleport_to = tut#15 # @set here/success_teleport_to = tut#16 # -@set here/failure_teleport_to = +@set here/failure_teleport_msg = The tomb is dark. You fumble your way through it. You think you can make out a coffin in front of you in the gloom. @@ -1104,17 +1117,24 @@ stairs down # @tel tut#14 # -@dig/teleport Tomb of the crowned queen +@dig Tomb of the crowned queen : tutorial_world.rooms.TeleportRoom = Tomb with statue of a crowned queen;crown;queen # +@desc Tomb of the crowned queen = +The entrance to this tomb shows a beautiful mural of a queen ruling +from her throne, respectful subjects kneeling before her. On her head +is a crown that seems to shine with magical power. +# +Tomb of the crowned queen +# @set here/puzzle_value = 2 # @set here/failure_teleport_to = tut#15 # @set here/success_teleport_to = tut#16 # -@set here/failure_teleport_to = +@set here/failure_teleport_msg = The tomb is dark. You fumble your way through it. You think you can make out a coffin in front of you in the gloom. @@ -1143,17 +1163,25 @@ stairs down # @tel tut#14 # -@dig/teleport Tomb of the shield +@dig Tomb of the shield : tutorial_world.rooms.TeleportRoom = Tomb with shield of arms;shield # +Tomb of the shield +# +@desc Tomb of the shield = +This tomb shows a warrior woman fighting shadowy creatures from the +top of a hill. Her sword lies broken on the ground before her but she +fights on with her battered shield - the scene depicts her just as she +rams the shield into an enemy in wild desperation. +# @set here/puzzle_value = 3 # @set here/failure_teleport_to = tut#15 # @set here/success_teleport_to = tut#16 # -@set here/failure_teleport_to = +@set here/failure_teleport_msg = The tomb is dark. You fumble your way through it. You think you can make out a coffin in front of you in the gloom. @@ -1182,17 +1210,26 @@ stairs down # @tel tut#14 # -@dig/teleport Tomb of the hero +@dig Tomb of the hero : tutorial_world.rooms.TeleportRoom = Tomb depicting a heroine fighting a monster;knight;hero;monster;beast # +@desc Tomb of the hero = +The entrance to this tomb shows a mural of an aging woman in a +warrior's outfit. She has white hair yet her sword-arm shows no sign +of weakness and her pose is straight. Children are gathered around her +feet and men and women from all the land come to seek the wisdom and +strength of the legendary hero. +# +Tomb of the hero +# @set here/puzzle_value = 4 # @set here/failure_teleport_to = tut#15 # @set here/success_teleport_to = tut#16 # -@set here/failure_teleport_to = +@set here/failure_teleport_msg = The tomb is dark. You fumble your way through it. You think you can make out a coffin in front of you in the gloom. diff --git a/evennia/contrib/tutorial_world/mob.py b/evennia/contrib/tutorial_world/mob.py index c125966a51..b1408f2aa1 100644 --- a/evennia/contrib/tutorial_world/mob.py +++ b/evennia/contrib/tutorial_world/mob.py @@ -13,7 +13,6 @@ from evennia import Command, CmdSet from evennia import logger from evennia.contrib.tutorial_world import objects as tut_objects - class CmdMobOnOff(Command): """ Activates/deactivates Mob @@ -290,7 +289,7 @@ class Mob(tut_objects.TutorialObject): order to block the mob from moving outside its area while allowing player-controlled characters to move normally. """ - if random.random() < 0.01: + if random.random() < 0.01 and self.db.irregular_msgs: self.location.msg_contents(random.choice(self.db.irregular_msgs)) if self.db.aggressive: # first check if there are any targets in the room. @@ -303,7 +302,7 @@ class Mob(tut_objects.TutorialObject): if exi.access(self, "traverse")] if exits: # randomly pick an exit - exit = random.choice(self.location.exits) + exit = random.choice(exits) # move there. self.move_to(exit.destination) else: @@ -316,7 +315,7 @@ class Mob(tut_objects.TutorialObject): scans adjacent rooms for enemies and moves towards them to attack if possible. """ - if random.random() < 0.01: + if random.random() < 0.01 and self.db.irregular_msgs: self.location.msg_contents(random.choice(self.db.irregular_msgs)) if self.db.aggressive: # first check if there are any targets in the room. @@ -348,7 +347,7 @@ class Mob(tut_objects.TutorialObject): the mob will bring its weapons to bear on any targets in the room. """ - if random.random() < 0.01: + if random.random() < 0.01 and self.db.irregular_msgs: self.location.msg_contents(random.choice(self.db.irregular_msgs)) # first make sure we have a target target = self._find_target(self.location) @@ -414,376 +413,3 @@ class Mob(tut_objects.TutorialObject): # we know it is a valid target. if self.db.aggressive and not self.ndb.is_attacking: self.start_attacking() - -# -##------------------------------------------------------------ -## -## Mob - mobile object -## -## This object utilizes exits and moves about randomly from -## room to room. -## -##------------------------------------------------------------ -# -#class Mob(tut_objects.TutorialObject): -# """ -# This type of mobile will roam from exit to exit at -# random intervals. Simply lock exits against the is_mob attribute -# to block them from the mob (lockstring = "traverse:not attr(is_mob)"). -# """ -# def at_object_creation(self): -# "This is called when the object is first created." -# self.db.tutorial_info = "This is a moving object. It moves randomly from room to room." -# -# self.scripts.add(tut_scripts.IrregularEvent) -# # this is a good attribute for exits to look for, to block -# # a mob from entering certain exits. -# self.db.is_mob = True -# self.db.last_location = None -# # only when True will the mob move. -# self.db.roam_mode = True -# # -# self.db.move_from -# self.location.msg_contents("With a cold breeze, %s drifts in the direction of %s." % (self.key, destination.key)) -# -# def announce_move_from(self, destination): -# "Called just before moving" -# self.location.msg_contents("With a cold breeze, %s drifts in the direction of %s." % (self.key, destination.key)) -# -# def announce_move_to(self, source_location): -# "Called just after arriving" -# self.location.msg_contents("With a wailing sound, %s appears from the %s." % (self.key, source_location.key)) -# -# def update_irregular(self): -# "Called at irregular intervals. Moves the mob." -# if self.roam_mode: -# exits = [ex for ex in self.location.exits -# if ex.access(self, "traverse")] -# if exits: -# # Try to make it so the mob doesn't backtrack. -# new_exits = [ex for ex in exits -# if ex.destination != self.db.last_location] -# if new_exits: -# exits = new_exits -# self.db.last_location = self.location -# # execute_cmd() allows the mob to respect exit and -# # exit-command locks, but may pose a problem if there is more -# # than one exit with the same name. -# # - see Enemy example for another way to move -# self.execute_cmd("%s" % exits[random.randint(0, len(exits) - 1)].key) -# -# -# -##------------------------------------------------------------ -## -## Enemy - mobile attacking object -## -## An enemy is a mobile that is aggressive against players -## in its vicinity. An enemy will try to attack characters -## in the same location. It will also pursue enemies through -## exits if possible. -## -## An enemy needs to have a Weapon object in order to -## attack. -## -## This particular tutorial enemy is a ghostly apparition that can only -## be hurt by magical weapons. It will also not truly "die", but only -## teleport to another room. Players defeated by the apparition will -## conversely just be teleported to a holding room. -## -##------------------------------------------------------------ -# -#class AttackTimer(DefaultScript): -# """ -# This script is what makes an eneny "tick". -# """ -# def at_script_creation(self): -# "This sets up the script" -# self.key = "AttackTimer" -# self.desc = "Drives an Enemy's combat." -# self.interval = random.randint(2, 3) # how fast the Enemy acts -# self.start_delay = True # wait self.interval before first call -# self.persistent = True -# -# def at_repeat(self): -# "Called every self.interval seconds." -# if self.obj.db.inactive: -# return -# # id(self.ndb.twisted_task) -# if self.obj.db.roam_mode: -# self.obj.roam() -# #return -# elif self.obj.db.battle_mode: -# #print "attack" -# self.obj.attack() -# return -# elif self.obj.db.pursue_mode: -# #print "pursue" -# self.obj.pursue() -# #return -# else: -# #dead mode. Wait for respawn. -# if not self.obj.db.dead_at: -# self.obj.db.dead_at = time.time() -# if (time.time() - self.obj.db.dead_at) > self.obj.db.dead_timer: -# self.obj.reset() -# -# -#class Enemy(Mob): -# """ -# This is a ghostly enemy with health (hit points). Their chance to hit, -# damage etc is determined by the weapon they are wielding, same as -# characters. -# -# An enemy can be in four modes: -# roam (inherited from Mob) - where it just moves around randomly -# battle - where it stands in one place and attacks players -# pursue - where it follows a player, trying to enter combat again -# dead - passive and invisible until it is respawned -# -# Upon creation, the following attributes describe the enemy's actions -# desc - description -# full_health - integer number > 0 -# defeat_location - unique name or #dbref to the location the player is -# taken when defeated. If not given, will remain in room. -# defeat_text - text to show player when they are defeated (just before -# being whisped away to defeat_location) -# defeat_text_room - text to show other players in room when a player -# is defeated -# win_text - text to show player when defeating the enemy -# win_text_room - text to show room when a player defeates the enemy -# respawn_text - text to echo to room when the mob is reset/respawn in -# that room. -# -# """ -# def at_object_creation(self): -# "Called at object creation." -# super(Enemy, self).at_object_creation() -# -# self.db.tutorial_info = "This moving object will attack players in the same room." -# -# # state machine modes -# self.db.roam_mode = True -# self.db.battle_mode = False -# self.db.pursue_mode = False -# self.db.dead_mode = False -# # health (change this at creation time) -# self.db.full_health = 20 -# self.db.health = 20 -# self.db.dead_at = time.time() -# self.db.dead_timer = 100 # how long to stay dead -# # this is used during creation to make sure the mob doesn't move away -# self.db.inactive = True -# # store the last player to hit -# self.db.last_attacker = None -# # where to take defeated enemies -# self.db.defeat_location = "darkcell" -# self.scripts.add(AttackTimer) -# -# def update_irregular(self): -# "the irregular event is inherited from Mob class" -# strings = self.db.irregular_echoes -# if strings: -# self.location.msg_contents(strings[random.randint(0, len(strings) - 1)]) -# -# def roam(self): -# "Called by Attack timer. Will move randomly as long as exits are open." -# -# # in this mode, the mob is healed. -# self.db.health = self.db.full_health -# players = [obj for obj in self.location.contents -# if utils.inherits_from(obj, BASE_CHARACTER_TYPECLASS) and not obj.is_superuser] -# if players: -# # we found players in the room. Attack. -# self.db.roam_mode = False -# self.db.pursue_mode = False -# self.db.battle_mode = True -# -# elif random.random() < 0.2: -# # no players to attack, move about randomly. -# exits = [ex.destination for ex in self.location.exits -# if ex.access(self, "traverse")] -# if exits: -# # Try to make it so the mob doesn't backtrack. -# new_exits = [ex for ex in exits -# if ex.destination != self.db.last_location] -# if new_exits: -# exits = new_exits -# self.db.last_location = self.location -# # locks should be checked here -# self.move_to(exits[random.randint(0, len(exits) - 1)]) -# else: -# # no exits - a dead end room. Respawn back to start. -# self.move_to(self.home) -# -# def attack(self): -# """ -# This is the main mode of combat. It will try to hit players in -# the location. If players are defeated, it will whisp them off -# to the defeat location. -# """ -# last_attacker = self.db.last_attacker -# players = [obj for obj in self.location.contents -# if utils.inherits_from(obj, BASE_CHARACTER_TYPECLASS) and not obj.is_superuser] -# if players: -# -# # find a target -# if last_attacker in players: -# # prefer to attack the player last attacking. -# target = last_attacker -# else: -# # otherwise attack a random player in location -# target = players[random.randint(0, len(players) - 1)] -# -# # try to use the weapon in hand -# attack_cmds = ("thrust", "pierce", "stab", "slash", "chop") -# cmd = attack_cmds[random.randint(0, len(attack_cmds) - 1)] -# self.execute_cmd("%s %s" % (cmd, target)) -# -# # analyze result. -# if target.db.health <= 0: -# # we reduced enemy to 0 health. Whisp them off to -# # the prison room. -# tloc = search_object(self.db.defeat_location) -# tstring = self.db.defeat_text -# if not tstring: -# tstring = "You feel your conciousness slip away ... you fall to the ground as " -# tstring += "the misty apparition envelopes you ...\n The world goes black ...\n" -# target.msg(tstring) -# ostring = self.db.defeat_text_room -# if tloc: -# if not ostring: -# ostring = "\n%s envelops the fallen ... and then their body is suddenly gone!" % self.key -# # silently move the player to defeat location -# # (we need to call hook manually) -# target.location = tloc[0] -# tloc[0].at_object_receive(target, self.location) -# elif not ostring: -# ostring = "%s falls to the ground!" % target.key -# self.location.msg_contents(ostring, exclude=[target]) -# # Pursue any stragglers after the battle -# self.battle_mode = False -# self.roam_mode = False -# self.pursue_mode = True -# else: -# # no players found, this could mean they have fled. -# # Switch to pursue mode. -# self.battle_mode = False -# self.roam_mode = False -# self.pursue_mode = True -# -# def pursue(self): -# """ -# In pursue mode, the enemy tries to find players in adjoining rooms, preferably -# those that previously attacked it. -# """ -# last_attacker = self.db.last_attacker -# players = [obj for obj in self.location.contents if utils.inherits_from(obj, BASE_CHARACTER_TYPECLASS) and not obj.is_superuser] -# if players: -# # we found players in the room. Maybe we caught up with some, -# # or some walked in on us before we had time to pursue them. -# # Switch to battle mode. -# self.battle_mode = True -# self.roam_mode = False -# self.pursue_mode = False -# else: -# # find all possible destinations. -# destinations = [ex.destination for ex in self.location.exits -# if ex.access(self, "traverse")] -# # find all players in the possible destinations. OBS-we cannot -# # just use the player's current position to move the Enemy; this -# # might have changed when the move is performed, causing the enemy -# # to teleport out of bounds. -# players = {} -# for dest in destinations: -# for obj in [o for o in dest.contents -# if utils.inherits_from(o, BASE_CHARACTER_TYPECLASS)]: -# players[obj] = dest -# if players: -# # we found targets. Move to intercept. -# if last_attacker in players: -# # preferably the one that last attacked us -# self.move_to(players[last_attacker]) -# else: -# # otherwise randomly. -# key = players.keys()[random.randint(0, len(players) - 1)] -# self.move_to(players[key]) -# else: -# # we found no players nearby. Return to roam mode. -# self.battle_mode = False -# self.roam_mode = True -# self.pursue_mode = False -# -# def at_hit(self, weapon, attacker, damage): -# """ -# Called when this object is hit by an enemy's weapon -# Should return True if enemy is defeated, False otherwise. -# -# In the case of players attacking, we handle all the events -# and information from here, so the return value is not used. -# """ -# -# self.db.last_attacker = attacker -# if not self.db.battle_mode: -# # we were attacked, so switch to battle mode. -# self.db.roam_mode = False -# self.db.pursue_mode = False -# self.db.battle_mode = True -# #self.scripts.add(AttackTimer) -# -# if not weapon.db.magic: -# # In the tutorial, the enemy is a ghostly apparition, so -# # only magical weapons can harm it. -# string = self.db.weapon_ineffective_text -# if not string: -# string = "Your weapon just passes through your enemy, causing no effect!" -# attacker.msg(string) -# return -# else: -# # an actual hit -# health = float(self.db.health) -# health -= damage -# self.db.health = health -# if health <= 0: -# string = self.db.win_text -# if not string: -# string = "After your last hit, %s folds in on itself, it seems to fade away into nothingness. " % self.key -# string += "In a moment there is nothing left but the echoes of its screams. But you have a " -# string += "feeling it is only temporarily weakened. " -# string += "You fear it's only a matter of time before it materializes somewhere again." -# attacker.msg(string) -# string = self.db.win_text_room -# if not string: -# string = "After %s's last hit, %s folds in on itself, it seems to fade away into nothingness. " % (attacker.name, self.key) -# string += "In a moment there is nothing left but the echoes of its screams. But you have a " -# string += "feeling it is only temporarily weakened. " -# string += "You fear it's only a matter of time before it materializes somewhere again." -# self.location.msg_contents(string, exclude=[attacker]) -# -# # put mob in dead mode and hide it from view. -# # AttackTimer will bring it back later. -# self.db.dead_at = time.time() -# self.db.roam_mode = False -# self.db.pursue_mode = False -# self.db.battle_mode = False -# self.db.dead_mode = True -# self.location = None -# else: -# self.location.msg_contents("%s wails, shudders and writhes." % self.key) -# return False -# -# def reset(self): -# """ -# If the mob was 'dead', respawn it to its home position and reset -# all modes and damage.""" -# if self.db.dead_mode: -# self.db.health = self.db.full_health -# self.db.roam_mode = True -# self.db.pursue_mode = False -# self.db.battle_mode = False -# self.db.dead_mode = False -# self.location = self.home -# string = self.db.respawn_text -# if not string: -# string = "%s fades into existence from out of thin air. It's looking pissed." % self.key -# self.location.msg_contents(string) diff --git a/evennia/locks/lockfuncs.py b/evennia/locks/lockfuncs.py index 11e4daf1b5..b46117b4b8 100644 --- a/evennia/locks/lockfuncs.py +++ b/evennia/locks/lockfuncs.py @@ -540,7 +540,6 @@ def has_player(accessing_obj, accessed_obj, *args, **kwargs): This is a useful lock for traverse-locking Exits to restrain NPC mobiles from moving outside their areas. """ - print "lock:", accessing_obj, accessed_obj, accessing_obj.has_player return hasattr(accessing_obj, "has_player") and accessing_obj.has_player def serversetting(accessing_obj, accessed_obj, *args, **kwargs): diff --git a/evennia/scripts/tickerhandler.py b/evennia/scripts/tickerhandler.py index c4a469eb10..79fbd096d8 100644 --- a/evennia/scripts/tickerhandler.py +++ b/evennia/scripts/tickerhandler.py @@ -87,7 +87,7 @@ class Ticker(object): """ for store_key, (obj, args, kwargs) in self.subscriptions.items(): hook_key = yield kwargs.get("_hook_key", "at_tick") - if not obj: + if not obj or not obj.pk: # object was deleted between calls self.remove(store_key) continue