da da da.

This commit is contained in:
Francesco Cappelli 2022-01-25 22:23:39 +01:00
parent 043cdfc230
commit db12c674ef
15 changed files with 734 additions and 212 deletions

View file

@ -1,6 +1,7 @@
from evennia import default_cmds, create_object, search_tag
from evennia.utils import inherits_from
from evennia.utils.eveditor import EvEditor
from evennia.prototypes import spawner
from commands.command import Command
from typeclasses.exits import BaseDoor
@ -24,6 +25,7 @@ def _descdoor_quit(caller):
caller.attributes.remove("evmenu_target")
caller.msg("Exited editor.")
class CmdDescDoor(Command):
"""
describe a BaseDoor in the current room.
@ -129,6 +131,7 @@ class CmdOpen(default_cmds.CmdOpen):
back_exit.db.return_exit = new_exit
return new_exit
class CmdUpdateLightState(Command):
"""
update room light state.
@ -160,14 +163,15 @@ class CmdUpdateLightState(Command):
target.check_light_state()
caller.msg("Performed update on {}!".format(target.key))
class CmdZone(Command):
"""
creates, deletes or lists zones
Usage:
zone[/list||/del||/addroom] [zonename] [= room]
zone[/add||/del||/map] [zonename] [= room|size]
Creates a new zone.
Manages zones.
"""
@ -182,73 +186,70 @@ class CmdZone(Command):
caller = self.caller
if "list" in self.switches:
string = "";
zones = search_tag(key="zone", category="general")
for zone in zones:
string += "|c{}|n ({})\n".format(zone.name, zone.dbref)
rooms = search_tag(key=zone.name, category="zoneId")
for room in rooms:
string += "- {} ({})\n".format(room.name, room.dbref)
caller.msg("Zones found: \n" + string)
return
if not self.args:
string = "Usage: zone[/list||/del||/addroom] [zonename] [= room]"
caller.msg(string)
if [ele for ele in ["del", "add", "map"] if(ele in self.switches)] and not self.args:
caller.msg(self.get_help(caller, self.cmdset))
return
if "del" in self.switches:
self.delete_zone(self.args)
elif "addroom" in self.switches:
self.add_room_to_zone(self.lhs, self.rhs)
self.delete_zone()
elif "map" in self.switches:
self.print_map()
elif "add" in self.switches:
zone, errors = Zone.create(key=self.lhs, size=self.rhs)
if not errors:
caller.msg("Created zone |w{}|n.".format(zone.name))
else:
caller.msg("Errors creating zone:|n{}|n.".format(errors))
else:
zone = create_object(Zone, key=self.args)
caller.msg("Created zone |w{}|n.".format(zone.name))
string = ""
zones = search_tag(key="zone", category="general")
for zone in zones:
string += "|c{}|n ({})\n".format(zone.name, zone.dbref)
rooms = search_tag(key=zone.name, category="zoneId")
for room in rooms:
string += "- {} ({}) ({},{})\n".format(room.name, room.dbref, room.db.x, room.db.y)
def delete_zone(self, zone_string):
caller.msg("Zones found: \n" + string)
def print_map(self):
caller = self.caller
zone = caller.search(zone_string, global_search=True, exact=True)
zone = caller.search(self.args, global_search=True, exact=True)
if not zone:
return
map_str = ""
for y in range(zone.db.size):
for x in range(zone.db.size):
map_str += zone.ndb.map[x][y]['room_map_icon']
map_str += '|/'
caller.msg(map_str)
def delete_zone(self):
caller = self.caller
zone = caller.search(self.args, global_search=True, exact=True)
if not zone:
return
if not inherits_from(zone, Zone):
caller.msg("{} is not a valid zone.",format(zone.name))
caller.msg("{} is not a valid zone.".format(zone.name))
return
key = zone.name
zone.delete()
caller.msg("Zone {} deleted.".format(key))
def add_room_to_zone(self, zone_string, room_string):
caller = self.caller
zone = caller.search(zone_string, global_search=True, exact=True)
if not zone:
return
if not inherits_from(zone, Zone):
caller.msg("{} is not a valid zone.",format(zone.name))
return
room = caller.search(room_string, global_search=True)
if not room:
return
if not inherits_from(room, Room):
caller.msg("{} is not a valid room.",format(room.name))
return
zone.add_room(room)
caller.msg("{} added to zone {}.".format(room.name, zone.name))
class CmdAddToZone(Command):
"""
add a room to a zone
Usage:
@addtozone obj = zone
addtozone obj = zone,x,y
Adds a room to an existing zone.
"""
key = "@addtozone"
key = "addtozone"
locks = "cmd:perm(zone) or perm(Builders)"
help_category = "Building"
@ -258,28 +259,124 @@ class CmdAddToZone(Command):
"""
caller = self.caller
if self.rhs:
# We have an =
zone = caller.search(self.rhs, global_search=True, exact=True)
if not zone:
self.msg("Zone %s doesn't exist." % self.rhs)
return
if not utils.inherits_from(zone, Zone):
self.msg("{r%s is not a valid zone.{n" % zone.name)
return
room = caller.search(self.lhs)
if not room:
self.msg("{rRoom %s doesn't exist.{n" % self.lhs)
return
if not utils.inherits_from(room, BaseRoom):
self.msg("{r%s is not a valid room.{n" % room.name)
return
room.add_to_zone(zone.name)
self.msg("Room %s (%s) added to zone %s (%s)." % (room.name, room.dbref, zone.name, zone.dbref))
else:
self.msg("{rUsage: @addtozone obj = zone{n")
if len(self.rhslist) < 3:
caller.msg(self.get_help(caller, self.cmdset))
return
zone_key, x, y = self.rhslist
if zone_key and x and y:
zone = caller.search(zone_key, global_search=True, exact=True)
if not zone:
caller.msg("Zone {} doesn't exist.".format(zone_key))
return
if not inherits_from(zone, Zone):
caller.msg("|r{} is not a valid zone.|n".format(zone.name))
return
room = caller.search(self.lhs, global_search=True, nofound_string="|rRoom {} doesn't exist.|n".format(self.lhs))
if not room:
return
if not inherits_from(room, Room):
caller.msg("|r{} is not a valid room.|n".format(room.name))
return
try:
if room.db.zone:
caller.msg("|r{} is already assigned to zone {}.|n".format(room.name, zone.name))
return
zone.add_room(room, x, y)
except ValueError as ve:
caller.msg(ve.args[0])
return
caller.msg("Room {} ({}) added to zone {} ({}).".format(room.name, room.dbref, zone.name, zone.dbref))
else:
caller.msg(self.get_help(caller, self.cmdset))
return
class CmdPopen(Command):
"""
open a new exit from prototype linking two rooms
Usage:
popen <prototype>[;alias;alias..] [,<return exit>[;alias;..]]] = <origin>,<destination>
Handles the creation of exits. If a destination is given, the exit
will point there. The <return exit> argument sets up an exit at the
destination leading back to the current room. Destination name
can be given both as a #dbref and a name, if that name is globally
unique.
"""
key = "popen"
locks = "cmd:perm(open) or perm(Builder)"
help_category = "Building"
new_obj_lockstring = "control:id({id}) or perm(Admin);delete:id({id}) or perm(Admin)"
def func(self):
"""
This is where the processing starts.
Uses the ObjManipCommand.parser() for pre-processing
as well as the self.create_exit() method.
"""
caller = self.caller
if not self.args or not self.rhs or len(self.rhslist) != 2:
caller.msg(self.get_help(caller, self.cmdset))
return
exit_prototype = self.lhs_objs[0]["name"]
exit_aliases = self.lhs_objs[0]["aliases"]
location_name, destination_name = self.rhslist
# first, check if the destination and origin exist.
destination = caller.search(destination_name, global_search=True)
if not destination:
return
location = caller.search(location_name, global_search=True)
if not location:
return
try:
exit_obj, *rest = spawner.spawn(exit_prototype)
except KeyError:
caller.msg("Prototype {} not found".format(exit_prototype))
return
exit_obj.location = location
exit_obj.destination = destination
exit_obj.aliases.add(exit_aliases)
return_exit_object = None
if len(self.lhs_objs) == 2:
return_exit_prototype = self.lhs_objs[1]["name"]
return_exit_aliases = self.lhs_objs[1]["aliases"]
try:
return_exit_object, *rest = spawner.spawn(return_exit_prototype)
except KeyError:
caller.msg("Return prototype {} not found, rolling back...".format(return_exit_prototype))
exit_obj.delete()
return
return_exit_object.location = destination
return_exit_object.destination = location
# BaseDoor requires a return exit
if inherits_from(exit_obj, "typeclasses.exits.BaseDoor"):
if not return_exit_object:
return_exit_object, *rest = spawner.spawn(exit_prototype)
return_exit_object.location = destination
return_exit_object.destination = location
exit_obj.db.return_exit = return_exit_object
return_exit_object.db.return_exit = exit_obj
caller.msg("Created exit {} from {} to {}.".format(exit_obj.name, location.name, destination.name))
if return_exit_object:
caller.msg("Created exit {} from {} to {}.".format(return_exit_object.name, destination.name, location.name))

View file

@ -14,7 +14,7 @@ from evennia.utils import inherits_from
from evennia.utils.utils import list_to_string
from evennia.utils.eveditor import EvEditor
from utils.building import create_room, create_exit
from utils.building import create_exit
from utils.utils import has_tag, fmt_light, fmt_dark, toggle_effect, has_effect, indefinite_article
from typeclasses.exits import BaseDoor
from typeclasses.rooms import IndoorRoom
@ -47,6 +47,39 @@ class Command(default_cmds.MuxCommand):
"""
def parse(self):
"""
We need to expand the default parsing to get all
the cases, see the module doc.
"""
# get all the normal parsing done (switches etc)
super().parse()
obj_defs = ([], []) # stores left- and right-hand side of '='
obj_attrs = ([], []) # "
for iside, arglist in enumerate((self.lhslist, self.rhslist)):
# lhslist/rhslist is already split by ',' at this point
for objdef in arglist:
aliases, option, attrs = [], None, []
if ":" in objdef:
objdef, option = [part.strip() for part in objdef.rsplit(":", 1)]
if ";" in objdef:
objdef, aliases = [part.strip() for part in objdef.split(";", 1)]
aliases = [alias.strip() for alias in aliases.split(";") if alias.strip()]
if "/" in objdef:
objdef, attrs = [part.strip() for part in objdef.split("/", 1)]
attrs = [part.strip().lower() for part in attrs.split("/") if part.strip()]
# store data
obj_defs[iside].append({"name": objdef, "option": option, "aliases": aliases})
obj_attrs[iside].append({"name": objdef, "attrs": attrs})
# store for future access
self.lhs_objs = obj_defs[0]
self.rhs_objs = obj_defs[1]
self.lhs_objattr = obj_attrs[0]
self.rhs_objattr = obj_attrs[1]
def at_post_cmd(self):
caller = self.caller
prompt = "|_|/°|w%s|n°: " % (caller.location)
@ -103,7 +136,7 @@ class CmdGet(Command):
caller.msg("Get what?")
return
if inherits_from(caller.location, IndoorRoom) and not caller.location.db.is_lit:
if inherits_from(caller.location, IndoorRoom) and not caller.location.db.is_lit and not caller.is_superuser:
caller.msg("Its too dark to get anything.")
return
@ -543,7 +576,12 @@ class CmdInventory(Command):
class CmdCast(Command):
"""
cast a spell.
Usage:
cast <spell> [at <target>]
Casts a spell.
"""
key = "cast"
aliases = ["cs"]
@ -555,16 +593,18 @@ class CmdCast(Command):
"""
Handle parsing of:
::
<spell> [at <target>]
<spell> [at|on <target>]
"""
self.args = args = self.args.strip().lower()
spell_name, target_name = "", ""
if " at " in args:
spell_name, *rest = args.split(" at ", 1)
target_name = rest[0] if rest else ""
elif " on " in args:
spell_name, *rest = args.split(" on ", 1)
else:
spell_name = args
spell_name, rest = args, []
target_name = rest[0] if rest else ""
self.spell_name = spell_name.strip()
self.target_name = target_name.strip()
@ -573,7 +613,7 @@ class CmdCast(Command):
caller = self.caller
if not self.args or not self.spell_name:
caller.msg("Usage: cast <spell> [at <target>]")
caller.msg(self.get_help(caller, self.cmdset))
return
spell_id = self.spell_name.replace(' ', '_')
@ -600,14 +640,16 @@ class CmdTestPy(Command):
def func(self):
caller = self.caller
caller.msg(self.lhs_objs)
pattern = re.compile(r'''((?:[^ "']|"[^"]*"|'[^']*')+)''')
self.args = pattern.split(self.lhs)[1::2]
# room = create_room(self.args[0].strip(" \"'"), int(self.args[1].strip(" \"'")), int(self.args[2].strip(" \"'")), self.args[3].strip(" \"'"))
# caller.msg(room)
exit = create_exit("exit_empty", caller.location, "north")
caller.msg(exit)
# exit = create_exit("exit_empty", caller.location, "north")
# caller.msg(exit)
# caller.msg(dkmud_oob=({"testarg": "valuetestarg"}))

View file

@ -32,6 +32,8 @@ from commands.builder import CmdUpdateLightState
from commands.builder import CmdOpen
from commands.builder import CmdDescDoor
from commands.builder import CmdZone
from commands.builder import CmdAddToZone
from commands.builder import CmdPopen
from utils.crafting import CmdCraft
@ -70,9 +72,12 @@ class CharacterCmdSet(default_cmds.CharacterCmdSet):
self.add(CmdOpen())
self.add(CmdDescDoor())
self.add(CmdZone())
self.add(CmdAddToZone())
self.add(CmdPopen())
self.add(CmdCraft())
class AccountCmdSet(default_cmds.AccountCmdSet):
"""
This is the cmdset available to the Account at all times. It is

View file

@ -39,6 +39,7 @@ GLOBAL_SCRIPTS = {
'repeats': 0, 'interval': 1}
}
PROTOTYPE_MODULES += ["world.tutorial_prototypes"]
CRAFT_RECIPE_MODULES = ['world.recipes_base']
######################################################################

View file

@ -36,8 +36,6 @@ class Mob(Object):
pass
def think(self):
if not self.db.action:
self.db.action = create_object(ActionIdle, key="action_idle")
self.db.action.prepare(self)

View file

@ -36,6 +36,8 @@ class Room(DefaultRoom):
self.db.x = 0
self.db.y = 0
self.db.map_icon = '|w⊡|n'
self.db.zone = None
@ -54,6 +56,8 @@ class IndoorRoom(Room):
"You can't see anything, but the air is damp. It feels like you are far underground.",
)
map_icon = '|w□|n'
def at_object_creation(self):
super().at_object_creation()
self.locks.add("search:all()")
@ -228,9 +232,28 @@ class Zone(DefaultRoom):
provide path-finding capabilities to mob.
"""
@classmethod
def create(cls, key, account=None, **kwargs):
description = kwargs.pop("description", "This is a zone.")
size = kwargs.pop("size")
size = MAP_SIZE if not size else size
zone, errors = super(Zone, cls).create(key, account=account, description=description, **kwargs)
if not errors:
try:
zone.db.size = int(size)
except ValueError:
errors.append("Size must be an integer.")
return zone, errors
def at_object_creation(self):
super().at_object_creation()
self.db.size = MAP_SIZE
self.tags.add("zone", category="general")
self.locks.add(";".join(["get:false()", "puppet:false()", "view:perm(zone) or perm(Builder)"]))
@ -239,41 +262,27 @@ class Zone(DefaultRoom):
def at_init(self):
super().at_init()
# when reloaded recalculate path-finding data
self.create_paths()
self._create_paths()
def create_paths(self):
self.ndb.sp_graph = spath.Graph()
self.ndb.map = [[{"room_id": -1} for i in range(MAP_SIZE)] for j in range(MAP_SIZE)]
def add_room(self, room, x, y):
xi = int(x)
yi = int(y)
rooms = search_tag(key=self.name, category="zoneId")
if xi < 0 or xi >= self.db.size or yi < 0 or yi >= self.db.size:
raise ValueError("Coordinates must be between 0 and {}.".format(self.db.size - 1))
for room in rooms:
self.add_room(room)
if self.ndb.map[xi][yi]['room_id'] != -1:
raise ValueError("Coordinates ({},{}) are not empty.".format(x, y))
def add_room(self, room):
# add to map
if 0 <= room.db.x < MAP_SIZE and 0 <= room.db.y < MAP_SIZE and self.ndb.map[room.db.x][room.db.y]["room_id"] == -1:
self.ndb.map[room.db.x][room.db.y]["room_id"] = room.dbref
else:
logger.log_err("Cannot add room {} to {} at position {}:{}.".format(room.dbref, self.dbref, room.db.x, room.db.y))
raise Exception("Cannot add room {} to {} at position {}:{}.".format(room.dbref, self.dbref, room.db.x, room.db.y))
room.db.x = xi
room.db.y = yi
# avoid inserting room into graph if is already inserted
if not self.ndb.sp_graph.is_vertex(room):
self.ndb.sp_graph.add_vertex(room)
room.tags.add(self.name, category="zoneId")
room.db.zone = self
self.update_room_exits(room)
def update_room_exits(self, room):
if self.ndb.sp_graph.is_vertex(room):
for ex in room.exits: # iterate exits in the room
if not self.ndb.sp_graph.is_edge(room, ex.destination):
self.ndb.sp_graph.add_edge(room, ex.destination, 1, ex.name)
room.db.zone = self.dbref
self._add_room_to_graph(room)
def remove_room_exit(self, exit_obj):
if self.ndb.sp_graph.is_vertex(exit_obj.location) and self.ndb.sp_graph.is_edge(exit_obj.location, exit_obj.destination):
if self.ndb.sp_graph.is_vertex(exit_obj.location) and self.ndb.sp_graph.is_edge(exit_obj.location,
exit_obj.destination):
self.ndb.sp_graph.del_edge(exit_obj.location, exit_obj.destination)
def delete(self):
@ -285,3 +294,35 @@ class Zone(DefaultRoom):
def shortest_path(self, start, end):
return spath.shortestPath(self.ndb.sp_graph, start, end)
def _create_paths(self):
self.ndb.sp_graph = spath.Graph()
self.ndb.map = [[{'room_id': -1, 'room_map_icon': "|=g∙|n"} for i in range(self.db.size)] for j in range(self.db.size)]
rooms = search_tag(key=self.name, category="zoneId")
for room in rooms:
self._add_room_to_graph(room)
def _add_room_to_graph(self, room):
# add to map
if 0 <= room.db.x < self.db.size and 0 <= room.db.y < self.db.size and self.ndb.map[room.db.x][room.db.y]["room_id"] == -1:
self.ndb.map[room.db.x][room.db.y]['room_id'] = room.dbref
self.ndb.map[room.db.x][room.db.y]['room_map_icon'] = room.db.map_icon
else:
logger.log_err("Cannot add room {} to {} at position {}:{}.".format(room.dbref, self.dbref, room.db.x, room.db.y))
raise Exception("Cannot add room {} to {} at position {}:{}.".format(room.dbref, self.dbref, room.db.x, room.db.y))
# avoid inserting room into graph if is already inserted
if not self.ndb.sp_graph.is_vertex(room):
self.ndb.sp_graph.add_vertex(room)
room.tags.add(self.name, category="zoneId")
room.db.zone = self
self._update_room_exits(room)
def _update_room_exits(self, room):
if self.ndb.sp_graph.is_vertex(room):
for ex in room.exits: # iterate exits in the room
if not self.ndb.sp_graph.is_edge(room, ex.destination):
self.ndb.sp_graph.add_edge(room, ex.destination, 1, ex.name)

View file

@ -1,22 +1,7 @@
from evennia.contrib.ingame_python import typeclasses
from evennia.prototypes import spawner
from evennia.utils import inherits_from
from evennia.utils.search import search_object
def create_room(room_prototype, x, y, zone_id):
zones = search_object(zone_id, typeclass="typeclasses.rooms.Zone", exact=True)
if not zones:
raise Exception("create_room: cannot find zone {}".format(zone_id))
zone = zones[0]
room, *rest = spawner.spawn(room_prototype)
room.db.x = x
room.db.y = y
zone.add_room(room)
return room
def create_exit(exit_prototype, location, direction):
x = location.db.x

View file

@ -120,6 +120,7 @@ a full example of the components for creating a sword from base components.
"""
from copy import copy
from evennia import logger
from evennia.utils.utils import callables_from_module, inherits_from, make_iter, iter_to_string
from evennia.commands.cmdset import CmdSet
from evennia.commands.command import Command
@ -232,7 +233,7 @@ class CraftingRecipeBase:
**kwargs: Any optional properties relevant to this send.
"""
self.crafter.msg(message, {"type": "crafting"})
self.crafter.msg(message, oob=({"type": "crafting"}))
def pre_craft(self, **kwargs):
"""
@ -382,6 +383,7 @@ class CraftingRecipe(CraftingRecipeBase):
## Properties on the class level:
- `name` (str): The name of this recipe. This should be globally unique.
- 'crafting_time' (int): The time needed for crafting.
### tools
@ -540,7 +542,9 @@ class CraftingRecipe(CraftingRecipeBase):
# general craft-failure msg to show after other error-messages.
failure_message = ""
# show after a successful craft
success_message = "You successfully craft {outputs}!"
success_message = "You craft {outputs}."
# recipe crafting time
crafting_time = 1
def __init__(self, crafter, *inputs, **kwargs):
"""
@ -921,6 +925,62 @@ def craft(crafter, recipe_name, *inputs, raise_exception=False, **kwargs):
return recipe.craft(raise_exception=raise_exception)
def can_craft(crafter, recipe_name, *inputs, **kwargs):
"""
Access function.Check if crafter can craft a given recipe from a source recipe module.
Args:
crafter (Object): The one doing the crafting.
recipe_name (str): The `CraftRecipe.name` to use. This uses fuzzy-matching
if the result is unique.
*inputs: Suitable ingredients and/or tools (Objects) to use in the crafting.
raise_exception (bool, optional): If crafting failed for whatever
reason, raise `CraftingError`. The user will still be informed by the
recipe.
**kwargs: Optional kwargs to pass into the recipe (will passed into
recipe.craft).
Returns:
list: Error messages, if any.
Raises:
CraftingError: If `raise_exception` is True and crafting failed to
produce an output. KeyError: If `recipe_name` failed to find a
matching recipe class (or the hit was not precise enough.)
Notes:
If no recipe_module is given, will look for a list `settings.CRAFT_RECIPE_MODULES` and
lastly fall back to the example module `"evennia.contrib."`
"""
# delayed loading/caching of recipes
_load_recipes()
RecipeClass = search_recipe(crafter, recipe_name)
if not RecipeClass:
raise KeyError(
f"No recipe in settings.CRAFT_RECIPE_MODULES has a name matching {recipe_name}"
)
recipe = RecipeClass(crafter, *inputs, **kwargs)
if recipe.allow_craft:
# override/extend craft_kwargs from initialization.
craft_kwargs = copy(recipe.craft_kwargs)
craft_kwargs.update(kwargs)
try:
recipe.pre_craft(**craft_kwargs)
except (CraftingError, CraftingValidationError):
logger.log_err(CraftingValidationError.args)
return False
else:
return True
return False
def search_recipe(crafter, recipe_name):
# delayed loading/caching of recipes
_load_recipes()
@ -933,7 +993,7 @@ def search_recipe(crafter, recipe_name):
# try in-match
matches = [key for key in _RECIPE_CLASSES if recipe_name in key]
if len(matches) == 1:
recipe_class = matches[0]
recipe_class = _RECIPE_CLASSES.get(matches[0], None)
return recipe_class
@ -1068,13 +1128,18 @@ class CmdCraft(Command):
return
tools.append(obj)
if not search_recipe(caller, self.recipe):
recipe_cls = search_recipe(caller, self.recipe)
if not recipe_cls:
caller.msg("You don't know how to craft {} {}.".format(indefinite_article(self.recipe), self.recipe))
return
tools_and_ingredients = tools + ingredients
if not can_craft(caller, recipe_cls.name, *tools_and_ingredients):
return
toggle_effect(caller, "is_busy")
caller.msg("You start crafting {} {}.".format(indefinite_article(self.recipe), self.recipe))
action_script = create_script("utils.crafting.CmdCraftComplete", obj=caller, interval=15, attributes=[("recipe", self.recipe), ("tools_and_ingredients", tools + ingredients)])
caller.msg("You start crafting {} {}.".format(indefinite_article(recipe_cls.name), recipe_cls.name))
action_script = create_script("utils.crafting.CmdCraftComplete", obj=caller, interval=recipe_cls.crafting_time, attributes=[("recipe", recipe_cls.name), ("tools_and_ingredients", tools_and_ingredients)])
caller.db.current_action = action_script

View file

@ -1,6 +1,5 @@
from evennia.prototypes import spawner
def has_tag(obj, key, category):
return obj.tags.get(key=key, category=category) != None;

View file

@ -2,6 +2,9 @@
# -must- be separated by at least one comment-line.
@tel #2
#
zone/add tutorial_zone = 32
#
@dig/tel ruined room;start_00:typeclasses.rooms.IndoorRoom
#
@desc here =
@ -23,6 +26,7 @@ clutched to the handle of a broken spear.
#
@lock skeleton = search:all()
#
@dig/tel long hall;hall;start_01:typeclasses.rooms.IndoorRoom
#
@desc start_01 =
@ -59,6 +63,7 @@ filling the floor with debris.
#delle assi portanti. Una parte delle pietre di copertura sono rovinate al suolo,
#riempiendo il pavimento di detriti.
#
@dig/tel old guardhouse;guardhouse;start_02:typeclasses.rooms.IndoorRoom
#
@desc start_02 =
@ -99,17 +104,15 @@ A big oak door, reinforced with iron bars across its frame.
It bears marks and burns all over its surface but hasn't been breached during the
siege.
#
zone tutorial_zone
addtozone start_00 = tutorial_zone, 16, 30
#
zone/addroom tutorial_zone = start_door_00
addtozone start_01 = tutorial_zone, 16, 29
#
zone/addroom tutorial_zone = start_door_01
addtozone start_02 = tutorial_zone, 15, 29
#
zone/addroom tutorial_zone = start_door_02
addtozone start_03 = tutorial_zone, 17, 29
#
zone/addroom tutorial_zone = start_door_03
#
zone/addroom tutorial_zone = start_door_04
addtozone start_04 = tutorial_zone, 18, 29
#
@tel start_00
#

45
world/batches/tutorial.ev Normal file
View file

@ -0,0 +1,45 @@
# We start from limbo. Remember that every command in the batchfile
# -must- be separated by at least one comment-line.
@tel #2
#
zone/add tutorial_zone = 32
#
# rooms
spawn/noloc start_00
#
spawn/noloc start_01
#
spawn/noloc start_02
#
spawn/noloc start_03
#
spawn/noloc start_04
#
# exits
popen start_door_00 = start_00,start_01
#
popen start_door_01 = start_01,start_02
#
popen start_door_02 = start_01,start_03
#
popen start_door_03 = start_03,start_04
#
#add rooms to starting zone
addtozone start_00 = tutorial_zone, 16, 30
#
addtozone start_01 = tutorial_zone, 16, 29
#
addtozone start_02 = tutorial_zone, 15, 29
#
addtozone start_03 = tutorial_zone, 17, 29
#
addtozone start_04 = tutorial_zone, 18, 29
#
tel start_00
#
spawn f_armored_skeleton
#
tel start_01
#
spawn f_rubble_01
#

View file

@ -48,133 +48,210 @@ See the `spawn` command and `evennia.prototypes.spawner.spawn` for more info.
"""
ROOM_EMPTY = {
"prototype_key": "room_empty",
# TEMPLATES
ROOM = {
"prototype_key": "room",
"prototype_tags": ["room"],
"key": "empty room",
"desc": "An empty room.",
"map_icon": "|w□|n",
"typeclass": "typeclasses.rooms.IndoorRoom"
}
EXIT_EMPTY = {
"prototype_key": "exit_empty",
EXIT = {
"prototype_key": "exit",
"prototype_tags": ["room", "exit"],
"key": "corridor",
"desc": "An empty corridor.",
"typeclass": "typeclasses.exits.BaseDoor"
}
BROKEN_CROWN = {
"prototype_key": "broken_crown",
"key": "broken crown",
"desc": "An old iron crown, dented and covered in rust.",
ITEM = {
"prototype_key": "item",
"prototype_tags": ["item"],
"key": "item",
"desc": "An unremarkable item made of dreams.",
"typeclass": "typeclasses.objects.Item"
}
ITEM_EQUIPPABLE = {
"prototype_key": "item_equippable",
"prototype_tags": ["item", "equippable"],
"key": "equippable item",
"desc": "An unremarkable equippable item made of dreams.",
"typeclass": "typeclasses.objects.EquippableItem",
"slot": 'head'
}
MULTICOLORED_ROBE = {
"prototype_key": "multicolored robe",
"key": "multicolored robe",
"desc": "A long robe, made of many different colored cloth patches.",
"typeclass": "typeclasses.objects.EquippableItem",
"slot": 'torso'
}
PLAIN_TROUSERS = {
"prototype_key": "plain trousers",
"key": "plain trousers",
"desc": "Simple but robust cloth trousers.",
"typeclass": "typeclasses.objects.EquippableItem",
"slot": 'legs'
}
LEATHER_BOOTS = {
"prototype_key": "leather boots",
"key": "leather boots",
"desc": "A worn pair of leather boots.",
"typeclass": "typeclasses.objects.EquippableItem",
"slot": 'foot'
FEATURE = {
"prototype_key": "feature",
"prototype_tags": ["feature"],
"key": "feature",
"desc": "Something slightly remarkable.",
"feature_desc": "A slightly remarkable |wfeature|n.",
"typeclass": "typeclasses.objects.Feature"
}
FEATURE_CONTAINER = {
"prototype_key": "feature_container",
"key": "chest",
"desc": "A chest.",
"feature_desc": "A |wchest|n lies on the floor.",
"prototype_tags": ["feature", "container"],
"key": "generic container",
"desc": "A generic container.",
"feature_desc": "A dreadful |wgeneric container|n lies on the floor.",
"typeclass": "typeclasses.objects.ContainerFeature"
}
FEATURE_SKELETON = {
"prototype_key": "feature_skeleton",
"key": "rugged skeleton",
"desc": "An old humanoid skeleton, eroded by the passage of time.",
"feature_desc": "A rugged humanoid |wskeleton|n lies on the floor, theirs bony hand still clutching a broken spear. What remains of theirs armor and clothings is too battered to let you recognize their origins.",
"typeclass": "typeclasses.objects.Feature"
# FEATURES
F_ARMORED_SKELETON = {
"prototype_parent": "FEATURE",
"prototype_tags": ["feature"],
"prototype_key": "f_armored_skeleton",
"key": "skeleton of a soldier in armor",
"aliases": ["skeleton"],
"desc": "The skeleton of a soldier, still locked in their armor now "
"rusty. They lie leaning against the barricade where he died, their bony hand "
"clutched to the handle of a broken spear.",
"feature_desc": "A |wskeleton|n in a broken armor is collapsed on the floor behind the table.",
"locks": "search:all()"
}
F_RUBBLE_01 = {
"prototype_parent": "FEATURE",
"prototype_tags": ["feature"],
"prototype_key": "f_rubble_01",
"key": "pile of stones",
"aliases": ["pile"],
"desc": "A large root system pierced the ceiling of this room, shattering one"
" of the load-bearing boards. Some of the covering stones now lie damaged on the ground,"
"filling the floor with debris.",
"feature_desc": "A |wpile of stones|n and a collapsed beam from the ceiling make it difficult to cross this area.",
"locks": "search:all()"
}
FEATURE_SKELETON = {
"prototype_parent": "FEATURE",
"prototype_tags": ["feature"],
"prototype_key": "feature_skeleton",
"key": "rugged skeleton",
"aliases": ["skeleton"],
"desc": "An old humanoid skeleton, eroded by the passage of time.",
"feature_desc": "A rugged humanoid |wskeleton|n lies on the floor, theirs bony hand still clutching a broken "
"spear. What remains of theirs armor and clothing is too battered to let you recognize their "
"origins.",
"locks": "search:all()"
}
SUMMONING_CIRCLE = {
"prototype_parent": "feature",
"prototype_tags": ["feature"],
"prototype_key": "summoning_circle",
"key": "circle of summoning",
"aliases": ["circle", "summoning circle"],
"desc": "A circular pattern of mystical runes drawn with blood.",
"feature_desc": "An arcane |wcircle of summoning|n is draw with blood on the floor.",
}
# ITEMS
STONE = {
"prototype_parent": "ITEM",
"prototype_tags": ["item"],
"prototype_key": "stone",
"key": "stone",
"desc": "An unremarkable stone made of granite.",
"aliases": ["granite stone"],
"typeclass": "typeclasses.objects.Item"
"desc": "An unremarkable stone made of granite."
}
BIG_STONE = {
"prototype_parent": "ITEM",
"prototype_tags": ["item"],
"prototype_key": "big stone",
"key": "big stone",
"desc": "An unremarkable stone made of granite. It seems very heavy.",
"aliases": ["big granite stone"],
"desc": "An unremarkable stone made of granite. It seems very heavy.",
"get_err_msg": "You are not strong enough to lift this stone.",
"locks": "get:attr_gt(strength, 50)",
"typeclass": "typeclasses.objects.Item"
}
BROKEN_CROWN = {
"prototype_parent": "ITEM_EQUIPPABLE",
"prototype_tags": ["item", "equippable"],
"prototype_key": "broken_crown",
"key": "broken crown",
"desc": "An old iron crown, dented and covered in rust.",
"slot": 'head'
}
MULTICOLORED_ROBE = {
"prototype_parent": "ITEM_EQUIPPABLE",
"prototype_tags": ["item", "equippable"],
"prototype_key": "multicolored robe",
"key": "multicolored robe",
"desc": "A long robe, made of many different colored cloth patches.",
"slot": 'torso'
}
PLAIN_TROUSERS = {
"prototype_parent": "ITEM_EQUIPPABLE",
"prototype_tags": ["item", "equippable"],
"prototype_key": "plain trousers",
"key": "plain trousers",
"desc": "Simple but robust cloth trousers.",
"slot": 'legs'
}
LEATHER_BOOTS = {
"prototype_parent": "ITEM_EQUIPPABLE",
"prototype_tags": ["item", "equippable"],
"prototype_key": "leather boots",
"key": "leather boots",
"desc": "A worn pair of leather boots.",
"slot": 'foot'
}
LANTERN = {
"prototype_parent": "ITEM",
"prototype_tags": ["item"],
"prototype_key": "lantern",
"key": "old lantern",
"desc": "An old lantern, still filled with oil.",
"aliases": ["lantern"],
"desc": "An old lantern, still filled with oil.",
"attrs": [("is_lit", True, None, None)],
"tags": [("emit_light", "effect", None)],
"locks": "light:all()",
"typeclass": "typeclasses.objects.Item"
}
BLADE_TOOL = {
"prototype_parent": "ITEM_EQUIPPABLE",
"prototype_tags": ["item", "equippable"],
"prototype_key": "blade tool",
"key": "steel blade",
"desc": "A steel blade, with an oak handle wrapped in cloth.",
"aliases": ["blade"],
"desc": "A steel blade, with an oak handle wrapped in cloth.",
"tags": [("blade", "crafting_tool", None)],
"typeclass": "typeclasses.objects.EquippableItem",
"slot": 'foot'
}
WOOD_MATERIAL = {
"prototype_parent": "ITEM",
"prototype_tags": ["item"],
"prototype_key": "wood_material",
"key": "piece of wood",
"desc": "An unremarkable piece of wood.",
"aliases": ["wood"],
"desc": "An unremarkable piece of wood.",
"tags": [("wood", "crafting_material", None)],
"typeclass": "typeclasses.objects.Item"
}
BLOOD_MATERIAL = {
"prototype_parent": "ITEM",
"prototype_tags": ["item"],
"prototype_key": "blood_material",
"key": "vial of blood",
"desc": "A vial of blood. Fresh.",
"aliases": ["blood, vial"],
"desc": "A vial of blood. Fresh.",
"tags": [("blood", "crafting_material", None)],
"typeclass": "typeclasses.objects.Item"
}
SUMMONING_CIRCLE = {
"prototype_key": "summoning_circle",
"key": "summoning circle",
"aliases": ["circle"],
"desc": "A circular pattern of mystical runes drawn with blood.",
"feature_desc": "An arcane |wcircle of summoning|n is draw with blood on the floor.",
"typeclass": "typeclasses.objects.Feature"
}
## example of module-based prototypes using

View file

@ -1,9 +1,42 @@
from utils.crafting import CraftingRecipe
class BloodRecipe(CraftingRecipe):
"""Some blood"""
name = "vial of blood" # name to refer to this recipe as
crafting_time = 5
tool_tags = ["blade"]
consumable_tags = []
output_prototypes = [
"blood_material"
]
output_names = ["a vial of blood"]
success_message = "You collect your blood in a vial."
def post_craft(self, craft_result, **kwargs):
result_obj = super().post_craft(craft_result, **kwargs)
if result_obj and self.crafter.attributes.has('health'):
self.crafter.db.health -= 1
return result_obj
class SummoningCircleRecipe(CraftingRecipe):
"""A summoning circle"""
name = "summoning circle" # name to refer to this recipe as
crafting_time = 20
tool_tags = []
consumable_tags = ["blood"]
output_prototypes = [
"summoning_circle"
]
success_message = "You draw an arcane circle on the ground."
class WoodenPuppetRecipe(CraftingRecipe):
"""A puppet"""
name = "wooden puppet" # name to refer to this recipe as
crafting_time = 15
tool_tags = ["blade"]
consumable_tags = ["wood"]
output_prototypes = [
@ -11,13 +44,3 @@ class WoodenPuppetRecipe(CraftingRecipe):
"typeclass": "typeclasses.objects.Item",
"desc": "A small carved doll"}
]
class SummoningCircleRecipe(CraftingRecipe):
"""A summoning circle"""
name = "summoning circle" # name to refer to this recipe as
tool_tags = []
consumable_tags = ["blood"]
output_prototypes = [
"summoning_circle"
]

View file

@ -8,10 +8,9 @@ from utils.utils import has_effect
def spell_light(caller, target, **kwargs):
if not target:
caller.msg("You need something to place your light on.")
return
target_obj = caller.search(target, location=[caller, caller.location])
target_obj = caller
else:
target_obj = caller.search(target, location=[caller, caller.location])
if not target_obj:
return

View file

@ -0,0 +1,142 @@
"""
Prototypes
A prototype is a simple way to create individualized instances of a
given typeclass. It is dictionary with specific key names.
For example, you might have a Sword typeclass that implements everything a
Sword would need to do. The only difference between different individual Swords
would be their key, description and some Attributes. The Prototype system
allows to create a range of such Swords with only minor variations. Prototypes
can also inherit and combine together to form entire hierarchies (such as
giving all Sabres and all Broadswords some common properties). Note that bigger
variations, such as custom commands or functionality belong in a hierarchy of
typeclasses instead.
A prototype can either be a dictionary placed into a global variable in a
python module (a 'module-prototype') or stored in the database as a dict on a
special Script (a db-prototype). The former can be created just by adding dicts
to modules Evennia looks at for prototypes, the latter is easiest created
in-game via the `olc` command/menu.
Prototypes are read and used to create new objects with the `spawn` command
or directly via `evennia.spawn` or the full path `evennia.prototypes.spawner.spawn`.
A prototype dictionary have the following keywords:
Possible keywords are:
- `prototype_key` - the name of the prototype. This is required for db-prototypes,
for module-prototypes, the global variable name of the dict is used instead
- `prototype_parent` - string pointing to parent prototype if any. Prototype inherits
in a similar way as classes, with children overriding values in their partents.
- `key` - string, the main object identifier.
- `typeclass` - string, if not set, will use `settings.BASE_OBJECT_TYPECLASS`.
- `location` - this should be a valid object or #dbref.
- `home` - valid object or #dbref.
- `destination` - only valid for exits (object or #dbref).
- `permissions` - string or list of permission strings.
- `locks` - a lock-string to use for the spawned object.
- `aliases` - string or list of strings.
- `attrs` - Attributes, expressed as a list of tuples on the form `(attrname, value)`,
`(attrname, value, category)`, or `(attrname, value, category, locks)`. If using one
of the shorter forms, defaults are used for the rest.
- `tags` - Tags, as a list of tuples `(tag,)`, `(tag, category)` or `(tag, category, data)`.
- Any other keywords are interpreted as Attributes with no category or lock.
These will internally be added to `attrs` (eqivalent to `(attrname, value)`.
See the `spawn` command and `evennia.prototypes.spawner.spawn` for more info.
"""
# ROOMS
START_00 = {
"prototype_parent": "ROOM",
"prototype_tags": ["room"],
"key": "ruined room",
"aliases": "start_00",
"desc": "This room, once royally adorned, now lies in ruins. |/"
"A violent battle must have been fought in this place,"
"mixed with the broken wood of the furniture stand out broken weapons"
"and bodies devoured by the passage of time. "
"The long oak table that once occupied the center of the room"
" it's now overturned against the wall to create a makeshift barricade."
}
START_01 = {
"prototype_parent": "ROOM",
"prototype_tags": ["room"],
"key": "long hall",
"aliases": ["hall", "start_01"],
"desc": "A long hall paved with large hewn stones, thick oak beams"
"still hold up the ceiling frescoed with gilded symbols. Dust corpuscles swirl"
"in the light, disturbed by your passage."
}
START_02 = {
"prototype_parent": "ROOM",
"prototype_tags": ["room"],
"key": "old guardhouse",
"aliases": ["guardhouse", "start_02"],
"desc": "An old guardhouse devastated by the fighting that took place in these halls."
"The only part that has been spared is the ceiling, completely covered with"
"peeling frescoes depicting scenes of martial life."
}
START_03 = {
"prototype_parent": "ROOM",
"prototype_tags": ["room"],
"key": "empty corridor",
"aliases": ["corridor", "start_03"],
"desc": "The sides of the corridor are lined with stone archways, each adorned by a"
"stone statue. All the statues have been broken behind recognition."
}
START_04 = {
"prototype_parent": "ROOM",
"prototype_tags": ["room"],
"key": "empty corridor",
"aliases": ["corridor", "start_04"],
"desc": "This building seems to have survived the ravages of time better than"
"most of the others. Its arched roof and wide spaces suggests that"
"this is a temple or church of some kind."
}
# EXITS
START_DOOR_00 = {
"prototype_parent": "EXIT",
"prototype_tags": ["exit"],
"key": "sculpted archway",
"aliases": ["archway", "start_door_00"],
"desc": "A beautifully sculpted arched entrance. Two figures are carved into the"
"stone on either side of the door, on the right Its, the muse of Deception, on"
"right Izzac, the muse of Authority."
}
START_DOOR_01 = {
"prototype_parent": "EXIT",
"prototype_tags": ["exit"],
"key": "open doorway",
"aliases": ["doorway", "start_door_01"],
"desc": "A large doorway, with no door. The rune '|y◧|n' is engraved on the granite jamb."
}
START_DOOR_02 = {
"prototype_parent": "EXIT",
"prototype_tags": ["exit"],
"key": "small doorway",
"aliases": ["doorway", "start_door_02"],
"desc": "A small doorway, with no door. The rune '|y◓|n' is engraved on the granite jamb."
}
START_DOOR_03 = {
"prototype_parent": "EXIT",
"prototype_tags": ["exit"],
"key": "reinforced door",
"aliases": ["door", "start_door_03"],
"desc": "A big oak door, reinforced with iron bars across its frame."
"It bears marks and burns all over its surface but hasn't been breached during the"
"siege."
}