Testing out combat

This commit is contained in:
Griatch 2023-03-25 16:56:57 +01:00
parent 97b11ccea7
commit 5c914eb8b0
7 changed files with 311 additions and 154 deletions

View file

@ -19,6 +19,10 @@ py self.cmdset.add("evennia.contrib.tutorials.evadventure.combat.TwitchAttackCmd
create sword:evennia.contrib.tutorials.evadventure.objects.EvAdventureWeapon
# create a consumable to use
create potion:evennia.contrib.tutorials.evadventure.objects.EvAdventureConsumable
# dig a combat arena
dig arena:evennia.contrib.tutorials.evadventure.rooms.EvAdventureRoom = arena,back

View file

@ -97,7 +97,7 @@ from evennia.commands.command import InterruptCommand
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 inherits_from, list_to_string
from evennia.utils.utils import display_len, inherits_from, list_to_string, pad
from . import rules
from .characters import EvAdventureCharacter
@ -209,15 +209,15 @@ class CombatAction:
self.combathandler.fleeing_combatants.pop(self.combatant, None)
class CombatActionDoNothing(CombatAction):
class CombatActionHold(CombatAction):
"""
Action that does nothing.
Note:
Refer to as 'nothing'
Refer to as 'hold'
action_dict = {
"key": "nothing"
"key": "hold"
}
"""
@ -287,9 +287,6 @@ class CombatActionStunt(CombatAction):
# to give.
defender = target if self.advantage else recipient
self.stunt_type = ABILITY_REVERSE_MAP.get(self.stunt_type, self.stunt_type)
self.defense_type = ABILITY_REVERSE_MAP.get(self.defense_type, self.defense_type)
if not is_success:
# trying to give advantage to recipient against target. Target defends against caller
is_success, _, txt = rules.dice.opposed_saving_throw(
@ -302,19 +299,19 @@ class CombatActionStunt(CombatAction):
)
# deal with results
self.msg(f"$You() $conj(attempt) stunt on $You(defender.key). {txt}")
self.msg(f"$You() $conj(attempt) stunt on $You({defender.key}). {txt}")
if is_success:
if self.advantage:
self.give_advantage(recipient, target)
else:
self.give_disadvantage(recipient, target)
self.msg(
f"%You() $conj(cause) $You({recipient.key}) "
f"$You() $conj(cause) $You({recipient.key}) "
f"to gain {'advantage' if self.advantage else 'disadvantage'} "
f"against $You({target.key})!"
)
else:
self.msg(f"$You({target.key}) resists! $You() $conj(fail) the stunt.")
self.msg(f"$You({target.key}) $conj(resist)! $You() $conj(fail) the stunt.")
class CombatActionUseItem(CombatAction):
@ -384,15 +381,23 @@ class CombatActionFlee(CombatAction):
def execute(self):
if self.combatant not in self.combathandler.fleeing_combatants:
# we record the turn on which we started fleeing
self.combathandler.fleeing_combatants[self.combatant] = self.combathandler.turn
combathandler = self.combathandler
flee_timeout = self.combathandler.flee_timeout
self.msg(
"$You() $conj(retreat), leaving yourself exposed while doing so (will escape in "
f"{flee_timeout} $pluralize(turn, {flee_timeout}))."
)
if self.combatant not in combathandler.fleeing_combatants:
# we record the turn on which we started fleeing
combathandler.fleeing_combatants[self.combatant] = self.combathandler.turn
# show how many turns until successful flight
current_turn = combathandler.turn
started_fleeing = combathandler.fleeing_combatants[self.combatant]
flee_timeout = combathandler.flee_timeout
time_left = flee_timeout - (current_turn - started_fleeing)
if time_left > 0:
self.msg(
"$You() $conj(retreat), being exposed to attack while doing so (will escape in "
f"{time_left} $pluralize(turn, {time_left}))."
)
def post_execute(self):
"""
@ -410,7 +415,7 @@ class EvAdventureCombatHandler(DefaultScript):
# available actions in combat
action_classes = {
"nothing": CombatActionDoNothing,
"hold": CombatActionHold,
"attack": CombatActionAttack,
"stunt": CombatActionStunt,
"use": CombatActionUseItem,
@ -422,10 +427,10 @@ class EvAdventureCombatHandler(DefaultScript):
max_action_queue_size = 1
# fallback action if not selecting anything
fallback_action_dict = {"key": "nothing"}
fallback_action_dict = {"key": "hold"}
# how many turns you must be fleeing before escaping
flee_timeout = 1
flee_timeout = 5
# persistent storage
@ -441,6 +446,9 @@ class EvAdventureCombatHandler(DefaultScript):
fleeing_combatants = AttributeProperty(dict)
defeated_combatants = AttributeProperty(list)
# usable script properties
# .is_active - show if timer is running
def msg(self, message, combatant=None, broadcast=True):
"""
Central place for sending messages to combatants. This allows
@ -475,11 +483,14 @@ class EvAdventureCombatHandler(DefaultScript):
def add_combatant(self, combatant):
"""
Add a new combatant to the battle.
Add a new combatant to the battle. Can be called multiple times safely.
Args:
*combatants (EvAdventureCharacter, EvAdventureNPC): Any number of combatants to add to
the combat.
Returns:
bool: If this combatant was newly added or not (it was already in combat).
"""
if combatant not in self.combatants:
self.combatants[combatant] = deque((), maxlen=self.max_action_queue_size)
@ -496,6 +507,18 @@ class EvAdventureCombatHandler(DefaultScript):
"""
self.combatants.pop(combatant, None)
# clean up twitch cmdset if it exists
combatant.cmdset.remove(TwitchCombatCmdSet)
# clean up menu if it exists
def start_combat(self, **kwargs):
"""
This actually starts the combat. It's safe to run this multiple times
since it will only start combat if it isn't already running.
"""
if not self.is_active:
self.start(**kwargs)
def stop_combat(self):
"""
@ -543,6 +566,69 @@ class EvAdventureCombatHandler(DefaultScript):
enemies = pcs
return allies, enemies
def get_combat_summary(self, combatant):
"""
Get a 'battle report' - an overview of the current state of combat from the perspective
of one of the sides.
Args:
combatant (EvAdventureCharacter, EvAdventureNPC): The combatant to get.
Returns:
EvTable: A table representing the current state of combat.
Example:
::
Goblin shaman (Perfect)[attack]
Gregor (Hurt)[attack] Goblin brawler(Hurt)[attack]
Bob (Perfect)[stunt] vs Goblin grunt 1 (Hurt)[attack]
Goblin grunt 2 (Perfect)[hold]
Goblin grunt 3 (Wounded)[flee]
"""
allies, enemies = self.get_sides(combatant)
# we must include outselves at the top of the list (we are not returned from get_sides)
allies.insert(0, combatant)
nallies, nenemies = len(allies), len(enemies)
# prepare colors and hurt-levels
allies = [
f"{ally} ({ally.hurt_level})[{self.get_next_action_dict(ally)['key']}]"
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'
vs_column = ["" for _ in range(max(nallies, nenemies))]
vs_column[len(vs_column) // 2] = "|wvs|n"
# the two allies / enemies columns should be centered vertically
diff = abs(nallies - nenemies)
top_empty = diff // 2
bot_empty = diff - top_empty
topfill = ["" for _ in range(top_empty)]
botfill = ["" for _ in range(bot_empty)]
if nallies >= nenemies:
enemies = topfill + enemies + botfill
else:
allies = topfill + allies + botfill
# make a table with three columns
return evtable.EvTable(
table=[
evtable.EvColumn(*allies, align="l"),
evtable.EvColumn(*vs_column, align="c"),
evtable.EvColumn(*enemies, align="r"),
],
border=None,
maxwidth=78,
)
def queue_action(self, combatant, action_dict):
"""
Queue an action by adding the new actiondict to the back of the queue. If the
@ -568,10 +654,29 @@ class EvAdventureCombatHandler(DefaultScript):
# everyone has inserted an action. Start next turn without waiting!
self.force_repeat()
def get_next_action_dict(self, combatant, rotate_queue=True):
"""
Give the action_dict for the next action that will be executed.
Args:
combatant (EvAdventureCharacter, EvAdventureNPC): The combatant to get the action for.
rotate_queue (bool, optional): Rotate the queue after getting the action dict.
Returns:
dict: The next action-dict in the queue.
"""
action_queue = self.combatants[combatant]
action_dict = action_queue[0] if action_queue else self.fallback_action_dict
if rotate_queue:
# rotate the queue to the left so that the first element is now the last one
action_queue.rotate(-1)
return action_dict
def execute_next_action(self, combatant):
"""
Perform a combatant's next queued action. Note that there is _always_ an action queued,
even if this action is 'do nothing'. We don't pop anything from the queue, instead we keep
even if this action is 'hold'. We don't pop anything from the queue, instead we keep
rotating the queue. When the queue has a length of one, this means just repeating the
same action over and over.
@ -584,10 +689,8 @@ class EvAdventureCombatHandler(DefaultScript):
queue will be rotated to the left and be `[b, c, a]` (so next time, `b` will be used).
"""
action_queue = self.combatants[combatant]
action_dict = action_queue[0] if action_queue else self.fallback_action_dict
# rotate the queue to the left so that the first element is now the last one
action_queue.rotate(-1)
# this gets the next dict and rotates the queue
action_dict = self.get_next_action_dict(combatant)
# use the action-dict to select and create an action from an action class
action_class = self.action_classes[action_dict["key"]]
@ -655,82 +758,38 @@ class EvAdventureCombatHandler(DefaultScript):
self.msg(txt)
self.stop_combat()
def get_combat_summary(self, combatant):
def at_repeat(self, **kwargs):
"""
Get a 'battle report' - an overview of the current state of combat.
Args:
combatant (EvAdventureCharacter, EvAdventureNPC): The combatant to get.
Returns:
EvTable: A table representing the current state of combat.
Example:
::
Goblin shaman
Ally (hurt) Goblin brawler
Bob vs Goblin grunt 1 (hurt)
Goblin grunt 2
Goblin grunt 3
This is called every time the script ticks (how fast depends on if this handler runs a
twitch- or turn-based combat).
"""
allies, enemies = self.get_sides(combatant)
# we must include outselves at the top of the list (we are not returned from get_sides)
allies.insert(0, combatant)
nallies, nenemies = len(allies), len(enemies)
# prepare colors and hurt-levels
allies = [f"{ally} ({ally.hurt_level})" for ally in allies]
enemies = [f"{enemy} ({enemy.hurt_level})" for enemy in enemies]
# the center column with the 'vs'
vs_column = ["" for _ in range(max(nallies, nenemies))]
vs_column[len(vs_column) // 2] = "vs"
# the two allies / enemies columns should be centered vertically
diff = abs(nallies - nenemies)
top_empty = diff // 2
bot_empty = diff - top_empty
topfill = ["" for _ in range(top_empty)]
botfill = ["" for _ in range(bot_empty)]
if nallies >= nenemies:
enemies = topfill + enemies + botfill
else:
allies = topfill + allies + botfill
# make a table with three columns
return evtable.EvTable(
table=[
evtable.EvColumn(*allies, align="l"),
evtable.EvColumn(*vs_column, align="c"),
evtable.EvColumn(*enemies, align="r"),
],
border=None,
width=78,
)
self.execute_full_turn()
def get_or_create_combathandler(combatant, combathandler_name="combathandler", combat_tick=5):
def get_or_create_combathandler(location, combat_tick=3, combathandler_name="combathandler"):
"""
Joins or continues combat. This is a access function that will either get the
combathandler on the current room or create a new one.
Args:
combatant (EvAdventureCharacter, EvAdventureNPC): The one to
location (EvAdventureRoom): Where to start the combat.
combat_tick (int): How often (in seconds) the combathandler will perform a tick. The
shorter this interval, the more 'twitch-like' the combat will be. E.g.
combathandler_name (str): If the combathandler should be stored with a different script
name. Changing this could allow multiple combats to coexist in the same location.
Returns:
CombatHandler: The new or created combathandler.
Notes:
The combathandler starts disabled; one needs to run `.start` on it once all
(initial) combatants are added.
"""
location = combatant.location
if not location:
raise CombatFailure("Cannot start combat without a location.")
combathandler = location.scripts.get(combathandler_name)
combathandler = location.scripts.get(combathandler_name).first()
if not combathandler:
combathandler = create_script(
EvAdventureCombatHandler,
@ -738,8 +797,8 @@ def get_or_create_combathandler(combatant, combathandler_name="combathandler", c
obj=location,
interval=combat_tick,
persistent=True,
autostart=False,
)
combathandler.add_combatant(combatant)
return combathandler
@ -766,9 +825,10 @@ Examples of commands:
- |yuse <item>|n - use/consume an item in your inventory
- |yuse <item> on <target>|n - use an item on an enemy or ally
- |yhold|n - hold your attack, doing nothing
- |yflee|n - start to flee or disengage from combat
Use |yhelp <command>|n for more info."""
Use |yhelp <command>|n for more info. Use |yhelp combat|n to re-show this list."""
class _CmdCombatBase(Command):
@ -779,14 +839,16 @@ class _CmdCombatBase(Command):
"""
combathandler_name = "combathandler"
combat_tick = 2
combat_tick = 3
flee_timeout = 5
@property
def combathandler(self):
combathandler = getattr(self, "combathandler", None)
combathandler = getattr(self, "_combathandler", None)
if not combathandler:
self.combathandler = combathandler = get_or_create_combathandler(self.caller)
self._combathandler = combathandler = get_or_create_combathandler(
self.caller.location, combat_tick=2
)
return combathandler
def parse(self):
@ -805,16 +867,20 @@ class TwitchCombatCmdSet(CmdSet):
"""
name = "Twitchcombat cmdset"
priority = 1
mergetype = "Union" # use Replace to lock down all other commands
no_exits = True # don't allow combatants to walk away
def at_cmdset_creation(self):
self.add(CmdTwitchAttack())
self.add(CmdLook())
self.add(CmdHelpCombat())
self.add(CmdHold())
self.add(CmdStunt())
self.add(CmdUseItem())
self.add(CmdWield())
self.add(CmdUseFlee())
self.add(CmdFlee())
class CmdTwitchAttack(_CmdCombatBase):
@ -852,47 +918,71 @@ class CmdTwitchAttack(_CmdCombatBase):
self.msg(f"{target.get_display_name(self.caller)} is already down.")
return
# this can be done over and over
if target.is_pc and not target.location.allow_pvp:
self.msg("PvP combat is not allowed here!")
return
# add combatants to combathandler. this can be done safely over and over
is_new = self.combathandler.add_combatant(self.caller)
if is_new:
# just joined combat - add the combat cmdset
self.caller.cmdset.add(CombatCmdSet)
self.caller.cmdset.add(TwitchCombatCmdSet, persistent=True)
self.msg(_COMBAT_HELP)
is_new = self.combathandler.add_combatant(target)
if is_new and target.is_pc:
# a pvp battle
target.cmdset.add(TwitchCombatCmdSet, persistent=True)
target.msg(_COMBAT_HELP)
self.combathandler.queue_action(self.caller, {"key": "attack", "target": target})
self.msg("You prepare to attack!")
self.combathandler.start_combat()
self.msg(f"You attack {target.get_display_name(self.caller)}!")
class CmdLook(default_cmds.CmdLook):
key = "look"
aliases = ["l"]
template = """
|c{room_name} |r(In Combat!)|n
{room_desc}
{combat_summary}
""".strip()
def func(self):
if not self.args:
# when looking around with no argument, show the room description followed by the
# current combat state.
location = self.caller.location
combathandler = get_or_create_combathandler(self.caller)
self.caller.msg(
self.template.format(
room_name=location.get_display_name(self.caller),
room_desc=caller.at_look(location),
combat_summary=combathandler.get_combat_summary(self.caller),
)
)
combathandler = get_or_create_combathandler(self.caller.location)
txt = str(combathandler.get_combat_summary(self.caller))
maxwidth = max(display_len(line) for line in txt.strip().split("\n"))
self.msg(f"|r{pad(' Combat Status ', width=maxwidth, fillchar='-')}|n\n{txt}")
else:
# use regular look to look at things
super().func()
class CmdHelpCombat(_CmdCombatBase):
"""
Re-show the combat command summary.
Usage:
help combat
"""
key = "help combat"
def func(self):
self.msg(_COMBAT_HELP)
class CmdHold(_CmdCombatBase):
"""
Hold back your blows, doing nothing.
Usage:
hold
"""
key = "hold"
def func(self):
self.combathandler.queue_action(self.caller, {"key": "hold"})
self.msg("You hold, doing nothing.")
class CmdStunt(_CmdCombatBase):
"""
Perform a combat stunt, that boosts an ally against a target, or
@ -923,26 +1013,46 @@ class CmdStunt(_CmdCombatBase):
def parse(self):
super().parse()
args = self.args
if not args:
self.msg("Usage: [ability] of <recipient> vs <target>")
raise InterruptCommand()
if "of" in args:
self.stunt_type, args = (part.strip() for part in args.split("of", 1))
else:
self.stunt_type, args = (part.strip() for part in args.split(None, 1))
# convert stunt-type to an Ability, like Ability.STR etc
if not self.stunt_type in ABILITY_REVERSE_MAP:
self.msg("That's not a valid ability.")
raise InterruptCommand()
self.stunt_type = ABILITY_REVERSE_MAP[self.stunt_type]
if " vs " in args:
self.recipient, self.target = (part.strip() for part in args.split(" vs "))
elif self.cmdname == "foil":
self.recipient, self.target = "me", args.strip()
else:
self.recipient, self.target = args.strip(), "me"
self.advantage = self.cmdname == "boost"
self.advantage = self.cmdname != "foil"
def func(self):
combathandler = self.combathandler
target = self.caller.search(self.target, candidates=combathandler.combatants.keys())
if not target:
return
recipient = self.caller.search(self.recipient, candidates=combathandler.combatants.keys())
if not recipient:
return
self.combathandler.queue_action(
self.caller,
{
"key": "stunt",
"recipient": self.recipient,
"target": self.target,
"recipient": recipient,
"target": target,
"advantage": self.advantage,
"stunt_type": self.stunt_type,
"defense_type": self.stunt_type,
@ -973,7 +1083,10 @@ class CmdUseItem(_CmdCombatBase):
super().parse()
args = self.args
if "on" in args:
if not args:
self.msg("What do you want to use?")
raise InterruptCommand()
elif "on" in args:
self.item, self.target = (part.strip() for part in args.split("on", 1))
else:
self.item, *target = args.split(None, 1)
@ -1017,6 +1130,12 @@ class CmdWield(_CmdCombatBase):
key = "wield"
help_category = "combat"
def parse(self):
if not self.args:
self.msg("What do you want to wield?")
raise InterruptCommand()
super().parse()
def func(self):
item = self.caller.search(
@ -1070,7 +1189,7 @@ class TwitchAttackCmdSet(CmdSet):
def _get_combathandler(caller):
evmenu = caller.ndb._evmenu
if not hasattr(evmenu, "combathandler"):
evmenu.combathandler = get_or_create_combathandler(caller)
evmenu.combathandler = get_or_create_combathandler(caller.location)
return evmenu.combathandler
@ -1330,8 +1449,8 @@ def node_combat(caller, raw_string, **kwargs):
"goto": (_queue_action, {"flee": {"key": "flee"}}),
},
{
"desc": "do nothing",
"goto": (_queue_action, {"action_dict": {"key": "nothing"}}),
"desc": "hold, doing nothing",
"goto": (_queue_action, {"action_dict": {"key": "hold"}}),
},
]
@ -1364,7 +1483,7 @@ class CmdTurnAttack(Command):
if not target:
return
combathandler = get_or_create_combathandler(self.caller, combat_tick=30)
combathandler = get_or_create_combathandler(self.caller.location, combat_tick=30)
combathandler.add_combatant(self.caller)
# build and start the menu

View file

@ -6,6 +6,7 @@ from random import choice
from evennia import DefaultCharacter
from evennia.typeclasses.attributes import AttributeProperty
from evennia.typeclasses.tags import TagProperty
from evennia.utils.evmenu import EvMenu
from evennia.utils.utils import make_iter
@ -53,6 +54,9 @@ class EvAdventureNPC(LivingMixin, DefaultCharacter):
weapon = AttributeProperty(default=WeaponEmptyHand, autocreate=False) # instead of inventory
coins = AttributeProperty(default=1, autocreate=False) # coin loot
# if this npc is attacked, everyone with the same tag in the current location will also be pulled into combat.
group = TagProperty("npcs")
@property
def strength(self):
return self.hit_dice
@ -88,7 +92,7 @@ class EvAdventureNPC(LivingMixin, DefaultCharacter):
"""
self.hp = self.hp_max
def ai_combat_next_action(self):
def ai_combat_next_action(self, **kwargs):
"""
The combat engine should ask this method in order to
get the next action the npc should perform in combat.

View file

@ -177,9 +177,30 @@ class EvAdventureWeapon(EvAdventureObject):
defense_type = AttributeProperty(Ability.ARMOR)
damage_roll = AttributeProperty("1d6")
def get_display_name(self, looker=None, **kwargs):
quality = self.quality
quality_txt = ""
if quality <= 0:
quality_txt = "|r(broken!)|n"
elif quality < 2:
quality_txt = "|y(damaged)|n"
elif quality < 3:
quality_txt = "|Y(chipped)|n"
return super().get_display_name(looker=looker, **kwargs) + quality_txt
def at_pre_use(self, user, *args, **kwargs):
if self.quality <= 0:
user.msg(f"{self.get_display_name(user)} is broken and can't be used!")
return False
return super().at_pre_use(user, *args, **kwargs)
def use(self, attacker, target, *args, advantage=False, disadvantage=False, **kwargs):
"""When a weapon is used, it attacks an opponent"""
location = attacker.location
is_hit, quality, txt = rules.dice.opposed_saving_throw(
attacker,
target,
@ -188,7 +209,11 @@ class EvAdventureWeapon(EvAdventureObject):
advantage=advantage,
disadvantage=disadvantage,
)
self.msg(f"$You() $conj(attack) $You({target.key}) with {self.key}: {txt}")
location.msg_contents(
f"$You() $conj(attack) $You({target.key}) with {self.key}: {txt}",
from_obj=attacker,
mapping={target.key: target},
)
if is_hit:
# enemy hit, calculate damage
dmg = rules.dice.roll(self.damage_roll)
@ -201,8 +226,8 @@ class EvAdventureWeapon(EvAdventureObject):
)
else:
message = f" $You() $conj(hit) $You({target.key}) for |r{dmg}|n damage!"
self.msg(message)
location.msg_contents(message, from_obj=attacker, mapping={target.key: target})
# call hook
target.at_damage(dmg, attacker=attacker)
@ -212,7 +237,11 @@ class EvAdventureWeapon(EvAdventureObject):
if quality is Ability.CRITICAL_FAILURE:
self.quality -= 1
message += ".. it's a |rcritical miss!|n, damaging the weapon."
self.msg(message)
location.msg_contents(message, from_obj=attacker, mapping={target.key: target})
def at_post_use(self, user, *args, **kwargs):
if self.quality <= 0:
user.msg(f"|r{self.get_display_name(user)} breaks and can no longer be used!")
class EvAdventureThrowable(EvAdventureWeapon, EvAdventureConsumable):

View file

@ -157,13 +157,13 @@ class EvAdventureRollEngine:
bontxt = f"(+{bonus})"
modtxt = ""
if modifier:
modtxt = f" + {modifier}" if modifier > 0 else f" - {abs(modifier)}"
modtxt = f"+ {modifier}" if modifier > 0 else f" - {abs(modifier)}"
qualtxt = f" ({quality.value}!)" if quality else ""
txt = (
f"rolled {dice_roll} on {rolltxt} "
f" rolled {dice_roll} on {rolltxt} "
f"+ {bonus_type.value}{bontxt}{modtxt} vs "
f"{target} -> |w{result}{qualtxt}|n"
f"{target} -> |w{'|GSuccess|w' if result else '|RFail|w'}{qualtxt}|n"
)
return (dice_roll + bonus + modifier) > target, quality, txt
@ -332,9 +332,11 @@ class EvAdventureRollEngine:
setattr(character, abi, current_abi)
character.msg(
"~" * 78 + "\n|yYou survive your brush with death, "
"~" * 78
+ "\n|yYou survive your brush with death, "
f"but are |r{result.upper()}|y and permanently |rlose {loss} {abi}|y.|n\n"
f"|GYou recover |g{new_hp}|G health|.\n" + "~" * 78
f"|GYou recover |g{new_hp}|G health|.\n"
+ "~" * 78
)

View file

@ -62,12 +62,12 @@ class EvAdventureCombatHandlerTest(BaseEvenniaTest):
# add target to combat
self.combathandler.add_combatant(self.target)
def _get_action(self, action_dict={"key": "nothing"}):
def _get_action(self, action_dict={"key": "hold"}):
action_class = self.combathandler.action_classes[action_dict["key"]]
return action_class(self.combathandler, self.combatant, action_dict)
def _run_actions(
self, action_dict, action_dict2={"key": "nothing"}, combatant_msg=None, target_msg=None
self, action_dict, action_dict2={"key": "hold"}, combatant_msg=None, target_msg=None
):
"""
Helper method to run an action and check so combatant saw the expected message.
@ -90,7 +90,7 @@ class EvAdventureCombatHandlerTest(BaseEvenniaTest):
self.assertEqual(
dict(chandler.action_classes),
{
"nothing": combat.CombatActionDoNothing,
"hold": combat.CombatActionHold,
"attack": combat.CombatActionAttack,
"stunt": combat.CombatActionStunt,
"use": combat.CombatActionUseItem,
@ -176,31 +176,31 @@ class EvAdventureCombatHandlerTest(BaseEvenniaTest):
def test_queue_and_execute_action(self):
"""Queue actions and execute"""
donothing = {"key": "nothing"}
hold = {"key": "hold"}
self.combathandler.queue_action(self.combatant, donothing)
self.combathandler.queue_action(self.combatant, hold)
self.assertEqual(
dict(self.combathandler.combatants),
{self.combatant: deque([donothing]), self.target: deque()},
{self.combatant: deque([hold]), self.target: deque()},
)
mock_action = Mock()
self.combathandler.action_classes["nothing"] = Mock(return_value=mock_action)
self.combathandler.action_classes["hold"] = Mock(return_value=mock_action)
self.combathandler.execute_next_action(self.combatant)
self.combathandler.action_classes["nothing"].assert_called_with(
self.combathandler, self.combatant, donothing
self.combathandler.action_classes["hold"].assert_called_with(
self.combathandler, self.combatant, hold
)
mock_action.execute.assert_called_once()
def test_execute_full_turn(self):
"""Run a full (passive) turn"""
donothing = {"key": "nothing"}
hold = {"key": "hold"}
self.combathandler.queue_action(self.combatant, donothing)
self.combathandler.queue_action(self.target, donothing)
self.combathandler.queue_action(self.combatant, hold)
self.combathandler.queue_action(self.target, hold)
self.combathandler.execute_next_action = Mock()
@ -216,7 +216,7 @@ class EvAdventureCombatHandlerTest(BaseEvenniaTest):
combatant = self.combatant
target = self.target
action = self._get_action({"key": "nothing"})
action = self._get_action({"key": "hold"})
self.assertTrue(action.can_use())
@ -235,10 +235,10 @@ class EvAdventureCombatHandlerTest(BaseEvenniaTest):
action.msg(f"$You() attack $You({target.key}).")
combatant.msg.assert_called_with(text=("You attack testmonster.", {}), from_obj=combatant)
def test_action__do_nothing(self):
"""Do nothing"""
def test_action__hold(self):
"""Hold, doing nothing"""
actiondict = {"key": "nothing"}
actiondict = {"key": "hold"}
self._run_actions(actiondict, actiondict)
self.assertEqual(self.combathandler.turn, 1)
@ -417,7 +417,7 @@ class EvAdventureCombatHandlerTest(BaseEvenniaTest):
from_obj=self.combatant,
)
# Check that enemies have advantage against you now
action = combat.CombatAction(self.combathandler, self.target, {"key": "nothing"})
action = combat.CombatAction(self.combathandler, self.target, {"key": "hold"})
self.assertTrue(action.has_advantage(self.target, self.combatant))
# second flee should remove combatant

View file

@ -34,13 +34,12 @@ from django.core.validators import validate_email as django_validate_email
from django.utils import timezone
from django.utils.html import strip_tags
from django.utils.translation import gettext as _
from evennia.utils import logger
from simpleeval import simple_eval
from twisted.internet import reactor, threads
from twisted.internet.defer import returnValue # noqa - used as import target
from twisted.internet.task import deferLater
from evennia.utils import logger
_MULTIMATCH_TEMPLATE = settings.SEARCH_MULTIMATCH_TEMPLATE
_EVENNIA_DIR = settings.EVENNIA_DIR
_GAME_DIR = settings.GAME_DIR