Created a state system. See
http://groups.google.com/group/evennia/browse_thread/thread/66a7ff6cce5303b7 for more detailed description. Created a new folder gamesrc/commands/examples and moved all examples in there. /Griatch
This commit is contained in:
parent
cae925c29b
commit
0efe2c3095
9 changed files with 415 additions and 30 deletions
|
|
@ -8,6 +8,7 @@ from traceback import format_exc
|
|||
from django.contrib.contenttypes.models import ContentType
|
||||
import defines_global
|
||||
import cmdtable
|
||||
import statetable
|
||||
import logger
|
||||
import comsys
|
||||
import alias_mgr
|
||||
|
|
@ -318,19 +319,40 @@ def handle(command):
|
|||
# Not logged in, look through the unlogged-in command table.
|
||||
command_table_lookup(command, cmdtable.GLOBAL_UNCON_CMD_TABLE,
|
||||
eval_perms=False)
|
||||
else:
|
||||
# Match against the 'idle' command.
|
||||
match_idle(command)
|
||||
# See if this is an aliased command.
|
||||
match_alias(command)
|
||||
# Check if the user is using a channel command.
|
||||
match_channel(command)
|
||||
# See if the user is trying to traverse an exit.
|
||||
match_exits(command)
|
||||
neighbor_match_found = match_neighbor_ctables(command)
|
||||
if not neighbor_match_found:
|
||||
# Retrieve the appropriate (if any) command function.
|
||||
command_table_lookup(command, cmdtable.GLOBAL_CMD_TABLE)
|
||||
|
||||
else:
|
||||
|
||||
state_name = command.source_object.get_state()
|
||||
state_cmd_table = statetable.GLOBAL_STATE_TABLE.get_cmd_table(state_name)
|
||||
|
||||
if state_name and state_cmd_table:
|
||||
# we are in a special state.
|
||||
|
||||
# check idle command.
|
||||
match_idle(command)
|
||||
# check for channel commands
|
||||
prev_command = command.command_string
|
||||
match_channel(command)
|
||||
if prev_command != command.command_string:
|
||||
# a channel command is handled normally also in the state
|
||||
command_table_lookup(command, cmdtable.GLOBAL_CMD_TABLE)
|
||||
else:
|
||||
command_table_lookup(command, state_cmd_table)
|
||||
else:
|
||||
#normal operation
|
||||
|
||||
# Match against the 'idle' command.
|
||||
match_idle(command)
|
||||
# See if this is an aliased command.
|
||||
match_alias(command)
|
||||
# Check if the user is using a channel command.
|
||||
match_channel(command)
|
||||
# See if the user is trying to traverse an exit.
|
||||
match_exits(command)
|
||||
neighbor_match_found = match_neighbor_ctables(command)
|
||||
if not neighbor_match_found:
|
||||
# Retrieve the appropriate (if any) command function.
|
||||
command_table_lookup(command, cmdtable.GLOBAL_CMD_TABLE)
|
||||
|
||||
"""
|
||||
By this point, we assume that the user has entered a command and not
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
"""
|
||||
Command Table Module
|
||||
---------------------
|
||||
|
||||
Each command entry consists of a key and a tuple containing a reference to the
|
||||
command's function, and a tuple of the permissions to match against. The user
|
||||
only need have one of the permissions in the permissions tuple to gain
|
||||
|
|
@ -19,7 +19,7 @@ from src.helpsys.management.commands.edit_helpfiles import add_help
|
|||
|
||||
class CommandTable(object):
|
||||
"""
|
||||
Stores command tables and performs lookups.
|
||||
Stores commands and performs lookups.
|
||||
"""
|
||||
ctable = None
|
||||
|
||||
|
|
@ -28,7 +28,7 @@ class CommandTable(object):
|
|||
self.ctable = {}
|
||||
|
||||
def add_command(self, command_string, function, priv_tuple=None,
|
||||
extra_vals=None, auto_help=False, staff_only=False):
|
||||
extra_vals=None, auto_help=False, staff_help=False, state=None):
|
||||
"""
|
||||
Adds a command to the command table.
|
||||
|
||||
|
|
@ -41,7 +41,7 @@ class CommandTable(object):
|
|||
auto_help (bool): If true, automatically creates/replaces a help topic with the
|
||||
same name as the command_string, using the functions's __doc__ property
|
||||
for help text.
|
||||
staff_help (bool): Only relevant if help_auto is activated; It True, makes the
|
||||
staff_help (bool): Only relevant if auto_help is activated; If True, makes the
|
||||
help topic (and all eventual subtopics) only visible to staff.
|
||||
|
||||
Note: the auto_help system also supports limited markup. If you divide your __doc__
|
||||
|
|
@ -57,7 +57,7 @@ class CommandTable(object):
|
|||
#add automatic help text from the command's doc string
|
||||
topicstr = command_string
|
||||
entrytext = function.__doc__
|
||||
add_help(topicstr, entrytext, staff_only=staff_only,
|
||||
add_help(topicstr, entrytext, staff_only=staff_help,
|
||||
force_create=True, auto_help=True)
|
||||
|
||||
def get_command_tuple(self, func_name):
|
||||
|
|
@ -67,6 +67,7 @@ class CommandTable(object):
|
|||
"""
|
||||
return self.ctable.get(func_name, False)
|
||||
|
||||
|
||||
"""
|
||||
Command tables
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -867,7 +867,7 @@ def cmd_recover(command):
|
|||
|
||||
|
||||
GLOBAL_CMD_TABLE.add_command("@recover", cmd_recover,
|
||||
priv_tuple=("genperms.builder"),auto_help=True,staff_only=True)
|
||||
priv_tuple=("genperms.builder"),auto_help=True,staff_help=True)
|
||||
|
||||
def cmd_destroy(command):
|
||||
"""
|
||||
|
|
@ -943,4 +943,4 @@ def cmd_destroy(command):
|
|||
source_object.emit_to("You schedule %s for destruction." % target_obj.get_name())
|
||||
|
||||
GLOBAL_CMD_TABLE.add_command("@destroy", cmd_destroy,
|
||||
priv_tuple=("genperms.builder"),auto_help=True,staff_only=True)
|
||||
priv_tuple=("genperms.builder"),auto_help=True,staff_help=True)
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ def _create_help(topicstr, entrytext, staff_only=False, force_create=False,
|
|||
new_entry.save()
|
||||
return [new_entry]
|
||||
|
||||
def _handle_help_markup(topicstr, entrytext, staff_only, identifier="<<TOPIC:"):
|
||||
def handle_help_markup(topicstr, entrytext, staff_only, identifier="<<TOPIC:"):
|
||||
"""
|
||||
Handle help markup in order to split help into subsections.
|
||||
Handles markup of the form <<TOPIC:STAFF:TopicTitle>> and
|
||||
|
|
@ -103,7 +103,7 @@ def _handle_help_markup(topicstr, entrytext, staff_only, identifier="<<TOPIC:"):
|
|||
staff_dict[topic] = staff_only
|
||||
return topic_dict, staff_dict
|
||||
|
||||
def _format_footer(top, text, topic_dict, staff_dict):
|
||||
def format_footer(top, text, topic_dict, staff_dict):
|
||||
"""
|
||||
Formats the subtopic with a 'Related Topics:' footer. If mixed
|
||||
staff-only flags are set, those help entries without the staff-only flag
|
||||
|
|
@ -147,13 +147,13 @@ def add_help(topicstr, entrytext, staff_only=False, force_create=False,
|
|||
identifier = '<<TOPIC:'
|
||||
if identifier in entrytext:
|
||||
#There is markup in the entry, so we should split the doc into separate subtopics
|
||||
topic_dict, staff_dict = _handle_help_markup(topicstr, entrytext,
|
||||
topic_dict, staff_dict = handle_help_markup(topicstr, entrytext,
|
||||
staff_only, identifier)
|
||||
topics = []
|
||||
for topic, text in topic_dict.items():
|
||||
|
||||
#format with nice footer
|
||||
entry = _format_footer(topic, text, topic_dict, staff_dict)
|
||||
entry = format_footer(topic, text, topic_dict, staff_dict)
|
||||
|
||||
if entry:
|
||||
#create the subtopic
|
||||
|
|
|
|||
|
|
@ -117,6 +117,10 @@ class Object(models.Model):
|
|||
|
||||
objects = ObjectManager()
|
||||
|
||||
#state system can set a particular command table to be used.
|
||||
state = None
|
||||
|
||||
|
||||
def __cmp__(self, other):
|
||||
"""
|
||||
Used to figure out if one object is the same as another.
|
||||
|
|
@ -981,5 +985,21 @@ class Object(models.Model):
|
|||
otype = int(self.type)
|
||||
return defines_global.OBJECT_TYPES[otype][1][0]
|
||||
|
||||
|
||||
def get_state(self):
|
||||
return self.state
|
||||
|
||||
def set_state(self, cmd_table=None):
|
||||
"""
|
||||
Set the state by defining which cmd_table is currently used.
|
||||
"""
|
||||
if self.is_player():
|
||||
self.state = cmd_table
|
||||
|
||||
def clear_state(self):
|
||||
self.state = None
|
||||
|
||||
|
||||
|
||||
# Deferred imports are poopy. This will require some thought to fix.
|
||||
from src import cmdhandler
|
||||
|
|
|
|||
228
src/statetable.py
Normal file
228
src/statetable.py
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
"""
|
||||
The state system allows the player to enter a state where only a special set of commands
|
||||
are available. This can be used for all sorts of useful things:
|
||||
- in-game menus (picking an option from a list)
|
||||
- inline text editors (entering text into a buffer)
|
||||
- npc conversation (select replies)
|
||||
- commands only available while in 'combat mode' etc
|
||||
This allows for more power than using rooms to build 'menus'.
|
||||
|
||||
Basically the GLOBAL_STATE_TABLE contains a dict with command tables keyed after the
|
||||
name of the state. To use, a function must set the 'state' variable on a player object
|
||||
using the obj.set_state() function. The GLOBAL_STATE_TABLE will then be searched by the
|
||||
command handler and if the state is defined its command table is used instead
|
||||
of the normal global command table.
|
||||
|
||||
The state system is pluggable, in the same way that commands are added to the global command
|
||||
table, commands are added to the GLOBAL_STATE_TABLE using add_command supplying in
|
||||
addition the name of the state.
|
||||
"""
|
||||
|
||||
from cmdtable import CommandTable
|
||||
from logger import log_errmsg
|
||||
import src.helpsys.management.commands.edit_helpfiles as edit_help
|
||||
|
||||
|
||||
class StateTable(object):
|
||||
|
||||
state_table = None
|
||||
|
||||
def __init__(self):
|
||||
self.state_table = {}
|
||||
self.help_index = StateHelpIndex()
|
||||
|
||||
def add_command(self, state_name, command_string, function, priv_tuple=None,
|
||||
extra_vals=None, auto_help=False, staff_help=False, help_global=False,
|
||||
exit_command=True):
|
||||
"""
|
||||
Access function; transparently add commands to a specific command table to
|
||||
reprsent a particular state. This command is similar to the normal
|
||||
command_table.add_command() function. See example in gamesrc/commands/examples.
|
||||
|
||||
command_string: (str) command name to run, like look, @list etc
|
||||
function: (reference) command function object
|
||||
state_name: (str) identifier of the state we tie this to.
|
||||
priv_tuple: (tuple) String tuple of permissions required for command.
|
||||
extra_vals: (dict) Dictionary to add to the Command object.
|
||||
|
||||
auto_help: (bool) Activate the auto_help system. By default this stores the
|
||||
help inside the statetable only (not in the main help database), and so
|
||||
the help entries are only available when the player is actually inside
|
||||
the state. Note that the auto_help system of state-commands do not
|
||||
support <<TOPIC:mytitle>> markup.
|
||||
staff_help: (bool) Help entry is only available for staff players.
|
||||
help_global: (bool) Also auto_add the help entry to the main help database. Be
|
||||
careful with overwriting same-named help topics if you define special
|
||||
versions of commands inside your state.
|
||||
|
||||
exit_command: (bool) Sets if the default @exit command is added to the state. Only
|
||||
one command needs to include this statement in order to add @exit. This is
|
||||
usually a good idea to make sure the player is not stuck, but you might want
|
||||
to turn this off if you know what you're doing and want to avoid players
|
||||
'escaping' the state (like in a combat state or similar), or when
|
||||
you need to do some special final cleanup or save operations before
|
||||
exiting (like in a text editor).
|
||||
"""
|
||||
|
||||
if not state_name:
|
||||
log_errmsg("Command %s was not given a state. Not added." % command_string)
|
||||
return
|
||||
|
||||
state_name = state_name.strip()
|
||||
if not self.state_table.has_key(state_name):
|
||||
|
||||
#create the state
|
||||
self.state_table[state_name] = CommandTable()
|
||||
#always add a help index even though it might not be used.
|
||||
self.help_index.add_state(state_name)
|
||||
|
||||
if exit_command:
|
||||
#add the @exit command
|
||||
self.state_table[state_name].add_command("@exit",
|
||||
cmd_state_exit,
|
||||
auto_help=True)
|
||||
if auto_help:
|
||||
#add help for @exit command
|
||||
self.help_index.add_state_help(state_name, "@exit",
|
||||
cmd_state_exit.__doc__)
|
||||
|
||||
#handle auto-help for the state
|
||||
if auto_help:
|
||||
|
||||
#make sure the state's help command is in place
|
||||
self.state_table[state_name].add_command('help',cmd_state_help)
|
||||
|
||||
#add the help text
|
||||
helptext = function.__doc__
|
||||
self.help_index.add_state_help(state_name,command_string,
|
||||
helptext,staff_only=staff_help)
|
||||
if not help_global:
|
||||
#if we don't want global help access, we need to
|
||||
#turn off auto_help before adding the command.
|
||||
auto_help = False
|
||||
|
||||
#finally add the new command to the state
|
||||
self.state_table[state_name].add_command(command_string,
|
||||
function, priv_tuple,
|
||||
extra_vals,auto_help,
|
||||
staff_help)
|
||||
|
||||
def get_cmd_table(self, state_name):
|
||||
"""
|
||||
Return the command table for a particular state.
|
||||
"""
|
||||
if self.state_table.has_key(state_name):
|
||||
return self.state_table[state_name]
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
class StateHelpIndex(object):
|
||||
"""
|
||||
Handles the dynamic state help system.
|
||||
"""
|
||||
help_index = None
|
||||
def __init__(self):
|
||||
self.help_index = {}
|
||||
self.identifier = '<<TOPIC:'
|
||||
|
||||
def add_state(self,state_name):
|
||||
"Create a new state"
|
||||
self.help_index[state_name] = {}
|
||||
|
||||
def add_state_help(self, state,command,text,staff_only=False):
|
||||
"""Store help for a command under a certain state.
|
||||
Supports <<TOPIC:MyTopic>> and <<TOPIC:STAFF:MyTopic>> markup."""
|
||||
if self.help_index.has_key(state):
|
||||
|
||||
if self.identifier in text:
|
||||
topic_dict, staff_dict = edit_help.handle_help_markup(command, text, staff_only,
|
||||
identifier=self.identifier)
|
||||
for topic, text in topic_dict.items():
|
||||
entry = edit_help.format_footer(topic,text,topic_dict,staff_dict)
|
||||
if entry:
|
||||
self.help_index[state][topic] = (staff_only, entry)
|
||||
else:
|
||||
self.help_index[state][command] = (staff_only, text)
|
||||
|
||||
def get_state_help(self,caller, state, command):
|
||||
"get help for a particular command within a state"
|
||||
if self.help_index.has_key(state) and self.help_index[state].has_key(command):
|
||||
help_tuple = self.help_index[state][command]
|
||||
if caller.is_staff() or not help_tuple[0]:
|
||||
return help_tuple[1]
|
||||
return None
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_state_index(self,caller, state):
|
||||
"list all help topics for a state"
|
||||
if self.help_index.has_key(state):
|
||||
if caller.is_staff():
|
||||
index = self.help_index[state].keys()
|
||||
else:
|
||||
index = []
|
||||
for key, tup in self.help_index[state].items():
|
||||
if not tup[0]:
|
||||
index.append(key)
|
||||
return sorted(index)
|
||||
else:
|
||||
return None
|
||||
|
||||
#default commands available for all special states
|
||||
|
||||
def cmd_state_exit(command):
|
||||
"""@exit (when in a state)
|
||||
|
||||
This command only works when inside a special game 'state' (like a menu or
|
||||
editor or similar place where you have fewer commands available than normal).
|
||||
|
||||
It aborts what you were doing and force-exits back to the normal mode of
|
||||
gameplay. Some states might deactivate the @exit command for various reasons.
|
||||
In those cases, read the help when in the state to learn more."""
|
||||
|
||||
source_object = command.source_object
|
||||
source_object.clear_state()
|
||||
source_object.emit_to("... Exited.")
|
||||
source_object.execute_cmd('look')
|
||||
|
||||
def cmd_state_help(command):
|
||||
"""
|
||||
help <topic> (while in a state)
|
||||
|
||||
In-state help system. This is NOT tied to the normal help
|
||||
system and is not stored in the database. It is intended as a quick
|
||||
reference for users when in the state; if you want a detailed description
|
||||
of the state itself, you should probably add it to the main help system
|
||||
so the user can read it at any time.
|
||||
If you don't want to use the auto-system, turn off auto_help
|
||||
for all commands in the state. You could then for example make a custom help command
|
||||
that displays just a short help summary page instead.
|
||||
"""
|
||||
|
||||
source_object = command.source_object
|
||||
state = source_object.get_state()
|
||||
args = command.command_argument
|
||||
help_index = GLOBAL_STATE_TABLE.help_index
|
||||
|
||||
if not args:
|
||||
index = help_index.get_state_index(source_object, state)
|
||||
if not index:
|
||||
source_object.emit_to("There is no help available here.")
|
||||
return
|
||||
s = "Help topics for %s:\n" % state
|
||||
for i in index:
|
||||
s += " %s\n" % i
|
||||
s = s[:-1]
|
||||
source_object.emit_to(s)
|
||||
return
|
||||
else:
|
||||
args = args.strip()
|
||||
help = help_index.get_state_help(source_object, state, args)
|
||||
if help:
|
||||
source_object.emit_to("%s" % help)
|
||||
else:
|
||||
source_object.emit_to("No help available on %s." % args)
|
||||
|
||||
|
||||
GLOBAL_STATE_TABLE = StateTable()
|
||||
Loading…
Add table
Add a link
Reference in a new issue