From fa4b4b01b1a8b5826ab33963440798b64bc1db3a Mon Sep 17 00:00:00 2001 From: Griatch Date: Sat, 9 Apr 2022 10:42:01 +0200 Subject: [PATCH] More evadventure fixes --- .../tutorials/evadventure/combat_turnbased.py | 96 ++++++++++++++----- evennia/game_template/typeclasses/channels.py | 2 +- 2 files changed, 73 insertions(+), 25 deletions(-) diff --git a/evennia/contrib/tutorials/evadventure/combat_turnbased.py b/evennia/contrib/tutorials/evadventure/combat_turnbased.py index 6da926fc91..8c1972df80 100644 --- a/evennia/contrib/tutorials/evadventure/combat_turnbased.py +++ b/evennia/contrib/tutorials/evadventure/combat_turnbased.py @@ -48,14 +48,52 @@ class CombatAction: """ key = 'action' + help_text = "Combat action to perform." + # action to echo to everyone. post_action_text = "{combatant} performed an action." - + optimal_range = 0 + # None for unlimited + max_uses = None + suboptimal_range = 1 # move actions can be combined with other actions is_move_action = False def __init__(self, combathandler, combatant): self.combathandler = combathandler self.combatant = combatant + self.uses = 0 + + def msg(self, message, broadcast=False): + if broadcast: + # send to everyone in combat. + self.combathandler.msg(message) + else: + # send only to the combatant. + self.combatant.msg(message) + + def get_help(self): + return "" + + def check_distance(self, distance, optimal_range=None, suboptimal_range=None): + """Call to easily check and warn for out-of-bound distance""" + + if optimal_range is None: + optimal_range = self.optimal_range + if suboptimal_range is None: + suboptimal_range = self.suboptimal_range + + if distance not in (self.suboptimal_distance, self.optimal_distance): + # if we are neither at optimal nor suboptimal distance, we can't do the stunt + # from here. + self.msg(f"|rYou can't perform {self.key} from {range_names[distance]} distance " + "(must be {range_names[suboptimal_distance]} or, even better, " + "{range_names[optimal_distance]}).|n") + return False + elif self.distance == self.suboptimal_distance: + self.msg(f"|yNote: Performing {self.key} from {range_names[distance]} works, but " + f"the optimal range is {range_names[optimal_range]} (you'll " + "act with disadvantage).") + return True def can_use(self, combatant, *args, **kwargs): """ @@ -71,13 +109,16 @@ class CombatAction: if available, should describe what the action does. """ - return True + return True if self.uses is None else self.uses < self.max_uses - def use(self, *args, **kwargs): - """ - Use action + def pre_perform(self, *args, **kwargs): + pass - """ + def perform(self, *args, **kwargs): + pass + + def post_perform(self, *args, **kwargs): + self.uses += 1 self.combathandler.msg(self.post_action_text.format(combatant=combatant)) @@ -86,9 +127,11 @@ class CombatActionDoNothing(CombatAction): Do nothing this turn. """ + help_text = "Hold you position, doing nothing." post_action_text = "{combatant} does nothing this turn." + class CombatActionStunt(CombatAction): """ Perform a stunt. @@ -96,28 +139,32 @@ class CombatActionStunt(CombatAction): """ optimal_distance = 0 suboptimal_distance = 1 - advantage = True + give_advantage = True + give_disadvantage = False + uses = 1 attack_type = "dexterity" defense_type = "dexterity" + help_text = ("Perform a stunt against a target. This will give you or an ally advantage " + "on your next action against the same target [range 0-1, one use per combat. " + "Bonus lasts for two turns].") - def can_use(self, combatant, defender, *args, **kwargs): - distance = self.combathandler.distance_matrix[attacker][defender] - - disadvantage = False - if self.suboptimal_distance == distance: - # stunts need to be within range - disadvantage = True - elif self.optimal_distance != distance: - # if we are neither at optimal nor suboptimal distance, we can't do the stunt - # from here. - return False, (f"you can't perform this stunt " - f"from {range_names[distance]} distance (must be " - f"{range_names[suboptimal_distance]} or, even better, " - f"{range_names[optimal_distance]}).") - - + def perform(self, attacker, defender, *args, beneficiary=None, **kwargs): + # quality doesn't matter for stunts, they are either successful or not + is_success, _ = rules.EvAdventureRollEngine.opposed_saving_throw( + attacker, defender, + attack_type=self.attack_type, + defense_type=self.defense_type, + advantage=False, disadvantage=disadvantage, + ) + if is_success: + beneficiary = beneficiary if beneficiary else attacker + if advantage: + self.gain_advantage(beneficiary, defender) + else: + self.gain_disadvantage(defender, beneficiary) + self.msg class EvAdventureCombatHandler(DefaultScript): @@ -300,7 +347,7 @@ class EvAdventureCombatHandler(DefaultScript): table.add_row(f"You ({combatant.hp} / {combatant.hp_max} health)") - dist_template = "|x(You)__{0}|x__{1}|x___{2}|x____{3}|x_____{4} |x({distname})" + dist_template = "|x(You)__{0}|x__{1}|x___{2}|x____{3}|x_____|R{4} |x({distname})" for comb in self.combatants: @@ -595,6 +642,7 @@ class EvAdventureCombatHandler(DefaultScript): return is_success + # combat menu def _register_action(caller, raw_string, **kwargs): diff --git a/evennia/game_template/typeclasses/channels.py b/evennia/game_template/typeclasses/channels.py index 241120ebf6..f16e8897dc 100644 --- a/evennia/game_template/typeclasses/channels.py +++ b/evennia/game_template/typeclasses/channels.py @@ -38,7 +38,7 @@ class Channel(DefaultChannel): to accounts that are currently online (optimized for very large sends) Useful hooks: - channel_prefix(msg, emit=False) - how the channel should be + channel_prefix() - how the channel should be prefixed when returning to user. Returns a string format_senders(senders) - should return how to display multiple senders to a channel