Prep for shopkeepers
This commit is contained in:
parent
c95c8213a0
commit
b4cccc9703
5 changed files with 157 additions and 12 deletions
|
|
@ -62,7 +62,7 @@ class LivingMixin:
|
||||||
Called when attacked and taking damage.
|
Called when attacked and taking damage.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
pass
|
self.hp -= damage
|
||||||
|
|
||||||
def at_defeat(self):
|
def at_defeat(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -288,8 +288,6 @@ class CombatActionAttack(CombatAction):
|
||||||
message = f" $You() $conj(hit) $You({defender.key}) for |r{dmg}|n damage!"
|
message = f" $You() $conj(hit) $You({defender.key}) for |r{dmg}|n damage!"
|
||||||
self.msg(message)
|
self.msg(message)
|
||||||
|
|
||||||
defender.hp -= dmg
|
|
||||||
|
|
||||||
# call hook
|
# call hook
|
||||||
defender.at_damage(dmg, attacker=attacker)
|
defender.at_damage(dmg, attacker=attacker)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ from random import choice
|
||||||
|
|
||||||
from evennia import DefaultCharacter
|
from evennia import DefaultCharacter
|
||||||
from evennia.typeclasses.attributes import AttributeProperty
|
from evennia.typeclasses.attributes import AttributeProperty
|
||||||
|
from evennia.utils.evmenu import EvMenu
|
||||||
|
from evennia.utils.utils import make_iter
|
||||||
|
|
||||||
from .characters import LivingMixin
|
from .characters import LivingMixin
|
||||||
from .enums import Ability, WieldLocation
|
from .enums import Ability, WieldLocation
|
||||||
|
|
@ -95,18 +97,140 @@ class EvAdventureNPC(LivingMixin, DefaultCharacter):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class EvAdventureShopKeeper(EvAdventureNPC):
|
class EvAdventureTalkativeNPC(EvAdventureNPC):
|
||||||
|
"""
|
||||||
|
Talkative NPCs can be addressed by `talk [to] <npc>`. This opens a chat menu with
|
||||||
|
communication options. The menu is created with the npc and we override the .create
|
||||||
|
to allow passing in the menu nodes.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
menudata = AttributeProperty(None, autocreate=False)
|
||||||
|
menu_kwargs = AttributeProperty(None, autocreate=False)
|
||||||
|
# text shown when greeting at the start of a conversation. If this is an
|
||||||
|
# iterable, a random reply will be chosen by the menu
|
||||||
|
hi_text = AttributeProperty("Hi!", autocreate=False)
|
||||||
|
|
||||||
|
def at_damage(self, damage, attacker=None):
|
||||||
|
"""
|
||||||
|
Talkative NPCs are generally immortal (we don't deduct HP here by default)."
|
||||||
|
|
||||||
|
"""
|
||||||
|
attacker.msg(f'{self.key} dodges the damage and shouts "|wHey! What are you doing?|n"')
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create(cls, key, account=None, **kwargs):
|
||||||
|
"""
|
||||||
|
Overriding the creation of the NPC, allowing some extra `**kwargs`.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
key (str): Name of the new object.
|
||||||
|
account (Account, optional): Account to attribute this object to.
|
||||||
|
|
||||||
|
Keyword Args:
|
||||||
|
description (str): Brief description for this object (same as default Evennia)
|
||||||
|
ip (str): IP address of creator (for object auditing) (same as default Evennia).
|
||||||
|
menudata (dict or str): The `menudata` argument to `EvMenu`. This is either a dict of
|
||||||
|
`{"nodename": <node_callable>,...}` or the python-path to a module containing
|
||||||
|
such nodes (see EvMenu docs). This will be used to generate the chat menu
|
||||||
|
chat menu for the character that talks to the NPC (which means the `at_talk` hook
|
||||||
|
is called (by our custom `talk` command).
|
||||||
|
menu_kwargs (dict): This will be passed as `**kwargs` into `EvMenu` when it
|
||||||
|
is created. Make sure this dict can be pickled to an Attribute.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple: `(new_character, errors)`. On error, the `new_character` is `None` and
|
||||||
|
`errors` is a `list` of error strings (an empty list otherwise).
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
menudata = kwargs.pop("menudata", None)
|
||||||
|
menu_kwargs = kwargs.pop("menu_kwargs", {})
|
||||||
|
|
||||||
|
# since this is a @classmethod we can't use super() here
|
||||||
|
new_object, errors = EvAdventureNPC.create(
|
||||||
|
key, account=account, attributes=(("menudata", menudata), ("menu_kwargs", menu_kwargs))
|
||||||
|
)
|
||||||
|
|
||||||
|
return new_object, errors
|
||||||
|
|
||||||
|
def at_talk(self, talker, startnode="node_start", session=None, **kwargs):
|
||||||
|
"""
|
||||||
|
Called by the `talk` command when another entity addresses us.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
talker (Object): The one talking to us.
|
||||||
|
startnode (str, optional): Allows to start in a different location in the menu tree.
|
||||||
|
The given node must exist in the tree.
|
||||||
|
session (Session, optional): The talker's current session, allows for routing
|
||||||
|
correctly in multi-session modes.
|
||||||
|
**kwargs: This will be passed into the `EvMenu` creation and appended and `menu_kwargs`
|
||||||
|
given to the NPC at creation.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
We pass `npc=self` into the EvMenu for easy back-reference. This will appear in the
|
||||||
|
`**kwargs` of the start node.
|
||||||
|
|
||||||
|
"""
|
||||||
|
menu_kwargs = {**self.menu_kwargs, **kwargs}
|
||||||
|
EvMenu(talker, self.menudata, startnode=startnode, session=session, npc=self, **menu_kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def node_start(caller, raw_string, **kwargs):
|
||||||
|
"""
|
||||||
|
This is the intended start menu node for the Talkative NPC interface. It will
|
||||||
|
use on-npc Attributes to build its message and will also pick its options
|
||||||
|
based on nodes named `node_start_*` are available in the node tree.
|
||||||
|
|
||||||
|
"""
|
||||||
|
# we presume a back-reference to the npc this is added when the menu is created
|
||||||
|
npc = kwargs["npc"]
|
||||||
|
|
||||||
|
# grab a (possibly random) welcome text
|
||||||
|
text = choice(make_iter(npc.hi_text))
|
||||||
|
|
||||||
|
# determine options based on `node_start_*` nodes available
|
||||||
|
toplevel_node_keys = [
|
||||||
|
node_key for node_key in caller.ndb._evmenu._menutree if node_key.startswith("node_start_")
|
||||||
|
]
|
||||||
|
options = []
|
||||||
|
for node_key in toplevel_node_keys:
|
||||||
|
option_name = node_key[11:].replace("_", " ").capitalized()
|
||||||
|
|
||||||
|
# we let the menu number the choices, so we don't use key here
|
||||||
|
options.append({"desc": option_name, "goto": node_key})
|
||||||
|
|
||||||
|
return text, options
|
||||||
|
|
||||||
|
|
||||||
|
class EvAdventureQuestGiver(EvAdventureTalkativeNPC):
|
||||||
|
"""
|
||||||
|
An NPC that acts as a dispenser of quests.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class EvAdventureShopKeeper(EvAdventureTalkativeNPC):
|
||||||
"""
|
"""
|
||||||
ShopKeeper NPC.
|
ShopKeeper NPC.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# how much extra the shopkeeper adds on top of the item cost
|
||||||
|
upsell_factor = AttributePropert(1.0, autocreate=False)
|
||||||
|
# how much of the raw cost the shopkeep is willing to pay when buying from character
|
||||||
|
miser_factor = Attribute(0.5, autocreate=False)
|
||||||
|
common_ware_prototypes = AttributeProperty([], autocreate=False)
|
||||||
|
|
||||||
class EvAdventureQuestGiver(EvAdventureNPC):
|
def at_damage(self, damage, attacker=None):
|
||||||
"""
|
"""
|
||||||
An NPC that acts as a dispenser of quests.
|
Immortal - we don't deduct any damage here.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
attacker.msg(
|
||||||
|
f"{self.key} brushes off the hit and shouts "
|
||||||
|
'"|wHey! This is not the way to get a discount!|n"'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class EvAdventureMob(EvAdventureNPC):
|
class EvAdventureMob(EvAdventureNPC):
|
||||||
|
|
|
||||||
|
|
@ -23,14 +23,37 @@ A shop is run by an NPC. It can provide one or more of several possible services
|
||||||
All shops are menu-driven. One starts talking to the npc and will then end up in their shop
|
All shops are menu-driven. One starts talking to the npc and will then end up in their shop
|
||||||
interface.
|
interface.
|
||||||
|
|
||||||
|
|
||||||
|
This is a series of menu nodes meant to be added as a mapping via
|
||||||
|
`EvAdventureShopKeeper.create(menudata={},...)`.
|
||||||
|
|
||||||
|
To make this pluggable, the shopkeeper start page will analyze the available nodes
|
||||||
|
and auto-add options to all nodes in the three named `node_start_*`. The last part of the
|
||||||
|
node name will be the name of the option capitalized, with underscores replaced by spaces, so
|
||||||
|
`node_start_sell_items` will become a top-level option `Sell items`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from random import choice
|
||||||
|
|
||||||
from evennia.utils.evmenu import EvMenu
|
from evennia.utils.evmenu import EvMenu
|
||||||
|
from evennia.utils.utils import make_iter
|
||||||
|
|
||||||
|
from .npcs import EvAdventureShopKeeper
|
||||||
|
|
||||||
|
# shop menu nodes to use for building a Shopkeeper npc
|
||||||
|
|
||||||
|
|
||||||
def start_npc_menu(caller, shopkeeper, **kwargs):
|
def node_start_buy(caller, raw_string, **kwargs):
|
||||||
"""
|
"""
|
||||||
Access function - start the NPC interaction/shop interface.
|
Menu node for the caller to buy items from the shopkeep. This assumes `**kwargs` contains
|
||||||
|
a kwarg `npc` referencing the npc/shopkeep being talked to.
|
||||||
|
|
||||||
|
Items available to sell are a combination of items in the shopkeep's inventory and prototypes
|
||||||
|
the list of `prototypes` stored in the Shopkeep's "common_ware_prototypes` Attribute. In the
|
||||||
|
latter case, the properties will be extracted from the prototype when inspecting it (object will
|
||||||
|
only spawn when bought).
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -2419,8 +2419,8 @@ class DefaultCharacter(DefaultObject):
|
||||||
All other kwargs will be passed into the create_object call.
|
All other kwargs will be passed into the create_object call.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
character (Object): A newly created Character of the given typeclass.
|
tuple: `(new_character, errors)`. On error, the `new_character` is `None` and
|
||||||
errors (list): A list of errors in string form, if any.
|
`errors` is a `list` of error strings (an empty list otherwise).
|
||||||
|
|
||||||
"""
|
"""
|
||||||
errors = []
|
errors = []
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue