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 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):
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue