Spawner

The spawner is a system for defining and creating individual objects from a base template called a
prototype. It is only designed for use with in-game Objects, not any other type of
entity.
The normal way to create a custom object in Evennia is to make a Typeclass. If you
haven’t read up on Typeclasses yet, think of them as normal Python classes that save to the database
behind the scenes. Say you wanted to create a “Goblin” enemy. A common way to do this would be to
first create a Mobile typeclass that holds everything common to mobiles in the game, like generic
AI, combat code and various movement methods. A Goblin subclass is then made to inherit from
Mobile. The Goblin class adds stuff unique to goblins, like group-based AI (because goblins are
smarter in a group), the ability to panic, dig for gold etc.
But now it’s time to actually start to create some goblins and put them in the world. What if we
wanted those goblins to not all look the same? Maybe we want grey-skinned and green-skinned goblins
or some goblins that can cast spells or which wield different weapons? We could make subclasses of
Goblin, like GreySkinnedGoblin and GoblinWieldingClub. But that seems a bit excessive (and a
lot of Python code for every little thing). Using classes can also become impractical when wanting
to combine them - what if we want a grey-skinned goblin shaman wielding a spear - setting up a web
of classes inheriting each other with multiple inheritance can be tricky.
This is what the prototype is for. It is a Python dictionary that describes these per-instance
changes to an object. The prototype also has the advantage of allowing an in-game builder to
customize an object without access to the Python backend. Evennia also allows for saving and
searching prototypes so other builders can find and use (and tweak) them later. Having a library of
interesting prototypes is a good reasource for builders. The OLC system allows for creating, saving,
loading and manipulating prototypes using a menu system.

The spawner takes a prototype and uses it to create (spawn) new, custom objects.

Using the OLC

Enter the olc command or @spawn/olc to enter the prototype wizard. This is a menu system for
creating, loading, saving and manipulating prototypes. It’s intended to be used by in-game builders
and will give a better understanding of prorotypes in general. Use help on each node of the menu
for more information. Below are further details about how prototypes work and how they are used.

The prototype

The prototype dictionary defines all possible database-properties of an Object. It has a fixed set
of allowed keys:

Prototype keys

All keys starting with prototype_ are for book keeping.

  • prototype_key - the ‘name’ of the prototype. While this can sometimes be skipped (such as when defining a prototype in a module), it’s generally best to always include this. It is used for book-keeping and storing of the prototype so you can find it later.
  • prototype_parent - If given, this should be the prototype_key of another prototype stored in the system or available in a module. This makes this prototype inherit the keys from the parent and only override what is needed. Give a tuple (parent1, parent2, ...) for multiple left-right inheritance.
  • prototype_desc - this is optional and used when listing the prototype in in-game listings.
  • protototype_tags - this is optional and allows for tagging the prototype in order to find it easier later.
  • prototype_locks - two lock types are supported: edit and spawn. The first lock restricts the copying and editing of the prototype when loaded through the OLC. The second determines who may use the prototype to create new objects.

The remaining keys determine actual aspects of the objects to spawn from this prototype:

  • key - the main object identifier. Defaults to “Spawned Object X”, where X is a random integer.
  • typeclass - python-path to the typeclass to use, if not set, will use settings.BASE_OBJECT_TYPECLASS.
  • location - this should be a #dbref.
  • home - a valid #dbref. Defaults to location or settings.DEFAULT_HOME if location does not exist.
  • destination - a valid #dbref. Only used by exits.
  • permissions - list of permission strings, like ["Accounts", "may_use_red_door"]
  • locks - a lock-string like "edit:all();control:perm(Builder)"
  • aliases - list of strings for use as aliases
  • tags - list Tags. These are given as tuples (tag, category, data).
  • attrs - list of Attributes. These are given as tuples (attrname, value, category, lockstring)
  • Any other keywords are interpreted as non-category Attributes and their values. This is convenient for simple Attributes - use attrs for full control of Attributes.

Deprecated as of Evennia 0.8:

  • ndb_<name> - sets the value of a non-persistent attribute ("ndb_" is stripped from the name). This is simply not useful in a prototype and is deprecated.
  • exec - This accepts a code snippet or a list of code snippets to run. This should not be used - use callables or protfuncs instead (see below).

Prototype values

The prototype supports values of several different types.

It can be a hard-coded valuei:

{"key": "An ugly goblin", ...}
It can also be a callable. This callable is called without arguments whenever the prototype is used to
spawn a new object:
{"key": _get_a_random_goblin_name, ...}
By use of Python lambda one can wrap the callable so as to make immediate settings in the
prototype:
{"key": lambda: random.choice(("Urfgar", "Rick the smelly", "Blargh the foul", ...)), ...}
Finally, the value can be a prototype function (Protfunc). This is a callable embedded in a
string in much the same way as an
InlineFunc - they are actually
parsed using the same parser. These work like callables, except what function can be used is
strictly limited to those made available as global functions in one of the modules listed in
settings.PROT_FUNC_MODULES. They thus allow for full control of the input/output, error handling
etc.
{"key": "$choice(Urfgar, Rick the smelly, Blargh the foul)", ...}
For developers with access to Python, using protfuncs in prototypes is generally not useful -
passing real Python functions is a lot more powerful. Their main use is to allow in-game builders to
do limited coding/scripting for their prototypes without giving them direct access raw Python.

Storing prototypes

Prototypes can be defined and stored in two ways

  • Stored as Scripts in the database. These are sometimes referred to as database-prototypes This is the only way for in-game builders to modify and add prototypes.

  • As dictionaries assigned to global variables in one of the modules defined in
    settings.PROTOTYPE_MODULES. Such modules are necessarily “read-only” from in-game and cannot be
    modified (but copies of them could be made into database-prototypes). They can be useful in order
    for developer to provide “starting” or “base” prototypes to build from.
       # in a module Evennia looks at for prototypes,
       # (like mygame/server/conf/prototypes.py)
    
       ORC_SHAMAN = {"key":"Orc shaman",
             "typeclass": "typeclasses.monsters.Orc",
             "weapon": "wooden staff",
             "health": 20}
    
    | Note that in the example above, ``"ORC_SHAMAN"`` will become the
      ``prototype_key`` of this prototype.
    | It’s the only case when ``prototype_key`` can be skipped in a
      prototype. However, if ``prototype_key``
    | was given explicitly, that would take precedence. This is a legacy
      behavior and it’s recommended
    | that you always add ``prototype_key`` explicitly to be consistent.
    

Using @spawn

The spawner can be used from inside the game through the Builder-only @spawn command. Assuming the
“goblin” typeclass is available to the system (either as a database-prototype or read from module),
you can spawn a new goblin with
@spawn goblin

You can also specify the prototype directly as a valid Python dictionary:

@spawn {"prototype_key": "shaman", \
    "key":"Orc shaman", \
        "prototype_parent": "goblin", \
        "weapon": "wooden staff", \
        "health": 20}

Using evennia.utils.spawner()

In code you access the spawner mechanism directly via the call

new_objects = evennia.utils.spawner.spawn(*prototypes)
All arguments are prototype dictionaries. The function will return a
matching list of created objects. Example:
obj1, obj2 = evennia.utils.spawner.spawn({"key": "Obj1", "desc": "A test"},
                                         {"key": "Obj2", "desc": "Another test"})
Note that no location will be set automatically when using evennia.utils.spawner.spawn(), you
have to specify location explicitly in the prototype dict.
If the prototypes you supply are using parent keywords, the spawner will look to
settings.PROTOTYPE_MODULES to determine which modules contain parents available to use. You can
use the prototype_modules keyword to change the list of available parent modules only for this
particular call. Finally, calling spawn(return_prototypes=True) will return a dictionary of all
the available prototypes from all available modules. In this case, no objects will be created or
returned - this is meant to be used for compiling help information for an end user.