Made the spawner more streamlined, and allowed the @spawn command to access prototypes in a file specified by the optional PROTOTYPE_MODULES setting.

This commit is contained in:
Griatch 2014-07-01 04:04:54 +02:00
parent 6eafe65076
commit 221d56fecd
4 changed files with 137 additions and 44 deletions

View file

@ -0,0 +1,56 @@
"""
Example prototypes read by the @spawn command but is also easily
available to use from code. Each prototype should be a dictionary. Use
the same name as the variable to refer to other prototypes.
Possible keywords are:
prototype - string pointing to parent prototype of this structure
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
aliases - string or list of strings
ndb_<name> - value of a nattribute (ndb_ is stripped)
any other keywords are interpreted as Attributes and their values.
See the @spawn command and src.utils.spawner for more info.
"""
from random import randint
NOBODY = {}
GOBLIN = {
"key": "goblin grunt",
"health": lambda: randint(20,30),
"resists": ["cold", "poison"],
"attacks": ["fists"],
"weaknesses": ["fire", "light"]
}
GOBLIN_WIZARD = {
"prototype": "GOBLIN",
"key": "goblin wizard",
"spells": ["fire ball", "lighting bolt"]
}
GOBLIN_ARCHER = {
"prototype": "GOBLIN",
"key": "goblin archer",
"attacks": ["short bow"]
}
ARCHWIZARD = {
"attacks": ["archwizard staff"],
}
GOBLIN_ARCHWIZARD = {
"key": "goblin archwizard",
"prototype" : ("GOBLIN_WIZARD", "ARCHWIZARD")
}

View file

@ -19,7 +19,7 @@ __all__ = ("ObjManipCommand", "CmdSetObjAlias", "CmdCopy",
"CmdUnLink", "CmdSetHome", "CmdListCmdSets", "CmdName",
"CmdOpen", "CmdSetAttribute", "CmdTypeclass", "CmdWipe",
"CmdLock", "CmdExamine", "CmdFind", "CmdTeleport",
"CmdScript", "CmdTag")
"CmdScript", "CmdTag", "CmdSpawn")
try:
# used by @set
@ -30,7 +30,7 @@ except ImportError:
# used by @find
CHAR_TYPECLASS = settings.BASE_CHARACTER_TYPECLASS
_PROTOTYPE_PARENTS = None
class ObjManipCommand(MuxCommand):
"""
@ -2244,17 +2244,33 @@ class CmdTag(MuxCommand):
string = "No tags attached to %s." % obj
self.caller.msg(string)
#
# To use the prototypes with the @spawn function, copy
# game/gamesrc/world/examples/prototypes.py up one level
# to game/gamesrc/world. Then add to game/settings.py the
# line
# PROTOTYPE_MODULES = ["game.gamesrc.commands.prototypes"]
# Reload the server and the prototypes should be available.
#
class CmdSpawn(MuxCommand):
"""
spawn objects from prototype
Usage:
@spawn {prototype dictionary}
@spawn[/switches] {prototype dictionary}
Switches:
noloc - allow location to None. Otherwise, location will default to
caller's current location
parents - show all available prototype parents
Example:
@spawn {"key":"goblin", "typeclass":"monster.Monster", "location":"#2"}
Dictionary keys:
{wprototype {n - name of parent prototype to use. Can be a list for
multiple inheritance (inherits left to right)
{wkey {n - string, the main object identifier
{wtypeclass {n - string, if not set, will use settings.BASE_OBJECT_TYPECLASS
{wlocation {n - this should be a valid object or #dbref
@ -2266,7 +2282,8 @@ class CmdSpawn(MuxCommand):
{wndb_{n<name> - value of a nattribute (ndb_ is stripped)
any other keywords are interpreted as Attributes and their values.
This command can't access prototype inheritance.
The parent prototypes are taken as dictionaries defined globally in
the settings.PROTOTYPE_MODULES.
"""
key = "@spawn"
@ -2274,9 +2291,22 @@ class CmdSpawn(MuxCommand):
help_category = "Building"
def func(self):
"Implements the spawn"
"Implements the spawner"
global _PROTOTYPE_PARENTS
if _PROTOTYPE_PARENTS is None:
if hasattr(settings, "PROTOTYPE_MODULES"):
# read prototype parents from setting
_PROTOTYPE_PARENTS = {}
for prototype_module in utils.make_iter(settings.PROTOTYPE_MODULES):
_PROTOTYPE_PARENTS.update(dict((key, val)
for key, val in utils.all_from_module(prototype_module).items() if isinstance(val, dict)))
if not self.args:
self.caller.msg("Usage: @spawn {key:value, key, value, ...}")
string = "Usage: @spawn {key:value, key, value, ...}\n" \
"Available prototypes: %s"
self.caller.msg(string % ", ".join(_PROTOTYPE_PARENTS.keys())
if _PROTOTYPE_PARENTS else None)
return
from src.utils.spawner import spawn
@ -2297,7 +2327,10 @@ class CmdSpawn(MuxCommand):
self.caller.msg("The prototype must be a Python dictionary.")
return
for obj in spawn(prototype):
if not "noloc" in self.switches and not "location" in prototype:
prototype["location"] = self.caller.location
for obj in spawn(prototype, prototype_parents=_PROTOTYPE_PARENTS):
self.caller.msg("Spawned %s." % obj.key)

View file

@ -18,7 +18,7 @@ GOBLIN = {
}
Possible keywords are:
prototype - dict, parent prototype of this structure (see below)
prototype - string parent prototype
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
@ -83,16 +83,16 @@ _CREATE_OBJECT_KWARGS = ("key", "location", "home", "destination")
_handle_dbref = lambda inp: handle_dbref(inp, ObjectDB)
def _get_prototype(dic, prot):
def _get_prototype(dic, prot, protparents):
"Recursively traverse a prototype dictionary, including multiple inheritance"
if "prototype" in dic:
# move backwards through the inheritance
prototypes = dic["prototype"]
if isinstance(prototypes, dict):
if not hasattr(prototypes, "__iter__"):
prototypes = (prototypes,)
for prototype in prototypes:
# Build the prot dictionary in reverse order, overloading
new_prot = _get_prototype(prototype, prot)
new_prot = _get_prototype(protparents.get(prototype, {}), prot, protparents)
prot.update(new_prot)
prot.update(dic)
prot.pop("prototype", None) # we don't need this anymore
@ -150,16 +150,21 @@ def _batch_create_object(*objparams):
return objs
def spawn(*prototypes):
def spawn(*prototypes, **kwargs):
"""
Spawn a number of prototyped objects. Each argument should be a
prototype dictionary.
The keyword argument "prototype_parents" holds a dictionary of
prototype dictionaries, each with a unique key. The given
prototypes may call these as parents using the "prototype" key.
"""
objsparams = []
for prototype in prototypes:
protparents = kwargs.get("prototype_parents", None)
prot = _get_prototype(prototype, {})
prot = _get_prototype(prototype, {}, protparents)
if not prot:
continue
@ -195,35 +200,32 @@ def spawn(*prototypes):
if __name__ == "__main__":
# testing
NOBODY = {}
GOBLIN = {
"key": "goblin grunt",
"health": lambda: randint(20,30),
"resists": ["cold", "poison"],
"attacks": ["fists"],
"weaknesses": ["fire", "light"]
}
GOBLIN_WIZARD = {
"prototype": GOBLIN,
"key": "goblin wizard",
"spells": ["fire ball", "lighting bolt"]
}
GOBLIN_ARCHER = {
"prototype": GOBLIN,
"key": "goblin archer",
"attacks": ["short bow"]
}
ARCHWIZARD = {
"attacks": ["archwizard staff"],
}
GOBLIN_ARCHWIZARD = {
"key": "goblin archwizard",
"prototype" : (GOBLIN_WIZARD, ARCHWIZARD)
}
protparents = {
"NOBODY": {},
"GOBLIN" : {
"key": "goblin grunt",
"health": lambda: randint(20,30),
"resists": ["cold", "poison"],
"attacks": ["fists"],
"weaknesses": ["fire", "light"]
},
"GOBLIN_WIZARD" : {
"prototype": "GOBLIN",
"key": "goblin wizard",
"spells": ["fire ball", "lighting bolt"]
},
"GOBLIN_ARCHER" : {
"prototype": "GOBLIN",
"key": "goblin archer",
"attacks": ["short bow"]
},
"ARCHWIZARD" : {
"attacks": ["archwizard staff"],
},
"GOBLIN_ARCHWIZARD" : {
"key": "goblin archwizard",
"prototype" : ("GOBLIN_WIZARD", "ARCHWIZARD")
}
}
# test
print [o.key for o in spawn(GOBLIN, GOBLIN_ARCHWIZARD)]
print [o.key for o in spawn(protparents["GOBLIN"], protparents["GOBLIN_ARCHWIZARD"], prototype_parents=protparents)]

View file

@ -792,6 +792,8 @@ def all_from_module(module):
Return all global-level variables from a module as a dict
"""
mod = mod_import(module)
if not mod:
return {}
return dict((key, val) for key, val in mod.__dict__.items()
if not (key.startswith("_") or ismodule(val)))