mirror of
https://github.com/evennia/evennia.git
synced 2026-03-16 21:06:30 +01:00
More work on tech demo area
This commit is contained in:
parent
1ed7ffa095
commit
afadb1001e
16 changed files with 275 additions and 252 deletions
|
|
@ -172,6 +172,8 @@ Up requirements to Django 4.0+, Twisted 22+, Python 3.9 or 3.10
|
|||
now return `None` instead of `.db.desc` if no sdesc is set; fallback in hook (inspectorCaracal)
|
||||
- Reworked text2html parser to avoid problems with stateful color tags (inspectorCaracal)
|
||||
- Simplified `EvMenu.options_formatter` hook to use `EvColumn` and f-strings (inspectorcaracal)
|
||||
- Allow `# CODE`, `# HEADER` etc as well as `#CODE`/`#HEADER` in batchcode
|
||||
files - this works better with black linting.
|
||||
|
||||
|
||||
## Evennia 0.9.5
|
||||
|
|
|
|||
|
|
@ -4,19 +4,17 @@ The base Command class.
|
|||
All commands in Evennia inherit from the 'Command' class in this module.
|
||||
|
||||
"""
|
||||
import re
|
||||
import math
|
||||
import inspect
|
||||
import math
|
||||
import re
|
||||
|
||||
from django.conf import settings
|
||||
from django.urls import reverse
|
||||
from django.utils.text import slugify
|
||||
|
||||
from evennia.locks.lockhandler import LockHandler
|
||||
from evennia.utils.utils import is_iter, fill, lazy_property, make_iter
|
||||
from evennia.utils.evtable import EvTable
|
||||
from evennia.utils.ansi import ANSIString
|
||||
|
||||
from evennia.utils.evtable import EvTable
|
||||
from evennia.utils.utils import fill, is_iter, lazy_property, make_iter
|
||||
|
||||
CMD_IGNORE_PREFIXES = settings.CMD_IGNORE_PREFIXES
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
"""
|
||||
EvAdventure - a complete game in Evennia.
|
||||
|
||||
This is an implementation of, and reference code to, the game created in the
|
||||
documentation's beginner tutorial.
|
||||
|
||||
"""
|
||||
|
|
@ -15,45 +15,69 @@ You can also build/rebuild individiaul #CODE blocks in the `batchcode/interactiv
|
|||
|
||||
"""
|
||||
|
||||
#HEADER
|
||||
# HEADER
|
||||
|
||||
# this is loaded at the top of every #CODE block
|
||||
|
||||
from evennia import create_object, search_object
|
||||
from evennia import DefaultExit
|
||||
from evennia import DefaultExit, create_object, search_object
|
||||
from evennia.contrib.tutorials import evadventure
|
||||
from evennia.contrib.tutorials.evadventure.objects import (
|
||||
EvAdventureObject, EvAdventureRunestone, EvAdventureRunestone, EvAdventureConsumable,
|
||||
EvAdventureObjectFiller)
|
||||
from evennia.contrib.tutorials.evadventure.rooms import EvAdventureRoom
|
||||
from evennia.contrib.tutorials.evadventure.combat_turnbasedA import EvAdventureCombatHandler
|
||||
from evennia.contrib.tutorials.evadventure import npcs
|
||||
from evennia.contrib.tutorials.evadventure.combat_turnbased import EvAdventureCombatHandler
|
||||
from evennia.contrib.tutorials.evadventure.objects import (
|
||||
EvAdventureConsumable,
|
||||
EvAdventureObject,
|
||||
EvAdventureObjectFiller,
|
||||
EvAdventureRunestone,
|
||||
)
|
||||
from evennia.contrib.tutorials.evadventure.rooms import EvAdventureRoom
|
||||
|
||||
#CODE
|
||||
# CODE
|
||||
|
||||
# Hub room evtechdemo#00
|
||||
# for other test areas to link back to. Connects in turn back to Limbo.
|
||||
|
||||
limbo = search_object("Limbo")
|
||||
hub_room = create_object(EvAdventureRoom, key="Techdemo Hub", aliases=("evtechdemo#00",),
|
||||
attributes=[("desc", "Central hub for EvAdventure tech demo.")])
|
||||
create_object(DefaultExit, key="EvAdventure Techdemo", aliases=("techdemo",),
|
||||
location=limbo, destination=hub_room)
|
||||
create_object(DefaultExit, key="Back to Limbo", aliases=("limbo", "back"),
|
||||
location=hub_room, destination=limbo)
|
||||
limbo = search_object("Limbo")[0]
|
||||
hub_room = create_object(
|
||||
EvAdventureRoom,
|
||||
key="Techdemo Hub",
|
||||
aliases=("evtechdemo#00",),
|
||||
attributes=[("desc", "Central hub for EvAdventure tech demo.")],
|
||||
)
|
||||
create_object(
|
||||
DefaultExit,
|
||||
key="EvAdventure Techdemo",
|
||||
aliases=("techdemo", "demo", "evadventure"),
|
||||
location=limbo,
|
||||
destination=hub_room,
|
||||
)
|
||||
create_object(
|
||||
DefaultExit,
|
||||
key="Back to Limbo",
|
||||
aliases=("limbo", "back"),
|
||||
location=hub_room,
|
||||
destination=limbo,
|
||||
)
|
||||
|
||||
|
||||
#CODE
|
||||
# CODE
|
||||
|
||||
# A combat room evtechdemo#01
|
||||
# with a static enemy
|
||||
|
||||
combat_room = create_object(EvAdventureRoom, key="Combat Arena", aliases=("evtechdemo#01",))
|
||||
combat_room_enemy = create_object(npcs.EvadventureMob, key="Training Dummy")
|
||||
combat_room_enemy = create_object(
|
||||
npcs.EvadventureMob, key="Training Dummy", aliases=("dummy",), location=combat_room
|
||||
)
|
||||
|
||||
# link to/back to hub
|
||||
hub_room = search_object("evtechdemo#00")
|
||||
create_object(DefaultExit, key="Back to Hub", aliases=("back", "hub"),
|
||||
location=combat_room, destination=hub_room)
|
||||
create_object(DefaultExit, key="combat test", aliases=("combat"),
|
||||
location=combat_room, destination=hub_room)
|
||||
hub_room = search_object("evtechdemo#00")[0]
|
||||
create_object(
|
||||
DefaultExit, key="combat test", aliases=("combat",), location=hub_room, destination=combat_room
|
||||
)
|
||||
create_object(
|
||||
DefaultExit,
|
||||
key="Back to Hub",
|
||||
aliases=("back", "hub"),
|
||||
location=combat_room,
|
||||
destination=hub_room,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,10 +5,11 @@ Base Character and NPCs.
|
|||
|
||||
from evennia.objects.objects import DefaultCharacter, DefaultObject
|
||||
from evennia.typeclasses.attributes import AttributeProperty
|
||||
from evennia.utils.utils import lazy_property, int2str
|
||||
from .objects import EvAdventureObject
|
||||
from evennia.utils.utils import int2str, lazy_property
|
||||
|
||||
from . import rules
|
||||
from .enums import Ability, WieldLocation
|
||||
from .objects import EvAdventureObject
|
||||
|
||||
|
||||
class EquipmentError(TypeError):
|
||||
|
|
@ -290,11 +291,12 @@ class EquipmentHandler:
|
|||
in the list after all).
|
||||
|
||||
"""
|
||||
return [obj for obj in slots[WieldLocation.BACKPACK]
|
||||
if obj.inventory_use_slot in (
|
||||
WieldLocation.WEAPON_HAND,
|
||||
WieldLocation.TWO_HANDS,
|
||||
WieldLocation.SHIELD_HAND)]
|
||||
return [
|
||||
obj
|
||||
for obj in slots[WieldLocation.BACKPACK]
|
||||
if obj.inventory_use_slot
|
||||
in (WieldLocation.WEAPON_HAND, WieldLocation.TWO_HANDS, WieldLocation.SHIELD_HAND)
|
||||
]
|
||||
|
||||
def get_wearable_objects_from_backpack(self):
|
||||
"""
|
||||
|
|
@ -307,11 +309,11 @@ class EquipmentHandler:
|
|||
in the list after all).
|
||||
|
||||
"""
|
||||
return [obj for obj in slots[WieldLocation.BACKPACK]
|
||||
if obj.inventory_use_slot in (
|
||||
WieldLocation.BODY,
|
||||
WieldLocation.HEAD
|
||||
)]
|
||||
return [
|
||||
obj
|
||||
for obj in slots[WieldLocation.BACKPACK]
|
||||
if obj.inventory_use_slot in (WieldLocation.BODY, WieldLocation.HEAD)
|
||||
]
|
||||
|
||||
def get_usable_objects_from_backpack(self):
|
||||
"""
|
||||
|
|
@ -327,7 +329,7 @@ class EquipmentHandler:
|
|||
|
||||
class LivingMixin:
|
||||
"""
|
||||
Helpers shared between all living things.
|
||||
Mixin class to use for all living things.
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -488,64 +490,3 @@ class EvAdventureCharacter(LivingMixin, DefaultCharacter):
|
|||
Called when character dies.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
class EvAdventureNPC(LivingMixin, DefaultCharacter):
|
||||
"""
|
||||
This is the base class for all non-player entities, including monsters. These
|
||||
generally don't advance in level but uses a simplified, abstract measure of how
|
||||
dangerous or competent they are - the 'hit dice' (HD).
|
||||
|
||||
HD indicates how much health they have and how hard they hit. In _Knave_, HD also
|
||||
defaults to being the bonus for all abilities. HP is 4 x Hit die (this can then be
|
||||
customized per-entity of course).
|
||||
|
||||
Morale is set explicitly per-NPC, usually between 7 and 9.
|
||||
|
||||
Monsters don't use equipment in the way PCs do, instead they have a fixed armor
|
||||
value, and their Abilities are dynamically generated from the HD (hit_dice).
|
||||
|
||||
If wanting monsters or NPCs that can level and work the same as PCs, base them off the
|
||||
EvAdventureCharacter class instead.
|
||||
|
||||
"""
|
||||
|
||||
hit_dice = AttributeProperty(default=1)
|
||||
armor = AttributeProperty(default=11)
|
||||
morale = AttributeProperty(default=9)
|
||||
hp = AttributeProperty(default=8)
|
||||
|
||||
@property
|
||||
def strength(self):
|
||||
return self.hit_dice
|
||||
|
||||
@property
|
||||
def dexterity(self):
|
||||
return self.hit_dice
|
||||
|
||||
@property
|
||||
def constitution(self):
|
||||
return self.hit_dice
|
||||
|
||||
@property
|
||||
def intelligence(self):
|
||||
return self.hit_dice
|
||||
|
||||
@property
|
||||
def wisdom(self):
|
||||
return self.hit_dice
|
||||
|
||||
@property
|
||||
def charisma(self):
|
||||
return self.hit_dice
|
||||
|
||||
@property
|
||||
def hp_max(self):
|
||||
return self.hit_dice * 4
|
||||
|
||||
def at_object_creation(self):
|
||||
"""
|
||||
Start with max health.
|
||||
|
||||
"""
|
||||
self.hp = self.hp_max
|
||||
|
|
|
|||
|
|
@ -99,15 +99,16 @@ Choose who to block:
|
|||
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from collections import defaultdict
|
||||
from datetime import datetime
|
||||
|
||||
from evennia.scripts.scripts import DefaultScript
|
||||
from evennia.typeclasses.attributes import AttributeProperty
|
||||
from evennia.utils import dbserialize, delay, evmenu, evtable
|
||||
from evennia.utils.utils import make_iter
|
||||
from evennia.utils import evtable, dbserialize, delay, evmenu
|
||||
from .enums import Ability
|
||||
from . import rules
|
||||
|
||||
from . import rules
|
||||
from .enums import Ability
|
||||
|
||||
COMBAT_HANDLER_KEY = "evadventure_turnbased_combathandler"
|
||||
COMBAT_HANDLER_INTERVAL = 60
|
||||
|
|
@ -242,7 +243,7 @@ class CombatActionAttack(CombatAction):
|
|||
# figure out disadvantage (gained by enemy stunts/actions)
|
||||
disadvantage = bool(self.combathandler.disadvantage_matrix[attacker].pop(defender, False))
|
||||
|
||||
is_hit, quality = rules.EvAdventureRollEngine.opposed_saving_throw(
|
||||
is_hit, quality = rules.dice.opposed_saving_throw(
|
||||
attacker,
|
||||
defender,
|
||||
attack_type=attacker.weapon.attack_type,
|
||||
|
|
@ -295,9 +296,9 @@ class CombatActionStunt(CombatAction):
|
|||
# quality doesn't matter for stunts, they are either successful or not
|
||||
|
||||
attacker = self.combatant
|
||||
advantage, disadvantage = False
|
||||
advantage, disadvantage = False, False
|
||||
|
||||
is_success, _ = rules.EvAdventureRollEngine.opposed_saving_throw(
|
||||
is_success, _ = rules.dice.opposed_saving_throw(
|
||||
attacker,
|
||||
defender,
|
||||
attack_type=self.attack_type,
|
||||
|
|
@ -333,6 +334,7 @@ class CombatActionUseItem(CombatAction):
|
|||
combat_post_use
|
||||
|
||||
"""
|
||||
|
||||
key = "Use Item"
|
||||
desc = "[U]se item"
|
||||
aliases = ("u", "item", "use item")
|
||||
|
|
@ -406,7 +408,7 @@ class CombatActionBlock(CombatAction):
|
|||
advantage = bool(self.advantage_matrix[combatant].pop(fleeing_target, False))
|
||||
disadvantage = bool(self.disadvantage_matrix[combatant].pop(fleeing_target, False))
|
||||
|
||||
is_success, _ = rules.EvAdventureRollEngine.opposed_saving_throw(
|
||||
is_success, _ = rules.dice.opposed_saving_throw(
|
||||
combatant,
|
||||
fleeing_target,
|
||||
attack_type=self.attack_type,
|
||||
|
|
@ -427,12 +429,23 @@ class CombatActionSwapWieldedWeaponOrSpell(CombatAction):
|
|||
Swap Wielded weapon or spell.
|
||||
|
||||
"""
|
||||
|
||||
key = "Swap weapon/rune/shield"
|
||||
desc = "Swap currently wielded weapon, shield or spell-rune."
|
||||
aliases = ("s", "swap", "draw", "swap weapon", "draw weapon",
|
||||
"swap rune", "draw rune", "swap spell", "draw spell")
|
||||
help_text = ("Draw a new weapon or spell-rune from your inventory, "
|
||||
"replacing your current loadout")
|
||||
aliases = (
|
||||
"s",
|
||||
"swap",
|
||||
"draw",
|
||||
"swap weapon",
|
||||
"draw weapon",
|
||||
"swap rune",
|
||||
"draw rune",
|
||||
"swap spell",
|
||||
"draw spell",
|
||||
)
|
||||
help_text = (
|
||||
"Draw a new weapon or spell-rune from your inventory, replacing your current loadout"
|
||||
)
|
||||
|
||||
next_menu_node = "node_select_wield_from_inventory"
|
||||
|
||||
|
|
@ -448,6 +461,7 @@ class CombatActionUseItem(CombatAction):
|
|||
Use an item from inventory.
|
||||
|
||||
"""
|
||||
|
||||
key = "Use an item from backpack"
|
||||
desc = "Use an item from your inventory."
|
||||
aliases = ("u", "use", "use item")
|
||||
|
|
@ -543,6 +557,29 @@ class EvAdventureCombatHandler(DefaultScript):
|
|||
self._end_turn()
|
||||
self._start_turn()
|
||||
|
||||
def _init_menu(self, combatant, session=None):
|
||||
"""
|
||||
Make sure combatant is in the menu. This is safe to call on a combatant already in a menu.
|
||||
|
||||
"""
|
||||
if not combatant.ndb._evmenu:
|
||||
# re-joining the menu is useful during testing
|
||||
evmenu.EvMenu(
|
||||
combatant,
|
||||
{
|
||||
"node_wait_start": node_wait_start,
|
||||
"node_select_target": node_select_target,
|
||||
"node_select_action": node_select_action,
|
||||
"node_wait_turn": node_wait_turn,
|
||||
},
|
||||
startnode="node_wait_turn",
|
||||
auto_quit=True,
|
||||
persistent=True,
|
||||
cmdset_mergetype="Union",
|
||||
session=session,
|
||||
combathandler=self, # makes this available as combatant.ndb._evmenu.combathandler
|
||||
)
|
||||
|
||||
def _reset_menu(self):
|
||||
"""
|
||||
Move menu to the action-selection node.
|
||||
|
|
@ -577,10 +614,12 @@ class EvAdventureCombatHandler(DefaultScript):
|
|||
# set -1 for unit tests
|
||||
warning_time = 15
|
||||
self._warn_time_task = delay(
|
||||
self.interval - warning_time, self._warn_time, warning_time)
|
||||
self.interval - warning_time, self._warn_time, warning_time
|
||||
)
|
||||
|
||||
for combatant in self.combatants:
|
||||
# cycle combat menu
|
||||
self._init_menu(combatant)
|
||||
combatant.ndb._evmenu.goto("node_select_action", "")
|
||||
|
||||
def _end_turn(self):
|
||||
|
|
@ -633,12 +672,12 @@ class EvAdventureCombatHandler(DefaultScript):
|
|||
for combatant in self.combatants:
|
||||
new_advantage_matrix[combatant] = {
|
||||
target: set_at_turn
|
||||
for target, set_at_turn in advantage_matrix.items()
|
||||
for target, set_at_turn in advantage_matrix[combatant].items()
|
||||
if set_at_turn > oldest_stunt_age
|
||||
}
|
||||
new_disadvantage_matrix[combatant] = {
|
||||
target: set_at_turn
|
||||
for target, set_at_turn in disadvantage_matrix.items()
|
||||
for target, set_at_turn in disadvantage_matrix[combatant].items()
|
||||
if set_at_turn > oldest_stunt_age
|
||||
}
|
||||
|
||||
|
|
@ -676,23 +715,7 @@ class EvAdventureCombatHandler(DefaultScript):
|
|||
action_class.key: action_class(self, combatant)
|
||||
for action_class in self.default_action_classes + custom_action_classes
|
||||
}
|
||||
|
||||
# start evmenu (menu node definitions at the end of this module)
|
||||
|
||||
evmenu.EvMenu(
|
||||
combatant,
|
||||
{
|
||||
"node_wait_start": node_wait_start,
|
||||
"node_select_target": node_select_target,
|
||||
"node_select_action": node_select_action,
|
||||
"node_wait_turn": node_wait_turn,
|
||||
},
|
||||
startnode="node_wait_turn",
|
||||
auto_quit=False,
|
||||
persistent=True,
|
||||
session=session,
|
||||
combathandler=self # makes this available as combatant.ndb._evmenu.combathandler
|
||||
)
|
||||
self._init_menu(combatant, session=session)
|
||||
|
||||
def remove_combatant(self, combatant):
|
||||
"""
|
||||
|
|
@ -823,9 +846,9 @@ class EvAdventureCombatHandler(DefaultScript):
|
|||
"""
|
||||
weapon_dmg_roll = attacker.weapon.damage_roll
|
||||
|
||||
dmg = rules.EvAdventureRollEngine.roll(weapon_dmg_roll)
|
||||
dmg = rules.dice.roll(weapon_dmg_roll)
|
||||
if critical:
|
||||
dmg += rules.EvAdventureRollEngine.roll(weapon_dmg_roll)
|
||||
dmg += rules.dice.roll(weapon_dmg_roll)
|
||||
|
||||
defender.hp -= dmg
|
||||
|
||||
|
|
@ -834,7 +857,7 @@ class EvAdventureCombatHandler(DefaultScript):
|
|||
|
||||
if defender.hp <= 0:
|
||||
# roll on death table. This may or may not kill you
|
||||
rules.EvAdventureRollEngine.roll_death(self)
|
||||
rules.dice.roll_death(self)
|
||||
|
||||
# tell everyone
|
||||
self.msg(defender.defeat_message(attacker, dmg))
|
||||
|
|
@ -870,8 +893,7 @@ class EvAdventureCombatHandler(DefaultScript):
|
|||
"""
|
||||
# get the instantiated action for this combatant
|
||||
action = self.combatant_actions[combatant].get(
|
||||
action_key,
|
||||
CombatActionDoNothing(self, combatant)
|
||||
action_key, CombatActionDoNothing(self, combatant)
|
||||
)
|
||||
|
||||
# store the action in the queue
|
||||
|
|
@ -912,13 +934,13 @@ def _register_action(caller, raw_string, **kwargs):
|
|||
Register action with handler.
|
||||
|
||||
"""
|
||||
action_key = kwargs.get["action_key"]
|
||||
action_key = kwargs.pop("action_key")
|
||||
action_args = kwargs["action_args"]
|
||||
action_kwargs = kwargs["action_kwargs"]
|
||||
action_target = kwargs.get("action_target")
|
||||
combat_handler = caller._evmenu.combathandler
|
||||
combat_handler.register_action(
|
||||
caller, action_key, action_target, *action_args, **action_kwargs)
|
||||
action_target = kwargs.pop("action_target", None)
|
||||
combat_handler = caller.ndb._evmenu.combathandler
|
||||
print("action_args", action_args, "action_kwargs", action_kwargs)
|
||||
combat_handler.register_action(caller, action_key, action_target, *action_args, **action_kwargs)
|
||||
|
||||
# move into waiting
|
||||
return "node_wait_turn"
|
||||
|
|
@ -930,41 +952,22 @@ def node_select_target(caller, raw_string, **kwargs):
|
|||
with all other actions.
|
||||
|
||||
"""
|
||||
action_key = kwargs.get("action_key")
|
||||
action_args = kwargs.get("action_args")
|
||||
action_kwargs = kwargs.get("action_kwargs")
|
||||
combat = caller.ndb._evmenu.combathandler
|
||||
text = "Select target for |w{action_key}|n."
|
||||
|
||||
# make the apply-self option always the first one, give it key 0
|
||||
kwargs["action_target"] = caller
|
||||
options = [
|
||||
{
|
||||
"key": "0",
|
||||
"desc": "(yourself)",
|
||||
"goto": (_register_action, kwargs)
|
||||
}
|
||||
]
|
||||
options = [{"key": "0", "desc": "(yourself)", "goto": (_register_action, kwargs)}]
|
||||
# filter out ourselves and then make options for everyone else
|
||||
combatants = [combatant for combatant in combat.combatants if combatant is not caller]
|
||||
for combatant in combatants:
|
||||
# automatic menu numbering starts from 1
|
||||
for inum, combatant in enumerate(combatants):
|
||||
kwargs["action_target"] = combatant
|
||||
options.append(
|
||||
{
|
||||
"desc": combatant.key,
|
||||
"goto": (_register_action, kwargs)
|
||||
}
|
||||
{"key": str(inum + 1), "desc": combatant.key, "goto": (_register_action, kwargs)}
|
||||
)
|
||||
|
||||
# add ability to cancel
|
||||
options.append(
|
||||
{
|
||||
"key": "_default",
|
||||
"desc": "(No input to Abort and go back)",
|
||||
"goto": "node_select_action"
|
||||
}
|
||||
)
|
||||
options.append({"key": "_default", "goto": "node_select_action"})
|
||||
|
||||
return text, options
|
||||
|
||||
|
|
@ -981,8 +984,10 @@ def node_select_wield_from_inventory(caller, raw_string, **kwargs):
|
|||
"""
|
||||
combat = caller.ndb._evmenu.combathandler
|
||||
loadout = caller.inventory.display_loadout()
|
||||
text = (f"{loadout}\nSelect weapon, spell or shield to draw. It will swap out "
|
||||
"anything already in the same hand (you can't change armor or helmet in combat).")
|
||||
text = (
|
||||
f"{loadout}\nSelect weapon, spell or shield to draw. It will swap out "
|
||||
"anything already in the same hand (you can't change armor or helmet in combat)."
|
||||
)
|
||||
|
||||
# get a list of all suitable weapons/spells/shields
|
||||
options = []
|
||||
|
|
@ -997,21 +1002,12 @@ def node_select_wield_from_inventory(caller, raw_string, **kwargs):
|
|||
)
|
||||
else:
|
||||
# normally working item
|
||||
kwargs['action_args'] = (obj,)
|
||||
options.append(
|
||||
{
|
||||
"desc": str(obj),
|
||||
"goto": (_register_action, kwargs)
|
||||
}
|
||||
)
|
||||
kwargs["action_args"] = (obj,)
|
||||
options.append({"desc": str(obj), "goto": (_register_action, kwargs)})
|
||||
|
||||
# add ability to cancel
|
||||
options.append(
|
||||
{
|
||||
"key": "_default",
|
||||
"desc": "(No input to Abort and go back)",
|
||||
"goto": "node_select_action"
|
||||
}
|
||||
{"key": "_default", "desc": "(No input to Abort and go back)", "goto": "node_select_action"}
|
||||
)
|
||||
|
||||
return text, options
|
||||
|
|
@ -1038,21 +1034,12 @@ def node_select_use_item_from_inventory(caller, raw_string, **kwargs):
|
|||
)
|
||||
else:
|
||||
# normally working item
|
||||
kwargs['action_args'] = (obj,)
|
||||
options.append(
|
||||
{
|
||||
"desc": str(obj),
|
||||
"goto": (_register_action, kwargs)
|
||||
}
|
||||
)
|
||||
kwargs["action_args"] = (obj,)
|
||||
options.append({"desc": str(obj), "goto": (_register_action, kwargs)})
|
||||
|
||||
# add ability to cancel
|
||||
options.append(
|
||||
{
|
||||
"key": "_default",
|
||||
"desc": "(No input to Abort and go back)",
|
||||
"goto": "node_select_action"
|
||||
}
|
||||
{"key": "_default", "desc": "(No input to Abort and go back)", "goto": "node_select_action"}
|
||||
)
|
||||
|
||||
return text, options
|
||||
|
|
@ -1063,8 +1050,8 @@ def _action_unavailable(caller, raw_string, **kwargs):
|
|||
Selecting an unavailable action.
|
||||
|
||||
"""
|
||||
action_key = kwargs.get["action_key"]
|
||||
caller.msg(f"Action '{action_key}' is currently not available.")
|
||||
action_key = kwargs["action_key"]
|
||||
caller.msg(f"|rAction |w{action_key}|r is currently not available.|n")
|
||||
# go back to previous node
|
||||
return
|
||||
|
||||
|
|
@ -1093,12 +1080,7 @@ def node_select_action(caller, raw_string, **kwargs):
|
|||
{
|
||||
"key": key,
|
||||
"desc": desc,
|
||||
"goto": (
|
||||
_action_unavailable,
|
||||
{
|
||||
"action_key": action.key
|
||||
}
|
||||
)
|
||||
"goto": (_action_unavailable, {"action_key": action.key}),
|
||||
}
|
||||
)
|
||||
elif action.next_menu_node is None:
|
||||
|
|
@ -1113,7 +1095,7 @@ def node_select_action(caller, raw_string, **kwargs):
|
|||
{
|
||||
"action_key": action.key,
|
||||
"action_args": (),
|
||||
"action_kwargs": kwargs,
|
||||
"action_kwargs": {},
|
||||
"action_target": None,
|
||||
},
|
||||
),
|
||||
|
|
@ -1130,7 +1112,8 @@ def node_select_action(caller, raw_string, **kwargs):
|
|||
{
|
||||
"action_key": action.key,
|
||||
"action_args": (),
|
||||
"action_kwargs": kwargs,
|
||||
"action_kwargs": {},
|
||||
"action_target": None,
|
||||
},
|
||||
),
|
||||
}
|
||||
|
|
@ -1139,8 +1122,7 @@ def node_select_action(caller, raw_string, **kwargs):
|
|||
options.append(
|
||||
{
|
||||
"key": "_default",
|
||||
"desc": "(No input to Abort and go back)",
|
||||
"goto": "node_select_action"
|
||||
"goto": "node_select_action",
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -1160,7 +1142,7 @@ def node_wait_turn(caller, raw_string, **kwargs):
|
|||
options = {
|
||||
"key": "_default",
|
||||
"desc": "(next round will start automatically)",
|
||||
"goto": "node_wait_turn"
|
||||
"goto": "node_wait_turn",
|
||||
}
|
||||
return text, options
|
||||
|
||||
|
|
@ -1177,7 +1159,7 @@ def node_wait_start(caller, raw_string, **kwargs):
|
|||
options = {
|
||||
"key": "_default",
|
||||
"desc": "(combat will start automatically)",
|
||||
"goto": "node_wait_start"
|
||||
"goto": "node_wait_start",
|
||||
}
|
||||
return text, options
|
||||
|
||||
|
|
@ -1218,10 +1200,13 @@ def join_combat(caller, *targets, session=None):
|
|||
combathandler = location.scripts.add(EvAdventureCombatHandler, autostart=False)
|
||||
created = True
|
||||
|
||||
if not hasattr(caller, "hp"):
|
||||
raise CombatFailure("You have no hp and so can't attack anyone.")
|
||||
|
||||
# it's safe to add a combatant to the same combat more than once
|
||||
combathandler.add_combatant(caller, session=session)
|
||||
for target in targets:
|
||||
combathandler.add_combatant(target, session=session)
|
||||
combathandler.add_combatant(target)
|
||||
|
||||
if created:
|
||||
combathandler.start_combat()
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
"""
|
||||
EvAdventure commands and cmdsets.
|
||||
|
||||
nextEvAdventure commands and cmdsets.
|
||||
|
||||
"""
|
||||
|
||||
from evennia import Command, default_cmds
|
||||
from . combat_turnbased import join_combat
|
||||
|
||||
from .combat_turnbased import CombatFailure, join_combat
|
||||
|
||||
|
||||
class EvAdventureCommand(Command):
|
||||
|
|
@ -17,6 +17,7 @@ class EvAdventureCommand(Command):
|
|||
where whitespace around the argument(s) are stripped.
|
||||
|
||||
"""
|
||||
|
||||
def parse(self):
|
||||
self.args = self.args.strip()
|
||||
|
||||
|
|
@ -38,6 +39,9 @@ class CmdAttackTurnBased(EvAdventureCommand):
|
|||
|
||||
"""
|
||||
|
||||
key = "attack"
|
||||
aliases = ("hit",)
|
||||
|
||||
def parse(self):
|
||||
super().parse()
|
||||
self.targets = [name.strip() for name in self.args.split(",")]
|
||||
|
|
@ -49,10 +53,15 @@ class CmdAttackTurnBased(EvAdventureCommand):
|
|||
target_objs = []
|
||||
for target in self.targets:
|
||||
target_obj = self.caller.search(target)
|
||||
if target_obj:
|
||||
if not target_obj:
|
||||
# show a warning but don't abort
|
||||
continue
|
||||
target_objs.append(target_obj)
|
||||
|
||||
if target_objs:
|
||||
join_combat(self.caller, *target_objs, session=self.session)
|
||||
try:
|
||||
join_combat(self.caller, *target_objs, session=self.session)
|
||||
except CombatFailure as err:
|
||||
self.caller.msg(f"|r{err}|n")
|
||||
else:
|
||||
self.caller.msg("|rFound noone to attack.|n")
|
||||
|
|
|
|||
|
|
@ -3,15 +3,72 @@ EvAdventure NPCs. This includes both friends and enemies, only separated by thei
|
|||
|
||||
"""
|
||||
|
||||
from .characters import EvAdventureCharacter
|
||||
from evennia import DefaultCharacter
|
||||
from evennia.typeclasses.attributes import AttributeProperty
|
||||
|
||||
class EvAdventureNPC(EvAdventureCharacter):
|
||||
from .characters import LivingMixin
|
||||
|
||||
|
||||
class EvAdventureNPC(LivingMixin, DefaultCharacter):
|
||||
"""
|
||||
Base typeclass for NPCs. They have the features of a Character except
|
||||
they have tooling for AI and for acting as quest-gives and shop-keepers.
|
||||
This is the base class for all non-player entities, including monsters. These
|
||||
generally don't advance in level but uses a simplified, abstract measure of how
|
||||
dangerous or competent they are - the 'hit dice' (HD).
|
||||
|
||||
HD indicates how much health they have and how hard they hit. In _Knave_, HD also
|
||||
defaults to being the bonus for all abilities. HP is 4 x Hit die (this can then be
|
||||
customized per-entity of course).
|
||||
|
||||
Morale is set explicitly per-NPC, usually between 7 and 9.
|
||||
|
||||
Monsters don't use equipment in the way PCs do, instead they have a fixed armor
|
||||
value, and their Abilities are dynamically generated from the HD (hit_dice).
|
||||
|
||||
If wanting monsters or NPCs that can level and work the same as PCs, base them off the
|
||||
EvAdventureCharacter class instead.
|
||||
|
||||
"""
|
||||
|
||||
hit_dice = AttributeProperty(default=1)
|
||||
armor = AttributeProperty(default=11)
|
||||
morale = AttributeProperty(default=9)
|
||||
hp = AttributeProperty(default=8)
|
||||
|
||||
@property
|
||||
def strength(self):
|
||||
return self.hit_dice
|
||||
|
||||
@property
|
||||
def dexterity(self):
|
||||
return self.hit_dice
|
||||
|
||||
@property
|
||||
def constitution(self):
|
||||
return self.hit_dice
|
||||
|
||||
@property
|
||||
def intelligence(self):
|
||||
return self.hit_dice
|
||||
|
||||
@property
|
||||
def wisdom(self):
|
||||
return self.hit_dice
|
||||
|
||||
@property
|
||||
def charisma(self):
|
||||
return self.hit_dice
|
||||
|
||||
@property
|
||||
def hp_max(self):
|
||||
return self.hit_dice * 4
|
||||
|
||||
def at_object_creation(self):
|
||||
"""
|
||||
Start with max health.
|
||||
|
||||
"""
|
||||
self.hp = self.hp_max
|
||||
|
||||
|
||||
class EvAdventureShopKeeper(EvAdventureNPC):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ Tags.
|
|||
from evennia.objects.objects import DefaultObject
|
||||
from evennia.typeclasses.attributes import AttributeProperty
|
||||
|
||||
from .enums import WieldLocation, Ability
|
||||
from .enums import Ability, WieldLocation
|
||||
|
||||
|
||||
class EvAdventureObject(DefaultObject):
|
||||
|
|
@ -44,6 +44,7 @@ class EvAdventureObjectFiller(EvAdventureObject):
|
|||
meaning it's unusable.
|
||||
|
||||
"""
|
||||
|
||||
quality = AttributeProperty(0)
|
||||
|
||||
|
||||
|
|
@ -53,6 +54,7 @@ class EvAdventureConsumable(EvAdventureObject):
|
|||
have a limited usage in this way.
|
||||
|
||||
"""
|
||||
|
||||
inventory_use_slot = AttributeProperty(WieldLocation.BACKPACK)
|
||||
size = AttributeProperty(0.25)
|
||||
uses = AttributeProperty(1)
|
||||
|
|
@ -91,6 +93,7 @@ class EvAdventureRunestone(EvAdventureWeapon):
|
|||
they are quite powerful (and scales with caster level).
|
||||
|
||||
"""
|
||||
|
||||
inventory_use_slot = AttributeProperty(WieldLocation.TWO_HANDS)
|
||||
|
||||
attack_type = AttributeProperty(Ability.INT)
|
||||
|
|
|
|||
|
|
@ -23,14 +23,13 @@ This module presents several singletons to import
|
|||
|
||||
"""
|
||||
from random import randint
|
||||
|
||||
from evennia.utils.evform import EvForm
|
||||
from evennia.utils.evtable import EvTable
|
||||
from .enums import Ability
|
||||
from .random_tables import (
|
||||
character_generation as chargen_table,
|
||||
death_and_dismemberment as death_table,
|
||||
)
|
||||
|
||||
from .enums import Ability
|
||||
from .random_tables import character_generation as chargen_table
|
||||
from .random_tables import death_and_dismemberment as death_table
|
||||
|
||||
# Basic rolls
|
||||
|
||||
|
|
|
|||
|
|
@ -19,8 +19,9 @@ class EvAdventureMixin:
|
|||
def setUp(self):
|
||||
super().setUp()
|
||||
self.location = create.create_object(EvAdventureRoom, key="testroom")
|
||||
self.character = create.create_object(EvAdventureCharacter, key="testchar",
|
||||
location=self.location)
|
||||
self.character = create.create_object(
|
||||
EvAdventureCharacter, key="testchar", location=self.location
|
||||
)
|
||||
self.helmet = create.create_object(
|
||||
EvAdventureObject,
|
||||
key="helmet",
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ class EvAdventureTurnbasedCombatHandlerTest(EvAdventureMixin, BaseEvenniaTest):
|
|||
Test the turn-based combat-handler implementation.
|
||||
|
||||
"""
|
||||
|
||||
maxDiff = None
|
||||
|
||||
@patch(
|
||||
|
|
@ -52,10 +53,7 @@ class EvAdventureTurnbasedCombatHandlerTest(EvAdventureMixin, BaseEvenniaTest):
|
|||
|
||||
self.combathandler.register_action(self.combatant, action.key)
|
||||
|
||||
self.assertEqual(
|
||||
self.combathandler.action_queue[self.combatant],
|
||||
(action, (), {})
|
||||
)
|
||||
self.assertEqual(self.combathandler.action_queue[self.combatant], (action, (), {}))
|
||||
|
||||
action.use = MagicMock()
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ from collections import defaultdict
|
|||
import inflect
|
||||
from django.conf import settings
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from evennia.commands import cmdset
|
||||
from evennia.commands.cmdsethandler import CmdSetHandler
|
||||
from evennia.objects.manager import ObjectManager
|
||||
|
|
@ -22,15 +21,9 @@ from evennia.scripts.scripthandler import ScriptHandler
|
|||
from evennia.typeclasses.attributes import ModelAttributeBackend, NickHandler
|
||||
from evennia.typeclasses.models import TypeclassBase
|
||||
from evennia.utils import ansi, create, funcparser, logger, search
|
||||
from evennia.utils.utils import (
|
||||
class_from_module,
|
||||
is_iter,
|
||||
lazy_property,
|
||||
list_to_string,
|
||||
make_iter,
|
||||
to_str,
|
||||
variable_from_module,
|
||||
)
|
||||
from evennia.utils.utils import (class_from_module, is_iter, lazy_property,
|
||||
list_to_string, make_iter, to_str,
|
||||
variable_from_module)
|
||||
|
||||
_INFLECT = inflect.engine()
|
||||
_MULTISESSION_MODE = settings.MULTISESSION_MODE
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
from evennia.utils.test_resources import BaseEvenniaTest, EvenniaTestCase
|
||||
from evennia import DefaultObject, DefaultCharacter, DefaultRoom, DefaultExit
|
||||
from evennia.typeclasses.attributes import AttributeProperty
|
||||
from evennia.typeclasses.tags import TagProperty, AliasProperty, PermissionProperty
|
||||
from evennia import DefaultCharacter, DefaultExit, DefaultObject, DefaultRoom
|
||||
from evennia.objects.models import ObjectDB
|
||||
from evennia.objects.objects import DefaultObject
|
||||
from evennia.typeclasses.attributes import AttributeProperty
|
||||
from evennia.typeclasses.tags import (AliasProperty, PermissionProperty,
|
||||
TagProperty)
|
||||
from evennia.utils import create
|
||||
from evennia.utils.test_resources import BaseEvenniaTest, EvenniaTestCase
|
||||
|
||||
|
||||
class DefaultObjectTest(BaseEvenniaTest):
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ class ScriptHandler(object):
|
|||
"""
|
||||
return ScriptDB.objects.get_all_scripts_on_obj(self.obj, key=key)
|
||||
|
||||
def delete(self, key=None):
|
||||
def remove(self, key=None):
|
||||
"""
|
||||
Forcibly delete a script from this object.
|
||||
|
||||
|
|
@ -149,7 +149,8 @@ class ScriptHandler(object):
|
|||
num += 1
|
||||
return num
|
||||
|
||||
# alias to delete
|
||||
# legacy aliases to remove
|
||||
delete = remove
|
||||
stop = delete
|
||||
|
||||
def all(self):
|
||||
|
|
|
|||
|
|
@ -5,13 +5,13 @@ ability to run timers.
|
|||
|
||||
"""
|
||||
|
||||
from django.utils.translation import gettext as _
|
||||
from evennia.scripts.manager import ScriptManager
|
||||
from evennia.scripts.models import ScriptDB
|
||||
from evennia.typeclasses.models import TypeclassBase
|
||||
from evennia.utils import create, logger
|
||||
from twisted.internet.defer import Deferred, maybeDeferred
|
||||
from twisted.internet.task import LoopingCall
|
||||
from django.utils.translation import gettext as _
|
||||
from evennia.typeclasses.models import TypeclassBase
|
||||
from evennia.scripts.models import ScriptDB
|
||||
from evennia.scripts.manager import ScriptManager
|
||||
from evennia.utils import create, logger
|
||||
|
||||
__all__ = ["DefaultScript", "DoNothing", "Store"]
|
||||
|
||||
|
|
@ -366,7 +366,11 @@ class ScriptBase(ScriptDB, metaclass=TypeclassBase):
|
|||
return
|
||||
|
||||
# call hook
|
||||
self.at_repeat()
|
||||
try:
|
||||
self.at_repeat()
|
||||
except Exception:
|
||||
logger.log_trace()
|
||||
raise
|
||||
|
||||
# check repeats
|
||||
if self.ndb._task:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue