Cleanup combat syntax, add flake8 config for legacy compat

This commit is contained in:
Griatch 2023-04-09 19:22:39 +02:00
parent 049e4fbb35
commit e88e6d1b1b
5 changed files with 209 additions and 252 deletions

2
.flake8 Normal file
View File

@ -0,0 +1,2 @@
[flake8]
max-line-length = 100

View File

@ -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

View File

@ -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 <target> using your equipped weapon/spell
3. Make [S]tunt <target/yourself> (gain/give advantage/disadvantage for future attacks)
4. S[W]ap weapon / spell rune
5. [U]se <item>
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: <enemy 3> (wounded)
1: <enemy 1> (hurt)
2: <enemy 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 <target> (give disadvantage DEX)
2: Feint <target> (get advantage DEX against target)
3: ...
-------------------- make stunt target submenu
Choose the target of your stunt:
0: Yourself 3: <combatant 3> (wounded)
1: <combatant 1> (hurt)
2: <combatant 2> (unharmed)
------------------- swap weapon or spell run
Choose the item to wield.
1: <item1>
2: <item2> (two hands)
3: <item3>
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,

View File

@ -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.

View File

@ -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):
""" """