More fixes to evadventure

This commit is contained in:
Griatch 2022-04-01 21:38:53 +02:00
parent ab2e84a40f
commit 39bf20c8f2
3 changed files with 114 additions and 22 deletions

View file

@ -328,6 +328,29 @@ class EvAdventureCharacter(DefaultCharacter):
""" """
# TODO # TODO
@property
def hurt_level(self):
"""
String describing how hurt this character is.
"""
percent = max(0, min(100, 100 * (self.hp / self.hp_max)))
if 95 < percent <= 100:
return "|gPerfect|n"
elif 80 < percent <= 95:
return "|gScraped|n"
elif 60 < percent <= 80:
return "|GBruised|n"
elif 45 < percent <= 60:
return "|yHurt|n"
elif 30 < percent <= 45:
return "|yWounded|n"
elif 15 < percent <= 30:
return "|rBadly wounded|n"
elif 1 < percent <= 15:
return "|rBarely hanging on|n"
elif percent == 0:
return "|RCollapsed!|n"
def heal(self, hp, healer=None): def heal(self, hp, healer=None):
""" """
Heal the character by a certain amount of HP. Heal the character by a certain amount of HP.

View file

@ -20,7 +20,7 @@ from collections import defaultdict
from evennia.scripts.scripts import DefaultScript from evennia.scripts.scripts import DefaultScript
from evennia.typeclasses.attributes import AttributeProperty from evennia.typeclasses.attributes import AttributeProperty
from evennia.utils.utils import make_iter from evennia.utils.utils import make_iter
from evennia.utils import evmenu from evennia.utils import evmenu, evtable
from . import rules from . import rules
MIN_RANGE = 0 MIN_RANGE = 0
@ -48,26 +48,37 @@ class CombatAction:
""" """
key = 'action' key = 'action'
status_text = "{combatant} performs an action." post_action_text = "{combatant} performed an action."
# move actions can be combined with other actions # move actions can be combined with other actions
is_move_action = False is_move_action = False
def __init__(self, combathandler): def __init__(self, combathandler, combatant):
self.combathandler = combathandler self.combathandler = combathandler
self.combatant = combatant
def can_use(self, combatant, *args, **kwargs): def can_use(self, combatant, *args, **kwargs):
""" """
Determine if combatant can use this action. Determine if combatant can use this action.
Args:
combatant (Object): The one performing the action.
*args: Any optional arguments.
**kwargs: Any optional keyword arguments.
Returns:
tuple: (bool, motivation) - if not available, will describe why,
if available, should describe what the action does.
""" """
return True return True
def use(self, combatant, *args, **kwargs): def use(self, *args, **kwargs):
""" """
Use action Use action
""" """
self.combathandler.msg(self.status_text.format(combatant=combatant)) self.combathandler.msg(self.post_action_text.format(combatant=combatant))
class CombatActionDoNothing(CombatAction): class CombatActionDoNothing(CombatAction):
@ -75,7 +86,7 @@ class CombatActionDoNothing(CombatAction):
Do nothing this turn. Do nothing this turn.
""" """
status_text = "{combatant} does nothing this turn." post_action_text = "{combatant} does nothing this turn."
class CombatActionStunt(CombatAction): class CombatActionStunt(CombatAction):
@ -83,6 +94,28 @@ class CombatActionStunt(CombatAction):
Perform a stunt. Perform a stunt.
""" """
optimal_distance = 0
suboptimal_distance = 1
advantage = True
attack_type = "dexterity"
defense_type = "dexterity"
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]}).")
@ -111,6 +144,12 @@ class EvAdventureCombatHandler(DefaultScript):
# actions that will be performed before a normal action # actions that will be performed before a normal action
move_actions = ("approach", "withdraw") move_actions = ("approach", "withdraw")
def at_init(self):
self.ndb.actions = {
"do_nothing": CombatActionDoNothing,
}
def _refresh_distance_matrix(self): def _refresh_distance_matrix(self):
""" """
Refresh the distance matrix, either after movement or when a Refresh the distance matrix, either after movement or when a
@ -248,6 +287,37 @@ class EvAdventureCombatHandler(DefaultScript):
self.combatants.remove(combatant) self.combatants.remove(combatant)
self._refresh_distance_matrix() self._refresh_distance_matrix()
def get_combat_summary(self, combatant):
"""
Get a summary of the current combat state.
You (5/10 health)
Foo (Hurt) distance: You__0__1___X____3_____4 (medium)
Bar (Perfect health): You__X__1___2____3_____4 (close)
"""
table = evtable.EvTable(border_width=0)
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})"
for comb in self.combatants:
if comb is combatant:
continue
name = combatant.key
distance = self.distance_matrix[combatant][comb]
dist_map = {i: '|wX' if i == distance else i for i in range(MAX_RANGE)}
dist_map["distname"] = RANGE_NAMES[distance]
health = f"{comb.hurt_level}"
distance_string = dist_template.format(**dist_map)
table.add_row(f"{name} ({health})", distance_string)
return str(table)
def msg(self, message, targets=None): def msg(self, message, targets=None):
""" """
Central place for sending messages to combatants. This allows Central place for sending messages to combatants. This allows
@ -399,10 +469,10 @@ class EvAdventureCombatHandler(DefaultScript):
elif self._get_optimal_distance(attacker) != distance: elif self._get_optimal_distance(attacker) != distance:
# if we are neither at optimal nor suboptimal distance, we can't do the stunt # if we are neither at optimal nor suboptimal distance, we can't do the stunt
# from here. # from here.
raise CombatFailure(f"You can't perform this stunt " raise combatfailure(f"you can't perform this stunt "
f"from {RANGE_NAMES[distance]} distance (must be " f"from {range_names[distance]} distance (must be "
f"{RANGE_NAMES[suboptimal_distance]} or, even better, " f"{range_names[suboptimal_distance]} or, even better, "
f"{RANGE_NAMES[optimal_distance]}).") f"{range_names[optimal_distance]}).")
# quality doesn't matter for stunts, they are either successful or not # quality doesn't matter for stunts, they are either successful or not
is_success, _ = rules.EvAdventureRollEngine.opposed_saving_throw( is_success, _ = rules.EvAdventureRollEngine.opposed_saving_throw(
attacker, defender, attacker, defender,
@ -582,7 +652,6 @@ def node_select_action(caller, raw_string, **kwargs):
text = combat.get_previous_turn_status(caller) text = combat.get_previous_turn_status(caller)
options = combat.get_available_options(caller) options = combat.get_available_options(caller)
# TODO - reshuffle options
options = { options = {
"desc": action, "desc": action,