mirror of
https://github.com/evennia/evennia.git
synced 2026-03-16 21:06:30 +01:00
More fixes to evadventure
This commit is contained in:
parent
ab2e84a40f
commit
39bf20c8f2
3 changed files with 114 additions and 22 deletions
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue