mirror of
https://github.com/evennia/evennia.git
synced 2026-03-26 17:56:32 +01:00
Refactor equipmenthandler
This commit is contained in:
parent
b2e41c2ddc
commit
df5ae68a3c
4 changed files with 314 additions and 296 deletions
|
|
@ -3,12 +3,12 @@ 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 . import rules
|
||||
from .enums import Ability, WieldLocation
|
||||
|
||||
|
||||
class EquipmentError(TypeError):
|
||||
|
|
@ -17,316 +17,226 @@ class EquipmentError(TypeError):
|
|||
|
||||
class EquipmentHandler:
|
||||
"""
|
||||
_Knave_ puts a lot of emphasis on the inventory. You have 20 inventory slots,
|
||||
Some things, like torches can fit multiple in one slot, other (like
|
||||
_Knave_ puts a lot of emphasis on the inventory. You have CON_DEFENSE inventory
|
||||
slots. Some things, like torches can fit multiple in one slot, other (like
|
||||
big weapons) use more than one slot. The items carried and wielded has a big impact
|
||||
on character customization - even magic requires carrying a runestone per spell.
|
||||
|
||||
The inventory also doubles as a measure of negative effects. Getting soaked in mud
|
||||
or slime could gunk up some of your inventory slots and make the items there unusuable
|
||||
until you cleaned them.
|
||||
|
||||
until you clean them.
|
||||
"""
|
||||
# these are the equipment slots available
|
||||
total_slots = 20
|
||||
wield_slots = ["shield", "weapon"]
|
||||
wear_slots = ["helmet", "armor"]
|
||||
save_attribute = "inventory_slots"
|
||||
|
||||
def __init__(self, obj):
|
||||
self.obj = obj
|
||||
self._slots_used = None
|
||||
self._wielded = None
|
||||
self._worn = None
|
||||
self._armor = None
|
||||
self._load()
|
||||
|
||||
def _wield_or_wear(self, item, action="wear"):
|
||||
def _load(self):
|
||||
"""
|
||||
Wield or wear a previously carried item in one of the supported wield/wear slots. Items need
|
||||
to have the wieldable/wearable tag and will get a wielded/worn tag. The slot to occupy is
|
||||
retrieved from the item itself.
|
||||
|
||||
Args:
|
||||
item (Object): The object to wield. This will replace any existing
|
||||
wieldable item in that spot.
|
||||
action (str): One of 'wield' or 'wear'.
|
||||
Returns:
|
||||
tuple: (slot, old_item - the slot-name this item was
|
||||
assigned to (like 'helmet') and any old item that was replaced in that location,.
|
||||
(else `old_item` is `None`). This is useful for returning info messages
|
||||
to the user.
|
||||
Raises:
|
||||
EquipmentError: If there is a problem wielding the item.
|
||||
|
||||
Notes:
|
||||
Since the action of wielding is so similar to wearing, we use the same code for both,
|
||||
just exchanging which slot to use and the wield/wear and wielded/worn texts.
|
||||
Load or create a new slot storage.
|
||||
|
||||
"""
|
||||
adjective = 'wearable' if action == 'wear' else 'wieldable'
|
||||
verb = "worn" if action == 'wear' else 'wielded'
|
||||
self.slots = self.obj.attributes.get(
|
||||
self.save_attribute,
|
||||
category="inventory",
|
||||
default={
|
||||
WieldLocation.WEAPON_HAND: None,
|
||||
WieldLocation.SHIELD_HAND: None,
|
||||
WieldLocation.TWO_HANDS: None,
|
||||
WieldLocation.BODY: None,
|
||||
WieldLocation.HEAD: None,
|
||||
WieldLocation.BACKPACK: []
|
||||
}
|
||||
)
|
||||
|
||||
if item not in self.obj.contents:
|
||||
raise EquipmentError(f"You need to pick it up before you can use it.")
|
||||
if item in self.wielded:
|
||||
raise EquipmentError(f"Already using {item.key}")
|
||||
if not item.tags.has(adjective, category="inventory"):
|
||||
# must have wieldable/wearable tag
|
||||
raise EquipmentError(f"Cannot {action} {item.key}")
|
||||
def _count_slots(self):
|
||||
"""
|
||||
Count slot usage. This is fetched from the .size Attribute of the
|
||||
object. The size can also be partial slots.
|
||||
|
||||
# see if an existing item already sits in the relevant slot
|
||||
if action == 'wear':
|
||||
slot = item.wear_slot
|
||||
old_item = self.worn.get(slot)
|
||||
self.worn[slot] = item
|
||||
else:
|
||||
slot = item.wield_slot
|
||||
old_item = self.wielded.get(slot)
|
||||
self.wielded[item]
|
||||
"""
|
||||
slots = self.slots
|
||||
wield_usage = sum(
|
||||
getattr(slotobj, "size", 0) or 0
|
||||
for slot, slotobj in slots.items()
|
||||
if slot is not WieldLocation.BACKPACK
|
||||
)
|
||||
backpack_usage = sum(
|
||||
getattr(slotobj, "size", 0) or 0
|
||||
for slotobj in slots[WieldLocation.BACKPACK]
|
||||
)
|
||||
return wield_usage + backpack_usage
|
||||
|
||||
# untag old, tag the new and store it in .wielded dict for easy access
|
||||
if old_item:
|
||||
old_item.tags.remove(verb, category="inventory")
|
||||
item.tags.add(verb, category="inventory")
|
||||
def _save(self):
|
||||
"""
|
||||
Save slot to storage.
|
||||
|
||||
return slot, old_item
|
||||
"""
|
||||
self.obj.attributes.add(self.save_attribute, category="inventory")
|
||||
|
||||
@property
|
||||
def slots_used(self):
|
||||
def max_slots(self):
|
||||
"""
|
||||
Return how many slots are used up (out of .total_slots). Certain, big items may use more
|
||||
than one slot. Also caches the results.
|
||||
The max amount of equipment slots ('carrying capacity') is based on
|
||||
the constitution defense.
|
||||
|
||||
"""
|
||||
slots_used = self._slots_used
|
||||
if slots_used is None:
|
||||
slots_used = self._slots_used = sum(
|
||||
item.inventory_slot_usage for item in self.contents
|
||||
)
|
||||
return slots_used
|
||||
return getattr(self.obj, Ability.CON_DEFENSE.value, 11)
|
||||
|
||||
@property
|
||||
def all(self):
|
||||
def validate_slot_usage(self, obj):
|
||||
"""
|
||||
Get all carried items. Used by an 'inventory' command.
|
||||
|
||||
"""
|
||||
return self.obj.contents
|
||||
|
||||
@property
|
||||
def worn(self):
|
||||
"""
|
||||
Get (and cache) all worn items.
|
||||
|
||||
"""
|
||||
worn = self._worn
|
||||
if worn is None:
|
||||
worn = self._worn = list(
|
||||
DefaultObject.objects
|
||||
.get_by_tag(["wearable", "worn"], category="inventory")
|
||||
.filter(db_location=self.obj)
|
||||
)
|
||||
return worn
|
||||
|
||||
@property
|
||||
def wielded(self):
|
||||
wielded = self._wielded
|
||||
if wielded is None:
|
||||
wielded = self._wielded = list(
|
||||
DefaultObject.objects
|
||||
.get_by_tag(["wieldable", "wielded"], category="inventory")
|
||||
.filter(db_location=self.obj)
|
||||
)
|
||||
return wielded
|
||||
|
||||
@property
|
||||
def carried(self):
|
||||
wielded_or_worn = self.wielded + self.worn
|
||||
return [item for item in self.contents if item not in wielded_or_worn]
|
||||
|
||||
@property
|
||||
def armor_defense(self):
|
||||
"""
|
||||
Figure out the total armor defense of the character. This is a combination
|
||||
of armor from worn items (helmets, armor) and wielded ones (shields).
|
||||
|
||||
"""
|
||||
armor = self._armor
|
||||
if armor is None:
|
||||
# recalculate and refresh cache. Default for unarmored enemy is armor defense of 11.
|
||||
armor = self._armor = sum(item.armor for item in self.worn + self.wielded) or 11
|
||||
return armor
|
||||
|
||||
def has_space(self, item):
|
||||
"""
|
||||
Check if there's room in equipment for this item.
|
||||
Check if obj can fit in equipment, based on its size.
|
||||
|
||||
Args:
|
||||
item (Object): An entity that takes up space.
|
||||
obj (EvAdventureObject): The object to add.
|
||||
|
||||
Returns:
|
||||
bool: If there's room or not.
|
||||
|
||||
Notes:
|
||||
Also informs the user of the failure.
|
||||
Raise:
|
||||
EquipmentError: If there's not enough room.
|
||||
|
||||
"""
|
||||
needed_slots = getattr(item, "inventory_slot_usage", 1)
|
||||
free = self.slots_used - needed_slots
|
||||
if free - needed_slots < 0:
|
||||
self.obj.msg(f"No space in inventory - {item} takes up {needed_slots}, "
|
||||
f"but $int2str({free}) $pluralize(is, {free}, are) available.")
|
||||
return False
|
||||
return True
|
||||
|
||||
def can_drop(self, item):
|
||||
"""
|
||||
Check if the item can be dropped - this is blocked by being worn or wielded.
|
||||
|
||||
Args:
|
||||
item (Object): The item to drop.
|
||||
|
||||
Returns:
|
||||
bool: If the object can be dropped.
|
||||
|
||||
Notes:
|
||||
Informs the user of a failure.
|
||||
|
||||
"""
|
||||
if item in self.wielded:
|
||||
self.msg("You are currently wielding {item.key}. Unwield it first.")
|
||||
return False
|
||||
if item in self.worn:
|
||||
self.msg("You are currently wearing {item.key}. Remove it first.")
|
||||
return False
|
||||
return True
|
||||
|
||||
def add(self, item):
|
||||
"""
|
||||
Add an item to the inventory. This will be called when picking something up. An item
|
||||
must be carried before it can be worn or wielded.
|
||||
|
||||
There is a max number of carry-slots.
|
||||
|
||||
Args:
|
||||
item (EvAdventureObject): The item to add (pick up).
|
||||
Raises:
|
||||
EquipmentError: If the item can't be added (usually because of lack of space).
|
||||
|
||||
"""
|
||||
slots_needed = item.inventory_slot_usage
|
||||
slots_already_used = self.slots_used
|
||||
|
||||
slots_free = self.total_slots - slots_already_used
|
||||
|
||||
if slot_needed > slots_free:
|
||||
raise EquipmentError(
|
||||
f"This requires {slots_needed} equipment slots - you have "
|
||||
f"$int2str({slots_free}) $pluralize(slot, {slots_free}) available.")
|
||||
# move to inventory
|
||||
item.location = self.obj
|
||||
self.slots_used += slots_needed
|
||||
|
||||
def remove(self, item):
|
||||
"""
|
||||
Remove (drop) an item from inventory. This will also un-wear or un-wield it.
|
||||
|
||||
Args:
|
||||
item (EvAdventureObject): The item to drop.
|
||||
Raises:
|
||||
EquipmentError: If the item can't be dropped (usually because we don't have it).
|
||||
|
||||
"""
|
||||
if item not in self.obj.contents:
|
||||
raise EquipmentError("You are not carrying this item.")
|
||||
self.slots_used -= item.inventory_slot_usage
|
||||
|
||||
def wear(self, item):
|
||||
"""
|
||||
Wear a previously carried item. The item itelf knows which slot it belongs in (like 'helmet'
|
||||
or 'armor').
|
||||
|
||||
Args:
|
||||
item (EvAdventureObject): The item to wear. Must already be carried.
|
||||
Returns:
|
||||
tuple: (slot, old_item - the slot-name this item was
|
||||
assigned to (like 'helmet') and any old item that was replaced in that location
|
||||
(else `old_item` is `None`). This is useful for returning info messages
|
||||
to the user.
|
||||
Raises:
|
||||
EquipmentError: If there is a problem wearing the item.
|
||||
|
||||
"""
|
||||
return self._wield_or_wear(item, action="wield")
|
||||
|
||||
def wield(self, item):
|
||||
"""
|
||||
Wield a previously carried item. The item itelf knows which wield-slot it belongs in (like
|
||||
'helmet' or 'armor').
|
||||
|
||||
Args:
|
||||
item (EvAdventureObject): The item to wield. Must already be carried.
|
||||
|
||||
Returns:
|
||||
tuple: (slot, old_item - the wield-slot-name this item was
|
||||
assigned to (like 'shield') and any old item that was replaced in that location
|
||||
(else `old_item` is `None`). This is useful for returning info messages
|
||||
to the user.
|
||||
Raises:
|
||||
EquipmentError: If there is a problem wielding the item.
|
||||
|
||||
"""
|
||||
return self._wield_or_wear(item, action="wear")
|
||||
|
||||
|
||||
class EvAdventureCharacter(DefaultCharacter):
|
||||
"""
|
||||
A Character for use with EvAdventure. This also works fine for
|
||||
monsters and NPCS.
|
||||
|
||||
"""
|
||||
|
||||
# these are the ability bonuses. Defense is always 10 higher
|
||||
strength = AttributeProperty(default=1)
|
||||
dexterity = AttributeProperty(default=1)
|
||||
constitution = AttributeProperty(default=1)
|
||||
intelligence = AttributeProperty(default=1)
|
||||
wisdom = AttributeProperty(default=1)
|
||||
charisma = AttributeProperty(default=1)
|
||||
|
||||
armor = AttributeProperty(default=1)
|
||||
|
||||
exploration_speed = AttributeProperty(default=120)
|
||||
combat_speed = AttributeProperty(default=40)
|
||||
|
||||
hp = AttributeProperty(default=4)
|
||||
hp_max = AttributeProperty(default=4)
|
||||
level = AttributeProperty(default=1)
|
||||
xp = AttributeProperty(default=0)
|
||||
|
||||
morale = AttributeProperty(default=9) # only used for NPC/monster morale checks
|
||||
|
||||
@lazy_property
|
||||
def equipment(self):
|
||||
"""Allows to access equipment like char.equipment.worn"""
|
||||
return EquipmentHandler(self)
|
||||
|
||||
@property
|
||||
def weapon(self):
|
||||
"""
|
||||
Quick access to the character's currently wielded weapon.
|
||||
Will return the "Unarmed" weapon if none other are found.
|
||||
|
||||
"""
|
||||
# TODO
|
||||
size = getattr(obj, "size", 0)
|
||||
max_slots = self.max_slots
|
||||
current_slot_usage = self._count_slots()
|
||||
if current_slot_usage + size > max_slots:
|
||||
slots_left = max_slots - current_slot_usage
|
||||
raise EquipmentError(f"Equipment full ({int2str(slots_left)} slots "
|
||||
f"remaining, {obj.key} needs {int2str(size)} "
|
||||
f"$pluralize(slot, {size})).")
|
||||
|
||||
@property
|
||||
def armor(self):
|
||||
"""
|
||||
Quick access to the character's current armor.
|
||||
Will return the "Unarmored" armor if none other are found.
|
||||
Armor provided by actually worn equipment/shield. For body armor
|
||||
this is a base value, like 12, for shield/helmet, it's a bonus, like +1.
|
||||
We treat values and bonuses equal and just add them up. This value
|
||||
can thus be 0, the 'unarmored' default should be handled by the calling
|
||||
method.
|
||||
|
||||
Returns:
|
||||
int: Armor from equipment.
|
||||
|
||||
"""
|
||||
# TODO
|
||||
slots = self.slots
|
||||
return sum((
|
||||
getattr(slots[WieldLocation.BODY], "armor", 0),
|
||||
getattr(slots[WieldLocation.SHIELD_HAND], "armor", 0),
|
||||
getattr(slots[WieldLocation.HEAD], "armor", 0),
|
||||
))
|
||||
|
||||
@property
|
||||
def weapon(self):
|
||||
"""
|
||||
Conveniently get the currently active weapon.
|
||||
|
||||
Returns:
|
||||
obj or None: The weapon. None if unarmored.
|
||||
|
||||
"""
|
||||
# first checks two-handed wield, then one-handed; the two
|
||||
# should never appear simultaneously anyhow (checked in `use` method).
|
||||
slots = self.slots
|
||||
weapon = slots[WieldLocation.TWO_HANDS]
|
||||
if not weapon:
|
||||
weapon = slots[WieldLocation.WEAPON_HAND]
|
||||
return weapon
|
||||
|
||||
def use(self, obj):
|
||||
"""
|
||||
Make use of item - this makes use of the object's wield slot to decide where
|
||||
it goes. If it doesn't have any, it goes into backpack.
|
||||
|
||||
Args:
|
||||
obj (EvAdventureObject): Thing to use.
|
||||
|
||||
Raises:
|
||||
EquipmentError: If there's no room in inventory. It will contains the details
|
||||
of the error, suitable to echo to user.
|
||||
|
||||
Notes:
|
||||
If using an item already in the backpack, it should first be `removed` from the
|
||||
backpack, before applying here - otherwise, it will be added a second time!
|
||||
|
||||
this will cleanly move any 'colliding' items to the backpack to
|
||||
make the use possible (such as moving sword + shield to backpack when wielding
|
||||
a two-handed weapon). If wanting to warn the user about this, it needs to happen
|
||||
before this call.
|
||||
|
||||
"""
|
||||
# first check if we have room for this
|
||||
self.validate_slot_usage(obj)
|
||||
|
||||
slots = self.slots
|
||||
use_slot = getattr(obj, "inventory_use_slot", WieldLocation.BACKPACK)
|
||||
|
||||
if use_slot is WieldLocation.TWO_HANDS:
|
||||
# two-handed weapons can't co-exist with weapon/shield-hand used items
|
||||
slots[WieldLocation.WEAPON_HAND] = slots[WieldLocation.SHIELD_HAND] = None
|
||||
slots[use_slot] = obj
|
||||
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
|
||||
slots[WieldLocation.TWO_HANDS] = None
|
||||
slots[use_slot] = obj
|
||||
elif use_slot is WieldLocation.BACKPACK:
|
||||
# backpack has multiple slots.
|
||||
slots[use_slot].append(obj)
|
||||
else:
|
||||
# for others (body, head), just replace whatever's there
|
||||
slots[use_slot] = obj
|
||||
|
||||
# store new state
|
||||
self._save()
|
||||
|
||||
def store(self, obj):
|
||||
"""
|
||||
Put something in the backpack specifically (even if it could be wield/worn).
|
||||
|
||||
"""
|
||||
# check if we have room
|
||||
self.validate_slot_usage(obj)
|
||||
self.slots[WieldLocation.BACKPACK].append(obj)
|
||||
self._save()
|
||||
|
||||
def remove(self, obj_or_slot):
|
||||
"""
|
||||
Remove specific object or objects from a slot.
|
||||
|
||||
Args:
|
||||
obj_or_slot (EvAdventureObject or WieldLocation): The specific object or
|
||||
location to empty. If this is WieldLocation.BACKPACK, all items
|
||||
in the backpack will be emptied and returned!
|
||||
Returns:
|
||||
list: A list of 0, 1 or more objects emptied from the inventory.
|
||||
|
||||
"""
|
||||
slots = self.slots
|
||||
ret = []
|
||||
if isinstance(obj_or_slot, WieldLocation):
|
||||
ret = slots[obj_or_slot]
|
||||
slots[obj_or_slot] = [] if obj_or_slot is WieldLocation.BACKPACK else None
|
||||
elif obj_or_slot in self.obj.contents:
|
||||
# object is in inventory, find out which slot and empty it
|
||||
for slot, objslot in slots:
|
||||
if slot is WieldLocation.BACKPACK:
|
||||
try:
|
||||
ret = objslot.remove(obj_or_slot)
|
||||
break
|
||||
except ValueError:
|
||||
pass
|
||||
elif objslot is obj_or_slot:
|
||||
ret = objslot
|
||||
slots[slot] = None
|
||||
break
|
||||
if ret:
|
||||
self._save()
|
||||
return ret
|
||||
|
||||
|
||||
class LivingMixin:
|
||||
"""
|
||||
Helpers shared between all living things.
|
||||
|
||||
"""
|
||||
|
||||
@property
|
||||
def hurt_level(self):
|
||||
|
|
@ -353,7 +263,7 @@ class EvAdventureCharacter(DefaultCharacter):
|
|||
|
||||
def heal(self, hp, healer=None):
|
||||
"""
|
||||
Heal the character by a certain amount of HP.
|
||||
Heal by a certain amount of HP.
|
||||
|
||||
"""
|
||||
damage = self.hp_max - self.hp
|
||||
|
|
@ -365,6 +275,54 @@ class EvAdventureCharacter(DefaultCharacter):
|
|||
else:
|
||||
self.msg(f"|g{healer.key} heals you for {healed} health.|n")
|
||||
|
||||
|
||||
class EvAdventureCharacter(LivingMixin, DefaultCharacter):
|
||||
"""
|
||||
A Character for use with EvAdventure. This also works fine for
|
||||
monsters and NPCS.
|
||||
|
||||
"""
|
||||
|
||||
# these are the ability bonuses. Defense is always 10 higher
|
||||
strength = AttributeProperty(default=1)
|
||||
dexterity = AttributeProperty(default=1)
|
||||
constitution = AttributeProperty(default=1)
|
||||
intelligence = AttributeProperty(default=1)
|
||||
wisdom = AttributeProperty(default=1)
|
||||
charisma = AttributeProperty(default=1)
|
||||
|
||||
exploration_speed = AttributeProperty(default=120)
|
||||
combat_speed = AttributeProperty(default=40)
|
||||
|
||||
hp = AttributeProperty(default=4)
|
||||
hp_max = AttributeProperty(default=4)
|
||||
level = AttributeProperty(default=1)
|
||||
xp = AttributeProperty(default=0)
|
||||
|
||||
morale = AttributeProperty(default=9) # only used for NPC/monster morale checks
|
||||
|
||||
@lazy_property
|
||||
def equipment(self):
|
||||
"""Allows to access equipment like char.equipment.worn"""
|
||||
return EquipmentHandler(self)
|
||||
|
||||
@property
|
||||
def weapon(self):
|
||||
"""
|
||||
Quick access to the character's currently wielded weapon.
|
||||
|
||||
"""
|
||||
self.equipment.weapon
|
||||
|
||||
@property
|
||||
def armor(self):
|
||||
"""
|
||||
Quick access to the character's current armor.
|
||||
Will return the "Unarmored" armor level (11) if none other are found.
|
||||
|
||||
"""
|
||||
self.equipment.armor or 11
|
||||
|
||||
def at_pre_object_receive(self, moved_object, source_location, **kwargs):
|
||||
"""
|
||||
Hook called by Evennia before moving an object here. Return False to abort move.
|
||||
|
|
@ -414,7 +372,6 @@ class EvAdventureCharacter(DefaultCharacter):
|
|||
"""
|
||||
self.equipment.remove(moved_object)
|
||||
|
||||
|
||||
def at_damage(self, dmg, attacker=None):
|
||||
"""
|
||||
Called when receiving damage for whatever reason. This
|
||||
|
|
@ -440,7 +397,7 @@ class EvAdventureCharacter(DefaultCharacter):
|
|||
"""
|
||||
|
||||
|
||||
class EvAdventureNPC(DefaultCharacter):
|
||||
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
|
||||
|
|
@ -452,19 +409,15 @@ class EvAdventureNPC(DefaultCharacter):
|
|||
|
||||
Morale is set explicitly per-NPC, usually between 7 and 9.
|
||||
|
||||
Monsters don't use equipment in the way PCs do, instead their weapons and equipment
|
||||
are baked into their HD (and/or dropped as loot when they go down). If you want monsters
|
||||
or NPCs that can level and work the same as PCs, base them off the EvAdventureCharacter
|
||||
class instead.
|
||||
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).
|
||||
|
||||
Unlike for a Character, we generate all the abilities dynamically based on HD.
|
||||
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)
|
||||
# note: this is the armor bonus, 10 lower than the armor defence (what is usually
|
||||
# referred to as ascending AC for many older D&D versions). So if AC is 14, this value
|
||||
# should be 4.
|
||||
armor = AttributeProperty(default=1)
|
||||
armor = AttributeProperty(default=11)
|
||||
morale = AttributeProperty(default=9)
|
||||
hp = AttributeProperty(default=8)
|
||||
|
||||
|
|
@ -502,4 +455,3 @@ class EvAdventureNPC(DefaultCharacter):
|
|||
|
||||
"""
|
||||
self.hp = self.hp_max
|
||||
|
||||
|
|
|
|||
60
evennia/contrib/tutorials/evadventure/enums.py
Normal file
60
evennia/contrib/tutorials/evadventure/enums.py
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
"""
|
||||
Enums are constants representing different things in EvAdventure. The advantage
|
||||
of using an Enum over, say, a string is that if you make a typo using an unknown
|
||||
enum, Python will give you an error while a typo in a string may go through silently.
|
||||
|
||||
It's used as a direct reference:
|
||||
|
||||
from enums import Ability
|
||||
|
||||
if abi is Ability.STR:
|
||||
# ...
|
||||
|
||||
To get the `value` of an enum (must always be hashable, useful for Attribute lookups), use
|
||||
`Ability.STR.value` (which would return 'strength' in our case).
|
||||
|
||||
"""
|
||||
from enum import Enum
|
||||
|
||||
class Ability(Enum):
|
||||
"""
|
||||
The six base abilities (defense is always bonus + 10)
|
||||
|
||||
"""
|
||||
STR = "strength"
|
||||
DEX = "dexterity"
|
||||
CON = "constitution"
|
||||
INT = "intelligence"
|
||||
WIS = "wisdom"
|
||||
CHA = "charisma"
|
||||
|
||||
STR_DEFENSE = "strength_defense"
|
||||
DEX_DEFENSE = "dexterity_defense"
|
||||
CON_DEFENSE = "constitution_defense"
|
||||
INT_DEFENSE = "intelligence_defense"
|
||||
WIS_DEFENSE = "wisdom_defense"
|
||||
CHA_DEFENSE = "charisma_defense"
|
||||
|
||||
ARMOR = "armor"
|
||||
HP = "hp"
|
||||
EXPLORATION_SPEED = "exploration_speed"
|
||||
COMBAT_SPEED = "combat_speed"
|
||||
LEVEL = "level"
|
||||
XP = "xp"
|
||||
|
||||
class WieldLocation(Enum):
|
||||
"""
|
||||
Wield (or wear) locations.
|
||||
|
||||
"""
|
||||
# wield/wear location
|
||||
BACKPACK = "backpack"
|
||||
WEAPON_HAND = "weapon_hand"
|
||||
SHIELD_HAND = "shield_hand"
|
||||
TWO_HANDS = "two_handed_weapons"
|
||||
BODY = "body" # armor
|
||||
HEAD = "head" # helmets
|
||||
|
||||
# combat-related
|
||||
OPTIMAL_DISTANCE = "optimal_distance"
|
||||
SUBOPTIMAL_DISTANCE = "suboptimal_distance"
|
||||
|
|
@ -3,11 +3,16 @@ All items in the game inherit from a base object. The properties (what you can d
|
|||
with an object, such as wear, wield, eat, drink, kill etc) are all controlled by
|
||||
Tags.
|
||||
|
||||
|
||||
|
||||
"""
|
||||
|
||||
from evennia.objects.objects import DefaultObject
|
||||
from evennia.typeclasses.attributes import AttributeProperty
|
||||
|
||||
from .enums import WieldLocation, Ability
|
||||
|
||||
|
||||
|
||||
class EvAdventureObject(DefaultObject):
|
||||
"""
|
||||
|
|
@ -15,12 +20,13 @@ class EvAdventureObject(DefaultObject):
|
|||
|
||||
"""
|
||||
# inventory management
|
||||
wield_slot = AttributeProperty(default=None)
|
||||
wear_slot = AttributeProperty(default=None)
|
||||
inventory_slot_usage = AttributeProperty(default=1)
|
||||
inventory_use_slot = AttributeProperty(default=WieldLocation.BACKPACK)
|
||||
# how many inventory slots it uses (can be a fraction)
|
||||
size = AttributeProperty(default=1)
|
||||
armor = AttributeProperty(default=0)
|
||||
# when 0, item is destroyed and is unusable
|
||||
quality = AttributeProperty(default=1)
|
||||
value = AttributeProperty(default=0)
|
||||
|
||||
|
||||
class EvAdventureObjectFiller(EvAdventureObject):
|
||||
|
|
@ -43,10 +49,10 @@ class EvAdventureWeapon(EvAdventureObject):
|
|||
Base weapon class for all EvAdventure weapons.
|
||||
|
||||
"""
|
||||
wield_slot = AttributeProperty(default="weapon")
|
||||
inventory_use_slot = AttributeProperty(WieldLocation.WEAPON_HAND)
|
||||
|
||||
attack_type = AttributeProperty(default="strength")
|
||||
defense_type = AttributeProperty(default="armor")
|
||||
attack_type = AttributeProperty(default=Ability.STR)
|
||||
defense_type = AttributeProperty(default=Ability.ARMOR)
|
||||
damage_roll = AttributeProperty(default="1d6")
|
||||
|
||||
# at which ranges this weapon can be used. If not listed, unable to use
|
||||
|
|
|
|||
|
|
@ -2713,7 +2713,7 @@ def run_in_main_thread(function_or_method, *args, **kwargs):
|
|||
return threads.blockingCallFromThread(reactor, function_or_method, *args, **kwargs)
|
||||
|
||||
|
||||
_INT2STR_MAP_NOUN = {1: "one", 2: "two", 3: "three", 4: "four", 5: "five", 6: "six",
|
||||
_INT2STR_MAP_NOUN = {0: "no", 1: "one", 2: "two", 3: "three", 4: "four", 5: "five", 6: "six",
|
||||
7: "seven", 8: "eight", 9: "nine", 10: "ten", 11: "eleven", 12: "twelve"}
|
||||
_INT2STR_MAP_ADJ = {1: "1st", 2: "2nd", 3: "3rd"} # rest is Xth.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue