Add non-working, initial concept for olc system.
This commit is contained in:
parent
ee4dd20fd9
commit
00b8dd4881
5 changed files with 535 additions and 0 deletions
0
evennia/utils/olc/__init__.py
Normal file
0
evennia/utils/olc/__init__.py
Normal file
148
evennia/utils/olc/olc.py
Normal file
148
evennia/utils/olc/olc.py
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
"""
|
||||
OLC - On-Line Creation
|
||||
|
||||
This module is the core of the Evennia online creation helper system.
|
||||
This is a resource intended for players with build privileges.
|
||||
|
||||
While the OLC command can be used to start the OLC "from the top", the
|
||||
system is also intended to be plugged in to enhance existing build commands
|
||||
with a more menu-like building style.
|
||||
|
||||
Functionality:
|
||||
|
||||
- Prototype management: Allows to create and edit Prototype
|
||||
dictionaries. Can store such Prototypes on the Builder Player as an Attribute
|
||||
or centrally on a central store that all builders can fetch prototypes from.
|
||||
- Creates a new entity either from an existing prototype or by creating the
|
||||
prototype on the fly for the sake of that single object (the prototype can
|
||||
then also be saved for future use).
|
||||
- Recording of session, for performing a series of recorded build actions in sequence.
|
||||
Stored so as to be possible to reproduce.
|
||||
- Export of objects created in recording mode to a batchcode file (Immortals only).
|
||||
|
||||
|
||||
"""
|
||||
|
||||
from collections import OrderedDict
|
||||
from time import time
|
||||
from evennia.utils.evmenu import EvMenu
|
||||
from evennia.commands.command import Command
|
||||
|
||||
|
||||
# OLC settings
|
||||
_SHOW_PROMPT = True # settings.OLC_SHOW_PROMPT
|
||||
_DEFAULT_PROMPT = "" # settings.OLC_DEFAULT_PROMPT
|
||||
_LEN_HISTORY = 10 # settings.OLC_HISTORY_LENGTH
|
||||
|
||||
|
||||
# OLC Session
|
||||
|
||||
def _new_session():
|
||||
|
||||
"""
|
||||
This generates an empty olcsession structure, which is used to hold state
|
||||
information in the olc but which can also be pickled.
|
||||
|
||||
Returns:
|
||||
olcsession (dict): An empty OLCSession.
|
||||
|
||||
Notes:
|
||||
This is a customized dict which the Attribute system will
|
||||
understand how to pickle and depickle since it provides
|
||||
iteration.
|
||||
"""
|
||||
return {
|
||||
# header info
|
||||
"caller": None, # the current user of this session
|
||||
"modified": time.now(), # last time this olcsession was active
|
||||
"db_model": None, # currently unused, ObjectDB for now
|
||||
"prompt_template": _DEFAULT_PROMPT, # prompt display
|
||||
"olcfields": OrderedDict(), # registered OLCFields. Order matters
|
||||
"prototype_key": "", # current active prototype key
|
||||
}
|
||||
|
||||
|
||||
def _update_prompt(osession):
|
||||
"""
|
||||
Update the OLC status prompt.
|
||||
|
||||
Returns:
|
||||
prompt (str): The prompt based on the
|
||||
prompt template, populated with
|
||||
the olcsession state.
|
||||
|
||||
"""
|
||||
return ""
|
||||
|
||||
|
||||
def search_entity(osession, query):
|
||||
"""
|
||||
Perform a query for a specified entity. Which type of entity is determined by the osession
|
||||
state.
|
||||
|
||||
Args:
|
||||
query (str): This is a string, a #dbref or an extended search
|
||||
|
||||
"""
|
||||
osession['db_model'].__class__.
|
||||
|
||||
|
||||
|
||||
|
||||
def display_prototype(osession):
|
||||
"""
|
||||
Display prototype fields according to the order of the registered olcfields.
|
||||
|
||||
"""
|
||||
# TODO: Simple one column display to begin with - make multi-column later
|
||||
pkey = osession['prototype_key']
|
||||
outtxt = ["=== {pkey} ===".format(pkey=pkey)]
|
||||
for field in osession['olcfields'].values():
|
||||
fname, flabel, fvalue = field.name, field.label, field.display()
|
||||
outtxt.append(" {fieldname} ({label}): {value}".format(fieldname=fname,
|
||||
label=flabel, value=fvalue))
|
||||
return '\n'.join(outtxt)
|
||||
|
||||
|
||||
def display_field_value(osession, fieldname):
|
||||
"""
|
||||
Display info about a specific field.
|
||||
"""
|
||||
field = osession['olcfields'].get(fieldname, None)
|
||||
if field:
|
||||
return "{fieldname}: {value}".format(fieldname=field.name, value=field.display())
|
||||
|
||||
|
||||
# Access function
|
||||
|
||||
def OLC(caller, target=None, startnode=None):
|
||||
"""
|
||||
This function is a common entry-point into the OLC menu system. It is used
|
||||
by Evennia systems to jump into the different possible start points of the
|
||||
OLC menu tree depending on what info is already available.
|
||||
|
||||
Args:
|
||||
caller (Object or Player): The one using the olc.
|
||||
target (Object, optional): Object to operate on, if any is known.
|
||||
startnode (str, optional): Where in the menu tree to start. If unset,
|
||||
will be decided by whether target is given or not.
|
||||
|
||||
"""
|
||||
startnode = startnode or (target and "node_edit_top") or "node_top"
|
||||
EvMenu(caller, "evennia.utils.olc.olc_menu", startnode=startnode, target=target)
|
||||
|
||||
|
||||
class CmdOLC(Command):
|
||||
"""
|
||||
Test OLC
|
||||
|
||||
Usage:
|
||||
olc [target]
|
||||
|
||||
Starts the olc to create a new object or to modify an existing one.
|
||||
|
||||
"""
|
||||
key = "olc"
|
||||
def func(self):
|
||||
OLC(self.caller, target=self.args)
|
||||
|
||||
291
evennia/utils/olc/olc_fields.py
Normal file
291
evennia/utils/olc/olc_fields.py
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
"""
|
||||
OLC fields describe how to edit and display a specific piece of data of a prototype within the OLC system.
|
||||
|
||||
The OLC system imports and adds these field classes to its prototype manipulation pages in order to
|
||||
know what data to read and how to display it.
|
||||
|
||||
"""
|
||||
from collections import deque
|
||||
# from django.conf import settings
|
||||
|
||||
_OLC_VALIDATION_ERROR = """
|
||||
Error storing data in {fieldname}:
|
||||
{value}
|
||||
The reported error was
|
||||
{error}
|
||||
"""
|
||||
|
||||
_LEN_HISTORY = 10 # settings.OLC_HISTORY_LENGTH
|
||||
|
||||
|
||||
class OLCField(object):
|
||||
"""
|
||||
This is the parent for all OLC fields. This docstring acts
|
||||
as the help text for the field.
|
||||
|
||||
"""
|
||||
# name of this field, for error reporting
|
||||
key = "Empty field"
|
||||
# if this field must have a value different than None
|
||||
required = False
|
||||
# used for displaying extra info in the OLC
|
||||
label = "Empty field"
|
||||
# initial value of field if not given
|
||||
initial = None
|
||||
# actions available on this field. Available actions
|
||||
# are replace, edit, append, remove, clear, help
|
||||
actions = ['replace', 'edit', 'remove', 'clear', 'help']
|
||||
|
||||
def __init__(self, olcsession):
|
||||
self.olcsession = olcsession
|
||||
self._value_history = deque([self.initial], _LEN_HISTORY)
|
||||
self._history_pos = 0
|
||||
self._has_changed = False
|
||||
|
||||
def __repr__(self):
|
||||
return self.display()
|
||||
|
||||
# storing data to the field in a history-aware way
|
||||
@property
|
||||
def value(self):
|
||||
return self._value_history[self._history_pos]
|
||||
|
||||
@value.setter
|
||||
def value(self, value):
|
||||
"""
|
||||
Update field value by updating the history.
|
||||
"""
|
||||
original_value = value
|
||||
try:
|
||||
value = self.validate(value)
|
||||
except Exception as err:
|
||||
errtext = _OLC_VALIDATION_ERROR.format(fieldname=self.key, value=original_value, error=err)
|
||||
self.olcsession.caller.msg(errtext)
|
||||
return
|
||||
if (self._value_history and isinstance(value, (basestring, bool, int, float)) and
|
||||
self._value_history[0] == value):
|
||||
# don't change/update history if re-adding the same thing
|
||||
return
|
||||
else:
|
||||
self._has_changed = True
|
||||
self._history_pos = 0
|
||||
self._value_history.appendleft(value)
|
||||
|
||||
@value.deleter
|
||||
def value(self):
|
||||
self.history_pos = 0
|
||||
self._value_history.appendleft(self.initial)
|
||||
|
||||
def history(self, step):
|
||||
"""
|
||||
Change history position.
|
||||
|
||||
Args:
|
||||
step (int): Step in the history stack. Positive movement
|
||||
means moving futher back in history (with a maximum
|
||||
of `settings.OLC_HISTORY_LENGTH`, negative steps
|
||||
moves towards recent history (with 0 being the latest
|
||||
value).
|
||||
|
||||
"""
|
||||
self._history_pos = min(len(self.value_history)-1, max(0, self._history_pos + step))
|
||||
|
||||
def has_changed(self):
|
||||
"""
|
||||
Check if this field has changed.
|
||||
|
||||
Returns:
|
||||
changed (bool): If the field changed or not.
|
||||
|
||||
"""
|
||||
return bool(self._has_changed)
|
||||
|
||||
# overloadable methods
|
||||
|
||||
def from_entity(self, entity, **kwargs):
|
||||
"""
|
||||
Populate this field from an entity.
|
||||
|
||||
Args:
|
||||
entity (any): An object to use for
|
||||
populating this field (like an Object).
|
||||
"""
|
||||
pass
|
||||
|
||||
def to_prototype(self, prototype):
|
||||
"""
|
||||
Store this field value in a prototype.
|
||||
|
||||
Args:
|
||||
prototype (dict): The prototype dict
|
||||
to update with the value of this field.
|
||||
"""
|
||||
pass
|
||||
|
||||
def validate(self, value, **kwargs):
|
||||
"""
|
||||
Validate/preprocess data to store in this field.
|
||||
|
||||
Args:
|
||||
value (any): An input value to
|
||||
validate
|
||||
|
||||
Kwargs:
|
||||
any (any): Optional info to send to field.
|
||||
|
||||
Returns:
|
||||
validated_value (any): The value, correctly
|
||||
validated and/or processed to store in this field.
|
||||
|
||||
Raises:
|
||||
Exception: If the field was given an
|
||||
invalid value to validate.
|
||||
|
||||
"""
|
||||
return str(value)
|
||||
|
||||
def display(self):
|
||||
"""
|
||||
How to display the field contents in the OLC display.
|
||||
|
||||
"""
|
||||
return self.value
|
||||
|
||||
|
||||
# OLCFields for all the standard model properties
|
||||
# key, location, destination, home, aliases,
|
||||
# permissions, tags, attributes
|
||||
# ...
|
||||
|
||||
|
||||
class OLCKeyField(OLCField):
|
||||
"""
|
||||
The name (key) of the object is its main identifier, used
|
||||
throughout listings even if may not always be visible to
|
||||
the end user.
|
||||
"""
|
||||
key = 'Name'
|
||||
required = True
|
||||
label = "The object's name"
|
||||
|
||||
def from_entity(self, entity, **kwargs):
|
||||
self.value = entity.db_key
|
||||
|
||||
def to_prototype(self, prototype):
|
||||
prototype['key'] = self.value
|
||||
|
||||
|
||||
class OLCLocationField(OLCField):
|
||||
"""
|
||||
An object's location is usually a Room but could be any
|
||||
other in-game entity. By convention, Rooms themselves have
|
||||
a None location. Objects are otherwise only placed in a
|
||||
None location to take them out of the game.
|
||||
"""
|
||||
key = 'Location'
|
||||
required = False
|
||||
label = "The object's current location"
|
||||
|
||||
def validate(self, value):
|
||||
return self.olcsession.search_by_string(value)
|
||||
|
||||
def from_entity(self, entity, **kwargs):
|
||||
self.value = entity.db_location
|
||||
|
||||
def to_prototype(self, prototype):
|
||||
prototype['location'] = self.value
|
||||
|
||||
|
||||
class OLCHomeField(OLCField):
|
||||
"""
|
||||
An object's home location acts as a fallback when various
|
||||
extreme situations occur. An example is when a location is
|
||||
deleted - all its content (except exits) are then not deleted
|
||||
but are moved to each object's home location.
|
||||
"""
|
||||
key = 'Home'
|
||||
required = True
|
||||
label = "The object's home location"
|
||||
|
||||
def validate(self, value):
|
||||
return self.olcsession.search_by_string(value)
|
||||
|
||||
def from_entity(self, entity, **kwargs):
|
||||
self.value = entity.db_home
|
||||
|
||||
def to_prototype(self, prototype):
|
||||
prototype['home'] = self.value
|
||||
|
||||
class OLCDestinationField(OLCField):
|
||||
"""
|
||||
An object's destination is usually not set unless the object
|
||||
represents an exit between game locations. If set, the
|
||||
destination should be set to the location you get to when
|
||||
passing through this exit.
|
||||
|
||||
"""
|
||||
key = 'Destination'
|
||||
required = False
|
||||
label = "The object's (usually exit's) destination"
|
||||
|
||||
def validate(self, value):
|
||||
return self.olcsession.search_by_string(value)
|
||||
|
||||
def from_entity(self, entity, **kwargs):
|
||||
self.value = entity.db_destination
|
||||
|
||||
def to_prototype(self, prototype):
|
||||
prototype['destination'] = self.value
|
||||
|
||||
|
||||
class OLCAliasField(OLCField):
|
||||
"""
|
||||
Specify as a comma-separated list. Use quotes around the
|
||||
alias if the alias itself contains a comma.
|
||||
|
||||
Aliases are alternate names for an object. An alias is just
|
||||
as fast to search for as a key and two objects are assumed
|
||||
to have the same name is *either* their name or any of their
|
||||
aliases match.
|
||||
|
||||
"""
|
||||
key = 'Aliases'
|
||||
required = False
|
||||
label = "The object's alternative name or names"
|
||||
actions = OLCField.actions + ['append']
|
||||
|
||||
def validate(self, value):
|
||||
return split_by_comma(value)
|
||||
|
||||
def from_entity(self, entity, **kwargs):
|
||||
self.value = list(entity.db_aliases.all())
|
||||
|
||||
def to_prototype(self, prototype):
|
||||
prototype['aliases'] = self.value
|
||||
|
||||
|
||||
class OLCTagField(OLCField):
|
||||
"""
|
||||
Specify as a comma-separated list of tagname or tagname:category.
|
||||
|
||||
Aliases are alternate names for an object. An alias is just
|
||||
as fast to search for as a key and two objects are assumed
|
||||
to have the same name is *either* their name or any of their
|
||||
aliases match.
|
||||
|
||||
"""
|
||||
key = 'Aliases'
|
||||
required = False
|
||||
label = "The object's (usually exit's) destination"
|
||||
actions = OLCField.actions + ['append']
|
||||
|
||||
def validate(self, value):
|
||||
return [tagstr.split(':', 1) if ':' in tagstr else (tagstr, None)
|
||||
for tagstr in split_by_comma(value)]
|
||||
|
||||
def from_entity(self, entity, **kwargs):
|
||||
self.value = entity.tags.all(return_key_and_category=True)
|
||||
|
||||
def to_prototype(self, prototype):
|
||||
prototype['tags'] = self.value
|
||||
|
||||
83
evennia/utils/olc/olc_menutree.py
Normal file
83
evennia/utils/olc/olc_menutree.py
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
"""
|
||||
This describes the menu structure/logic of the OLC system editor, using the EvMenu subsystem. The
|
||||
various nodes are modular and will when possible make use of the various utilities of the OLC rather
|
||||
than hard-coding things in each node.
|
||||
|
||||
Menu structure:
|
||||
|
||||
start:
|
||||
new object
|
||||
edit object <dbref>
|
||||
manage prototypes
|
||||
export session to batchcode file (immortals only)
|
||||
|
||||
new/edit object:
|
||||
Protoype
|
||||
Typeclass
|
||||
Key
|
||||
Location
|
||||
Destination
|
||||
PErmissions
|
||||
LOcks
|
||||
Attributes
|
||||
TAgs
|
||||
Scripts
|
||||
|
||||
create/update object
|
||||
copy object
|
||||
save prototype
|
||||
save/delete object
|
||||
update existing objects
|
||||
|
||||
manage prototypes
|
||||
list prototype
|
||||
search prototype
|
||||
import prototype (from global store)
|
||||
|
||||
export session
|
||||
|
||||
"""
|
||||
|
||||
def node_top(caller, raw_input):
|
||||
# top level node
|
||||
# links to edit, manage, export
|
||||
text = """OnLine Creation System"""
|
||||
options = ({"key": ("|yN|new", "new", "n"),
|
||||
"desc": "New object",
|
||||
"goto": "node_new_top",
|
||||
"exec": _obj_to_prototype},
|
||||
{"key": ("|yE|ndit", "edit", "e", "m"),
|
||||
"desc": "Edit existing object",
|
||||
"goto": "node_edit_top",
|
||||
"exec": _obj_to_prototype},
|
||||
{"key": ("|yP|nrototype", "prototype", "manage", "p", "m"),
|
||||
"desc": "Manage prototypes",
|
||||
"goto": "node_prototype_top"},
|
||||
{"key": ("E|yx|nport", "export", "x"),
|
||||
"desc": "Export to prototypes",
|
||||
"goto": "node_prototype_top"},
|
||||
{"key": ("|yQ|nuit", "quit", "q"),
|
||||
"desc": "Quit OLC",
|
||||
"goto": "node_quit"},)
|
||||
return text, options
|
||||
|
||||
def node_quit(caller, raw_input):
|
||||
return 'Exiting.', None
|
||||
|
||||
def node_new_top(caller, raw_input):
|
||||
pass
|
||||
|
||||
def node_edit_top(caller, raw_input):
|
||||
# edit top level
|
||||
text = """Edit object"""
|
||||
|
||||
|
||||
def node_prototype_top(caller, raw_input):
|
||||
# manage prototypes
|
||||
pass
|
||||
|
||||
|
||||
def node_export_top(caller, raw_input):
|
||||
# export top level
|
||||
pass
|
||||
|
||||
13
evennia/utils/olc/olc_utils.py
Normal file
13
evennia/utils/olc/olc_utils.py
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
"""
|
||||
Miscellaneous utilities for the OLC system.
|
||||
|
||||
"""
|
||||
import csv
|
||||
|
||||
|
||||
def search_by_string(olcsession, query):
|
||||
pass
|
||||
|
||||
|
||||
def split_by_comma(string):
|
||||
return csv.reader([string], skipinitialspace=True)
|
||||
Loading…
Add table
Add a link
Reference in a new issue