Finish Twitch-combat tutorial
This commit is contained in:
parent
d2551aaaa7
commit
5b2e9bd5a1
17 changed files with 1273 additions and 49 deletions
|
|
@ -15,7 +15,7 @@ type self = evennia.contrib.tutorials.evadventure.characters.EvAdventureCharacte
|
|||
|
||||
# assign us the twitch combat cmdset (requires superuser/developer perms)
|
||||
|
||||
py self.cmdset.add("evennia.contrib.tutorials.evadventure.combat_twitch.TwitchAttackCmdSet", persistent=True)
|
||||
py self.cmdset.add("evennia.contrib.tutorials.evadventure.combat_twitch.TwitchCombatCmdSet", persistent=True)
|
||||
|
||||
# Create and give us a weapons (this will use defaults on the class)
|
||||
|
||||
|
|
|
|||
|
|
@ -64,6 +64,13 @@ class LivingMixin:
|
|||
else:
|
||||
self.msg(f"You are healed for {healed} health.")
|
||||
|
||||
def at_attacked(self, attacker, **kwargs):
|
||||
"""
|
||||
Called when being attacked / combat starts.
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
def at_damage(self, damage, attacker=None):
|
||||
"""
|
||||
Called when attacked and taking damage.
|
||||
|
|
|
|||
|
|
@ -204,7 +204,6 @@ class CombatActionStunt(CombatAction):
|
|||
"|yHaving succeeded, you hold back to plan your next move.|n [hold]",
|
||||
broadcast=False,
|
||||
)
|
||||
combathandler.queue_action(attacker, combathandler.fallback_action_dict)
|
||||
else:
|
||||
self.msg(f"$You({defender.key}) $conj(resist)! $You() $conj(fail) the stunt.")
|
||||
|
||||
|
|
@ -240,7 +239,6 @@ class CombatActionUseItem(CombatAction):
|
|||
)
|
||||
item.at_post_use(user, target)
|
||||
# to back to idle after this
|
||||
self.combathandler.queue_action(self.combatant, self.combathandler.fallback_action_dict)
|
||||
|
||||
|
||||
class CombatActionWield(CombatAction):
|
||||
|
|
@ -260,13 +258,12 @@ class CombatActionWield(CombatAction):
|
|||
|
||||
def execute(self):
|
||||
self.combatant.equipment.move(self.item)
|
||||
self.combathandler.queue_action(self.combatant, self.combathandler.fallback_action_dict)
|
||||
|
||||
|
||||
# main combathandler
|
||||
|
||||
|
||||
class EvAdventureCombatHandlerBase(DefaultScript):
|
||||
class EvAdventureCombatBaseHandler(DefaultScript):
|
||||
"""
|
||||
This script is created when a combat starts. It 'ticks' the combat and tracks
|
||||
all sides of it.
|
||||
|
|
@ -481,13 +478,14 @@ class EvAdventureCombatHandlerBase(DefaultScript):
|
|||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def queue_action(self, combatant, action_dict):
|
||||
def queue_action(self, action_dict, combatant=None):
|
||||
"""
|
||||
Queue an action by adding the new actiondict.
|
||||
|
||||
Args:
|
||||
combatant (EvAdventureCharacter, EvAdventureNPC): A combatant queueing the action.
|
||||
action_dict (dict): A dict describing the action class by name along with properties.
|
||||
combatant (EvAdventureCharacter, EvAdventureNPC, optional): A combatant queueing the
|
||||
action.
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ from .combat_base import (
|
|||
CombatActionStunt,
|
||||
CombatActionUseItem,
|
||||
CombatActionWield,
|
||||
EvAdventureCombatHandlerBase,
|
||||
EvAdventureCombatBaseHandler,
|
||||
)
|
||||
from .enums import Ability
|
||||
|
||||
|
|
@ -70,7 +70,7 @@ class CombatActionFlee(CombatAction):
|
|||
)
|
||||
|
||||
|
||||
class EvAdventureTurnbasedCombatHandler(EvAdventureCombatHandlerBase):
|
||||
class EvAdventureTurnbasedCombatHandler(EvAdventureCombatBaseHandler):
|
||||
"""
|
||||
A version of the combathandler, handling turn-based combat.
|
||||
|
||||
|
|
|
|||
|
|
@ -15,12 +15,12 @@ from .combat_base import (
|
|||
CombatActionStunt,
|
||||
CombatActionUseItem,
|
||||
CombatActionWield,
|
||||
EvAdventureCombatHandlerBase,
|
||||
EvAdventureCombatBaseHandler,
|
||||
)
|
||||
from .enums import ABILITY_REVERSE_MAP
|
||||
|
||||
|
||||
class EvAdventureCombatTwitchHandler(EvAdventureCombatHandlerBase):
|
||||
class EvAdventureCombatTwitchHandler(EvAdventureCombatBaseHandler):
|
||||
"""
|
||||
This is created on the combatant when combat starts. It tracks only the combatants
|
||||
side of the combat and handles when the next action will happen.
|
||||
|
|
@ -39,8 +39,8 @@ class EvAdventureCombatTwitchHandler(EvAdventureCombatHandlerBase):
|
|||
|
||||
# dynamic properties
|
||||
|
||||
advantages_against = AttributeProperty(dict)
|
||||
disadvantages_against = AttributeProperty(dict)
|
||||
advantage_against = AttributeProperty(dict)
|
||||
disadvantage_against = AttributeProperty(dict)
|
||||
|
||||
action_dict = AttributeProperty(dict)
|
||||
fallback_action_dict = AttributeProperty({"key": "hold", "dt": 0})
|
||||
|
|
@ -48,7 +48,7 @@ class EvAdventureCombatTwitchHandler(EvAdventureCombatHandlerBase):
|
|||
# stores the current ticker reference, so we can manipulate it later
|
||||
current_ticker_ref = AttributeProperty(None)
|
||||
|
||||
def msg(self, message, broadcast=True):
|
||||
def msg(self, message, broadcast=True, **kwargs):
|
||||
"""
|
||||
Central place for sending messages to combatants. This allows
|
||||
for adding any combat-specific text-decoration in one place.
|
||||
|
|
@ -124,7 +124,7 @@ class EvAdventureCombatTwitchHandler(EvAdventureCombatHandlerBase):
|
|||
some future boost)
|
||||
|
||||
"""
|
||||
self.advantages_against[target] = True
|
||||
self.advantage_against[target] = True
|
||||
|
||||
def give_disadvantage(self, recipient, target):
|
||||
"""
|
||||
|
|
@ -136,7 +136,7 @@ class EvAdventureCombatTwitchHandler(EvAdventureCombatHandlerBase):
|
|||
an enemy.
|
||||
|
||||
"""
|
||||
self.disadvantages_against[target] = True
|
||||
self.disadvantage_against[target] = True
|
||||
|
||||
def has_advantage(self, combatant, target):
|
||||
"""
|
||||
|
|
@ -147,7 +147,7 @@ class EvAdventureCombatTwitchHandler(EvAdventureCombatHandlerBase):
|
|||
target (Character or NPC): The target to check advantage against.
|
||||
|
||||
"""
|
||||
return self.advantages_against.get(target, False)
|
||||
return self.advantage_against.get(target, False)
|
||||
|
||||
def has_disadvantage(self, combatant, target):
|
||||
"""
|
||||
|
|
@ -158,14 +158,15 @@ class EvAdventureCombatTwitchHandler(EvAdventureCombatHandlerBase):
|
|||
target (Character or NPC): The target to check disadvantage against.
|
||||
|
||||
"""
|
||||
return self.disadvantages_against.get(target, False)
|
||||
return self.disadvantage_against.get(target, False)
|
||||
|
||||
def queue_action(self, action_dict):
|
||||
def queue_action(self, action_dict, combatant=None):
|
||||
"""
|
||||
Schedule the next action to fire.
|
||||
|
||||
Args:
|
||||
action_dict (dict): The new action-dict to initialize.
|
||||
combatant: Unused.
|
||||
|
||||
"""
|
||||
if action_dict["key"] not in self.action_classes:
|
||||
|
|
@ -323,7 +324,7 @@ class CmdAttack(_BaseTwitchCombatCommand):
|
|||
combathandler = self.get_or_create_combathandler(target)
|
||||
# we use a fixed dt of 3 here, to mimic Diku style; one could also picture
|
||||
# attacking at a different rate, depending on skills/weapon etc.
|
||||
combathandler.queue_action({"key": "attack", "target": target, "dt": 3})
|
||||
combathandler.queue_action({"key": "attack", "target": target, "dt": 3, "repeat": True})
|
||||
combathandler.msg(f"$You() $conj(attack) $You({target.key})!", self.caller)
|
||||
|
||||
|
||||
|
|
@ -435,8 +436,6 @@ class CmdStunt(_BaseTwitchCombatCommand):
|
|||
self.target = target.strip()
|
||||
|
||||
def func(self):
|
||||
combathandler = self.get_or_create_combathandler(self.target)
|
||||
|
||||
target = self.caller.search(self.target)
|
||||
if not target:
|
||||
return
|
||||
|
|
@ -444,6 +443,8 @@ class CmdStunt(_BaseTwitchCombatCommand):
|
|||
if not recipient:
|
||||
return
|
||||
|
||||
combathandler = self.get_or_create_combathandler(target)
|
||||
|
||||
combathandler.queue_action(
|
||||
{
|
||||
"key": "stunt",
|
||||
|
|
@ -452,6 +453,7 @@ class CmdStunt(_BaseTwitchCombatCommand):
|
|||
"advantage": self.advantage,
|
||||
"stunt_type": self.stunt_type,
|
||||
"defense_type": self.stunt_type,
|
||||
"dt": 3,
|
||||
},
|
||||
)
|
||||
combathandler.msg("$You() prepare a stunt!", self.caller)
|
||||
|
|
@ -498,7 +500,7 @@ class CmdUseItem(_BaseTwitchCombatCommand):
|
|||
return
|
||||
|
||||
combathandler = self.get_or_create_combathandler(self.target)
|
||||
combathandler.queue_action({"key": "use", "item": item, "target": target})
|
||||
combathandler.queue_action({"key": "use", "item": item, "target": target, "dt": 3})
|
||||
combathandler.msg(
|
||||
f"$You() prepare to use {item.get_display_name(self.caller)}!", self.caller
|
||||
)
|
||||
|
|
@ -539,11 +541,11 @@ class CmdWield(_BaseTwitchCombatCommand):
|
|||
self.msg("(You must carry the item to wield it.)")
|
||||
return
|
||||
combathandler = self.get_or_create_combathandler()
|
||||
combathandler.queue_action({"key": "wield", "item": item})
|
||||
combathandler.queue_action({"key": "wield", "item": item, "dt": 3})
|
||||
combathandler.msg(f"$You() reach for {item.get_display_name(self.caller)}!", self.caller)
|
||||
|
||||
|
||||
class TwitchAttackCmdSet(CmdSet):
|
||||
class TwitchCombatCmdSet(CmdSet):
|
||||
"""
|
||||
Add to character, to be able to attack others in a twitch-style way.
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -54,7 +54,8 @@ class EvAdventureNPC(LivingMixin, DefaultCharacter):
|
|||
weapon = AttributeProperty(default=BARE_HANDS, 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.
|
||||
# if this npc is attacked, everyone with the same tag in the current location will also be
|
||||
# pulled into combat.
|
||||
group = TagProperty("npcs")
|
||||
|
||||
@property
|
||||
|
|
@ -91,8 +92,16 @@ class EvAdventureNPC(LivingMixin, DefaultCharacter):
|
|||
|
||||
"""
|
||||
self.hp = self.hp_max
|
||||
self.tags.add("npcs", category="group")
|
||||
|
||||
def ai_combat_next_action(self, **kwargs):
|
||||
def at_attacked(self, attacker, **kwargs):
|
||||
"""
|
||||
Called when being attacked and combat starts.
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
def ai_next_action(self, **kwargs):
|
||||
"""
|
||||
The combat engine should ask this method in order to
|
||||
get the next action the npc should perform in combat.
|
||||
|
|
@ -247,7 +256,7 @@ class EvAdventureMob(EvAdventureNPC):
|
|||
# chance (%) that this enemy will loot you when defeating you
|
||||
loot_chance = AttributeProperty(75, autocreate=False)
|
||||
|
||||
def ai_combat_next_action(self, combathandler):
|
||||
def ai_next_action(self, **kwargs):
|
||||
"""
|
||||
Called to get the next action in combat.
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ class _CombatTestBase(EvenniaTestCase):
|
|||
self.target.msg = Mock()
|
||||
|
||||
|
||||
class TestEvAdventureCombatHandlerBase(_CombatTestBase):
|
||||
class TestEvAdventureCombatBaseHandler(_CombatTestBase):
|
||||
"""
|
||||
Test the base functionality of the base combat handler.
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ class TestEvAdventureCombatHandlerBase(_CombatTestBase):
|
|||
def setUp(self):
|
||||
"""This also tests the `get_or_create_combathandler` classfunc"""
|
||||
super().setUp()
|
||||
self.combathandler = combat_base.EvAdventureCombatHandlerBase.get_or_create_combathandler(
|
||||
self.combathandler = combat_base.EvAdventureCombatBaseHandler.get_or_create_combathandler(
|
||||
self.location, key="combathandler"
|
||||
)
|
||||
|
||||
|
|
@ -109,7 +109,7 @@ class TestCombatActionsBase(_CombatTestBase):
|
|||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.combathandler = combat_base.EvAdventureCombatHandlerBase.get_or_create_combathandler(
|
||||
self.combathandler = combat_base.EvAdventureCombatBaseHandler.get_or_create_combathandler(
|
||||
self.location, key="combathandler"
|
||||
)
|
||||
# we need to mock all NotImplemented methods
|
||||
|
|
@ -552,11 +552,11 @@ class TestEvAdventureTwitchCombatHandler(EvenniaCommandTestMixin, _CombatTestBas
|
|||
|
||||
def test_give_advantage(self):
|
||||
self.combatant_combathandler.give_advantage(self.combatant, self.target)
|
||||
self.assertTrue(self.combatant_combathandler.advantages_against[self.target])
|
||||
self.assertTrue(self.combatant_combathandler.advantage_against[self.target])
|
||||
|
||||
def test_give_disadvantage(self):
|
||||
self.combatant_combathandler.give_disadvantage(self.combatant, self.target)
|
||||
self.assertTrue(self.combatant_combathandler.disadvantages_against[self.target])
|
||||
self.assertTrue(self.combatant_combathandler.disadvantage_against[self.target])
|
||||
|
||||
@patch("evennia.contrib.tutorials.evadventure.combat_twitch.unrepeat", new=Mock())
|
||||
@patch("evennia.contrib.tutorials.evadventure.combat_twitch.repeat", new=Mock(return_value=999))
|
||||
|
|
|
|||
23
evennia/contrib/tutorials/evadventure/tests/test_npcs.py
Normal file
23
evennia/contrib/tutorials/evadventure/tests/test_npcs.py
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
"""
|
||||
Test NPC classes.
|
||||
|
||||
"""
|
||||
|
||||
from evennia import create_object
|
||||
from evennia.utils.test_resources import EvenniaTest
|
||||
|
||||
from .. import npcs
|
||||
|
||||
|
||||
class TestNPCBase(EvenniaTest):
|
||||
def test_npc_base(self):
|
||||
npc = create_object(
|
||||
npcs.EvAdventureNPC,
|
||||
key="TestNPC",
|
||||
attributes=[("hit_dice", 4), ("armor", 1), ("morale", 9)],
|
||||
)
|
||||
|
||||
self.assertEqual(npc.hp_multiplier, 4)
|
||||
self.assertEqual(npc.hp, 16)
|
||||
self.assertEqual(npc.strength, 4)
|
||||
self.assertEqual(npc.charisma, 4)
|
||||
Loading…
Add table
Add a link
Reference in a new issue