diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000000..7da1f9608e --- /dev/null +++ b/.flake8 @@ -0,0 +1,2 @@ +[flake8] +max-line-length = 100 diff --git a/evennia/contrib/tutorials/evadventure/combat_base.py b/evennia/contrib/tutorials/evadventure/combat_base.py index 1e8f021d2e..4253800249 100644 --- a/evennia/contrib/tutorials/evadventure/combat_base.py +++ b/evennia/contrib/tutorials/evadventure/combat_base.py @@ -12,24 +12,12 @@ This establishes the basic building blocks for combat: """ -import random -from collections import defaultdict, deque - -from evennia import CmdSet, Command, create_script, default_cmds -from evennia.commands.command import InterruptCommand +from evennia import create_script from evennia.scripts.scripts import DefaultScript from evennia.typeclasses.attributes import AttributeProperty -from evennia.utils import dbserialize, delay, evmenu, evtable, logger -from evennia.utils.utils import display_len, inherits_from, list_to_string, pad +from evennia.utils import evtable from . import rules -from .characters import EvAdventureCharacter -from .enums import ABILITY_REVERSE_MAP, Ability, ObjType -from .npcs import EvAdventureNPC -from .objects import EvAdventureObject - -COMBAT_HANDLER_KEY = "evadventure_turnbased_combathandler" -COMBAT_HANDLER_INTERVAL = 30 class CombatFailure(RuntimeError): @@ -39,7 +27,7 @@ class CombatFailure(RuntimeError): """ -# Combat action classes +# Combaw action classes class CombatAction: @@ -149,9 +137,9 @@ class CombatActionAttack(CombatAction): class CombatActionStunt(CombatAction): """ Perform a stunt the grants a beneficiary (can be self) advantage on their next action against a - target. Whenever performing a stunt that would affect another negatively (giving them disadvantage - against an ally, or granting an advantage against them, we need to make a check first. We don't - do a check if giving an advantage to an ally or ourselves. + target. Whenever performing a stunt that would affect another negatively (giving them + disadvantage against an ally, or granting an advantage against them, we need to make a check + first. We don't do a check if giving an advantage to an ally or ourselves. action_dict = { "key": "stunt", @@ -159,8 +147,8 @@ class CombatActionStunt(CombatAction): "target": Character/NPC, "advantage": bool, # if False, it's a disadvantage "stunt_type": Ability, # what ability (like STR, DEX etc) to use to perform this stunt. - "defense_type": Ability, # what ability to use to defend against (negative) effects of this - stunt. + "defense_type": Ability, # what ability to use to defend against (negative) effects of + this stunt. } Note: @@ -298,7 +286,7 @@ class EvAdventureCombatHandlerBase(DefaultScript): fallback_action_dict = AttributeProperty({"key": "hold"}, autocreate=False) @classmethod - def get_or_create_combathandler(cls, obj, combathandler_key="combathandler", **kwargs): + def get_or_create_combathandler(cls, obj, **kwargs): """ Get or create a combathandler on `obj`. @@ -306,23 +294,29 @@ class EvAdventureCombatHandlerBase(DefaultScript): obj (any): The Typeclassed entity to store the CombatHandler Script on. This could be a location (for turn-based combat) or a Character (for twitch-based combat). Keyword Args: - combathandler_key (str): They key name for the script. Will be 'combathandler' by default. + combathandler_key (str): They key name for the script. Will be 'combathandler' by + default. **kwargs: Arguments to the Script, if it is created. """ if not obj: raise CombatFailure("Cannot start combat without a place to do it!") + combathandler_key = kwargs.pop("key", "combathandler") combathandler = obj.ndb.combathandler if not combathandler: - combathandler = obj.scripts.get(combathandler_name).first() + combathandler = obj.scripts.get(combathandler_key).first() if not combathandler: # have to create from scratch persistent = kwargs.pop("persistent", True) combathandler = create_script( - cls, key=combathandler_key, obj=obj, persistent=persistent, **kwargs + cls, + key=combathandler_key, + obj=obj, + persistent=persistent, + **kwargs, ) - self.caller.ndb.combathandler = combathandler + obj.ndb.combathandler = combathandler return combathandler def msg(self, message, combatant=None, broadcast=True): @@ -438,7 +432,7 @@ class EvAdventureCombatHandlerBase(DefaultScript): this with group mechanics). """ - raise NotImplemented + raise NotImplementedError def give_advantage(self, recipient, target): """ @@ -452,7 +446,7 @@ class EvAdventureCombatHandlerBase(DefaultScript): some future boost) """ - raise NotImplemented + raise NotImplementedError def give_disadvantage(self, recipient, target): """ @@ -460,10 +454,11 @@ class EvAdventureCombatHandlerBase(DefaultScript): Args: recipient (Character or NPC): The one to get the disadvantage. - target (Character or NPC): The one against which the target gains disadvantage, usually an enemy. + target (Character or NPC): The one against which the target gains disadvantage, usually + an enemy. """ - raise NotImplemented + raise NotImplementedError def has_advantage(self, combatant, target): """ @@ -474,7 +469,7 @@ class EvAdventureCombatHandlerBase(DefaultScript): target (Character or NPC): The target to check advantage against. """ - raise NotImplemented + raise NotImplementedError def has_disadvantage(self, combatant, target): """ @@ -485,7 +480,7 @@ class EvAdventureCombatHandlerBase(DefaultScript): target (Character or NPC): The target to check disadvantage against. """ - raise NotImplemented + raise NotImplementedError def queue_action(self, combatant, action_dict): """ @@ -503,7 +498,7 @@ class EvAdventureCombatHandlerBase(DefaultScript): to make room. """ - raise NotImplemented + raise NotImplementedError def execute_next_action(self, combatant): """ @@ -514,14 +509,14 @@ class EvAdventureCombatHandlerBase(DefaultScript): """ - raise NotImplemented + raise NotImplementedError def start_combat(self): """ Start combat. """ - raise NotImplemented + raise NotImplementedError def check_stop_combat(self): """ @@ -537,10 +532,10 @@ class EvAdventureCombatHandlerBase(DefaultScript): bool: If `True`, the `stop_combat` method sho """ - raise NotImplemented + raise NotImplementedError def stop_combat(self): """ Stop combat. This should also do all cleanup. """ - raise NotImplemented + raise NotImplementedError diff --git a/evennia/contrib/tutorials/evadventure/combat_turnbased.py b/evennia/contrib/tutorials/evadventure/combat_turnbased.py index e184340135..62d35b9d45 100644 --- a/evennia/contrib/tutorials/evadventure/combat_turnbased.py +++ b/evennia/contrib/tutorials/evadventure/combat_turnbased.py @@ -3,98 +3,35 @@ EvAdventure Turn-based combat This implements a turn-based (Final Fantasy, etc) style of MUD combat. -choose their next action. If they don't react before a timer runs out, the previous action -will be repeated. This means that a 'twitch' style combat can be created using the same -mechanism, by just speeding up each 'turn'. +In this variation, all combatants are sharing the same combat handler, sitting on the current room. +The user will receive a menu of combat options and each combatat has a certain time time (e.g. 30s) +to select their next action or do nothing. To speed up play, as soon as everyone in combat selected +their next action, the next turn runs immediately, regardless of the timeout. -The combat is handled with a `Script` shared between all combatants; this tracks the state -of combat and handles all timing elements. +With this example, all chosen combat actions are considered to happen at the same time (so you are +able to kill and be killed in the same turn). -Unlike in base _Knave_, the MUD version's combat is simultaneous; everyone plans and executes -their turns simultaneously with minimum downtime. +Unlike in twitch-like combat, there is no movement while in turn-based combat. Fleeing is a select +action that takes several vulnerable turns to complete. -This version is simplified to not worry about things like optimal range etc. So a bow can be used -the same as a sword in battle. One could add a 1D range mechanism to add more strategy by requiring -optimizal positioning. - -The combat is controlled through a menu: - -------------------- main menu -Combat - -You have 30 seconds to choose your next action. If you don't decide, you will hesitate and do -nothing. Available actions: - -1. [A]ttack/[C]ast spell at using your equipped weapon/spell -3. Make [S]tunt (gain/give advantage/disadvantage for future attacks) -4. S[W]ap weapon / spell rune -5. [U]se -6. [F]lee/disengage (takes one turn, during which attacks have advantage against you) -8. [H]esitate/Do nothing - -You can also use say/emote between rounds. -As soon as all combatants have made their choice (or time out), the round will be resolved -simultaneusly. - --------------------- attack/cast spell submenu - -Choose the target of your attack/spell: -0: Yourself 3: (wounded) -1: (hurt) -2: (unharmed) - -------------------- make stunt submenu - -Stunts are special actions that don't cause damage but grant advantage for you or -an ally for future attacks - or grant disadvantage to your enemy's future attacks. -The effects of stunts start to apply *next* round. The effect does not stack, can only -be used once and must be taken advantage of within 5 rounds. - -Choose stunt: -1: Trip (give disadvantage DEX) -2: Feint (get advantage DEX against target) -3: ... - --------------------- make stunt target submenu - -Choose the target of your stunt: -0: Yourself 3: (wounded) -1: (hurt) -2: (unharmed) - -------------------- swap weapon or spell run - -Choose the item to wield. -1: -2: (two hands) -3: -4: ... - -------------------- use item - -Choose item to use. -1: Healing potion (+1d6 HP) -2: Magic pebble (gain advantage, 1 use) -3: Potion of glue (give disadvantage to target) - -------------------- Hesitate/Do nothing - -You hang back, passively defending. - -------------------- Disengage - -You retreat, getting ready to get out of combat. Use two times in a row to -leave combat. You flee last in a round. """ +import random +from collections import defaultdict + +from evennia import AttributeProperty, CmdSet, Command, EvMenu +from evennia.utils import inherits_from, list_to_string + +from .characters import EvAdventureCharacter from .combat_base import ( CombatAction, + CombatActionAttack, CombatActionHold, CombatActionStunt, - CombatActionUserItem, + CombatActionUseItem, CombatActionWield, - EvAdventureCombatHandler, + EvAdventureCombatHandlerBase, ) from .enums import Ability @@ -133,7 +70,7 @@ class CombatActionFlee(CombatAction): ) -class EvAdventureTurnbasedCombatHandler(EvAdventureCombatHandler): +class EvAdventureTurnbasedCombatHandler(EvAdventureCombatHandlerBase): """ A version of the combathandler, handling turn-based combat. @@ -175,31 +112,32 @@ class EvAdventureTurnbasedCombatHandler(EvAdventureCombatHandler): # usable script properties # .is_active - show if timer is running - def give_advantage(self, recipient, target): + def give_advantage(self, combatant, target): """ Let a benefiter gain advantage against the target. Args: - recipient (Character or NPC): The one to gain the advantage. This may or may not + combatant (Character or NPC): The one to gain the advantage. This may or may not be the same entity that creates the advantage in the first place. target (Character or NPC): The one against which the target gains advantage. This could (in principle) be the same as the benefiter (e.g. gaining advantage on some future boost) """ - self.advantage_matrix[recipient][target] = True + self.advantage_matrix[combatant][target] = True - def give_disadvantage(self, recipient, target, **kwargs): + def give_disadvantage(self, combatant, target, **kwargs): """ Let an affected party gain disadvantage against a target. Args: recipient (Character or NPC): The one to get the disadvantage. - target (Character or NPC): The one against which the target gains disadvantage, usually an enemy. + target (Character or NPC): The one against which the target gains disadvantage, usually + an enemy. """ - self.disadvantage_matrix[recipient][target] = True - self.combathandler.advantage_matrix[recipient][target] = False + self.disadvantage_matrix[combatant][target] = True + self.combathandler.advantage_matrix[combatant][target] = False def has_advantage(self, combatant, target, **kwargs): """ @@ -210,7 +148,7 @@ class EvAdventureTurnbasedCombatHandler(EvAdventureCombatHandler): target (Character or NPC): The target to check advantage against. """ - return bool(self.combathandler.advantage_matrix[recipient].pop(target, False)) or ( + return bool(self.combathandler.advantage_matrix[combatant].pop(target, False)) or ( target in self.combathandler.fleeing_combatants ) @@ -223,11 +161,9 @@ class EvAdventureTurnbasedCombatHandler(EvAdventureCombatHandler): target (Character or NPC): The target to check disadvantage against. """ - - def has_disadvantage(self, recipient, target): - return bool(self.combathandler.disadvantage_matrix[recipient].pop(target, False)) or ( - recipient in self.combathandler.fleeing_combatants - ) + return bool(self.combathandler.disadvantage_matrix[combatant].pop(target, False)) or ( + combatant in self.combathandler.fleeing_combatants + ) def add_combatant(self, combatant): """ @@ -370,7 +306,7 @@ class EvAdventureTurnbasedCombatHandler(EvAdventureCombatHandler): """ # this gets the next dict and rotates the queue - action_dict = self.combatants.get(combatants, self.fallback_action_dict) + action_dict = self.combatants.get(combatant, self.fallback_action_dict) # use the action-dict to select and create an action from an action class action_class = self.action_classes[action_dict["key"]] @@ -763,18 +699,7 @@ def node_combat(caller, raw_string, **kwargs): # Add this command to the Character cmdset to make turn-based combat available. -class _CmdTurnCombatBase(_CmdCombatBase): - """ - Override parent class to slow down the tick for more clearly turn-based play. - - """ - - combathandler_name = "combathandler" - combat_tick = 30 - flee_timeout = 2 - - -class CmdTurnAttack(_CmdTurnCombatBase): +class CmdTurnAttack(Command): """ Start or join combat. @@ -800,7 +725,7 @@ class CmdTurnAttack(_CmdTurnCombatBase): return if not hasattr(target, "hp"): - self.msg(f"You can't attack that.") + self.msg("You can't attack that.") return elif target.hp <= 0: self.msg(f"{target.get_display_name(self.caller)} is already down.") @@ -810,14 +735,18 @@ class CmdTurnAttack(_CmdTurnCombatBase): self.msg("PvP combat is not allowed here!") return + combathandler = EvAdventureTurnbasedCombatHandler.get_or_create_combathandler( + self.caller.location + ) + # add combatants to combathandler. this can be done safely over and over - self.combathandler.add_combatant(self.caller) - self.combathandler.queue_action(self.caller, {"key": "attack", "target": target}) - self.combathandler.add_combatant(target) - self.combathandler.start_combat() + combathandler.add_combatant(self.caller) + combathandler.queue_action(self.caller, {"key": "attack", "target": target}) + combathandler.add_combatant(target) + combathandler.start_combat() # build and start the menu - evmenu.EvMenu( + EvMenu( self.caller, { "node_choose_enemy_target": node_choose_enemy_target, diff --git a/evennia/contrib/tutorials/evadventure/combat_twitch.py b/evennia/contrib/tutorials/evadventure/combat_twitch.py index 15c1058886..2d8927ab30 100644 --- a/evennia/contrib/tutorials/evadventure/combat_twitch.py +++ b/evennia/contrib/tutorials/evadventure/combat_twitch.py @@ -4,21 +4,20 @@ EvAdventure Twitch-based combat This implements a 'twitch' (aka DIKU or other traditional muds) style of MUD combat. """ -from evennia import AttributeProperty +from evennia import AttributeProperty, CmdSet, default_cmds from evennia.commands.command import Command, InterruptCommand -from evennia.scripts.scripts import DefaultScript -from evennia.utils.create import create_script -from evennia.utils.utils import repeat, unrepeat +from evennia.utils.utils import display_len, inherits_from, list_to_string, pad, repeat, unrepeat -from .combat import ( +from .characters import EvAdventureCharacter +from .combat_base import ( CombatActionAttack, CombatActionHold, CombatActionStunt, - CombatActionUserItem, + CombatActionUseItem, CombatActionWield, EvAdventureCombatHandlerBase, ) -from .enums import ABILITY_REVERSE_MAP, Ability, ObjType +from .enums import ABILITY_REVERSE_MAP class EvAdventureCombatTwitchHandler(EvAdventureCombatHandlerBase): @@ -44,7 +43,7 @@ class EvAdventureCombatTwitchHandler(EvAdventureCombatHandlerBase): disadvantages_against = AttributeProperty(dict) action_dict = AttributeProperty(dict) - fallback_action_dict = AttributePropety({"key": "hold", "dt": 0}) + fallback_action_dict = AttributeProperty({"key": "hold", "dt": 0}) # stores the current ticker reference, so we can manipulate it later current_ticker_ref = AttributeProperty(None) @@ -107,7 +106,8 @@ class EvAdventureCombatTwitchHandler(EvAdventureCombatHandlerBase): Args: recipient (Character or NPC): The one to get the disadvantage. - target (Character or NPC): The one against which the target gains disadvantage, usually an enemy. + target (Character or NPC): The one against which the target gains disadvantage, usually + an enemy. """ self.disadvantages_against[target] = True @@ -166,6 +166,7 @@ class EvAdventureCombatTwitchHandler(EvAdventureCombatHandlerBase): """ Triggered after a delay by the command """ + combatant = self.obj action_dict = self.action_dict action_class = self.action_classes[action_dict["key"]] action = action_class(self, combatant, action_dict) @@ -176,8 +177,8 @@ class EvAdventureCombatTwitchHandler(EvAdventureCombatHandlerBase): if not action_dict.get("repeat", True): # not a repeating action, use the fallback (normally the original attack) - self.action_dict = fallback_action_dict - self.queue_action(fallback_action_dict.get("dt", 0)) + self.action_dict = self.fallback_action_dict + self.queue_action(self.fallback_action_dict.get("dt", 0)) def check_stop_combat(self): # check if one side won the battle. @@ -243,7 +244,7 @@ class _BaseTwitchCombatCommand(Command): Get or create the combathandler assigned to this combatant. """ - return EvAdventureCombatHandlerBase.get_or_create_combathandler(self.caller) + return EvAdventureCombatTwitchHandler.get_or_create_combathandler(self.caller) class CmdAttack(_BaseTwitchCombatCommand): @@ -261,7 +262,7 @@ class CmdAttack(_BaseTwitchCombatCommand): help_category = "combat" def func(self): - target = self.search(lhs) + target = self.search(self.lhs) if not target: return @@ -448,7 +449,7 @@ class CmdUseItem(_BaseTwitchCombatCommand): ) -class CmdWield(_CmdCombatBase): +class CmdWield(_BaseTwitchCombatCommand): """ Wield a weapon or spell-rune. You will the wield the item, swapping with any other item(s) you were wielded before. diff --git a/evennia/contrib/tutorials/evadventure/tests/test_combat.py b/evennia/contrib/tutorials/evadventure/tests/test_combat.py index 8a9e87787d..d39168fbdd 100644 --- a/evennia/contrib/tutorials/evadventure/tests/test_combat.py +++ b/evennia/contrib/tutorials/evadventure/tests/test_combat.py @@ -8,37 +8,30 @@ from unittest.mock import Mock, call, patch from evennia.utils import create from evennia.utils.ansi import strip_ansi -from evennia.utils.test_resources import BaseEvenniaTest +from evennia.utils.test_resources import EvenniaCommandTestMixin, EvenniaTestCase -from .. import combat +from .. import combat_base, combat_turnbased, combat_twitch from ..characters import EvAdventureCharacter from ..enums import Ability, WieldLocation from ..npcs import EvAdventureMob from ..objects import EvAdventureConsumable, EvAdventureRunestone, EvAdventureWeapon from ..rooms import EvAdventureRoom -from .mixins import EvAdventureMixin -class EvAdventureCombatHandlerTest(BaseEvenniaTest): +class _CombatTestBase(EvenniaTestCase): """ - Test methods on the turn-based combat handler + Set up common entities for testing combat: + + - `location` (key=testroom) + - `combatant` (key=testchar) + - `target` (key=testmonster)` + + We also mock the `.msg` method of both `combatant` and `target` so we can + see what was sent. """ - maxDiff = None - - # make sure to mock away all time-keeping elements - @patch( - "evennia.contrib.tutorials.evadventure.combat.EvAdventureCombatHandler.interval", - new=-1, - ) - @patch( - "evennia.contrib.tutorials.evadventure.combat.delay", - new=Mock(return_value=None), - ) def setUp(self): - super().setUp() - self.location = create.create_object(EvAdventureRoom, key="testroom") self.combatant = create.create_object( EvAdventureCharacter, key="testchar", location=self.location @@ -58,8 +51,82 @@ class EvAdventureCombatHandlerTest(BaseEvenniaTest): self.combatant.msg = Mock() self.target.msg = Mock() - self.combathandler = combat.get_or_create_combathandler(self.combatant) + +class TestEvAdventureCombatHandlerBase(_CombatTestBase): + """ + Test the base functionality of the base combat handler. + + """ + + def setUp(self): + """This also tests the `get_or_create_combathandler` classfunc""" + super().setUp() + self.combathandler = combat_base.EvAdventureCombatHandlerBase.get_or_create_combathandler( + self.location, key="combathandler" + ) + + def test_combathandler_msg(self): + """Test sending messages to all in handler""" + + self.location.msg_contents = Mock() + + self.combathandler.msg("test_message") + + self.location.msg_contents.assert_called_with( + "test_message", + exclude=[], + from_obj=None, + mapping={"testchar": self.combatant, "testmonster": self.target}, + ) + + def test_get_combat_summary(self): + """Test combat summary""" + + self.combathandler.get_sides = Mock(return_value=(self.combatant, self.target)) + + # as seen from one side + result = str(self.combathandler.get_combat_summary(self.combatant)) + + self.assertEqual( + strip_ansi(result), + " testchar (Perfect) vs testmonster (Perfect) ", + ) + + # as seen from other side + result = str(self.combathandler.get_combat_summary(self.target)) + + self.assertEqual( + strip_ansi(result), + " testmonster (Perfect) vs testchar (Perfect) ", + ) + + +class EvAdventureTurnbasedCombatHandlerTest(_CombatTestBase): + """ + Test methods on the turn-based combat handler and actions + + """ + + maxDiff = None + + # make sure to mock away all time-keeping elements + @patch( + "evennia.contrib.tutorials.evadventure.combat_turnbased.EvAdventureTurnbasedCombatHandler.interval", # noqa + new=-1, + ) + @patch( + "evennia.contrib.tutorials.evadventure.combat_turnbased.delay", + new=Mock(return_value=None), + ) + def setUp(self): + super().setUp() # add target to combat + self.combathandler = ( + combat_turnbased.EvAdventureTurnebasedCombatHandler.get_or_create_combathandler( + self.location, key="combathandler" + ) + ) + self.combathandler.add_combatant(self.combatant) self.combathandler.add_combatant(self.target) def _get_action(self, action_dict={"key": "hold"}): @@ -86,16 +153,16 @@ class EvAdventureCombatHandlerTest(BaseEvenniaTest): """Testing all is set up correctly in the combathandler""" chandler = self.combathandler - self.assertEqual(dict(chandler.combatants), {self.combatant: deque(), self.target: deque()}) + self.assertEqual(dict(chandler.combatants), {self.combatant: {}, self.target: {}}) self.assertEqual( dict(chandler.action_classes), { - "hold": combat.CombatActionHold, - "attack": combat.CombatActionAttack, - "stunt": combat.CombatActionStunt, - "use": combat.CombatActionUseItem, - "wield": combat.CombatActionWield, - "flee": combat.CombatActionFlee, + "hold": combat_turnbased.CombatActionHold, + "attack": combat_turnbased.CombatActionAttack, + "stunt": combat_turnbased.CombatActionStunt, + "use": combat_turnbased.CombatActionUseItem, + "wield": combat_turnbased.CombatActionWield, + "flee": combat_turnbased.CombatActionFlee, }, ) self.assertEqual(chandler.flee_timeout, 1) @@ -104,20 +171,6 @@ class EvAdventureCombatHandlerTest(BaseEvenniaTest): self.assertEqual(dict(chandler.fleeing_combatants), {}) self.assertEqual(dict(chandler.defeated_combatants), {}) - def test_combathandler_msg(self): - """Test sending messages to all in handler""" - - self.location.msg_contents = Mock() - - self.combathandler.msg("test_message") - - self.location.msg_contents.assert_called_with( - "test_message", - exclude=[], - from_obj=None, - mapping={"testchar": self.combatant, "testmonster": self.target}, - ) - def test_remove_combatant(self): """Remove a combatant.""" @@ -154,25 +207,6 @@ class EvAdventureCombatHandlerTest(BaseEvenniaTest): allies, enemies = self.combathandler.get_sides(self.target) self.assertEqual((allies, enemies), ([target2], [self.combatant, combatant2])) - def test_get_combat_summary(self): - """Test combat summary""" - - # as seen from one side - result = str(self.combathandler.get_combat_summary(self.combatant)) - - self.assertEqual( - strip_ansi(result), - " testchar (Perfect) vs testmonster (Perfect) ", - ) - - # as seen from other side - result = str(self.combathandler.get_combat_summary(self.target)) - - self.assertEqual( - strip_ansi(result), - " testmonster (Perfect) vs testchar (Perfect) ", - ) - def test_queue_and_execute_action(self): """Queue actions and execute""" @@ -210,31 +244,6 @@ class EvAdventureCombatHandlerTest(BaseEvenniaTest): [call(self.combatant), call(self.target)], any_order=True ) - def test_combat_action(self): - """General tests of action functionality""" - - combatant = self.combatant - target = self.target - - action = self._get_action({"key": "hold"}) - - self.assertTrue(action.can_use()) - - action.give_advantage(combatant, target) - action.give_disadvantage(combatant, target) - - self.assertTrue(action.has_advantage(combatant, target)) - self.assertTrue(action.has_disadvantage(combatant, target)) - - action.lose_advantage(combatant, target) - action.lose_disadvantage(combatant, target) - - self.assertFalse(action.has_advantage(combatant, target)) - self.assertFalse(action.has_disadvantage(combatant, target)) - - action.msg(f"$You() attack $You({target.key}).") - combatant.msg.assert_called_with(text=("You attack testmonster.", {}), from_obj=combatant) - def test_action__hold(self): """Hold, doing nothing""" @@ -246,7 +255,6 @@ class EvAdventureCombatHandlerTest(BaseEvenniaTest): @patch("evennia.contrib.tutorials.evadventure.combat.rules.randint") def test_attack__miss(self, mock_randint): - actiondict = {"key": "attack", "target": self.target} mock_randint.return_value = 8 # target has default armor 11, so 8+1 str will miss @@ -298,7 +306,7 @@ class EvAdventureCombatHandlerTest(BaseEvenniaTest): "stunt_type": Ability.STR, "defense_type": Ability.DEX, } - mock_randint.return_value = 11 # 11+1 dex vs DEX 11 defence is success + mock_randint.return_value = 11 # 11+1 dex vs DEX 11 defence is success self._run_actions(action_dict) self.assertEqual( bool(self.combathandler.advantage_matrix[self.combatant][self.target]), True @@ -314,7 +322,7 @@ class EvAdventureCombatHandlerTest(BaseEvenniaTest): "stunt_type": Ability.STR, "defense_type": Ability.DEX, } - mock_randint.return_value = 11 # 11+1 dex vs DEX 11 defence is success + mock_randint.return_value = 11 # 11+1 dex vs DEX 11 defence is success self._run_actions(action_dict) self.assertEqual( bool(self.combathandler.disadvantage_matrix[self.target][self.combatant]), True @@ -417,10 +425,32 @@ class EvAdventureCombatHandlerTest(BaseEvenniaTest): from_obj=self.combatant, ) # Check that enemies have advantage against you now - action = combat.CombatAction(self.combathandler, self.target, {"key": "hold"}) + action = combat_turnbased.CombatAction(self.combathandler, self.target, {"key": "hold"}) self.assertTrue(action.has_advantage(self.target, self.combatant)) # second flee should remove combatant self._run_actions(action_dict) # this ends combat, so combathandler should be gone self.assertIsNone(self.combathandler.pk) + + +class TestEvAdventureTwitchCombatHandler(EvenniaCommandTestMixin, _CombatTestBase): + def setUp(self): + super().setUp() + + self.combatant_combathandler = ( + combat_twitch.EvAdventureCombatTwitchHandler.get_or_create_combathandler( + self.combatant, key="combathandler" + ) + ) + self.target_combathandler = ( + combat_twitch.EvAdventureCombatTwitchHandler.get_or_create_combathandler( + self.target, key="combathandler" + ) + ) + + def test_get_sides(self): + """ """ + + def test_queue_and_execute_action(self): + """ """