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

@ -175,7 +175,7 @@ if trait1 > trait2:
The static trait has a `base` value and an optional `mod`-ifier and 'mult'-iplier. The static trait has a `base` value and an optional `mod`-ifier and 'mult'-iplier.
The modifier defaults to 0, and the multiplier to 1.0, for no change in value. The modifier defaults to 0, and the multiplier to 1.0, for no change in value.
A typical use of a static trait would be a Strength stat or Skill value. That is, A typical use of a static trait would be a Strength stat or Skill value. That is,
somethingthat varies slowly or not at all, and which may be modified in-place. somethingthat varies slowly or not at all, and which may be modified in-place.
```python ```python
@ -207,9 +207,9 @@ somethingthat varies slowly or not at all, and which may be modified in-place.
A counter describes a value that can move from a base. The `.current` property A counter describes a value that can move from a base. The `.current` property
is the thing usually modified. It starts at the `.base`. One can also add a is the thing usually modified. It starts at the `.base`. One can also add a
modifier, which is added to both the base and to current. '.value' is then formed modifier, which is added to both the base and to current. '.value' is then formed
by multiplying by the multiplier, which defaults to 1.0 for no change. The min/max by multiplying by the multiplier, which defaults to 1.0 for no change. The min/max
of the range are optional, a boundary set to None will remove it. A suggested use of the range are optional, a boundary set to None will remove it. A suggested use
for a Counter Trait would be to track skill values. for a Counter Trait would be to track skill values.
```python ```python
@ -1148,7 +1148,7 @@ class Trait:
class StaticTrait(Trait): class StaticTrait(Trait):
""" """
Static Trait. This is a single value with a modifier, Static Trait. This is a single value with a modifier,
multiplier, and no concept of a 'current' value or min/max etc. multiplier, and no concept of a 'current' value or min/max etc.
value = (base + mod) * mult value = (base + mod) * mult
@ -1189,7 +1189,7 @@ class StaticTrait(Trait):
def mult(self): def mult(self):
"""The trait's multiplier.""" """The trait's multiplier."""
return self._data["mult"] return self._data["mult"]
@mult.setter @mult.setter
def mult(self, amount): def mult(self, amount):
if type(amount) in (int, float): if type(amount) in (int, float):
@ -1378,7 +1378,7 @@ class CounterTrait(Trait):
@property @property
def mult(self): def mult(self):
return self._data["mult"] return self._data["mult"]
@mult.setter @mult.setter
def mult(self, amount): def mult(self, amount):
if type(amount) in (int, float): if type(amount) in (int, float):
@ -1596,11 +1596,11 @@ class GaugeTrait(CounterTrait):
if value + self.base < self.min: if value + self.base < self.min:
value = self.min - self.base value = self.min - self.base
self._data["mod"] = value self._data["mod"] = value
@property @property
def mult(self): def mult(self):
return self._data["mult"] return self._data["mult"]
@mult.setter @mult.setter
def mult(self, amount): def mult(self, amount):
if type(amount) in (int, float): if type(amount) in (int, float):

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:
return True combatant (Object): The one performing the action.
*args: Any optional arguments.
**kwargs: Any optional keyword arguments.
def use(self, combatant, *args, **kwargs): Returns:
tuple: (bool, motivation) - if not available, will describe why,
if available, should describe what the action does.
"""
return True
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,