diff --git a/docs/source/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Combat-Base.md b/docs/source/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Combat-Base.md index 4770e38ffc..49fd617b27 100644 --- a/docs/source/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Combat-Base.md +++ b/docs/source/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Combat-Base.md @@ -204,6 +204,8 @@ class EvAdventureCombatBaseHandler(DefaultScript): This helper method uses `obj.scripts.get()` to find if the combat script already exists 'on' the provided `obj`. If not, it will create it using Evennia's [create_script](evennia.utils.create.create_script) function. For some extra speed we cache the handler as `obj.ndb.combathandler` The `.ndb.` (non-db) means that handler is cached only in memory. +To know if the cache is out of date, we make sure to also check if the combathandler we got has an `id` that is not `None` . If it's `None`, this means the database entity was deleted and we just got its cached python representation from memory - we need to recreate it. + This is a `classmethod`, meaning it should be used on the handler class directly (rather than on an _instance_ of said class). This makes sense because this method actually should return the new instance. As a class method we'll need to call this directly on the class, like this: diff --git a/evennia/contrib/tutorials/evadventure/combat_base.py b/evennia/contrib/tutorials/evadventure/combat_base.py index 3d25256d0f..1b8687aa2c 100644 --- a/evennia/contrib/tutorials/evadventure/combat_base.py +++ b/evennia/contrib/tutorials/evadventure/combat_base.py @@ -298,9 +298,9 @@ class EvAdventureCombatBaseHandler(DefaultScript): combathandler_key = kwargs.pop("key", "combathandler") combathandler = obj.ndb.combathandler - if not combathandler: + if not combathandler or not combathandler.id: combathandler = obj.scripts.get(combathandler_key).first() - if not combathandler or not combathandler.id: + if not combathandler: # have to create from scratch persistent = kwargs.pop("persistent", True) combathandler = create_script( diff --git a/evennia/contrib/tutorials/evadventure/combat_turnbased.py b/evennia/contrib/tutorials/evadventure/combat_turnbased.py index 989ef965c0..ea801a8d3c 100644 --- a/evennia/contrib/tutorials/evadventure/combat_turnbased.py +++ b/evennia/contrib/tutorials/evadventure/combat_turnbased.py @@ -307,6 +307,15 @@ class EvAdventureTurnbasedCombatHandler(EvAdventureCombatBaseHandler): action.execute() action.post_execute() + if action_dict.get("repeat", False): + # queue the action again *without updating the *.ndb.did_action list* (otherwise + # we'd always auto-end the turn if everyone used repeating actions and there'd be + # no time to change it before the next round) + self.combatants[combatant] = action_dict + else: + # if not a repeat, set the fallback action + self.combatants[combatant] = self.fallback_action_dict + def check_stop_combat(self): """Check if it's time to stop combat""" @@ -319,8 +328,6 @@ class EvAdventureTurnbasedCombatHandler(EvAdventureCombatBaseHandler): self.combatants.pop(combatant) self.defeated_combatants.append(combatant) self.msg("|r$You() $conj(fall) to the ground, defeated.|n", combatant=combatant) - else: - self.combatants[combatant] = self.fallback_action_dict # check if anyone managed to flee flee_timeout = self.flee_timeout @@ -704,7 +711,7 @@ def node_combat(caller, raw_string, **kwargs): _step_wizard, { "steps": ["node_choose_enemy_target"], - "action_dict": {"key": "attack", "target": None}, + "action_dict": {"key": "attack", "target": None, "repeat": True}, }, ), }, @@ -768,7 +775,7 @@ def node_combat(caller, raw_string, **kwargs): }, { "desc": "flee!", - "goto": (_queue_action, {"action_dict": {"key": "flee"}}), + "goto": (_queue_action, {"action_dict": {"key": "flee", "repeat": True}}), }, { "desc": "hold, doing nothing", diff --git a/evennia/contrib/tutorials/evadventure/tests/test_combat.py b/evennia/contrib/tutorials/evadventure/tests/test_combat.py index 1df39859a0..ac6042d8a8 100644 --- a/evennia/contrib/tutorials/evadventure/tests/test_combat.py +++ b/evennia/contrib/tutorials/evadventure/tests/test_combat.py @@ -502,7 +502,7 @@ class EvAdventureTurnbasedCombatHandlerTest(_CombatTestBase): """ self.assertEqual(self.combathandler.turn, 0) - action_dict = {"key": "flee"} + action_dict = {"key": "flee", "repeat": True} # first flee records the fleeing state self.combathandler.flee_timeout = 2 # to make sure @@ -510,6 +510,9 @@ class EvAdventureTurnbasedCombatHandlerTest(_CombatTestBase): self.assertEqual(self.combathandler.turn, 1) self.assertEqual(self.combathandler.fleeing_combatants[self.combatant], 1) + # action_dict should still be in place due to repeat + self.assertEqual(self.combathandler.combatants[self.combatant], action_dict) + self.combatant.msg.assert_called_with( text=( "You retreat, being exposed to attack while doing so (will escape in 1 turn).", diff --git a/evennia/utils/evmenu.py b/evennia/utils/evmenu.py index cc84d2a8ae..015757427d 100644 --- a/evennia/utils/evmenu.py +++ b/evennia/utils/evmenu.py @@ -556,7 +556,7 @@ class EvMenu: by default in all nodes of the menu. This will print out the current state of the menu. Deactivate for production use! When the debug flag is active, the `persistent` flag is deactivated. - **kwargs: All kwargs will become initialization variables on `caller.ndb._menutree`, + **kwargs: All kwargs will become initialization variables on `caller.ndb._evmenu`, to be available at run. Raises: @@ -650,7 +650,7 @@ class EvMenu: # store ourself on the object self.caller.ndb._evmenu = self - # DEPRECATED - for backwards-compatibility. Use `.ndb._evmenu` instead + # TODO DEPRECATED - for backwards-compatibility. Use `.ndb._evmenu` instead self.caller.ndb._menutree = self if persistent: @@ -974,6 +974,7 @@ class EvMenu: self._quitting = True self.caller.cmdset.remove(EvMenuCmdSet) del self.caller.ndb._evmenu + del self.caller.ndb._menutree # TODO Deprecated if self._persistent: self.caller.attributes.remove("_menutree_saved") self.caller.attributes.remove("_menutree_saved_startnode")