More evadventure tests
This commit is contained in:
parent
1290f648d5
commit
dece979169
2 changed files with 57 additions and 22 deletions
|
|
@ -27,6 +27,8 @@ from evennia.utils import evmenu, evtable, dbserialize
|
||||||
from .enums import Ability
|
from .enums import Ability
|
||||||
from . import rules
|
from . import rules
|
||||||
|
|
||||||
|
# for simplicity, we have a default duration for advantages/disadvantages
|
||||||
|
|
||||||
|
|
||||||
class CombatFailure(RuntimeError):
|
class CombatFailure(RuntimeError):
|
||||||
"""
|
"""
|
||||||
|
|
@ -108,7 +110,6 @@ class CombatActionDoNothing(CombatAction):
|
||||||
post_action_text = "{combatant} does nothing this turn."
|
post_action_text = "{combatant} does nothing this turn."
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class CombatActionStunt(CombatAction):
|
class CombatActionStunt(CombatAction):
|
||||||
"""
|
"""
|
||||||
Perform a stunt. A stunt grants an advantage to yours or another player for their next
|
Perform a stunt. A stunt grants an advantage to yours or another player for their next
|
||||||
|
|
@ -126,9 +127,6 @@ class CombatActionStunt(CombatAction):
|
||||||
give_disadvantage = False
|
give_disadvantage = False
|
||||||
max_uses = 1
|
max_uses = 1
|
||||||
priority = -1
|
priority = -1
|
||||||
# how many turns the stunt's effect apply (that is, how quickly it must be used before the
|
|
||||||
# advantage/disadvantage is lost).
|
|
||||||
duration = 5
|
|
||||||
attack_type = Ability.DEX
|
attack_type = Ability.DEX
|
||||||
defense_type = Ability.DEX
|
defense_type = Ability.DEX
|
||||||
help_text = ("Perform a stunt against a target. This will give you or an ally advantage "
|
help_text = ("Perform a stunt against a target. This will give you or an ally advantage "
|
||||||
|
|
@ -173,7 +171,7 @@ class CombatActionAttack(CombatAction):
|
||||||
# figure out advantage (gained by previous stunts)
|
# figure out advantage (gained by previous stunts)
|
||||||
advantage = bool(self.combathandler.advantage_matrix[attacker].pop(defender, False))
|
advantage = bool(self.combathandler.advantage_matrix[attacker].pop(defender, False))
|
||||||
|
|
||||||
# figure out disadvantage (by distance or by previous action)
|
# figure out disadvantage (gained by enemy stunts/actions)
|
||||||
disadvantage = bool(self.combathandler.disadvantage_matrix[attacker].pop(defender, False))
|
disadvantage = bool(self.combathandler.disadvantage_matrix[attacker].pop(defender, False))
|
||||||
|
|
||||||
is_hit, quality = rules.EvAdventureRollEngine.opposed_saving_throw(
|
is_hit, quality = rules.EvAdventureRollEngine.opposed_saving_throw(
|
||||||
|
|
@ -274,6 +272,10 @@ class EvAdventureCombatHandler(DefaultScript):
|
||||||
of all active participants. It's also possible to join (or leave) the fray later.
|
of all active participants. It's also possible to join (or leave) the fray later.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# we use the same duration for all stunts
|
||||||
|
stunt_duration = 3
|
||||||
|
|
||||||
# these will all be checked if they are available at a given time.
|
# these will all be checked if they are available at a given time.
|
||||||
all_action_classes = [
|
all_action_classes = [
|
||||||
CombatActionDoNothing,
|
CombatActionDoNothing,
|
||||||
|
|
@ -287,7 +289,6 @@ class EvAdventureCombatHandler(DefaultScript):
|
||||||
|
|
||||||
# stores all combatants active in the combat
|
# stores all combatants active in the combat
|
||||||
combatants = AttributeProperty(list())
|
combatants = AttributeProperty(list())
|
||||||
combatant_actions = AttributeProperty(defaultdict(dict))
|
|
||||||
action_queue = AttributeProperty(dict())
|
action_queue = AttributeProperty(dict())
|
||||||
|
|
||||||
turn_stats = AttributeProperty(defaultdict(list))
|
turn_stats = AttributeProperty(defaultdict(list))
|
||||||
|
|
@ -300,9 +301,19 @@ class EvAdventureCombatHandler(DefaultScript):
|
||||||
|
|
||||||
fleeing_combatants = AttributeProperty(default=list())
|
fleeing_combatants = AttributeProperty(default=list())
|
||||||
|
|
||||||
|
# how often this script ticks - the length of each turn (in seconds)
|
||||||
|
interval = 60
|
||||||
|
|
||||||
# actions that will be performed before a normal action
|
def at_repeat(self, **kwargs):
|
||||||
move_actions = ("approach", "withdraw")
|
"""
|
||||||
|
Called every self.interval seconds. The main tick of the script.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if self.turn == 0:
|
||||||
|
self._start_turn()
|
||||||
|
else:
|
||||||
|
self._end_turn()
|
||||||
|
self._start_turn()
|
||||||
|
|
||||||
def _update_turn_stats(self, combatant, message):
|
def _update_turn_stats(self, combatant, message):
|
||||||
"""
|
"""
|
||||||
|
|
@ -332,9 +343,8 @@ class EvAdventureCombatHandler(DefaultScript):
|
||||||
# do all actions
|
# do all actions
|
||||||
for combatant in self.combatants:
|
for combatant in self.combatants:
|
||||||
# read the current action type selected by the player
|
# read the current action type selected by the player
|
||||||
action_class, args, kwargs = self.action_queue[combatant]
|
action, args, kwargs = self.action_queue.get(
|
||||||
# get the already initialized CombatAction instance (where state can be tracked)
|
combatant, (CombatActionDoNothing(self, combatant), (), {}))
|
||||||
action = self.combatant_actions[combatant][action_class]
|
|
||||||
# perform the action on the CombatAction instance
|
# perform the action on the CombatAction instance
|
||||||
action.use(combatant, *args, **kwargs)
|
action.use(combatant, *args, **kwargs)
|
||||||
|
|
||||||
|
|
@ -354,9 +364,10 @@ class EvAdventureCombatHandler(DefaultScript):
|
||||||
self.msg(f"{combatant.key} disengaged and left combat.")
|
self.msg(f"{combatant.key} disengaged and left combat.")
|
||||||
self.remove_combatant(combatant)
|
self.remove_combatant(combatant)
|
||||||
|
|
||||||
# refresh stunt timeouts
|
# refresh stunt timeouts (note - self.stunt_duration is the same for
|
||||||
|
# all stunts; # for more complex use we could store the action and let action have a
|
||||||
oldest_stunt_age = self.turn - STUNT_DURATION
|
# 'duration' property to use instead.
|
||||||
|
oldest_stunt_age = self.turn - self.stunt_duration
|
||||||
|
|
||||||
advantage_matrix = self.advantage_matrix
|
advantage_matrix = self.advantage_matrix
|
||||||
disadvantage_matrix = self.disadvantage_matrix
|
disadvantage_matrix = self.disadvantage_matrix
|
||||||
|
|
@ -382,13 +393,10 @@ class EvAdventureCombatHandler(DefaultScript):
|
||||||
def add_combatant(self, combatant):
|
def add_combatant(self, combatant):
|
||||||
if combatant not in self.combatants:
|
if combatant not in self.combatants:
|
||||||
self.combatants.append(combatant)
|
self.combatants.append(combatant)
|
||||||
for action_class in self.all_action_classes:
|
|
||||||
self.combatant_actions[combatant][action_class] = action_class(self, combatant)
|
|
||||||
|
|
||||||
def remove_combatant(self, combatant):
|
def remove_combatant(self, combatant):
|
||||||
if combatant in self.combatants:
|
if combatant in self.combatants:
|
||||||
self.combatants.remove(combatant)
|
self.combatants.remove(combatant)
|
||||||
self.combatant_actions.pop(combatant, None)
|
|
||||||
|
|
||||||
def get_combat_summary(self, combatant):
|
def get_combat_summary(self, combatant):
|
||||||
"""
|
"""
|
||||||
|
|
@ -504,19 +512,18 @@ class EvAdventureCombatHandler(DefaultScript):
|
||||||
# defender still alive
|
# defender still alive
|
||||||
self.msg(defender)
|
self.msg(defender)
|
||||||
|
|
||||||
def register_action(self, combatant, action=None, *args, **kwargs):
|
def register_action(self, action, combatant, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Register an action by-name.
|
Register an action by-name.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
combatant (Object): The one performing the action.
|
combatant (Object): The one performing the action.
|
||||||
action (CombatAction): An available action, will be prepended with `action_` and
|
action (CombatAction): An available action class to use.
|
||||||
used to call the relevant handler on this script.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if not action:
|
if not action:
|
||||||
action = CombatActionDoNothing
|
action = CombatActionDoNothing
|
||||||
self.action_queue[combatant] = (action, args, kwargs)
|
self.action_queue[combatant] = (action(self, combatant), args, kwargs)
|
||||||
|
|
||||||
|
|
||||||
# combat menu
|
# combat menu
|
||||||
|
|
|
||||||
|
|
@ -181,16 +181,44 @@ class EvAdventureTurnbasedCombatHandlerTest(EvAdventureMixin, BaseEvenniaTest):
|
||||||
Test the turn-based combat-handler implementation.
|
Test the turn-based combat-handler implementation.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@patch("evennia.contrib.tutorials.evadventure.combat_turnbased"
|
||||||
|
".EvAdventureCombatHandler.interval", new=-1)
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
self.combathandler = combat_turnbased.EvAdventureCombatHandler.objects.create()
|
self.combathandler = combat_turnbased.EvAdventureCombatHandler.objects.create()
|
||||||
|
self.combatant = self.character
|
||||||
self.target = create.create_object(EvAdventureCharacter, key="testchar2")
|
self.target = create.create_object(EvAdventureCharacter, key="testchar2")
|
||||||
self.combathandler.add_combatant(self.character)
|
self.combathandler.add_combatant(self.combatant)
|
||||||
self.combathandler.add_combatant(self.target)
|
self.combathandler.add_combatant(self.target)
|
||||||
|
|
||||||
def test_remove_combatant(self):
|
def test_remove_combatant(self):
|
||||||
self.combathandler.remove_combatant(self.character)
|
self.combathandler.remove_combatant(self.character)
|
||||||
|
|
||||||
|
def test_start_turn(self):
|
||||||
|
self.combathandler._start_turn()
|
||||||
|
self.assertEqual(self.combathandler.turn, 1)
|
||||||
|
self.combathandler._start_turn()
|
||||||
|
self.assertEqual(self.combathandler.turn, 2)
|
||||||
|
|
||||||
|
def test_end_of_turn__empty(self):
|
||||||
|
self.combathandler._end_turn()
|
||||||
|
|
||||||
|
def test_register_and_run_action(self):
|
||||||
|
action = combat_turnbased.CombatActionAttack
|
||||||
|
action.use = MagicMock()
|
||||||
|
|
||||||
|
self.combathandler.register_action(action, self.combatant)
|
||||||
|
self.combathandler._end_turn()
|
||||||
|
action.use.assert_called_once()
|
||||||
|
|
||||||
|
@patch("evennia.contrib.tutorials.evadventure.combat_turnbased.rules.randint")
|
||||||
|
def test_attack(self, mock_randint):
|
||||||
|
mock_randint = 8
|
||||||
|
self.combathandler.register_action(
|
||||||
|
combat_turnbased.CombatActionAttack,
|
||||||
|
self.combatant, self.target)
|
||||||
|
self.combathandler._end_turn()
|
||||||
|
|
||||||
|
|
||||||
class EvAdventureRollEngineTest(BaseEvenniaTest):
|
class EvAdventureRollEngineTest(BaseEvenniaTest):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue