Fixing tests for turnbased combat
This commit is contained in:
parent
e88e6d1b1b
commit
b1c765b50f
4 changed files with 290 additions and 159 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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):
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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,25 +81,207 @@ 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))
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
strip_ansi(result),
|
strip_ansi(result),
|
||||||
" testchar (Perfect) vs testmonster (Perfect) ",
|
" testchar (Perfect) vs testmonster (Perfect) ",
|
||||||
)
|
)
|
||||||
|
|
||||||
# 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(
|
||||||
strip_ansi(result),
|
strip_ansi(result),
|
||||||
" testmonster (Perfect) vs testchar (Perfect) ",
|
" testmonster (Perfect) vs testchar (Perfect) ",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue