mirror of
https://github.com/evennia/evennia.git
synced 2026-03-23 16:26:30 +01:00
Made simple map, start shops
This commit is contained in:
parent
b6533d0744
commit
2f28c8982d
5 changed files with 242 additions and 38 deletions
|
|
@ -69,3 +69,20 @@ class WieldLocation(Enum):
|
|||
# combat-related
|
||||
OPTIMAL_DISTANCE = "optimal_distance"
|
||||
SUBOPTIMAL_DISTANCE = "suboptimal_distance"
|
||||
|
||||
|
||||
class ObjType(Enum):
|
||||
"""
|
||||
Object types
|
||||
|
||||
"""
|
||||
|
||||
WEAPON = "weapon"
|
||||
ARMOR = "armor"
|
||||
SHIELD = "shield"
|
||||
HELMET = "helmet"
|
||||
CONSUMABLE = "consumable"
|
||||
GEAR = "gear"
|
||||
MAGIC = "magic"
|
||||
QUEST = "quest"
|
||||
TREASURE = "treasure"
|
||||
|
|
|
|||
|
|
@ -3,14 +3,26 @@ 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.
|
||||
|
||||
Every object has one of a few `obj_type`-category tags:
|
||||
- weapon
|
||||
- armor
|
||||
- shield
|
||||
- helmet
|
||||
- consumable (potions, torches etc)
|
||||
- magic (runestones, magic items)
|
||||
- quest (quest-items)
|
||||
- treasure (valuable to sell)
|
||||
|
||||
It's possible for an item to have more than one tag, such as a golden helmet (helmet+treasure) or
|
||||
rune sword (weapon+quest).
|
||||
|
||||
"""
|
||||
|
||||
from evennia import AttributeProperty, TagProperty
|
||||
from evennia.objects.objects import DefaultObject
|
||||
from evennia.typeclasses.attributes import AttributeProperty
|
||||
from evennia.utils.utils import make_iter
|
||||
|
||||
from .enums import Ability, WieldLocation
|
||||
from .enums import Ability, ObjType, WieldLocation
|
||||
|
||||
|
||||
class EvAdventureObject(DefaultObject):
|
||||
|
|
@ -23,15 +35,23 @@ class EvAdventureObject(DefaultObject):
|
|||
inventory_use_slot = AttributeProperty(WieldLocation.BACKPACK)
|
||||
# how many inventory slots it uses (can be a fraction)
|
||||
size = AttributeProperty(1)
|
||||
armor = AttributeProperty(0)
|
||||
# items that are usable (like potions) have a value larger than 0. Wieldable items
|
||||
# like weapons, armor etc are not 'usable' in this respect.
|
||||
uses = AttributeProperty(0)
|
||||
# when 0, item is destroyed and is unusable
|
||||
quality = AttributeProperty(1)
|
||||
value = AttributeProperty(0)
|
||||
|
||||
help_text = AttributeProperty("")
|
||||
# can also be an iterable, for adding multiple obj-type tags
|
||||
obj_type = ObjType.TREASURE.value
|
||||
|
||||
def at_object_creation(self):
|
||||
for obj_type in make_iter(self.obj_type):
|
||||
self.tags.add(obj_type, category="obj_type")
|
||||
|
||||
def has_obj_type(self, objtype):
|
||||
"""
|
||||
Check if object is of a particular type.
|
||||
|
||||
typeobj_enum (enum.ObjType): A type to check, like enums.TypeObj.TREASURE.
|
||||
|
||||
"""
|
||||
return objtype.value in make_iter(self.obj_type)
|
||||
|
||||
def get_help(self):
|
||||
"""
|
||||
|
|
@ -93,9 +113,30 @@ class EvAdventureObjectFiller(EvAdventureObject):
|
|||
|
||||
"""
|
||||
|
||||
obj_type = ObjType.QUEST.value # can't be sold
|
||||
quality = AttributeProperty(0)
|
||||
|
||||
|
||||
class EvAdventureQuest(EvAdventureObject):
|
||||
"""
|
||||
A quest object. These cannot be sold and only be used for quest resolution.
|
||||
|
||||
"""
|
||||
|
||||
obj_type = ObjType.QUEST.value
|
||||
value = AttributeProperty(0)
|
||||
|
||||
|
||||
class EvAdventureTreasure(EvAdventureObject):
|
||||
"""
|
||||
A 'treasure' is mainly useful to sell for coin.
|
||||
|
||||
"""
|
||||
|
||||
obj_type = ObjType.TREASURE.value
|
||||
value = AttributeProperty(100)
|
||||
|
||||
|
||||
class EvAdventureConsumable(EvAdventureObject):
|
||||
"""
|
||||
Item that can be 'used up', like a potion or food. Weapons, armor etc does not
|
||||
|
|
@ -103,7 +144,7 @@ class EvAdventureConsumable(EvAdventureObject):
|
|||
|
||||
"""
|
||||
|
||||
inventory_use_slot = AttributeProperty(WieldLocation.BACKPACK)
|
||||
obj_type = ObjType.CONSUMABLE.value
|
||||
size = AttributeProperty(0.25)
|
||||
uses = AttributeProperty(1)
|
||||
|
||||
|
|
@ -134,25 +175,13 @@ class EvAdventureConsumable(EvAdventureObject):
|
|||
self.delete()
|
||||
|
||||
|
||||
class EvAdventureWeapon(EvAdventureObject):
|
||||
"""
|
||||
Base weapon class for all EvAdventure weapons.
|
||||
|
||||
"""
|
||||
|
||||
inventory_use_slot = AttributeProperty(WieldLocation.WEAPON_HAND)
|
||||
|
||||
attack_type = AttributeProperty(Ability.STR)
|
||||
defense_type = AttributeProperty(Ability.ARMOR)
|
||||
damage_roll = AttributeProperty("1d6")
|
||||
|
||||
|
||||
class WeaponEmptyHand:
|
||||
"""
|
||||
This is used when you wield no weapons. We won't create any db-object for it.
|
||||
This is a dummy-class loaded when you wield no weapons. We won't create any db-object for it.
|
||||
|
||||
"""
|
||||
|
||||
obj_type = ObjType.WEAPON.value
|
||||
key = "Empty Fists"
|
||||
inventory_use_slot = WieldLocation.WEAPON_HAND
|
||||
attack_type = Ability.STR
|
||||
|
|
@ -164,6 +193,23 @@ class WeaponEmptyHand:
|
|||
return "<WeaponEmptyHand>"
|
||||
|
||||
|
||||
class EvAdventureWeapon(EvAdventureObject):
|
||||
"""
|
||||
Base weapon class for all EvAdventure weapons.
|
||||
|
||||
"""
|
||||
|
||||
obj_type = ObjType.WEAPON.value
|
||||
inventory_use_slot = AttributeProperty(WieldLocation.WEAPON_HAND)
|
||||
quality = AttributeProperty(3)
|
||||
|
||||
# what ability used to attack with this weapon
|
||||
attack_type = AttributeProperty(Ability.STR)
|
||||
# what defense stat of the enemy it must defeat
|
||||
defense_type = AttributeProperty(Ability.ARMOR)
|
||||
damage_roll = AttributeProperty("1d6")
|
||||
|
||||
|
||||
class EvAdventureRunestone(EvAdventureWeapon):
|
||||
"""
|
||||
Base class for magic runestones. In _Knave_, every spell is represented by a rune stone
|
||||
|
|
@ -173,8 +219,44 @@ class EvAdventureRunestone(EvAdventureWeapon):
|
|||
|
||||
"""
|
||||
|
||||
obj_type = (ObjType.WEAPON.value, ObjType.MAGIC.value)
|
||||
inventory_use_slot = AttributeProperty(WieldLocation.TWO_HANDS)
|
||||
quality = AttributeProperty(3)
|
||||
|
||||
attack_type = AttributeProperty(Ability.INT)
|
||||
defense_type = AttributeProperty(Ability.CON)
|
||||
defense_type = AttributeProperty(Ability.DEX)
|
||||
damage_roll = AttributeProperty("1d8")
|
||||
|
||||
|
||||
class EvAdventureArmor(EvAdventureObject):
|
||||
"""
|
||||
Base class for all wearable Armors.
|
||||
|
||||
"""
|
||||
|
||||
obj_type = ObjType.ARMOR.value
|
||||
inventory_use_slot = AttributeProperty(WieldLocation.BODY)
|
||||
armor = AttributeProperty(11)
|
||||
quality = AttributeProperty(3)
|
||||
|
||||
|
||||
class EvAdventureShield(EvAdventureArmor):
|
||||
"""
|
||||
Base class for all Shields.
|
||||
|
||||
"""
|
||||
|
||||
obj_type = ObjType.SHIELD.value
|
||||
inventory_use_slot = AttributeProperty(WieldLocation.SHIELD_HAND)
|
||||
armor = AttributeProperty(1)
|
||||
|
||||
|
||||
class EvAdventureHelmet(EvAdventureArmor):
|
||||
"""
|
||||
Base class for all Helmets.
|
||||
|
||||
"""
|
||||
|
||||
obj_type = ObjType.HELMET.value
|
||||
inventory_use_slot = AttributeProperty(WieldLocation.HEAD)
|
||||
armor = AttributeProperty(1)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,28 @@ EvAdventure rooms.
|
|||
|
||||
"""
|
||||
|
||||
from evennia import AttributeProperty, DefaultRoom, TagProperty
|
||||
from copy import deepcopy
|
||||
|
||||
from evennia import AttributeProperty, DefaultCharacter, DefaultRoom, TagProperty
|
||||
from evennia.utils.utils import inherits_from
|
||||
|
||||
_MAP_GRID = [
|
||||
[" ", " ", " ", " ", " "],
|
||||
[" ", " ", " ", " ", " "],
|
||||
[" ", " ", "@", " ", " "],
|
||||
[" ", " ", " ", " ", " "],
|
||||
[" ", " ", " ", " ", " "],
|
||||
]
|
||||
_EXIT_GRID_SHIFT = {
|
||||
"north": (0, 1, "|"),
|
||||
"east": (1, 0, "-"),
|
||||
"south": (0, -1, "|"),
|
||||
"west": (-1, 0, "-"),
|
||||
"northeast": (1, 1, "/"),
|
||||
"southeast": (1, -1, "\\"),
|
||||
"southwest": (-1, -1, "/"),
|
||||
"northwest": (-1, 1, "\\"),
|
||||
}
|
||||
|
||||
|
||||
class EvAdventureRoom(DefaultRoom):
|
||||
|
|
@ -18,8 +39,39 @@ class EvAdventureRoom(DefaultRoom):
|
|||
allow_pvp = False
|
||||
allow_death = False
|
||||
|
||||
def format_appearance(self, appearance, looker, **kwargs):
|
||||
"""Don't left-strip the appearance string"""
|
||||
return appearance.rstrip()
|
||||
|
||||
class EvAdventurePvPRoom(DefaultRoom):
|
||||
def get_display_header(self, looker, **kwargs):
|
||||
"""
|
||||
Display the current location as a mini-map.
|
||||
"""
|
||||
if not inherits_from(looker, DefaultCharacter):
|
||||
# we don't need a map for npcs/mobs
|
||||
return ""
|
||||
|
||||
# build a map
|
||||
map_grid = deepcopy(_MAP_GRID)
|
||||
dx0, dy0 = 2, 2
|
||||
map_grid[dy0][dx0] = "|w@|n"
|
||||
for exi in self.exits:
|
||||
dx, dy, symbol = _EXIT_GRID_SHIFT.get(exi.key, (None, None, None))
|
||||
if symbol is None:
|
||||
# we have a non-cardinal direction to go to - mark us blue to indicate this
|
||||
map_grid[dy0][dx0] = "|b>|n"
|
||||
continue
|
||||
map_grid[dy0 + dy][dx0 + dx] = symbol
|
||||
if exi.destination != self:
|
||||
map_grid[dy0 + dy + dy][dx0 + dx + dx] = "X"
|
||||
|
||||
# Note that on the grid, dy is really going *downwards* (origo is
|
||||
# in the top left), so we need to reverse the order at the end to mirror it
|
||||
# vertically and have it come out right.
|
||||
return " " + "\n ".join("".join(line) for line in reversed(map_grid))
|
||||
|
||||
|
||||
class EvAdventurePvPRoom(EvAdventureRoom):
|
||||
"""
|
||||
Room where PvP can happen, but noone gets killed.
|
||||
|
||||
|
|
|
|||
36
evennia/contrib/tutorials/evadventure/shops.py
Normal file
36
evennia/contrib/tutorials/evadventure/shops.py
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
"""
|
||||
EvAdventure Shop system.
|
||||
|
||||
|
||||
A shop is run by an NPC. It can provide one or more of several possible services:
|
||||
|
||||
- Buy from a pre-set list of (possibly randomized) items. Cost is based on the item's value,
|
||||
adjusted by how stingy the shopkeeper is. When bought this way, the item is
|
||||
generated on the fly and passed to the player character's inventory. Inventory files are
|
||||
a list of prototypes, normally from a prototype-file. A random selection of items from each
|
||||
inventory file is available.
|
||||
- Sell items to the shop for a certain percent of their value. One could imagine being able
|
||||
to buy back items again, but we will instead _destroy_ sold items, so as to remove them
|
||||
from circulation. In-game we can say it's because the merchants collect the best stuff
|
||||
to sell to collectors in the big city later. Each merchant buys a certain subset of items
|
||||
based on their tags.
|
||||
- Buy a service. For a cost, a certain action is performed for the character; this applies
|
||||
immediately when bought. The most notable services are healing and converting coin to XP.
|
||||
- Buy rumors - this is echoed to the player for a price. Different merchants could have
|
||||
different rumors (or randomized ones).
|
||||
- Quest - gain or hand in a quest for a merchant.
|
||||
|
||||
All shops are menu-driven. One starts talking to the npc and will then end up in their shop
|
||||
interface.
|
||||
|
||||
"""
|
||||
|
||||
from evennia.utils.evmenu import EvMenu
|
||||
|
||||
|
||||
def start_npc_menu(caller, shopkeeper, **kwargs):
|
||||
"""
|
||||
Access function - start the NPC interaction/shop interface.
|
||||
|
||||
|
||||
"""
|
||||
|
|
@ -1367,6 +1367,20 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
|
|||
"""
|
||||
return ""
|
||||
|
||||
def format_appearance(self, appearance, looker, **kwargs):
|
||||
"""
|
||||
Final processing of the entire appearance string. Called by `return_appearance`.
|
||||
|
||||
Args:
|
||||
appearance (str): The compiled appearance string.
|
||||
looker (Object): Object doing the looking.
|
||||
**kwargs: Arbitrary data for use when overriding.
|
||||
Returns:
|
||||
str: The final formatted output.
|
||||
|
||||
"""
|
||||
return appearance.strip()
|
||||
|
||||
def return_appearance(self, looker, **kwargs):
|
||||
"""
|
||||
Main callback used by 'look' for the object to describe itself.
|
||||
|
|
@ -1398,17 +1412,20 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
|
|||
if not looker:
|
||||
return ""
|
||||
|
||||
# populate the appearance_template string. It's a good idea to strip it and
|
||||
# let the client add any extra spaces instead.
|
||||
return self.appearance_template.format(
|
||||
name=self.get_display_name(looker, **kwargs),
|
||||
desc=self.get_display_desc(looker, **kwargs),
|
||||
header=self.get_display_header(looker, **kwargs),
|
||||
footer=self.get_display_footer(looker, **kwargs),
|
||||
exits=self.get_display_exits(looker, **kwargs),
|
||||
characters=self.get_display_characters(looker, **kwargs),
|
||||
things=self.get_display_things(looker, **kwargs),
|
||||
).strip()
|
||||
# populate the appearance_template string.
|
||||
return self.format_appearance(
|
||||
self.appearance_template.format(
|
||||
name=self.get_display_name(looker, **kwargs),
|
||||
desc=self.get_display_desc(looker, **kwargs),
|
||||
header=self.get_display_header(looker, **kwargs),
|
||||
footer=self.get_display_footer(looker, **kwargs),
|
||||
exits=self.get_display_exits(looker, **kwargs),
|
||||
characters=self.get_display_characters(looker, **kwargs),
|
||||
things=self.get_display_things(looker, **kwargs),
|
||||
),
|
||||
looker,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
#
|
||||
# Hook methods
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue