Fixing tests for turnbased combat

This commit is contained in:
Griatch 2023-04-10 13:12:41 +02:00
parent e88e6d1b1b
commit b1c765b50f
4 changed files with 290 additions and 159 deletions

View file

@ -12,7 +12,7 @@ This establishes the basic building blocks for combat:
""" """
from evennia import create_script from evennia import Command, create_script
from evennia.scripts.scripts import DefaultScript from evennia.scripts.scripts import DefaultScript
from evennia.typeclasses.attributes import AttributeProperty from evennia.typeclasses.attributes import AttributeProperty
from evennia.utils import evtable from evennia.utils import evtable
@ -27,9 +27,6 @@ class CombatFailure(RuntimeError):
""" """
# Combaw action classes
class CombatAction: class CombatAction:
""" """
Parent class for all actions. Parent class for all actions.
@ -130,7 +127,9 @@ class CombatActionAttack(CombatAction):
target = self.target target = self.target
if weapon.at_pre_use(attacker, target): if weapon.at_pre_use(attacker, target):
weapon.use(attacker, target, advantage=self.has_advantage(attacker, target)) weapon.use(
attacker, target, advantage=self.combathandler.has_advantage(attacker, target)
)
weapon.at_post_use(attacker, target) weapon.at_post_use(attacker, target)
@ -204,7 +203,7 @@ class CombatActionStunt(CombatAction):
"|yHaving succeeded, you hold back to plan your next move.|n [hold]", "|yHaving succeeded, you hold back to plan your next move.|n [hold]",
broadcast=False, broadcast=False,
) )
combathandler.queue_action(attacker, combathandler.default_action_dict) combathandler.queue_action(attacker, combathandler.fallback_action_dict)
else: else:
self.msg(f"$You({defender.key}) $conj(resist)! $You() $conj(fail) the stunt.") self.msg(f"$You({defender.key}) $conj(resist)! $You() $conj(fail) the stunt.")
@ -235,8 +234,8 @@ class CombatActionUseItem(CombatAction):
item.use( item.use(
user, user,
target, target,
advantage=self.has_advantage(user, target), advantage=self.combathandler.has_advantage(user, target),
disadvantage=self.has_disadvantage(user, target), disadvantage=self.combathandler.has_disadvantage(user, target),
) )
item.at_post_use(user, target) item.at_post_use(user, target)
# to back to idle after this # to back to idle after this
@ -365,11 +364,11 @@ class EvAdventureCombatHandlerBase(DefaultScript):
Example: Example:
:: ::
Goblin shaman (Perfect)[attack] Goblin shaman (Perfect)
Gregor (Hurt)[attack] Goblin brawler(Hurt)[attack] Gregor (Hurt) Goblin brawler(Hurt)
Bob (Perfect)[stunt] vs Goblin grunt 1 (Hurt)[attack] Bob (Perfect) vs Goblin grunt 1 (Hurt)
Goblin grunt 2 (Perfect)[hold] Goblin grunt 2 (Perfect)
Goblin grunt 3 (Wounded)[flee] Goblin grunt 3 (Wounded)
""" """
allies, enemies = self.get_sides(combatant) allies, enemies = self.get_sides(combatant)
@ -378,14 +377,8 @@ class EvAdventureCombatHandlerBase(DefaultScript):
nallies, nenemies = len(allies), len(enemies) nallies, nenemies = len(allies), len(enemies)
# prepare colors and hurt-levels # prepare colors and hurt-levels
allies = [ allies = [f"{ally} ({ally.hurt_level})" for ally in allies]
f"{ally} ({ally.hurt_level})[{self.get_next_action_dict(ally)['key']}]" enemies = [f"{enemy} ({enemy.hurt_level})" for enemy in enemies]
for ally in allies
]
enemies = [
f"{enemy} ({enemy.hurt_level})[{self.get_next_action_dict(enemy)['key']}]"
for enemy in enemies
]
# the center column with the 'vs' # the center column with the 'vs'
vs_column = ["" for _ in range(max(nallies, nenemies))] vs_column = ["" for _ in range(max(nallies, nenemies))]
@ -523,13 +516,11 @@ class EvAdventureCombatHandlerBase(DefaultScript):
Check if this combat should be aborted, whatever this means for the particular Check if this combat should be aborted, whatever this means for the particular
the particular combat type. the particular combat type.
Stop the combat immediately. This should also do all needed cleanup.
Keyword Args: Keyword Args:
kwargs: Any extra keyword args used. kwargs: Any extra keyword args used.
Returns: Returns:
bool: If `True`, the `stop_combat` method sho bool: If `True`, the `stop_combat` method should be called.
""" """
raise NotImplementedError raise NotImplementedError

View file

@ -87,13 +87,10 @@ class EvAdventureTurnbasedCombatHandler(EvAdventureCombatHandlerBase):
} }
# how many turns you must be fleeing before escaping # how many turns you must be fleeing before escaping
flee_timeout = AttributeProperty(3, autocreate=False) flee_timeout = AttributeProperty(1, autocreate=False)
# how many turns you must be fleeing before escaping
flee_timeout = AttributeProperty(3, autocreate=False)
# fallback action if not selecting anything # fallback action if not selecting anything
fallback_action_dict = AttributeProperty({"key": "attack"}, autocreate=False) fallback_action_dict = AttributeProperty({"key": "hold"}, autocreate=False)
# persistent storage # persistent storage
@ -137,7 +134,6 @@ class EvAdventureTurnbasedCombatHandler(EvAdventureCombatHandlerBase):
""" """
self.disadvantage_matrix[combatant][target] = True self.disadvantage_matrix[combatant][target] = True
self.combathandler.advantage_matrix[combatant][target] = False
def has_advantage(self, combatant, target, **kwargs): def has_advantage(self, combatant, target, **kwargs):
""" """
@ -148,8 +144,8 @@ class EvAdventureTurnbasedCombatHandler(EvAdventureCombatHandlerBase):
target (Character or NPC): The target to check advantage against. target (Character or NPC): The target to check advantage against.
""" """
return bool(self.combathandler.advantage_matrix[combatant].pop(target, False)) or ( return bool(self.advantage_matrix[combatant].pop(target, False)) or (
target in self.combathandler.fleeing_combatants target in self.fleeing_combatants
) )
def has_disadvantage(self, combatant, target): def has_disadvantage(self, combatant, target):
@ -161,8 +157,8 @@ class EvAdventureTurnbasedCombatHandler(EvAdventureCombatHandlerBase):
target (Character or NPC): The target to check disadvantage against. target (Character or NPC): The target to check disadvantage against.
""" """
return bool(self.combathandler.disadvantage_matrix[combatant].pop(target, False)) or ( return bool(self.disadvantage_matrix[combatant].pop(target, False)) or (
combatant in self.combathandler.fleeing_combatants combatant in self.fleeing_combatants
) )
def add_combatant(self, combatant): def add_combatant(self, combatant):

View file

@ -156,11 +156,11 @@ class EvAdventureCombatTwitchHandler(EvAdventureCombatHandlerBase):
unrepeat(self.current_ticker_ref) unrepeat(self.current_ticker_ref)
if dt <= 0: if dt <= 0:
# no repeat # no repeat
self.tickerhandler_ref = None self.current_ticker_ref = None
else: else:
# always schedule the task to be repeating, cancel later otherwise. We store # always schedule the task to be repeating, cancel later otherwise. We store
# the tickerhandler's ref to make sure we can remove it later # the tickerhandler's ref to make sure we can remove it later
self.tickerhandler_ref = repeat(dt, self.execute_next_action, id_string="combat") self.current_ticker_ref = repeat(dt, self.execute_next_action, id_string="combat")
def execute_next_action(self): def execute_next_action(self):
""" """
@ -178,15 +178,21 @@ class EvAdventureCombatTwitchHandler(EvAdventureCombatHandlerBase):
if not action_dict.get("repeat", True): if not action_dict.get("repeat", True):
# not a repeating action, use the fallback (normally the original attack) # not a repeating action, use the fallback (normally the original attack)
self.action_dict = self.fallback_action_dict self.action_dict = self.fallback_action_dict
self.queue_action(self.fallback_action_dict.get("dt", 0)) self.queue_action(self.fallback_action_dict)
def check_stop_combat(self): def check_stop_combat(self):
# check if one side won the battle. # check if one side won the battle.
allies, enemies = self.get_sides() allies, enemies = self.get_sides()
allies.append(self.obj)
# remove all dead combatants
allies = [comb for comb in allies if comb.hp > 0]
enemies = [comb for comb in enemies if comb.hp > 0]
if not allies and not enemies: if not allies and not enemies:
txt = "Noone stands after the dust settles." self.msg("Noone stands after the dust settles.")
self.msg(txt) self.stop_combat()
return return
if not allies or not enemies: if not allies or not enemies:

View file

@ -3,7 +3,6 @@ Test EvAdventure combat.
""" """
from collections import deque
from unittest.mock import Mock, call, patch from unittest.mock import Mock, call, patch
from evennia.utils import create from evennia.utils import create
@ -82,7 +81,7 @@ class TestEvAdventureCombatHandlerBase(_CombatTestBase):
def test_get_combat_summary(self): def test_get_combat_summary(self):
"""Test combat summary""" """Test combat summary"""
self.combathandler.get_sides = Mock(return_value=(self.combatant, self.target)) self.combathandler.get_sides = Mock(return_value=([], [self.target]))
# as seen from one side # as seen from one side
result = str(self.combathandler.get_combat_summary(self.combatant)) result = str(self.combathandler.get_combat_summary(self.combatant))
@ -93,6 +92,7 @@ class TestEvAdventureCombatHandlerBase(_CombatTestBase):
) )
# as seen from other side # as seen from other side
self.combathandler.get_sides = Mock(return_value=([], [self.combatant]))
result = str(self.combathandler.get_combat_summary(self.target)) result = str(self.combathandler.get_combat_summary(self.target))
self.assertEqual( self.assertEqual(
@ -101,6 +101,187 @@ class TestEvAdventureCombatHandlerBase(_CombatTestBase):
) )
class TestCombatActionsBase(_CombatTestBase):
"""
A class for testing all subclasses of CombatAction in combat_base.py
"""
def setUp(self):
super().setUp()
self.combathandler = combat_base.EvAdventureCombatHandlerBase.get_or_create_combathandler(
self.location, key="combathandler"
)
# we need to mock all NotImplemented methods
self.combathandler.get_sides = Mock(return_value=([], [self.target]))
self.combathandler.give_advantage = Mock()
self.combathandler.give_disadvantage = Mock()
self.combathandler.remove_advantage = Mock()
self.combathandler.remove_disadvantage = Mock()
self.combathandler.get_advantage = Mock()
self.combathandler.get_disadvantage = Mock()
self.combathandler.has_advantage = Mock()
self.combathandler.has_disadvantage = Mock()
self.combathandler.queue_action = Mock()
def test_base_action(self):
action = combat_base.CombatAction(
self.combathandler, self.combatant, {"key": "hold", "foo": "bar"}
)
self.assertEqual(action.key, "hold")
self.assertEqual(action.foo, "bar")
self.assertEqual(action.combathandler, self.combathandler)
self.assertEqual(action.combatant, self.combatant)
@patch("evennia.contrib.tutorials.evadventure.combat_base.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
action = combat_base.CombatActionAttack(self.combathandler, self.combatant, actiondict)
action.execute()
self.assertEqual(self.target.hp, 4)
@patch("evennia.contrib.tutorials.evadventure.combat_base.rules.randint")
def test_attack__success(self, mock_randint):
actiondict = {"key": "attack", "target": self.target}
mock_randint.return_value = 11 # 11 + 1 str will hit beat armor 11
self.target.hp = 20
action = combat_base.CombatActionAttack(self.combathandler, self.combatant, actiondict)
action.execute()
self.assertEqual(self.target.hp, 9)
@patch("evennia.contrib.tutorials.evadventure.combat_base.rules.randint")
def test_stunt_fail(self, mock_randint):
action_dict = {
"key": "stunt",
"recipient": self.combatant,
"target": self.target,
"advantage": True,
"stunt_type": Ability.STR,
"defense_type": Ability.DEX,
}
mock_randint.return_value = 8 # fails 8+1 dex vs DEX 11 defence
action = combat_base.CombatActionStunt(self.combathandler, self.combatant, action_dict)
action.execute()
self.combathandler.give_advantage.assert_not_called()
@patch("evennia.contrib.tutorials.evadventure.combat_base.rules.randint")
def test_stunt_advantage__success(self, mock_randint):
action_dict = {
"key": "stunt",
"recipient": self.combatant,
"target": self.target,
"advantage": True,
"stunt_type": Ability.STR,
"defense_type": Ability.DEX,
}
mock_randint.return_value = 11 # 11+1 dex vs DEX 11 defence is success
action = combat_base.CombatActionStunt(self.combathandler, self.combatant, action_dict)
action.execute()
self.combathandler.give_advantage.assert_called_with(self.combatant, self.target)
@patch("evennia.contrib.tutorials.evadventure.combat_base.rules.randint")
def test_stunt_disadvantage__success(self, mock_randint):
action_dict = {
"key": "stunt",
"recipient": self.target,
"target": self.combatant,
"advantage": False,
"stunt_type": Ability.STR,
"defense_type": Ability.DEX,
}
mock_randint.return_value = 11 # 11+1 dex vs DEX 11 defence is success
action = combat_base.CombatActionStunt(self.combathandler, self.combatant, action_dict)
action.execute()
self.combathandler.give_disadvantage.assert_called_with(self.target, self.combatant)
def test_use_item(self):
"""
Use up a potion during combat.
"""
item = create.create_object(
EvAdventureConsumable, key="Healing potion", attributes=[("uses", 2)]
)
item.use = Mock()
action_dict = {
"key": "use",
"item": item,
"target": self.target,
}
self.assertEqual(item.uses, 2)
action = combat_base.CombatActionUseItem(self.combathandler, self.combatant, action_dict)
action.execute()
self.assertEqual(item.uses, 1)
action.execute()
self.assertEqual(item.pk, None) # deleted, it was used up
def test_swap_wielded_weapon_or_spell(self):
"""
First draw a weapon (from empty fists), then swap that out to another weapon, then
swap to a spell rune.
"""
sword = create.create_object(EvAdventureWeapon, key="sword")
zweihander = create.create_object(
EvAdventureWeapon,
key="zweihander",
attributes=(("inventory_use_slot", WieldLocation.TWO_HANDS),),
)
runestone = create.create_object(EvAdventureRunestone, key="ice rune")
# check hands are empty
self.assertEqual(self.combatant.weapon.key, "Empty Fists")
self.assertEqual(self.combatant.equipment.slots[WieldLocation.WEAPON_HAND], None)
self.assertEqual(self.combatant.equipment.slots[WieldLocation.TWO_HANDS], None)
# swap to sword
actiondict = {"key": "wield", "item": sword}
action = combat_base.CombatActionWield(self.combathandler, self.combatant, actiondict)
action.execute()
self.assertEqual(self.combatant.weapon, sword)
self.assertEqual(self.combatant.equipment.slots[WieldLocation.WEAPON_HAND], sword)
self.assertEqual(self.combatant.equipment.slots[WieldLocation.TWO_HANDS], None)
# swap to zweihander (two-handed sword)
actiondict["item"] = zweihander
action = combat_base.CombatActionWield(self.combathandler, self.combatant, actiondict)
action.execute()
self.assertEqual(self.combatant.weapon, zweihander)
self.assertEqual(self.combatant.equipment.slots[WieldLocation.WEAPON_HAND], None)
self.assertEqual(self.combatant.equipment.slots[WieldLocation.TWO_HANDS], zweihander)
# swap to runestone (also using two hands)
actiondict["item"] = runestone
action = combat_base.CombatActionWield(self.combathandler, self.combatant, actiondict)
action.execute()
self.assertEqual(self.combatant.weapon, runestone)
self.assertEqual(self.combatant.equipment.slots[WieldLocation.WEAPON_HAND], None)
self.assertEqual(self.combatant.equipment.slots[WieldLocation.TWO_HANDS], runestone)
# swap back to normal one-handed sword
actiondict["item"] = sword
action = combat_base.CombatActionWield(self.combathandler, self.combatant, actiondict)
action.execute()
self.assertEqual(self.combatant.weapon, sword)
self.assertEqual(self.combatant.equipment.slots[WieldLocation.WEAPON_HAND], sword)
self.assertEqual(self.combatant.equipment.slots[WieldLocation.TWO_HANDS], None)
class EvAdventureTurnbasedCombatHandlerTest(_CombatTestBase): class EvAdventureTurnbasedCombatHandlerTest(_CombatTestBase):
""" """
Test methods on the turn-based combat handler and actions Test methods on the turn-based combat handler and actions
@ -111,18 +292,17 @@ class EvAdventureTurnbasedCombatHandlerTest(_CombatTestBase):
# make sure to mock away all time-keeping elements # make sure to mock away all time-keeping elements
@patch( @patch(
"evennia.contrib.tutorials.evadventure.combat_turnbased.EvAdventureTurnbasedCombatHandler.interval", # noqa (
"evennia.contrib.tutorials.evadventure."
"combat_turnbased.EvAdventureTurnbasedCombatHandler.interval"
),
new=-1, new=-1,
) )
@patch(
"evennia.contrib.tutorials.evadventure.combat_turnbased.delay",
new=Mock(return_value=None),
)
def setUp(self): def setUp(self):
super().setUp() super().setUp()
# add target to combat # add target to combat
self.combathandler = ( self.combathandler = (
combat_turnbased.EvAdventureTurnebasedCombatHandler.get_or_create_combathandler( combat_turnbased.EvAdventureTurnbasedCombatHandler.get_or_create_combathandler(
self.location, key="combathandler" self.location, key="combathandler"
) )
) )
@ -141,7 +321,7 @@ class EvAdventureTurnbasedCombatHandlerTest(_CombatTestBase):
""" """
self.combathandler.queue_action(self.combatant, action_dict) self.combathandler.queue_action(self.combatant, action_dict)
self.combathandler.queue_action(self.target, action_dict2) self.combathandler.queue_action(self.target, action_dict2)
self.combathandler.execute_full_turn() self.combathandler.at_repeat()
if combatant_msg is not None: if combatant_msg is not None:
# this works because we mock combatant.msg in SetUp # this works because we mock combatant.msg in SetUp
self.combatant.msg.assert_called_with(combatant_msg) self.combatant.msg.assert_called_with(combatant_msg)
@ -153,7 +333,10 @@ class EvAdventureTurnbasedCombatHandlerTest(_CombatTestBase):
"""Testing all is set up correctly in the combathandler""" """Testing all is set up correctly in the combathandler"""
chandler = self.combathandler chandler = self.combathandler
self.assertEqual(dict(chandler.combatants), {self.combatant: {}, self.target: {}}) self.assertEqual(
dict(chandler.combatants),
{self.combatant: {"key": "hold"}, self.target: {"key": "hold"}},
)
self.assertEqual( self.assertEqual(
dict(chandler.action_classes), dict(chandler.action_classes),
{ {
@ -175,8 +358,7 @@ class EvAdventureTurnbasedCombatHandlerTest(_CombatTestBase):
"""Remove a combatant.""" """Remove a combatant."""
self.combathandler.remove_combatant(self.target) self.combathandler.remove_combatant(self.target)
self.assertEqual(dict(self.combathandler.combatants), {self.combatant: {"key": "hold"}})
self.assertEqual(dict(self.combathandler.combatants), {self.combatant: deque()})
def test_stop_combat(self): def test_stop_combat(self):
"""Stopping combat, making sure combathandler is deleted.""" """Stopping combat, making sure combathandler is deleted."""
@ -215,7 +397,7 @@ class EvAdventureTurnbasedCombatHandlerTest(_CombatTestBase):
self.combathandler.queue_action(self.combatant, hold) self.combathandler.queue_action(self.combatant, hold)
self.assertEqual( self.assertEqual(
dict(self.combathandler.combatants), dict(self.combathandler.combatants),
{self.combatant: deque([hold]), self.target: deque()}, {self.combatant: {"key": "hold"}, self.target: {"key": "hold"}},
) )
mock_action = Mock() mock_action = Mock()
@ -238,14 +420,14 @@ class EvAdventureTurnbasedCombatHandlerTest(_CombatTestBase):
self.combathandler.execute_next_action = Mock() self.combathandler.execute_next_action = Mock()
self.combathandler.execute_full_turn() self.combathandler.at_repeat()
self.combathandler.execute_next_action.assert_has_calls( self.combathandler.execute_next_action.assert_has_calls(
[call(self.combatant), call(self.target)], any_order=True [call(self.combatant), call(self.target)], any_order=True
) )
def test_action__hold(self): def test_action__action_ticks_turn(self):
"""Hold, doing nothing""" """Test that action execution ticks turns"""
actiondict = {"key": "hold"} actiondict = {"key": "hold"}
self._run_actions(actiondict, actiondict) self._run_actions(actiondict, actiondict)
@ -253,26 +435,9 @@ class EvAdventureTurnbasedCombatHandlerTest(_CombatTestBase):
self.combatant.msg.assert_not_called() self.combatant.msg.assert_not_called()
@patch("evennia.contrib.tutorials.evadventure.combat.rules.randint") @patch("evennia.contrib.tutorials.evadventure.combat_base.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
self._run_actions(actiondict)
self.assertEqual(self.target.hp, 4)
@patch("evennia.contrib.tutorials.evadventure.combat.rules.randint")
def test_attack__success__still_alive(self, mock_randint):
actiondict = {"key": "attack", "target": self.target}
mock_randint.return_value = 11 # 11 + 1 str will hit beat armor 11
# make sure target survives
self.target.hp = 20
self._run_actions(actiondict)
self.assertEqual(self.target.hp, 9)
@patch("evennia.contrib.tutorials.evadventure.combat.rules.randint")
def test_attack__success__kill(self, mock_randint): def test_attack__success__kill(self, mock_randint):
"""Test that the combathandler is deleted once there are no more enemies"""
actiondict = {"key": "attack", "target": self.target} actiondict = {"key": "attack", "target": self.target}
mock_randint.return_value = 11 # 11 + 1 str will hit beat armor 11 mock_randint.return_value = 11 # 11 + 1 str will hit beat armor 11
@ -281,7 +446,7 @@ class EvAdventureTurnbasedCombatHandlerTest(_CombatTestBase):
# after this the combat is over # after this the combat is over
self.assertIsNone(self.combathandler.pk) self.assertIsNone(self.combathandler.pk)
@patch("evennia.contrib.tutorials.evadventure.combat.rules.randint") @patch("evennia.contrib.tutorials.evadventure.combat_base.rules.randint")
def test_stunt_fail(self, mock_randint): def test_stunt_fail(self, mock_randint):
action_dict = { action_dict = {
"key": "stunt", "key": "stunt",
@ -296,8 +461,9 @@ class EvAdventureTurnbasedCombatHandlerTest(_CombatTestBase):
self.assertEqual(self.combathandler.advantage_matrix[self.combatant], {}) self.assertEqual(self.combathandler.advantage_matrix[self.combatant], {})
self.assertEqual(self.combathandler.disadvantage_matrix[self.combatant], {}) self.assertEqual(self.combathandler.disadvantage_matrix[self.combatant], {})
@patch("evennia.contrib.tutorials.evadventure.combat.rules.randint") @patch("evennia.contrib.tutorials.evadventure.combat_base.rules.randint")
def test_stunt_advantage__success(self, mock_randint): def test_stunt_advantage__success(self, mock_randint):
"""Test so the advantage matrix is updated correctly"""
action_dict = { action_dict = {
"key": "stunt", "key": "stunt",
"recipient": self.combatant, "recipient": self.combatant,
@ -312,8 +478,9 @@ class EvAdventureTurnbasedCombatHandlerTest(_CombatTestBase):
bool(self.combathandler.advantage_matrix[self.combatant][self.target]), True bool(self.combathandler.advantage_matrix[self.combatant][self.target]), True
) )
@patch("evennia.contrib.tutorials.evadventure.combat.rules.randint") @patch("evennia.contrib.tutorials.evadventure.combat_base.rules.randint")
def test_stunt_disadvantage__success(self, mock_randint): def test_stunt_disadvantage__success(self, mock_randint):
"""Test so the disadvantage matrix is updated correctly"""
action_dict = { action_dict = {
"key": "stunt", "key": "stunt",
"recipient": self.target, "recipient": self.target,
@ -328,81 +495,6 @@ class EvAdventureTurnbasedCombatHandlerTest(_CombatTestBase):
bool(self.combathandler.disadvantage_matrix[self.target][self.combatant]), True bool(self.combathandler.disadvantage_matrix[self.target][self.combatant]), True
) )
def test_use_item(self):
"""
Use up a potion during combat.
"""
item = create.create_object(
EvAdventureConsumable, key="Healing potion", attributes=[("uses", 2)]
)
item.use = Mock()
action_dict = {
"key": "use",
"item": item,
"target": self.target,
}
self.assertEqual(item.uses, 2)
self._run_actions(action_dict)
self.assertEqual(item.uses, 1)
self._run_actions(action_dict)
self.assertEqual(item.pk, None) # deleted, it was used up
def test_swap_wielded_weapon_or_spell(self):
"""
First draw a weapon (from empty fists), then swap that out to another weapon, then
swap to a spell rune.
"""
sword = create.create_object(EvAdventureWeapon, key="sword")
zweihander = create.create_object(
EvAdventureWeapon,
key="zweihander",
attributes=(("inventory_use_slot", WieldLocation.TWO_HANDS),),
)
runestone = create.create_object(EvAdventureRunestone, key="ice rune")
# check hands are empty
self.assertEqual(self.combatant.weapon.key, "Empty Fists")
self.assertEqual(self.combatant.equipment.slots[WieldLocation.WEAPON_HAND], None)
self.assertEqual(self.combatant.equipment.slots[WieldLocation.TWO_HANDS], None)
# swap to sword
actiondict = {"key": "wield", "item": sword}
self._run_actions(actiondict)
self.assertEqual(self.combatant.weapon, sword)
self.assertEqual(self.combatant.equipment.slots[WieldLocation.WEAPON_HAND], sword)
self.assertEqual(self.combatant.equipment.slots[WieldLocation.TWO_HANDS], None)
# swap to zweihander (two-handed sword)
actiondict["item"] = zweihander
self._run_actions(actiondict)
self.assertEqual(self.combatant.weapon, zweihander)
self.assertEqual(self.combatant.equipment.slots[WieldLocation.WEAPON_HAND], None)
self.assertEqual(self.combatant.equipment.slots[WieldLocation.TWO_HANDS], zweihander)
# swap to runestone (also using two hands)
actiondict["item"] = runestone
self._run_actions(actiondict)
self.assertEqual(self.combatant.weapon, runestone)
self.assertEqual(self.combatant.equipment.slots[WieldLocation.WEAPON_HAND], None)
self.assertEqual(self.combatant.equipment.slots[WieldLocation.TWO_HANDS], runestone)
# swap back to normal one-handed sword
actiondict["item"] = sword
self._run_actions(actiondict)
self.assertEqual(self.combatant.weapon, sword)
self.assertEqual(self.combatant.equipment.slots[WieldLocation.WEAPON_HAND], sword)
self.assertEqual(self.combatant.equipment.slots[WieldLocation.TWO_HANDS], None)
def test_flee__success(self): def test_flee__success(self):
""" """
Test fleeing twice, leading to leaving combat. Test fleeing twice, leading to leaving combat.
@ -414,19 +506,20 @@ class EvAdventureTurnbasedCombatHandlerTest(_CombatTestBase):
# first flee records the fleeing state # first flee records the fleeing state
self._run_actions(action_dict) self._run_actions(action_dict)
self.combathandler.flee_timeout = 1 # to make sure
self.assertEqual(self.combathandler.turn, 1) self.assertEqual(self.combathandler.turn, 1)
self.assertEqual(self.combathandler.fleeing_combatants[self.combatant], 1) self.assertEqual(self.combathandler.fleeing_combatants[self.combatant], 1)
self.combatant.msg.assert_called_with( self.combatant.msg.assert_called_with(
text=( text=(
"You retreat, leaving yourself exposed while doing so (will escape in 1 turn).", "You retreat, being exposed to attack while doing so (will escape in 1 turn).",
{}, {},
), ),
from_obj=self.combatant, from_obj=self.combatant,
) )
# Check that enemies have advantage against you now # Check that enemies have advantage against you now
action = combat_turnbased.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)) self.assertTrue(action.combathandler.has_advantage(self.target, self.combatant))
# second flee should remove combatant # second flee should remove combatant
self._run_actions(action_dict) self._run_actions(action_dict)
@ -450,7 +543,52 @@ class TestEvAdventureTwitchCombatHandler(EvenniaCommandTestMixin, _CombatTestBas
) )
def test_get_sides(self): def test_get_sides(self):
""" """ sides = self.combatant_combathandler.get_sides(self.combatant)
self.assertEqual(sides, ([], [self.target]))
def test_queue_and_execute_action(self): @patch("evennia.contrib.tutorials.evadventure.combat_twitch.unrepeat", new=Mock())
""" """ @patch("evennia.contrib.tutorials.evadventure.combat_twitch.repeat", new=Mock(return_value=999))
def test_queue_action(self):
"""Test so the queue action cleans up tickerhandler correctly"""
actiondict = {"key": "hold"}
self.combatant_combathandler.queue_action(actiondict)
self.assertIsNone(self.combatant_combathandler.current_ticker_ref)
actiondict = {"key": "hold", "dt": 5}
self.combatant_combathandler.queue_action(actiondict)
self.assertEqual(self.combatant_combathandler.current_ticker_ref, 999)
@patch("evennia.contrib.tutorials.evadventure.combat_twitch.unrepeat", new=Mock())
@patch("evennia.contrib.tutorials.evadventure.combat_twitch.repeat", new=Mock())
def test_execute_next_action(self):
self.combatant_combathandler.action_dict = {
"key": "hold",
"dummy": "foo",
"repeat": False,
} # to separate from fallback
self.combatant_combathandler.execute_next_action()
# should now be back to fallback
self.assertEqual(
self.combatant_combathandler.action_dict,
self.combatant_combathandler.fallback_action_dict,
)
@patch("evennia.contrib.tutorials.evadventure.combat_twitch.unrepeat", new=Mock())
def test_check_stop_combat(self):
"""Test if combat should stop"""
# noone remains
self.combatant_combathandler.get_sides = Mock(return_value=([], []))
self.combatant_combathandler.stop_combat = Mock()
self.combatant_combathandler.check_stop_combat()
self.combatant.msg.assert_called_with()
self.combatant_combathandler.stop_combat.assert_called()
# one side wiped out
self.combatant_combathandler.get_sides = Mock(return_value=([], []))
self.combatant_combathandler.check_stop_combat()
self.combatant.msg.assert_called_with()