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 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.
```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
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
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
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
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.
```python
@ -1148,7 +1148,7 @@ class 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.
value = (base + mod) * mult
@ -1189,7 +1189,7 @@ class StaticTrait(Trait):
def mult(self):
"""The trait's multiplier."""
return self._data["mult"]
@mult.setter
def mult(self, amount):
if type(amount) in (int, float):
@ -1378,7 +1378,7 @@ class CounterTrait(Trait):
@property
def mult(self):
return self._data["mult"]
@mult.setter
def mult(self, amount):
if type(amount) in (int, float):
@ -1596,11 +1596,11 @@ class GaugeTrait(CounterTrait):
if value + self.base < self.min:
value = self.min - self.base
self._data["mod"] = value
@property
def mult(self):
return self._data["mult"]
@mult.setter
def mult(self, amount):
if type(amount) in (int, float):

View file

@ -328,6 +328,29 @@ class EvAdventureCharacter(DefaultCharacter):
"""
# 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):
"""
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.typeclasses.attributes import AttributeProperty
from evennia.utils.utils import make_iter
from evennia.utils import evmenu
from evennia.utils import evmenu, evtable
from . import rules
MIN_RANGE = 0
@ -48,26 +48,37 @@ class CombatAction:
"""
key = 'action'
status_text = "{combatant} performs an action."
post_action_text = "{combatant} performed an action."
# move actions can be combined with other actions
is_move_action = False
def __init__(self, combathandler):
def __init__(self, combathandler, combatant):
self.combathandler = combathandler
self.combatant = combatant
def can_use(self, combatant, *args, **kwargs):
"""
Determine if combatant can use this action.
"""
return True
Args:
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
"""
self.combathandler.msg(self.status_text.format(combatant=combatant))
self.combathandler.msg(self.post_action_text.format(combatant=combatant))
class CombatActionDoNothing(CombatAction):
@ -75,7 +86,7 @@ class CombatActionDoNothing(CombatAction):
Do nothing this turn.
"""
status_text = "{combatant} does nothing this turn."
post_action_text = "{combatant} does nothing this turn."
class CombatActionStunt(CombatAction):
@ -83,6 +94,28 @@ class CombatActionStunt(CombatAction):
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
move_actions = ("approach", "withdraw")
def at_init(self):
self.ndb.actions = {
"do_nothing": CombatActionDoNothing,
}
def _refresh_distance_matrix(self):
"""
Refresh the distance matrix, either after movement or when a
@ -248,6 +287,37 @@ class EvAdventureCombatHandler(DefaultScript):
self.combatants.remove(combatant)
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):
"""
Central place for sending messages to combatants. This allows
@ -399,10 +469,10 @@ class EvAdventureCombatHandler(DefaultScript):
elif self._get_optimal_distance(attacker) != distance:
# if we are neither at optimal nor suboptimal distance, we can't do the stunt
# from here.
raise CombatFailure(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]}).")
raise combatfailure(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]}).")
# quality doesn't matter for stunts, they are either successful or not
is_success, _ = rules.EvAdventureRollEngine.opposed_saving_throw(
attacker, defender,
@ -582,7 +652,6 @@ def node_select_action(caller, raw_string, **kwargs):
text = combat.get_previous_turn_status(caller)
options = combat.get_available_options(caller)
# TODO - reshuffle options
options = {
"desc": action,