Add all needed evadventure commands
This commit is contained in:
parent
e1439104e0
commit
2848e31f4b
8 changed files with 490 additions and 61 deletions
|
|
@ -2,9 +2,10 @@
|
||||||
General Character commands usually available to all characters
|
General Character commands usually available to all characters
|
||||||
"""
|
"""
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from evennia.utils import utils
|
|
||||||
from evennia.typeclasses.attributes import NickTemplateInvalid
|
from evennia.typeclasses.attributes import NickTemplateInvalid
|
||||||
|
from evennia.utils import utils
|
||||||
|
|
||||||
COMMAND_DEFAULT_CLASS = utils.class_from_module(settings.COMMAND_DEFAULT_CLASS)
|
COMMAND_DEFAULT_CLASS = utils.class_from_module(settings.COMMAND_DEFAULT_CLASS)
|
||||||
|
|
||||||
|
|
@ -501,7 +502,7 @@ class CmdGive(COMMAND_DEFAULT_CLASS):
|
||||||
Usage:
|
Usage:
|
||||||
give <inventory obj> <to||=> <target>
|
give <inventory obj> <to||=> <target>
|
||||||
|
|
||||||
Gives an items from your inventory to another character,
|
Gives an item from your inventory to another person,
|
||||||
placing it in their inventory.
|
placing it in their inventory.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -538,7 +539,7 @@ class CmdGive(COMMAND_DEFAULT_CLASS):
|
||||||
return
|
return
|
||||||
|
|
||||||
# give object
|
# give object
|
||||||
success = to_give.move_to(target, quiet=True, move_type="get")
|
success = to_give.move_to(target, quiet=True, move_type="give")
|
||||||
if not success:
|
if not success:
|
||||||
caller.msg("This could not be given.")
|
caller.msg("This could not be given.")
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,14 @@ Character class.
|
||||||
|
|
||||||
from evennia.objects.objects import DefaultCharacter
|
from evennia.objects.objects import DefaultCharacter
|
||||||
from evennia.typeclasses.attributes import AttributeProperty
|
from evennia.typeclasses.attributes import AttributeProperty
|
||||||
|
from evennia.utils.evmenu import ask_yes_no
|
||||||
|
from evennia.utils.logger import log_trace
|
||||||
from evennia.utils.utils import lazy_property
|
from evennia.utils.utils import lazy_property
|
||||||
|
|
||||||
from . import rules
|
from . import rules
|
||||||
from .equipment import EquipmentHandler
|
from .equipment import EquipmentError, EquipmentHandler
|
||||||
from .quests import EvAdventureQuestHandler
|
from .quests import EvAdventureQuestHandler
|
||||||
|
from .utils import get_obj_stats
|
||||||
|
|
||||||
|
|
||||||
class LivingMixin:
|
class LivingMixin:
|
||||||
|
|
@ -79,7 +82,7 @@ class LivingMixin:
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def at_loot(self, looted):
|
def at_do_loot(self, looted):
|
||||||
"""
|
"""
|
||||||
Called when looting another entity.
|
Called when looting another entity.
|
||||||
|
|
||||||
|
|
@ -87,9 +90,9 @@ class LivingMixin:
|
||||||
looted: The thing to loot.
|
looted: The thing to loot.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
looted.get_loot()
|
looted.at_looted()
|
||||||
|
|
||||||
def get_loot(self, looter):
|
def at_looted(self, looter):
|
||||||
"""
|
"""
|
||||||
Called when being looted (after defeat).
|
Called when being looted (after defeat).
|
||||||
|
|
||||||
|
|
@ -186,12 +189,14 @@ class EvAdventureCharacter(LivingMixin, DefaultCharacter):
|
||||||
Args:
|
Args:
|
||||||
moved_object (Object): Object to move into this one (that is, into inventory).
|
moved_object (Object): Object to move into this one (that is, into inventory).
|
||||||
source_location (Object): Source location moved from.
|
source_location (Object): Source location moved from.
|
||||||
**kwargs: Passed from move operation; unused here.
|
**kwargs: Passed from move operation; the `move_type` is useful; if someone is giving
|
||||||
|
us something (`move_type=='give'`) we want to ask first.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: If move should be allowed or not.
|
bool: If move should be allowed or not.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
# this will raise EquipmentError if inventory is full
|
||||||
return self.equipment.validate_slot_usage(moved_object)
|
return self.equipment.validate_slot_usage(moved_object)
|
||||||
|
|
||||||
def at_object_receive(self, moved_object, source_location, **kwargs):
|
def at_object_receive(self, moved_object, source_location, **kwargs):
|
||||||
|
|
@ -205,15 +210,18 @@ class EvAdventureCharacter(LivingMixin, DefaultCharacter):
|
||||||
**kwargs: Passed from move operation; unused here.
|
**kwargs: Passed from move operation; unused here.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self.equipment.add(moved_object)
|
try:
|
||||||
|
self.equipment.add(moved_object)
|
||||||
|
except EquipmentError as err:
|
||||||
|
log_trace(f"at_object_receive error: {err}")
|
||||||
|
|
||||||
def at_pre_object_leave(self, leaving_object, destination, **kwargs):
|
def at_pre_object_leave(self, leaving_object, destination, **kwargs):
|
||||||
"""
|
"""
|
||||||
Hook called when dropping an item. We don't allow to drop weilded/worn items
|
Hook called when dropping an item. We don't allow to drop weilded/worn items
|
||||||
(need to unwield/remove them first).
|
(need to unwield/remove them first). Return False to
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return self.equipment.can_remove(leaving_object)
|
return True
|
||||||
|
|
||||||
def at_object_leave(self, moved_object, destination, **kwargs):
|
def at_object_leave(self, moved_object, destination, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
@ -264,7 +272,7 @@ class EvAdventureCharacter(LivingMixin, DefaultCharacter):
|
||||||
# don't allow looting in pvp
|
# don't allow looting in pvp
|
||||||
return not self.location.allow_pvp
|
return not self.location.allow_pvp
|
||||||
|
|
||||||
def get_loot(self, looter):
|
def at_looted(self, looter):
|
||||||
"""
|
"""
|
||||||
Called when being looted.
|
Called when being looted.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -765,7 +765,7 @@ class EvAdventureCombatHandler(DefaultScript):
|
||||||
for enemy in defeated_enemies:
|
for enemy in defeated_enemies:
|
||||||
try:
|
try:
|
||||||
if ally.pre_loot(enemy):
|
if ally.pre_loot(enemy):
|
||||||
enemy.get_loot(ally)
|
enemy.at_looted(ally)
|
||||||
ally.post_loot(enemy)
|
ally.post_loot(enemy)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.log_trace()
|
logger.log_trace()
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,35 @@ New commands:
|
||||||
inventory
|
inventory
|
||||||
wield/wear <item>
|
wield/wear <item>
|
||||||
unwield/remove <item>
|
unwield/remove <item>
|
||||||
give <item> to <target>
|
give <item or coin> to <character>
|
||||||
|
talk <npc>
|
||||||
|
|
||||||
|
To install, add the `EvAdventureCmdSet` from this module to the default character cmdset:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# in mygame/commands/default_cmds.py
|
||||||
|
|
||||||
|
from evennia.contrib.tutorials.evadventure.commands import EvAdventureCmdSet # <---
|
||||||
|
|
||||||
|
# ...
|
||||||
|
|
||||||
|
class CharacterCmdSet(CmdSet):
|
||||||
|
def at_cmdset_creation(self):
|
||||||
|
# ...
|
||||||
|
self.add(EvAdventureCmdSet) # <-----
|
||||||
|
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from evennia import Command, default_cmds
|
from evennia import CmdSet, Command, InterruptCommand
|
||||||
|
from evennia.utils.evmenu import EvMenu
|
||||||
|
from evennia.utils.utils import inherits_from
|
||||||
|
|
||||||
from .combat_turnbased import CombatFailure, join_combat
|
from .combat_turnbased import CombatFailure, join_combat
|
||||||
|
from .enums import WieldLocation
|
||||||
|
from .equipment import EquipmentError
|
||||||
|
from .npcs import EvAdventureTalkativeNPC
|
||||||
|
from .utils import get_obj_stats
|
||||||
|
|
||||||
|
|
||||||
class EvAdventureCommand(Command):
|
class EvAdventureCommand(Command):
|
||||||
|
|
@ -93,12 +115,343 @@ class CmdInventory(EvAdventureCommand):
|
||||||
self.caller.msg(self.caller.equipment.display_loadout())
|
self.caller.msg(self.caller.equipment.display_loadout())
|
||||||
|
|
||||||
|
|
||||||
class CmdWield(EvAdventureCommand):
|
class CmdWieldOrWear(EvAdventureCommand):
|
||||||
"""
|
"""
|
||||||
Wield a weapon/shield or wear armor.
|
Wield a weapon/shield, or wear a piece of armor or a helmet.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
wield <item>
|
wield <item>
|
||||||
wear <item>
|
wear <item>
|
||||||
|
|
||||||
|
The item will automatically end up in the suitable spot, replacing whatever
|
||||||
|
was there previously.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
key = "wield"
|
||||||
|
aliases = ("wear",)
|
||||||
|
|
||||||
|
out_txts = {
|
||||||
|
WieldLocation.BACKPACK: "You shuffle the position of {key} around in your backpack.",
|
||||||
|
WieldLocation.TWO_HANDS: "You hold {key} with both hands.",
|
||||||
|
WieldLocation.WEAPON_HAND: "You hold {key} in your strongest hand, ready for action.",
|
||||||
|
WieldLocation.SHIELD_HAND: "You hold {key} in your off hand, ready to protect you.",
|
||||||
|
WieldLocation.BODY: "You strap {key} on yourself.",
|
||||||
|
WieldLocation.HEAD: "You put {key} on your head.",
|
||||||
|
}
|
||||||
|
|
||||||
|
def func(self):
|
||||||
|
# find the item among those in equipment
|
||||||
|
item = self.caller.search(self.args, candidates=self.caller.equipment.all(only_objs=True))
|
||||||
|
if not item:
|
||||||
|
# An 'item not found' error will already have been reported; we add another line
|
||||||
|
# here for clarity.
|
||||||
|
self.caller.msg("You must carry the item you want to wield or wear.")
|
||||||
|
return
|
||||||
|
|
||||||
|
use_slot = getattr(item, "inventory_use_slot", WieldLocation.BACKPACK)
|
||||||
|
|
||||||
|
# check what is currently in this slot
|
||||||
|
current = self.caller.equipment.slots[use_slot]
|
||||||
|
|
||||||
|
if current == item:
|
||||||
|
self.caller.msg(f"You are already using {item.key} here.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# move it to the right slot based on the type of object
|
||||||
|
self.caller.equipment.use(item)
|
||||||
|
|
||||||
|
# inform the user of the change (and potential swap)
|
||||||
|
if current:
|
||||||
|
self.caller.msg(f"Returning {current.key} to the backpack.")
|
||||||
|
self.caller.msg(self.out_txts[use_slot].format(key=item.key))
|
||||||
|
|
||||||
|
|
||||||
|
class CmdRemove(EvAdventureCommand):
|
||||||
|
"""
|
||||||
|
Remove a remove a weapon/shield, armor or helmet.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
remove <item>
|
||||||
|
unwield <item>
|
||||||
|
unwear <item>
|
||||||
|
|
||||||
|
To remove an item from the backpack, use |wdrop|n instead.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
key = "remove"
|
||||||
|
aliases = ("unwield", "unwear")
|
||||||
|
|
||||||
|
def func(self):
|
||||||
|
caller = self.caller
|
||||||
|
|
||||||
|
# find the item among those in equipment
|
||||||
|
item = caller.search(self.args, candidates=caller.equipment.all(only_objs=True))
|
||||||
|
if not item:
|
||||||
|
# An 'item not found' error will already have been reported
|
||||||
|
return
|
||||||
|
|
||||||
|
current_slot = caller.equipment.get_current_slot(item)
|
||||||
|
|
||||||
|
if current_slot is WieldLocation.BACKPACK:
|
||||||
|
# we don't allow dropping this way since it may be unexepected by users who forgot just
|
||||||
|
# where their item currently is.
|
||||||
|
caller.msg(
|
||||||
|
f"You already stashed away {item.key} in your backpack. Use 'drop' if "
|
||||||
|
"you want to get rid of it."
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
caller.equipment.remove(item)
|
||||||
|
caller.equipment.add(item)
|
||||||
|
caller.msg(f"You stash {item.key} in your backpack.")
|
||||||
|
|
||||||
|
|
||||||
|
# give / accept menu
|
||||||
|
|
||||||
|
|
||||||
|
def _rescind_gift(caller, raw_string, **kwargs):
|
||||||
|
"""
|
||||||
|
Called when giver rescinds their gift in `node_give` below.
|
||||||
|
It means they entered 'cancel' on the gift screen.
|
||||||
|
|
||||||
|
"""
|
||||||
|
# kill the gift menu for the receiver immediately
|
||||||
|
receiver = kwargs["receiver"]
|
||||||
|
receiver.ndb._evmenu.close_menu()
|
||||||
|
receiver.msg("The offer was rescinded.")
|
||||||
|
return "node_end"
|
||||||
|
|
||||||
|
|
||||||
|
def node_give(caller, raw_string, **kwargs):
|
||||||
|
"""
|
||||||
|
This will show to the giver until receiver accepts/declines. It allows them
|
||||||
|
to rescind their offer.
|
||||||
|
|
||||||
|
The `caller` here is the one giving the item. We also make sure to feed
|
||||||
|
the 'item' and 'receiver' into the Evmenu.
|
||||||
|
|
||||||
|
"""
|
||||||
|
item = kwargs["item"]
|
||||||
|
receiver = kwargs["receiver"]
|
||||||
|
text = f"""
|
||||||
|
You are offering {item.key} to {receiver.get_display_name(looker=caller)}.
|
||||||
|
|wWaiting for them to accept or reject the offer ...|n
|
||||||
|
""".strip()
|
||||||
|
|
||||||
|
options = {
|
||||||
|
"key": ("cancel", "abort"),
|
||||||
|
"desc": "Rescind your offer.",
|
||||||
|
"goto": (_rescind_gift, kwargs),
|
||||||
|
}
|
||||||
|
return text, options
|
||||||
|
|
||||||
|
|
||||||
|
def _accept_or_reject_gift(caller, raw_string, **kwargs):
|
||||||
|
"""
|
||||||
|
Called when receiver enters yes/no in `node_receive` below. We first need to
|
||||||
|
figure out which.
|
||||||
|
|
||||||
|
"""
|
||||||
|
item = kwargs["item"]
|
||||||
|
giver = kwargs["giver"]
|
||||||
|
if raw_string.lower() in ("yes", "y"):
|
||||||
|
# they accepted - move the item!
|
||||||
|
item = giver.equipment.remove(item)
|
||||||
|
if item:
|
||||||
|
try:
|
||||||
|
# this will also add them to the equipment backpack, if possible
|
||||||
|
item.move_to(caller, quiet=True, move_type="give")
|
||||||
|
except EquipmentError:
|
||||||
|
caller.location.msg_contents(
|
||||||
|
f"$You({giver.key.key}) $conj(try) to give "
|
||||||
|
f"{item.key} to $You({caller.key}), but they can't accept it since their "
|
||||||
|
"inventory is full.",
|
||||||
|
mapping={giver.key: giver, caller.key: caller},
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
caller.location.msg_contents(
|
||||||
|
f"$You({giver.key}) $conj(give) {item.key} to $You({caller.key}), "
|
||||||
|
"and they accepted the offer.",
|
||||||
|
mapping={giver.key: giver, caller.key: caller},
|
||||||
|
)
|
||||||
|
giver.ndb._evmenu.close_menu()
|
||||||
|
return "node_end"
|
||||||
|
|
||||||
|
|
||||||
|
def node_receive(caller, raw_string, **kwargs):
|
||||||
|
"""
|
||||||
|
Will show to the receiver and allow them to accept/decline the offer for
|
||||||
|
as long as the giver didn't rescind it.
|
||||||
|
|
||||||
|
The `caller` here is the one receiving the item. We also make sure to feed
|
||||||
|
the 'item' and 'giver' into the EvMenu.
|
||||||
|
|
||||||
|
"""
|
||||||
|
item = kwargs["item"]
|
||||||
|
giver = kwargs["giver"]
|
||||||
|
text = f"""
|
||||||
|
{giver.get_display_name()} is offering you {item.key}:
|
||||||
|
|
||||||
|
{get_obj_stats(item)}
|
||||||
|
|
||||||
|
[Your inventory usage: {caller.equipment.get_slot_usage_string()}]
|
||||||
|
|wDo you want to accept the given item? Y/[N]
|
||||||
|
"""
|
||||||
|
options = ({"key": "_default", "goto": (_accept_or_reject_gift, kwargs)},)
|
||||||
|
return text, options
|
||||||
|
|
||||||
|
|
||||||
|
def node_end(caller, raw_string, **kwargs):
|
||||||
|
return "", None
|
||||||
|
|
||||||
|
|
||||||
|
class CmdGive(EvAdventureCommand):
|
||||||
|
"""
|
||||||
|
Give item or money to another person. Items need to be accepted before
|
||||||
|
they change hands. Money changes hands immediately with no wait.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
give <item> to <receiver>
|
||||||
|
give <number of coins> [coins] to receiver
|
||||||
|
|
||||||
|
If item name includes ' to ', surround it in quotes.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
give apple to ranger
|
||||||
|
give "road to happiness" to sad ranger
|
||||||
|
give 10 coins to ranger
|
||||||
|
give 12 to ranger
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
key = "give"
|
||||||
|
|
||||||
|
def parse(self):
|
||||||
|
"""
|
||||||
|
Parsing is a little more complex for this command.
|
||||||
|
|
||||||
|
"""
|
||||||
|
super().parse()
|
||||||
|
args = self.args
|
||||||
|
if " to " not in args:
|
||||||
|
self.caller.msg(
|
||||||
|
"Usage: give <item> to <recevier>. Specify e.g. '10 coins' to pay money. "
|
||||||
|
"Use quotes around the item name it if includes the substring ' to '. "
|
||||||
|
)
|
||||||
|
raise InterruptCommand
|
||||||
|
|
||||||
|
self.item_name = ""
|
||||||
|
self.coins = 0
|
||||||
|
|
||||||
|
# make sure we can use '...' to include items with ' to ' in the name
|
||||||
|
if args.startswith('"') and args.count('"') > 1:
|
||||||
|
end_ind = args[1:].index('"') + 1
|
||||||
|
item_name = args[:end_ind]
|
||||||
|
_, receiver_name = args.split(" to ", 1)
|
||||||
|
elif args.startswith("'") and args.count("'") > 1:
|
||||||
|
end_ind = args[1:].index("'") + 1
|
||||||
|
item_name = args[:end_ind]
|
||||||
|
_, receiver_name = args.split(" to ", 1)
|
||||||
|
else:
|
||||||
|
item_name, receiver_name = args.split(" to ", 1)
|
||||||
|
|
||||||
|
# a coin count rather than a normal name
|
||||||
|
if " coins" in item_name:
|
||||||
|
item_name = item_name[:-6]
|
||||||
|
if item_name.isnumeric():
|
||||||
|
self.coins = max(0, int(item_name))
|
||||||
|
|
||||||
|
self.item_name = item_name
|
||||||
|
self.receiver_name = receiver_name
|
||||||
|
|
||||||
|
def func(self):
|
||||||
|
caller = self.caller
|
||||||
|
|
||||||
|
receiver = caller.search(self.receiver_name)
|
||||||
|
if not receiver:
|
||||||
|
return
|
||||||
|
|
||||||
|
# giving of coins is always accepted
|
||||||
|
|
||||||
|
if self.coins:
|
||||||
|
current_coins = caller.coins
|
||||||
|
if caller.coins < current_coins:
|
||||||
|
caller.msg("You only have |y{current_coins}|n to give.")
|
||||||
|
return
|
||||||
|
# do transaction
|
||||||
|
caller.coins -= self.coins
|
||||||
|
receiver.coins += self.coins
|
||||||
|
caller.location.msg_contents(
|
||||||
|
f"$You() $conj(give) $You(receiver.key) {self.coins} coins."
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
# giving of items require acceptance before it happens
|
||||||
|
|
||||||
|
item = caller.search(self.item_name, candidates=caller.equipment.all(only_objs=True))
|
||||||
|
if not item:
|
||||||
|
return
|
||||||
|
|
||||||
|
# testing hook
|
||||||
|
if not item.at_pre_give(caller, receiver):
|
||||||
|
return
|
||||||
|
|
||||||
|
# before we start menus, we must check so either part is not already in a menu,
|
||||||
|
# that would be annoying otherwise
|
||||||
|
if receiver.ndb._evmenu:
|
||||||
|
caller.msg(
|
||||||
|
f"{receiver.get_display_name(looker=caller)} seems busy talking to someone else."
|
||||||
|
)
|
||||||
|
return
|
||||||
|
if caller.ndb._evmenu:
|
||||||
|
caller.msg("Close the current menu first.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# this starts evmenus for both parties
|
||||||
|
EvMenu(
|
||||||
|
receiver, {"node_receive": node_receive, "node_end": node_end}, item=item, giver=caller
|
||||||
|
)
|
||||||
|
EvMenu(caller, {"node_give": node_give, "node_end": node_end}, item=item, receiver=receiver)
|
||||||
|
|
||||||
|
|
||||||
|
class CmdTalk(EvAdventureCommand):
|
||||||
|
"""
|
||||||
|
Start a conversations with shop keepers and other NPCs in the world.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
talk <npc>
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
key = "talk"
|
||||||
|
|
||||||
|
def func(self):
|
||||||
|
target = self.search(self.args)
|
||||||
|
if not target:
|
||||||
|
return
|
||||||
|
|
||||||
|
if not inherits_from(target, EvAdventureTalkativeNPC):
|
||||||
|
self.caller.msg(
|
||||||
|
f"{target.get_display_name(looker=self.caller)} does not seem very talkative."
|
||||||
|
)
|
||||||
|
return
|
||||||
|
target.at_talk(self.caller)
|
||||||
|
|
||||||
|
|
||||||
|
class EvAdventureCmdSet(CmdSet):
|
||||||
|
"""
|
||||||
|
Groups all commands in one cmdset which can be added in one go to the DefaultCharacter cmdset.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
key = "evadventure"
|
||||||
|
|
||||||
|
def at_cmdset_creation(self):
|
||||||
|
self.add(CmdAttackTurnBased())
|
||||||
|
self.add(CmdInventory())
|
||||||
|
self.add(CmdWieldOrWear())
|
||||||
|
self.add(CmdRemove())
|
||||||
|
self.add(CmdGive())
|
||||||
|
self.add(CmdTalk())
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,10 @@ Knave has a system of Slots for its inventory.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from evennia.utils.utils import inherits_from
|
||||||
|
|
||||||
from .enums import Ability, WieldLocation
|
from .enums import Ability, WieldLocation
|
||||||
from .objects import WeaponEmptyHand
|
from .objects import EvAdventureObject, WeaponEmptyHand
|
||||||
|
|
||||||
|
|
||||||
class EquipmentError(TypeError):
|
class EquipmentError(TypeError):
|
||||||
|
|
@ -81,6 +83,16 @@ class EquipmentHandler:
|
||||||
"""
|
"""
|
||||||
return getattr(self.obj, Ability.CON.value, 1) + 10
|
return getattr(self.obj, Ability.CON.value, 1) + 10
|
||||||
|
|
||||||
|
def get_slot_usage_string(self):
|
||||||
|
"""
|
||||||
|
Get a slot usage/max string for display.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: The usage string.
|
||||||
|
|
||||||
|
"""
|
||||||
|
return f"|b{self.count_slots()}/{self.max_slots}|n"
|
||||||
|
|
||||||
def validate_slot_usage(self, obj):
|
def validate_slot_usage(self, obj):
|
||||||
"""
|
"""
|
||||||
Check if obj can fit in equipment, based on its size.
|
Check if obj can fit in equipment, based on its size.
|
||||||
|
|
@ -92,7 +104,10 @@ class EquipmentHandler:
|
||||||
EquipmentError: If there's not enough room.
|
EquipmentError: If there's not enough room.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
size = getattr(obj, "size", 0)
|
if not inherits_from(obj, EvAdventureObject):
|
||||||
|
raise EquipmentError(f"{obj.key} is not something that can be equipped.")
|
||||||
|
|
||||||
|
size = obj.size
|
||||||
max_slots = self.max_slots
|
max_slots = self.max_slots
|
||||||
current_slot_usage = self.count_slots()
|
current_slot_usage = self.count_slots()
|
||||||
if current_slot_usage + size > max_slots:
|
if current_slot_usage + size > max_slots:
|
||||||
|
|
@ -104,25 +119,21 @@ class EquipmentHandler:
|
||||||
)
|
)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def all(self):
|
def get_current_slot(self, obj):
|
||||||
"""
|
"""
|
||||||
Get all objects in inventory, regardless of location.
|
Check which slot-type the given object is in.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
obj (EvAdventureObject): The object to check.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list: A flat list of item tuples `[(item, WieldLocation),...]`
|
WieldLocation: A location the object is in. None if the object
|
||||||
starting with the wielded ones, backpack content last.
|
is not in the inventory at all.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
slots = self.slots
|
for equipment_item, slot in self.all():
|
||||||
lst = [
|
if obj == equipment_item:
|
||||||
(slots[WieldLocation.WEAPON_HAND], WieldLocation.WEAPON_HAND),
|
return slot
|
||||||
(slots[WieldLocation.SHIELD_HAND], WieldLocation.SHIELD_HAND),
|
|
||||||
(slots[WieldLocation.TWO_HANDS], WieldLocation.TWO_HANDS),
|
|
||||||
(slots[WieldLocation.BODY], WieldLocation.BODY),
|
|
||||||
(slots[WieldLocation.HEAD], WieldLocation.HEAD),
|
|
||||||
] + [(item, WieldLocation.BACKPACK) for item in slots[WieldLocation.BACKPACK]]
|
|
||||||
# remove any None-results from empty slots
|
|
||||||
return [tup for tup in lst if item[0]]
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def armor(self):
|
def armor(self):
|
||||||
|
|
@ -205,10 +216,10 @@ class EquipmentHandler:
|
||||||
|
|
||||||
return f"{weapon_str}{shield_str}\n{armor_str}{helmet_str}"
|
return f"{weapon_str}{shield_str}\n{armor_str}{helmet_str}"
|
||||||
|
|
||||||
def use(self, obj):
|
def move(self, obj):
|
||||||
"""
|
"""
|
||||||
Make use of item - this makes use of the object's wield slot to decide where
|
Moves item to the place it things it should be in - this makes use of the object's wield
|
||||||
it goes. If it doesn't have any, it goes into backpack.
|
slot to decide where it goes. If it doesn't have any, it goes into backpack.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
obj (EvAdventureObject): Thing to use.
|
obj (EvAdventureObject): Thing to use.
|
||||||
|
|
@ -238,7 +249,7 @@ class EquipmentHandler:
|
||||||
slots[WieldLocation.WEAPON_HAND] = slots[WieldLocation.SHIELD_HAND] = None
|
slots[WieldLocation.WEAPON_HAND] = slots[WieldLocation.SHIELD_HAND] = None
|
||||||
slots[use_slot] = obj
|
slots[use_slot] = obj
|
||||||
elif use_slot in (WieldLocation.WEAPON_HAND, WieldLocation.SHIELD_HAND):
|
elif use_slot in (WieldLocation.WEAPON_HAND, WieldLocation.SHIELD_HAND):
|
||||||
# can't keep a two-handed weapon if adding a one-handede weapon or shield
|
# can't keep a two-handed weapon if adding a one-handed weapon or shield
|
||||||
slots[WieldLocation.TWO_HANDS] = None
|
slots[WieldLocation.TWO_HANDS] = None
|
||||||
slots[use_slot] = obj
|
slots[use_slot] = obj
|
||||||
elif use_slot is WieldLocation.BACKPACK:
|
elif use_slot is WieldLocation.BACKPACK:
|
||||||
|
|
@ -255,19 +266,19 @@ class EquipmentHandler:
|
||||||
"""
|
"""
|
||||||
Put something in the backpack specifically (even if it could be wield/worn).
|
Put something in the backpack specifically (even if it could be wield/worn).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
obj (EvAdventureObject): The object to add.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
This will not change the object's `.location`, this must be done
|
||||||
|
by the calling code.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# check if we have room
|
# check if we have room
|
||||||
self.validate_slot_usage(obj)
|
self.validate_slot_usage(obj)
|
||||||
self.slots[WieldLocation.BACKPACK].append(obj)
|
self.slots[WieldLocation.BACKPACK].append(obj)
|
||||||
self._save()
|
self._save()
|
||||||
|
|
||||||
def can_remove(self, leaving_object):
|
|
||||||
"""
|
|
||||||
Called to check if the object can be removed.
|
|
||||||
|
|
||||||
"""
|
|
||||||
return True # TODO - some things may not be so easy, like mud
|
|
||||||
|
|
||||||
def remove(self, obj_or_slot):
|
def remove(self, obj_or_slot):
|
||||||
"""
|
"""
|
||||||
Remove specific object or objects from a slot.
|
Remove specific object or objects from a slot.
|
||||||
|
|
@ -279,6 +290,10 @@ class EquipmentHandler:
|
||||||
Returns:
|
Returns:
|
||||||
list: A list of 0, 1 or more objects emptied from the inventory.
|
list: A list of 0, 1 or more objects emptied from the inventory.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
This will not change the object's `.location`, this must be done separately
|
||||||
|
by the calling code.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
slots = self.slots
|
slots = self.slots
|
||||||
ret = []
|
ret = []
|
||||||
|
|
@ -354,21 +369,28 @@ class EquipmentHandler:
|
||||||
character = self.obj
|
character = self.obj
|
||||||
return [obj for obj in self.slots[WieldLocation.BACKPACK] if obj.at_pre_use(character)]
|
return [obj for obj in self.slots[WieldLocation.BACKPACK] if obj.at_pre_use(character)]
|
||||||
|
|
||||||
def get_obj_stats(self, obj):
|
def all(self, only_objs=False):
|
||||||
"""
|
"""
|
||||||
Get a string of stats about the object.
|
Get all objects in inventory, regardless of location.
|
||||||
|
|
||||||
|
Keyword Args:
|
||||||
|
only_objs (bool): Only return a flat list of objects, not tuples.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list: A list of item tuples `[(item, WieldLocation),...]`
|
||||||
|
starting with the wielded ones, backpack content last. If `only_objs` is set,
|
||||||
|
this will just be a flat list of objects.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
objmap = dict(self.all())
|
slots = self.slots
|
||||||
carried = objmap.get(obj)
|
lst = [
|
||||||
carried = f"Worn: [{carried.value}]" if carried else ""
|
(slots[WieldLocation.WEAPON_HAND], WieldLocation.WEAPON_HAND),
|
||||||
|
(slots[WieldLocation.SHIELD_HAND], WieldLocation.SHIELD_HAND),
|
||||||
return f"""
|
(slots[WieldLocation.TWO_HANDS], WieldLocation.TWO_HANDS),
|
||||||
|c{self.key}|n Value: |y{self.value}|n coins {carried}
|
(slots[WieldLocation.BODY], WieldLocation.BODY),
|
||||||
|
(slots[WieldLocation.HEAD], WieldLocation.HEAD),
|
||||||
{self.desc}
|
] + [(item, WieldLocation.BACKPACK) for item in slots[WieldLocation.BACKPACK]]
|
||||||
|
# remove any None-results from empty slots
|
||||||
Slots: |w{self.size}|n Used from: |w{self.use_slot.value}|n
|
if only_objs:
|
||||||
Quality: |w{self.quality}|n Uses: |wself.uses|n
|
return [tup[0] for tup in lst if tup[0]]
|
||||||
Attacks using: |w{self.attack_type.value}|n against |w{self.defense_type.value}|n
|
return [tup for tup in lst if tup[0]]
|
||||||
Damage roll: |w{self.damage_roll}"""
|
|
||||||
|
|
|
||||||
|
|
@ -280,7 +280,7 @@ class EvAdventureMob(EvAdventureNPC):
|
||||||
"""
|
"""
|
||||||
self.at_death()
|
self.at_death()
|
||||||
|
|
||||||
def at_loot(self, looted):
|
def at_do_loot(self, looted):
|
||||||
"""
|
"""
|
||||||
Called when mob gets to loot a PC.
|
Called when mob gets to loot a PC.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,3 +2,47 @@
|
||||||
Various utilities.
|
Various utilities.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
_OBJ_STATS = """
|
||||||
|
|c{key}|n Value: approx. |y{value}|n coins {carried}
|
||||||
|
|
||||||
|
{desc}
|
||||||
|
|
||||||
|
Slots: |w{size}|n Used from: |w{use_slot_name}|n
|
||||||
|
Quality: |w{quality}|n Uses: |wuses|n
|
||||||
|
Attacks using: |w{attack_type_name}|n against |w{defense_type_value}|n
|
||||||
|
Damage roll: |w{damage_roll}""".strip()
|
||||||
|
|
||||||
|
|
||||||
|
def get_obj_stats(obj, owner=None):
|
||||||
|
"""
|
||||||
|
Get a string of stats about the object.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
obj (EvAdventureObject): The object to get stats for.
|
||||||
|
owner (EvAdventureCharacter, optional): If given, it allows us to
|
||||||
|
also get information about if the item is currently worn/wielded.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: A stat string to show about the object.
|
||||||
|
|
||||||
|
"""
|
||||||
|
carried = ""
|
||||||
|
if owner:
|
||||||
|
objmap = dict(owner.equipment.all())
|
||||||
|
carried = objmap.get(obj)
|
||||||
|
carried = f"Worn: [{carried.value}]" if carried else ""
|
||||||
|
|
||||||
|
return _OBJ_STATS.format(
|
||||||
|
key=obj.key,
|
||||||
|
value=obj.value,
|
||||||
|
carried=carried,
|
||||||
|
desc=obj.db.desc,
|
||||||
|
size=obj.size,
|
||||||
|
use_slot=obj.use_slot.value,
|
||||||
|
quality=obj.quality,
|
||||||
|
uses=obj.uses,
|
||||||
|
attack_type=obj.attack_type.value,
|
||||||
|
defense_type=obj.defense_type.value,
|
||||||
|
damage_roll=obj.damage_roll,
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -1744,10 +1744,11 @@ def ask_yes_no(
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
A helper question for asking a simple yes/no question. This will cause
|
A helper function for asking a simple yes/no question. This will cause
|
||||||
the system to pause and wait for input from the player.
|
the system to pause and wait for input from the player.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
caller (Object): The entity being asked.
|
||||||
prompt (str): The yes/no question to ask. This takes an optional formatting
|
prompt (str): The yes/no question to ask. This takes an optional formatting
|
||||||
marker `{options}` which will be filled with 'Y/N', '[Y]/N' or
|
marker `{options}` which will be filled with 'Y/N', '[Y]/N' or
|
||||||
'Y/[N]' depending on the setting of `default`. If `allow_abort` is set,
|
'Y/[N]' depending on the setting of `default`. If `allow_abort` is set,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue