- Made many small bugfixes to the @parent and @create functions as well as their underlying methods.

- Made it so user #1 is also affected by the on_player_creation() function.
- Added an event folder for custom events, including a working example
- Expanded the example commands and parents to include the changes to how they should be initialized.
- Added an optional ansi scheme (not active by default)
This commit is contained in:
Griatch 2009-04-25 20:51:12 +00:00
parent a32840002c
commit a9dbac8aae
16 changed files with 397 additions and 35 deletions

View file

@ -1,18 +1,48 @@
"""
This is an example command module that may be copied and used to serve as the
basis to newly created modules. You'll need to make sure that this or any new
modules are added to settings.py under CUSTOM_COMMAND_MODULES or
CUSTOM_UNLOGGED_COMMAND_MODULES, which are tuples of module import path strings.
This is an example command module for showing the pluggable command system
in action.
You'll need to make sure that this or any new modules you create are added to
game/settings.py under CUSTOM_COMMAND_MODULES or CUSTOM_UNLOGGED_COMMAND_MODULES,
which are tuples of module import path strings.
See src/config_defaults.py for more details.
E.g. to add this example command for testing, your entry in game/settings.py would
look like this:
CUSTOM_COMMAND_MODULES = ('game.gamesrc.commands.example',)
(note the extra comma at the end to make this into a Python tuple. It's only
needed if you have only one entry.)
"""
# This is the common global CommandTable object which we'll be adding the
# example command to.
# example command to. We can add any number of commands this way in the
# same file.
from src.cmdtable import GLOBAL_CMD_TABLE
def cmd_example(command):
"""
This is the help text for the 'example' command, a command to
show how the pluggable command system works.
For testing, you can try calling this with different switches and
arguments, like
> example/test/test2 Hello
and see what is returned.
<<TOPIC:example_auto_help>>
This is a subtopic to the main example command help entry.
Note that this text is auto-added since auto_help=True
was set in the call to add_function. Any number of subtopics like
this one can be added on the fly using the auto-help system. See
help topics on 'help' and 'help_staff' for more information and
options.
"""
An example command to show how the pluggable command system works.
"""
# By building one big string and passing it at once, we cut down on a lot
# of emit_to() calls, which is generally a good idea.
retval = "----- Example Command -----\n\r"
@ -33,5 +63,32 @@ def cmd_example(command):
# Extra variables passed with cmdtable.py's add_command().
retval += " Extra vars: %s\n\r" % command.extra_vars
command.source_object.emit_to(retval)
# Add the command to the common global command table.
GLOBAL_CMD_TABLE.add_command("example", cmd_example),
# Add the command to the common global command table. Note that
# since auto_help=True, help entries named "example" and
# "example_auto_help" (as defined in the __doc__ string) will
# automatically be created for us.
GLOBAL_CMD_TABLE.add_command("example", cmd_example, auto_help=True),
#another simple example
def cmd_emote_smile(command):
"""
Simplistic 'smile' emote.
"""
#get the source object (that is, the player using the command)
caller = command.source_object
#find name of caller
name = caller.get_name(show_dbref=False)
#get the location caller is at
location = caller.get_location()
#build the emote
text = "%s smiles." % name
#emit the emote to everyone at the current location
location.emit_to_contents(text)
#add to global command table (no auto_help activated)
GLOBAL_CMD_TABLE.add_command('smile', cmd_emote_smile)

View file

View file

@ -0,0 +1,55 @@
"""
Example of the event system. To try it out, make sure to import it from somewhere
covered by @reload (like the script parent). Create an object inheriting
the red_button parent to see its effects (e.g. @create button=examples/red_button)
Technically the event don't contain any game logics, all it does is locate all
objects inheriting to a particular script parent and calls one of its functions
at a regular interval.
"""
from src.events import IntervalEvent
from src.scheduler import add_event
from src.objects.models import Object
#the logger is useful for debugging since there is no source object to send to
from src.logger import log_infomsg
#Example of the event system. This example adds an event to the red_button parent
#in parents/examples. It makes the button blink temptingly at a regular interval.
class EventBlinkButton(IntervalEvent):
"""
This event lets the button flash at regular intervals.
"""
def __init__(self):
"""
A custom init method also storing the source object.
"""
super(EventBlinkButton, self).__init__()
self.name = 'event_blink_red_button'
#how often to blink, in seconds
self.interval = 30
#the description is seen when you run @ps in-game.
self.description = "Blink red buttons regularly."
def event_function(self):
"""
This stub function is automatically fired every self.interval seconds.
In this case we do a search for all objects inheriting from the correct
parent and call a function on them.
"""
#find all objects inheriting from red_button (parents are per definition
#stored with the gamesrc/parent/ drawer as a base)
parent = 'examples.red_button'
buttons = Object.objects.global_object_script_parent_search(parent)
log_infomsg("buttons found: %s" % buttons)
for b in buttons:
b.scriptlink.blink()
#create and add the event to the global handler
blink_event = EventBlinkButton()
add_event(blink_event)

View file

@ -19,4 +19,4 @@ def class_factory(source_obj):
source_obj: (Object) A reference to the object being scripted (the child).
"""
return BasicObject(source_obj)
return BasicObject(source_obj)

View file

@ -0,0 +1,55 @@
"""
Simple example of a custom modified object, derived from the base object.
If you want to make this your new default object type, move this into
gamesrc/parents and set SCRIPT_DEFAULT_OBJECT = 'custom_basicobject'
in game/settings.py.
Generally, if you want to conveniently set future objects to inherit from this
script parent (not as a default), this files and others like it need to be
located under the game/gamesrc/parent directory.
"""
from game.gamesrc.parents.base.basicobject import BasicObject
class CustomBasicObject(BasicObject):
def at_object_creation(self):
"""
This function is called whenever the object is created. Use
this instead of __init__ to set start attributes etc on a
particular object type.
"""
#Set an "sdesc" (short description) attribute on object,
#defaulting to its given name
#get the stored object related to this class
obj = self.scripted_obj
#find out the object's name
name = obj.get_name(fullname=False,
show_dbref=False,
show_flags=False)
#assign the name to the new attribute
obj.set_attribute('sdesc',name)
def at_object_destruction(self, pobject=None):
"""
This is triggered when an object is about to be destroyed via
@destroy ONLY. If an object is deleted via delete(), it is assumed
that this method is to be skipped.
values:
* pobject: (Object) The object requesting the action.
"""
pass
def class_factory(source_obj):
"""
This method is called by any script you retrieve (via the scripthandler). It
creates an instance of the class and returns it transparently.
source_obj: (Object) A reference to the object being scripted (the child).
"""
return CustomBasicObject(source_obj)

View file

@ -0,0 +1,67 @@
"""
This is an example of customizing the basic player character object.
You will want to do this to add all sorts of custom things like
attributes, skill values, injuries and so on.
If you want to make this the default player object for all players, move it
into gamesrc/parents and set SCRIPT_DEFAULT_PLAYER = 'custom_basicplayer'
in game/settings.py.
"""
from game.gamesrc.parents.base.basicplayer import BasicPlayer
class CustomBasicPlayer(BasicPlayer):
def at_player_creation(self):
"""
Called when player object is first created. Use this
instead of __init__ to define any custom attributes
all your player characters should have.
"""
#Example: Adding a default sdesc (short description)
#get the stored object related to this class
pobject = self.scripted_obj
#set the attribute
pobject.set_attribute('sdesc', 'A normal person')
def at_pre_login(self, session):
"""
Called when the player has entered the game but has not
logged in yet.
"""
pass
def at_post_login(self, session):
"""
This command is called after the player has logged in but
before he is allowed to give any commands.
"""
#get the object linked to this class
pobject = self.scripted_obj
#find out more about our object
name = pobject.get_name(fullname=False,
show_dbref=False,
show_flags=False)
sdesc = pobject.get_attribute_value('sdesc')
#send a greeting using our new sdesc attribute
pobject.emit_to("You are now logged in as %s - %s." % (name, sdesc))
#tell everyone else we're here
pobject.get_location().emit_to_contents("%s - %s, has connected." %
(name, sdesc), exclude=pobject)
#show us our surroundings
pobject.execute_cmd("look")
def class_factory(source_obj):
"""
This method is called by any script you retrieve (via the scripthandler). It
creates an instance of the class and returns it transparently.
source_obj: (Object) A reference to the object being scripted (the child).
"""
return CustomBasicPlayer(source_obj)

View file

@ -1,35 +1,110 @@
"""
An example script parent for a
An example script parent for a nice red button object. It has
custom commands defined on itself that are only useful in relation to this
particular object. See example.py in gamesrc/commands for more info
on the pluggable command system.
Assuming this script remains in gamesrc/parents/examples, create an object
of this type using @create button=examples.red_button
This file also shows the use of the Event system to make the button
send a message to the players at regular intervals. Note that if you create a
test button you must drop it before you will see its messages!
"""
from game.gamesrc.parents.base.basicobject import BasicObject
#you have to import the event definition(s) from somewhere covered by @reload,
# - this is as good a place as any. Uncomment to turn off event system.
import game.gamesrc.events.example
#
#commands on the button object
#
def cmd_push_button(command):
"""
An example command to show how the pluggable command system works.
"""
# By building one big string and passing it at once, we cut down on a lot
# of emit_to() calls, which is generally a good idea.
retval = "You have pushed the button on: %s" % (command.scripted_obj.get_name())
This is a simple command that handles a user pressing the
button by returning a message.
"""
retval = "There is a loud bang: BOOOM!"
command.source_object.emit_to(retval)
def cmd_pull_button(command):
"""
An example of a second defined command (for those who
don't know how a button works ... ;) )
"""
retval = "A button is meant to be pushed, not pulled!"
command.source_object.emit_to(retval)
#
#The object itself
#
class RedButton(BasicObject):
def __init__(self, scripted_obj, *args, **kwargs):
"""
This is called when class_factory() instantiates a temporary instance
of the script parent. This is typically not something you want to
mess with much.
"""
# Calling the super classes __init__ is critical! Never forget to do
# Calling the super class' __init__ is critical! Never forget to do
# this or everything else from here on out will fail.
super(RedButton, self).__init__(scripted_obj, args, kwargs)
# Add the command to the object's command table.
# Add the commands to the object's command table (this is about
#the only thing you should use the __init__ for).
self.command_table.add_command("pushbutton", cmd_push_button)
self.command_table.add_command("pullbutton", cmd_pull_button)
def at_object_creation(self):
"""
This function is called when object is created. Use this
preferably over __init__.
In this case all we do is add the commandtable
to the object's own command_table variable; this makes
the commands we've added to COMMAND_TABLE available to
the user whenever the object is around.
"""
#get stored object related to this class
obj = self.scripted_obj
obj.set_description("This is your standard big red button.")
obj.set_attribute("breakpoint", 10)
obj.set_attribute("count", 0)
def blink(self):
"""If the event system is active, it will regularly call this function to make
the button blink. Note the use of attributes to store the variable count and
breakpoint in a persistent way."""
obj = self.scripted_obj
try:
count = int(obj.get_attribute_value("count"))
breakpoint = int(obj.get_attribute_value("breakpoint"))
except TypeError:
return
if count <= breakpoint:
if int(count) == int(breakpoint):
s = "The button flashes, then goes dark. "
s += "Looks like the lamp just broke."
else:
s = "The red button flashes, demanding your attention."
count += 1
obj.set_attribute("count",count)
obj.get_location().emit_to_contents(s)
def class_factory(source_obj):
"""
This method is called any script you retrieve (via the scripthandler). It
This method is called by any script you retrieve (via the scripthandler). It
creates an instance of the class and returns it transparently.
source_obj: (Object) A reference to the object being scripted (the child).
"""
return RedButton(source_obj)
return RedButton(source_obj)

View file

@ -105,10 +105,42 @@ class MuxANSIParser(BaseParser):
(r'%cw', ANSITable.ansi["white"]),
(r'%cW', ANSITable.ansi["back_white"]),
]
class ExtendedANSIParser(MuxANSIParser):
"""
Extends the standard mux colour commands with {-style commands
(shortcuts for writing light/dark text without background)
"""
def __init__(self):
super(ExtendedANSIParser, self).__init__()
hilite = ANSITable.ansi['hilite']
normal = ANSITable.ansi['normal']
self.ansi_subs.extend( [
(r'{r', hilite + ANSITable.ansi['red']),
(r'{R', normal + ANSITable.ansi['red']),
(r'{g', hilite + ANSITable.ansi['green']),
(r'{G', normal + ANSITable.ansi['green']),
(r'{y', hilite + ANSITable.ansi['yellow']),
(r'{Y', normal + ANSITable.ansi['yellow']),
(r'{b', hilite + ANSITable.ansi['blue']),
(r'{B', normal + ANSITable.ansi['blue']),
(r'{m', hilite + ANSITable.ansi['magenta']),
(r'{M', normal + ANSITable.ansi['magenta']),
(r'{c', hilite + ANSITable.ansi['cyan']),
(r'{C', normal + ANSITable.ansi['cyan']),
(r'{w', hilite + ANSITable.ansi['white']), #white
(r'{W', normal + ANSITable.ansi['white']), #light grey
(r'{x', hilite + ANSITable.ansi['black']), #dark grey
(r'{X', normal + ANSITable.ansi['black']), #pure black
(r'{n', normal) #reset
] )
def parse_ansi(string, strip_ansi=False, strip_formatting=False, parser=MuxANSIParser()):
ANSI_PARSER = MuxANSIParser()
#ANSI_PARSER = ExtendedANSIParser()
def parse_ansi(string, strip_ansi=False, strip_formatting=False, parser=ANSI_PARSER):
"""
Parses a string, subbing color codes as needed.
"""
return parser.parse_ansi(string, strip_ansi=strip_ansi,
strip_formatting=strip_formatting)
strip_formatting=strip_formatting)

View file

@ -303,7 +303,7 @@ def cmd_examine(command):
con_exits.append(obj)
elif obj.is_thing():
con_things.append(obj)
# Render Contents display.
if con_players or con_things:
s += str("%sContents:%s" % (ANSITable.ansi["hilite"],

View file

@ -51,17 +51,18 @@ def cmd_parent(command):
# Clear parent if command was @parent obj= or obj=none
if not parent_name or parent_name.lower() == "none":
target_obj.set_script_parent(None)
target_obj.scriptlink.at_object_creation()
new_parent = target_obj.scriptlink()
source_object.emit_to("%s reverted to its default parent (%s)." %
(target_obj, new_parent))
return
# If we reach this point, attempt to change parent.
# If we reach this point, attempt to change parent.
former_parent = target_obj.get_scriptlink()
if target_obj.set_script_parent(parent_name):
#new script path added; initialize the parent
target_obj.scriptlink.at_object_creation()
s = "%s's parent is now %s (instead of %s).\n\r"
s += "Note that the new parent type could have overwritten "
s += "same-named attributes on the existing object."

View file

@ -32,4 +32,4 @@ class HelpEntry(models.Model):
"""
Gets the entry text for in-game viewing.
"""
return ansi.parse_ansi(self.entrytext)
return ansi.parse_ansi(self.entrytext)

View file

@ -32,6 +32,7 @@ def create_objects():
# Create the matching PLAYER object in the object DB.
god_user_obj = Object(id=1, type=defines_global.OTYPE_PLAYER)
god_user_obj.set_name(god_user.username)
god_user_obj.scriptlink.at_player_creation()
god_user_obj.save()
# Limbo is the initial starting room.
@ -39,6 +40,7 @@ def create_objects():
limbo_obj.set_owner(god_user_obj)
limbo_obj.set_name('%ch%ccLimbo%cn')
limbo_obj.set_description("Welcome to your new Evennia-based game. From here you are ready to begin development. If you should need help or would like to participate in community discussions, visit http://evennia.com.")
limbo_obj.scriptlink.at_object_creation()
limbo_obj.save()
# Now that Limbo exists, set the user up in Limbo.
@ -71,7 +73,7 @@ def create_config_values():
Creates the initial config values.
"""
ConfigValue(conf_key="default_home", conf_value="2").save()
ConfigValue(conf_key="idle_timeout", conf_value="1800").save()
ConfigValue(conf_key="idle_timeout", conf_value="3600").save()
ConfigValue(conf_key="money_name_singular", conf_value="Credit").save()
ConfigValue(conf_key="money_name_plural", conf_value="Credits").save()
ConfigValue(conf_key="player_dbnum_start", conf_value="2").save()

View file

@ -79,6 +79,15 @@ class ObjectManager(models.Manager):
return o_query.exclude(type__in=[defines_global.OTYPE_GARBAGE,
defines_global.OTYPE_GOING])
def global_object_script_parent_search(self, script_parent):
"""
Searches through all objects returning those which has a certain script parent.
"""
o_query = self.filter(script_parent__exact=script_parent)
return o_query.exclude(type__in=[defines_global.OTYPE_GARBAGE,
defines_global.OTYPE_GOING])
def list_search_object_namestr(self, searchlist, ostring, dbref_only=False,
limit_types=False, match_type="fuzzy"):
"""
@ -350,4 +359,4 @@ class ObjectManager(models.Manager):
user_object.emit_to("Welcome to %s, %s.\n\r" % (
ConfigValue.objects.get_configvalue('site_name'),
user_object.get_name(show_dbref=False)))
command.session.add_default_channels()
command.session.add_default_channels()

View file

@ -18,6 +18,8 @@ from src import logger
import src.flags
from src.util import functions_general
from src.logger import log_infomsg
class Attribute(models.Model):
"""
Attributes are things that are specific to different types of objects. For
@ -598,8 +600,8 @@ class Object(models.Model):
attrib_obj.attr_value = new_value
attrib_obj.save()
else:
if not new_value:
# Attribute object and we have given a doesn't exist, create it.
if new_value:
# No object currently exist, so create it.
new_attrib = Attribute()
new_attrib.attr_name = attribute
new_attrib.attr_value = new_value
@ -800,10 +802,10 @@ class Object(models.Model):
attrib: (str) The attribute's name.
"""
if self.has_attribute(attrib):
if self.has_attribute(attrib):
attrib = Attribute.objects.filter(attr_object=self).filter(attr_name=attrib)
return attrib[0].attr_value
else:
else:
return default
def get_attribute_obj(self, attrib):

View file

@ -20,5 +20,12 @@ def add_event(event):
Args:
* event: (IntervalEvent) The event to add to the scheduler.
"""
schedule.append(event)
event.start_event_loop()
#don't add multiple instances of the same event
if event in schedule:
return
#i = schedule.index(event)
#schedule[i] = event
else:
schedule.append(event)
event.start_event_loop()

View file

@ -58,7 +58,7 @@ def scriptlink(source_obj, scriptname):
# Store the module reference for later fast retrieval.
CACHED_SCRIPTS[scriptname] = modreference
except ImportError:
logger.log_infomsg('Error importing %s: %s' % (modname, format_exc()))
logger.log_infomsg('Error importing %s: %s' % (scriptname, format_exc()))
os.chdir(settings.BASE_PATH)
return
except OSError: