Updated contrib to use the Google style docstrings are per #709. Also

remove long deprecated code-block usage in menusystem - use callback functions
instead.
This commit is contained in:
Griatch 2015-05-17 12:43:12 +02:00
parent 64c6d06d0f
commit 3e9263e207
10 changed files with 352 additions and 172 deletions

View file

@ -104,7 +104,9 @@ class TradeTimeout(DefaultScript):
This times out the trade request, in case player B did not reply in time. This times out the trade request, in case player B did not reply in time.
""" """
def at_script_creation(self): def at_script_creation(self):
"called when script is first created" """
Called when script is first created
"""
self.key = "trade_request_timeout" self.key = "trade_request_timeout"
self.desc = "times out trade requests" self.desc = "times out trade requests"
self.interval = TRADE_TIMEOUT self.interval = TRADE_TIMEOUT
@ -113,13 +115,17 @@ class TradeTimeout(DefaultScript):
self.persistent = False self.persistent = False
def at_repeat(self): def at_repeat(self):
"called once" """
called once
"""
if self.ndb.tradeevent: if self.ndb.tradeevent:
self.obj.ndb.tradeevent.finish(force=True) self.obj.ndb.tradeevent.finish(force=True)
self.obj.msg("Trade request timed out.") self.obj.msg("Trade request timed out.")
def is_valid(self): def is_valid(self):
"Only valid if the trade has not yet started" """
Only valid if the trade has not yet started
"""
return self.obj.ndb.tradeevent and not self.obj.ndb.tradeevent.trade_started return self.obj.ndb.tradeevent and not self.obj.ndb.tradeevent.trade_started
@ -130,12 +136,19 @@ class TradeHandler(object):
""" """
def __init__(self, partA, partB): def __init__(self, partA, partB):
""" """
Initializes the trade. This is called when part A tries to initiate Initializes the trade. This is called when part A tries to
a trade with part B. The trade will not start until part B repeats initiate a trade with part B. The trade will not start until
this command (B will then call the self.join() command) part B repeats this command (B will then call the self.join()
command)
Args:
partA (object): The party trying to start barter.
partB (object): The party asked to barter.
Notes:
We also store the back-reference from the respective party
to this object.
We also store the back-reference from the respective party to
this object.
""" """
# parties # parties
self.partA = partA self.partA = partA
@ -152,9 +165,14 @@ class TradeHandler(object):
def msg(self, party, string): def msg(self, party, string):
""" """
Relay a message to the other party. This allows Relay a message to the other party. This allows the calling
the calling command to not have to worry about command to not have to worry about which party they are in the
which party they are in the handler. handler.
Args:
party (object): One of partA or B. The method will figure
out which is which.
string (str): Text to send.
""" """
if self.partA == party: if self.partA == party:
self.partB.msg(string) self.partB.msg(string)
@ -165,7 +183,16 @@ class TradeHandler(object):
self.party.msg(string) self.party.msg(string)
def get_other(self, party): def get_other(self, party):
"Returns the other party of the trade" """
Returns the other party of the trade
Args:
partyX (object): One of the parties of the negotiation
Returns:
partyY (object): The other party, not partyX.
"""
if self.partA == party: if self.partA == party:
return self.partB return self.partB
if self.partB == party: if self.partB == party:
@ -175,6 +202,10 @@ class TradeHandler(object):
def join(self, partB): def join(self, partB):
""" """
This is used once B decides to join the trade This is used once B decides to join the trade
Args:
partB (object): The party accepting the barter.
""" """
print "join:", self.partB, partB, self.partB == partB, type(self.partB), type(partB) print "join:", self.partB, partB, self.partB == partB, type(self.partB), type(partB)
if self.partB == partB: if self.partB == partB:
@ -186,7 +217,11 @@ class TradeHandler(object):
def unjoin(self, partB): def unjoin(self, partB):
""" """
This is used if B decides not to join the trade This is used if B decides not to join the trade.
Args:
partB (object): The party leaving the barter.
""" """
if self.partB == partB: if self.partB == partB:
self.finish() self.finish()
@ -198,6 +233,11 @@ class TradeHandler(object):
Change the current standing offer. We leave it up to the Change the current standing offer. We leave it up to the
command to do the actual checks that the offer consists command to do the actual checks that the offer consists
of real, valid, objects. of real, valid, objects.
Args:
party (object): Who is making the offer
args (objects or str): Offerings.
""" """
if self.trade_started: if self.trade_started:
# reset accept statements whenever an offer changes # reset accept statements whenever an offer changes
@ -212,15 +252,25 @@ class TradeHandler(object):
def list(self): def list(self):
""" """
Returns two lists of objects on offer, separated by partA/B. List current offers.
Returns:
offers (tuple): A tuple with two lists, (A_offers, B_offers).
""" """
return self.partA_offers, self.partB_offers return self.partA_offers, self.partB_offers
def search(self, offername): def search(self, offername):
""" """
Returns an object on offer, based on a search criterion. Search current offers.
If the search criterion is an integer, treat it as an
index to return in the list of offered items Args:
offername (str or int): Object to search for, or its index in
the list of offered items.
Returns:
offer (object): An object on offer, based on the search criterion.
""" """
all_offers = self.partA_offers + self.partB_offers all_offers = self.partA_offers + self.partB_offers
if isinstance(offername, int): if isinstance(offername, int):
@ -242,7 +292,18 @@ class TradeHandler(object):
""" """
Accept the current offer. Accept the current offer.
Returns True if this closes the deal, False otherwise Args:
party (object): The party accepting the deal.
Returns:
result (object): `True` if this closes the deal, `False`
otherwise
Notes:
This will only close the deal if both parties have
accepted independently. This is done by calling the
`finish()` method.
""" """
if self.trade_started: if self.trade_started:
if party == self.partA: if party == self.partA:
@ -255,9 +316,20 @@ class TradeHandler(object):
def decline(self, party): def decline(self, party):
""" """
Remove an previously accepted status (changing ones mind) Decline the offer (or change one's mind).
Args:
party (object): Party declining the deal.
Returns:
did_decline (bool): `True` if there was really an
`accepted` status to change, `False` otherwise.
Notes:
If previously having used the `accept` command, this
function will only work as long as the other party has not
yet accepted.
returns True if there was really a status to change, False otherwise.
""" """
if self.trade_started: if self.trade_started:
if party == self.partA: if party == self.partA:
@ -276,6 +348,12 @@ class TradeHandler(object):
def finish(self, force=False): def finish(self, force=False):
""" """
Conclude trade - move all offers and clean up Conclude trade - move all offers and clean up
Args:
force (bool, optional): Force cleanup regardless of if the
trade was accepted or not (if not, no goods will change
hands but trading will stop anyway)
""" """
fin = False fin = False
if self.trade_started and self.partA_accepted and self.partB_accepted: if self.trade_started and self.partA_accepted and self.partB_accepted:
@ -303,8 +381,8 @@ class TradeHandler(object):
class CmdTradeBase(Command): class CmdTradeBase(Command):
""" """
Base command for Trade commands to inherit from. Implements Base command for Trade commands to inherit from. Implements the
the custom parsing. custom parsing.
""" """
def parse(self): def parse(self):
""" """

View file

@ -2,9 +2,9 @@
Contribution - Griatch 2011 Contribution - Griatch 2011
[Note - with the advent of MULTISESSION_MODE=2, this is not really [Note - with the advent of MULTISESSION_MODE=2, this is not really as
as necessary anymore - the ooclook and @charcreate commands in that necessary anymore - the ooclook and @charcreate commands in that mode
mode replaces this module with better functionality.] replaces this module with better functionality.]
This is a simple character creation commandset. A suggestion is to This is a simple character creation commandset. A suggestion is to
test this together with menu_login, which doesn't create a Character test this together with menu_login, which doesn't create a Character
@ -18,11 +18,11 @@ while puppeting a Character already before.
Installation: Installation:
Read the instructions in contrib/examples/cmdset.py in Read the instructions in contrib/examples/cmdset.py in order to create
order to create a new default cmdset module for Evennia to use (copy a new default cmdset module for Evennia to use (copy the template up
the template up one level, and change the settings file's relevant one level, and change the settings file's relevant variables to point
variables to point to the cmdsets inside). If you already have such to the cmdsets inside). If you already have such a module you should
a module you should of course use that. of course use that.
Next import this module in your custom cmdset module and add the Next import this module in your custom cmdset module and add the
following line to the end of OOCCmdSet's at_cmdset_creation(): following line to the end of OOCCmdSet's at_cmdset_creation():

View file

@ -4,8 +4,8 @@ Dice - rolls dice for roleplaying, in-game gambling or GM:ing
Evennia contribution - Griatch 2012 Evennia contribution - Griatch 2012
This module implements a full-fledged dice-roller and a 'dice' command to This module implements a full-fledged dice-roller and a 'dice' command
go with it. It uses standard RPG 'd'-syntax (e.g. 2d6 to roll two to go with it. It uses standard RPG 'd'-syntax (e.g. 2d6 to roll two
six-sided die) and also supports modifiers such as 3d6 + 5. six-sided die) and also supports modifiers such as 3d6 + 5.
One can also specify a standard Python operator in order to specify One can also specify a standard Python operator in order to specify
@ -39,31 +39,53 @@ def roll_dice(dicenum, dicetype, modifier=None, conditional=None, return_tuple=F
""" """
This is a standard dice roller. This is a standard dice roller.
Input: Args:
dicenum - number of dice to roll (the result to be added) dicenum (int): Number of dice to roll (the result to be added).
dicetype - number of sides of the dice to be rolled dicetype (int): Number of sides of the dice to be rolled.
modifier - tuple (operator, value), where operator is a character string modifier (tuple): A tuple `(operator, value)`, where operator is
with one of +,-,/ or *. The entire result of the dice rolls will one of `"+"`, `"-"`, `"/"` or `"*"`. The result of the dice
be modified by this value. roll(s) will be modified by this value.
conditional - tuple (conditional, value), where conditional is a character conditional (tuple): A tuple `(conditional, value)`, where
string with one of ==,<,>,>=,<= or !=. conditional is one of `"=="`,`"<"`,`">"`,`">="`,`"<=`" or "`!=`".
return_tuple - return result as a tuple containing all relevant info This allows the roller to directly return a result depending
return_tuple - (default False) - return a tuple with all individual roll on if the conditional was passed or not.
results return_tuple (bool): Return a tuple with all individual roll
All input numbers are converted to integers. results or not.
Returns: Returns:
normally returns the result roll_result (int): The result of the roll + modifiers. This is the
if return_tuple=True, returns a tuple (result, outcome, diff, rolls) default return.
In this tuple, outcome and diff will be None if conditional is condition_result (bool): A True/False value returned if `conditional`
not set. rolls is itself a tuple holding all the individual is set but not `return_tuple`. This effectively hides the result
rolls in the case of multiple die-rolls. of the roll.
full_result (tuple): If, return_tuple` is `True`, instead
return a tuple `(result, outcome, diff, rolls)`. Here,
`result` is the normal result of the roll + modifiers.
`outcome` and `diff` are the boolean result of the roll and
absolute difference to the `conditional` input; they will
be will be `None` if `conditional` is not set. `rolls` is
itself a tuple holding all the individual rolls in the case of
multiple die-rolls.
Raises: Raises:
TypeError if non-supported modifiers or conditionals are given. TypeError if non-supported modifiers or conditionals are given.
Notes:
All input numbers are converted to integers.
Examples:
print roll_dice(2, 6) # 2d6
<<< 7
print roll_dice(1, 100, ('+', 5) # 1d100 + 5
<<< 34
print roll_dice(1, 20, conditional=('<', 10) # let'say we roll 3
<<< True
print roll_dice(3, 10, return_tuple=True)
<<< (11, None, None, (2, 5, 4))
print roll_dice(2, 20, ('-', 2), conditional=('>=', 10), return_tuple=True)
<<< (8, False, 2, (4, 6)) # roll was 4 + 6 - 2 = 8
""" """
dicelimit = 0 # This is the maximum number of dice that can be used in a single roll.
dicenum = int(dicenum) dicenum = int(dicenum)
dicetype = int(dicetype) dicetype = int(dicetype)
@ -90,7 +112,10 @@ def roll_dice(dicenum, dicetype, modifier=None, conditional=None, return_tuple=F
if return_tuple: if return_tuple:
return (result, outcome, diff, rolls) return (result, outcome, diff, rolls)
else: else:
return result if conditional:
return outcome
else:
return result
RE_PARTS = re.compile(r"(d|\+|-|/|\*|<|>|<=|>=|!=|==)") RE_PARTS = re.compile(r"(d|\+|-|/|\*|<|>|<=|>=|!=|==)")
RE_MOD = re.compile(r"(\+|-|/|\*)") RE_MOD = re.compile(r"(\+|-|/|\*)")

View file

@ -158,8 +158,16 @@ class ExtendedRoom(DefaultRoom):
def replace_timeslots(self, raw_desc, curr_time): def replace_timeslots(self, raw_desc, curr_time):
""" """
Filter so that only time markers `<timeslot>...</timeslot>` of the Filter so that only time markers `<timeslot>...</timeslot>` of
correct timeslot remains in the description. the correct timeslot remains in the description.
Args:
raw_desc (str): The unmodified description.
curr_time (str): A timeslot identifier.
Returns:
description (str): A possibly moified description.
""" """
if raw_desc: if raw_desc:
regextuple = REGEXMAP[curr_time] regextuple = REGEXMAP[curr_time]
@ -171,14 +179,24 @@ class ExtendedRoom(DefaultRoom):
def return_detail(self, key): def return_detail(self, key):
""" """
This will attempt to match a "detail" to look for in the room. A detail This will attempt to match a "detail" to look for in the room.
is a way to offer more things to look at in a room without having to
add new objects. For this to work, we require a custom `look` command that
allows for `look <detail>` - the look command should defer to this
method on the current location (if it exists) before giving up on
finding the target.
Details are not season-sensitive, but are parsed for timeslot markers. Args:
key (str): A detail identifier.
Returns:
detail (str or None): A detail mathing the given key.
Notes:
A detail is a way to offer more things to look at in a room
without having to add new objects. For this to work, we
require a custom `look` command that allows for `look
<detail>` - the look command should defer to this method on
the current location (if it exists) before giving up on
finding the target.
Details are not season-sensitive, but are parsed for timeslot
markers.
""" """
try: try:
detail = self.db.details.get(key.lower(), None) detail = self.db.details.get(key.lower(), None)
@ -192,7 +210,17 @@ class ExtendedRoom(DefaultRoom):
return None return None
def return_appearance(self, looker): def return_appearance(self, looker):
"This is called when e.g. the look command wants to retrieve the description of this object." """
This is called when e.g. the look command wants to retrieve
the description of this object.
Args:
looker (Object): The object looking at us.
Returns:
description (str): Our description.
"""
raw_desc = self.db.raw_desc or "" raw_desc = self.db.raw_desc or ""
update = False update = False

View file

@ -8,8 +8,8 @@ insert custom markers in their text to indicate gender-aware
messaging. It relies on a modified msg() and is meant as an messaging. It relies on a modified msg() and is meant as an
inspiration and starting point to how to do stuff like this. inspiration and starting point to how to do stuff like this.
When in use, all messages being sent to the character will make use When in use, all messages being sent to the character will make use of
of the character's gender, for example the echo the character's gender, for example the echo
``` ```
char.msg("%s falls on {p face with a thud." % char.key) char.msg("%s falls on {p face with a thud." % char.key)

View file

@ -19,8 +19,10 @@ CMDSET_UNLOGGEDIN = "contrib.menu_login.UnloggedInCmdSet"
That's it. Reload the server and try to log in to see it. That's it. Reload the server and try to log in to see it.
The initial login "graphic" is taken from strings in the module given You will want to change the login "graphic", which defaults to give
by settings.CONNECTION_SCREEN_MODULE. information about commands which are not used in this version of the
login. You can change the screen used by editing
`mygame/server/conf/connection_screens.py`.
""" """

View file

@ -11,14 +11,29 @@ in one or more columns.
The menu system consists of a MenuTree object populated by MenuNode The menu system consists of a MenuTree object populated by MenuNode
objects. Nodes are linked together with automatically created commands objects. Nodes are linked together with automatically created commands
so the player may select and traverse the menu. Each node can display so the player may select and traverse the menu. Each node can display
text and show options, but also execute arbitrary code to act on the text and show options, but also execute a callback to act on the
system and the calling object when they are selected. system and the calling object when they are selected.
There is also a simple Yes/No function supplied. This will create a There is also a simple Yes/No function as well as a one-level choice
one-off Yes/No question and executes a given code depending on which function supplied. This will create a one-off Yes/No question or a
choice was made. one-level choice. These helpers will execute a given callback
depending on which choice was made.
To test, add this to the default cmdset To start a menu, define the nodes of the menu and then add the
following to a command `func` you can call:
```python
menu = MenuTree(self.caller, nodes=(...))
menu.start()
```
This will switch you into menu-mode. See `contrib/menu_login.py` for an
example of usage.
For a simple demonstration, add `CmdMenuTest` from this module to the default cmdset.
""" """
from types import MethodType from types import MethodType
@ -49,8 +64,6 @@ class CmdMenuNode(Command):
menutree = None menutree = None
callback = None callback = None
# deprecated
code = None
def func(self): def func(self):
"Execute a selection" "Execute a selection"
@ -60,12 +73,6 @@ class CmdMenuNode(Command):
self.callback() self.callback()
except Exception, e: except Exception, e:
self.caller.msg("%s\n{rThere was an error with this selection.{n" % e) self.caller.msg("%s\n{rThere was an error with this selection.{n" % e)
elif self.code:
evennia.logger.log_depmsg("menusystem.code is deprecated. Use menusystem.func.")
try:
exec(self.code)
except Exception, e:
self.caller.msg("%s\n{rThere was an error with this selection.{n" % e)
else: else:
self.caller.msg("{rThis option is not available.{n") self.caller.msg("{rThis option is not available.{n")
@ -173,8 +180,19 @@ class MenuTree(object):
enter and where to exit the menu tree. If nodes is given, it enter and where to exit the menu tree. If nodes is given, it
should be a list of valid node objects to add to the tree. should be a list of valid node objects to add to the tree.
exec_end - if not None, will execute the given command string caller (Object): The caller triggering the menu
directly after the menu system has been exited. nodes (tuple, optional): A tuple of `MenuNode` objects. This need
not be in any particular order.
startnode (str, optional): The key of the first `MenuNode` to jump
to when starting the menu. Defaults to "START".
endnode (str, optional): The key of the end node. When
instructed to go to this node (by any means), the menu
will be gracefully exited. Defaults to "END".
exec_end (str, optional): If not `None`, this command name will be executed
directly after the menu system has been exited. It is
normally useful for making sure the user realizes their UI
mode has changed.
""" """
self.tree = {} self.tree = {}
self.startnode = startnode self.startnode = startnode
@ -187,7 +205,8 @@ class MenuTree(object):
def start(self): def start(self):
""" """
Initialize the menu Initialize the menu and go to the starting node.
""" """
self.goto(self.startnode) self.goto(self.startnode)
@ -195,6 +214,9 @@ class MenuTree(object):
""" """
Add a menu node object to the tree. Each node itself keeps Add a menu node object to the tree. Each node itself keeps
track of which nodes it is connected to. track of which nodes it is connected to.
Args:
menunode (MenuNode): The node to add.
""" """
self.tree[menunode.key] = menunode self.tree[menunode.key] = menunode
@ -202,6 +224,10 @@ class MenuTree(object):
""" """
Go to a key in the tree. This sets up the cmdsets on the Go to a key in the tree. This sets up the cmdsets on the
caller so that they match the choices in that node. caller so that they match the choices in that node.
Args:
key (str): The node-key to go to.
""" """
if key == self.endnode: if key == self.endnode:
# if we was given the END node key, we clean up immediately. # if we was given the END node key, we clean up immediately.
@ -210,7 +236,7 @@ class MenuTree(object):
if self.exec_end is not None: if self.exec_end is not None:
self.caller.execute_cmd(self.exec_end) self.caller.execute_cmd(self.exec_end)
return return
# not exiting, look for a valid code. # not exiting, look for a valid node
node = self.tree.get(key, None) node = self.tree.get(key, None)
# make caller available on node # make caller available on node
node.caller = self.caller node.caller = self.caller
@ -222,13 +248,6 @@ class MenuTree(object):
except Exception: except Exception:
logger.log_trace() logger.log_trace()
self.caller.msg("{rNode callback could not be executed for node %s. Continuing anyway.{n" % key) self.caller.msg("{rNode callback could not be executed for node %s. Continuing anyway.{n" % key)
if node.code:
# Execute eventual code active on this node. self.caller is available at this point.
evennia.logger.log_depmsg("menusystem.code is deprecated. Use menusystem.callback.")
try:
exec(node.code)
except Exception:
self.caller.msg("{rCode could not be executed for node %s. Continuing anyway.{n" % key)
# initialize - this creates new cmdset # initialize - this creates new cmdset
node.init(self) node.init(self)
# clean old menu cmdset and replace with the new one # clean old menu cmdset and replace with the new one
@ -252,42 +271,48 @@ class MenuNode(object):
""" """
def __init__(self, key, text="", links=None, linktexts=None, def __init__(self, key, text="", links=None, linktexts=None,
keywords=None, cols=1, helptext=None, keywords=None, cols=1, helptext=None,
selectcmds=None, callback=None, code="", nodefaultcmds=False, separator=""): selectcmds=None, callback=None, nodefaultcmds=False, separator=""):
""" """
key - the unique identifier of this node. Initialize the node.
text - is the text that will be displayed at top when viewing this
node. Args:
links - a list of keys for unique menunodes this is connected to. key (str): The unique identifier of this node.
The actual keys will not printed - keywords will be used text (str, optional): The text that will be displayed at
(or a number) top when viewing this node.
linktexts - an optional list of texts to describe the links. Must Kwargs:
match link list if defined. Entries can be None to not links (list): A liist of keys for unique menunodes this is connected to.
generate any extra text for a particular link. The actual keys will not printed - keywords will be
keywords - an optional list of unique keys for choosing links. Must used (or a number)
match links list. If not given, index numbers will be used. linktexts (list)- A list of texts to describe the links. Must
Also individual list entries can be None and will be replaed match order of `links` list if defined. Entries can be
by indices. If CMD_NOMATCH or CMD_NOENTRY, no text will be None to not generate any extra text for a particular
generated to indicate the option exists. link.
cols - how many columns to use for displaying options. keywords (list): A list of unique keys for choosing links. Must
helptext - if defined, this is shown when using the help command match links list. If not given, index numbers will be
instead of the normal help index. used. Also individual list entries can be None and
selectcmds- a list of custom cmdclasses for handling each option. will be replaed by indices. If CMD_NOMATCH or
Must match links list, but some entries may be set to None CMD_NOENTRY, no text will be generated to indicate the
to use default menu cmds. The given command's key will be option exists.
used for the menu list entry unless it's CMD_NOMATCH or cols (int): How many columns to use for displaying options.
CMD_NOENTRY, in which case no text will be generated. These helptext (str): If defined, this is shown when using the help command
commands have access to self.menutree and so can be used to instead of the normal help index.
select nodes. selectcmds (list): A list of custom cmdclasses for
code - functional code. Deprecated. This will be executed just before this handling each option. Must match links list, but some
node is loaded (i.e. as soon after it's been selected from entries may be set to None to use default menu cmds.
another node). self.caller is available to call from this The given command's key will be used for the menu list
code block, as well as the evennia flat API. entry unless it's CMD_NOMATCH or CMD_NOENTRY, in which
callback - function callback. This will be called as callback(currentnode) just case no text will be generated. These commands have
before this node is loaded (i.e. as soon as possible as it's access to self.menutree and so can be used to select
been selected from another node). currentnode.caller is available. nodes.
nodefaultcmds - if true, don't offer the default help and look commands callback (function): Function callback. This will be
in the node called as callback(currentnode) just before this node is
separator - this string will be put on the line between menu nodes. loaded (i.e. as soon as possible as it's been selected
from another node). currentnode.caller is available.
nodefaultcmds (bool): If `True`, don't offer the default
help and look commands in the node
separator (str): This string will be put on the line
between menu nodes.
""" """
self.key = key self.key = key
self.cmdset = None self.cmdset = None
@ -296,15 +321,11 @@ class MenuNode(object):
self.keywords = keywords self.keywords = keywords
self.cols = cols self.cols = cols
self.selectcmds = selectcmds self.selectcmds = selectcmds
self.code = code
self.callback = MethodType(callback, self, MenuNode) if callback else None self.callback = MethodType(callback, self, MenuNode) if callback else None
self.nodefaultcmds = nodefaultcmds self.nodefaultcmds = nodefaultcmds
self.separator = separator self.separator = separator
Nlinks = len(self.links) Nlinks = len(self.links)
if code:
evennia.logger.log_depmsg("menusystem.code is deprecated. Use menusystem.callback.")
# validate the input # validate the input
if not self.links: if not self.links:
self.links = [] self.links = []
@ -399,17 +420,21 @@ class MenuNode(object):
# make use the node system since there is only one level of choice. # make use the node system since there is only one level of choice.
# #
def prompt_yesno(caller, question="", yesfunc=None, nofunc=None, yescode="", nocode="", default="N"): def prompt_yesno(caller, question="", yesfunc=None, nofunc=None, default="N"):
""" """
This sets up a simple yes/no questionnaire. Question will be This sets up a simple yes/no questionnaire. Question will be
asked, followed by a Y/[N] prompt where the [x] signifies the asked, followed by a Y/[N] prompt where the [x] signifies the
default selection. Note that this isn't making use of the menu default selection. Note that this isn't actually making use of the
node system. menu node system, but does use the MenuCmdSet.
Args:
caller (Object): The object triggering the prompt.
question (str, optional): The Yes/No question asked.
yesfunc (function, optional): Callback for a Yes answer.
nofunc (functionm optional): Callback for a No answer.
default (str, optional): Default used if caller just hits
return. Either `"Y"` or `"N"`
yesfunc - function callback to be called as yesfunc(self) when choosing yes (self.caller is available)
nofunc - function callback to be called as yesfunc(self) when choosing no (self.caller is available)
yescode - deprecated, executable code
nocode - "
""" """
# creating and defining commands # creating and defining commands
@ -441,14 +466,6 @@ def prompt_yesno(caller, question="", yesfunc=None, nofunc=None, yescode="", noc
self.caller.execute_cmd('%s' % default) self.caller.execute_cmd('%s' % default)
defaultcmd.callback = MethodType(_defaultcmd, defaultcmd, CmdMenuNode) defaultcmd.callback = MethodType(_defaultcmd, defaultcmd, CmdMenuNode)
# code exec is deprecated:
if yescode:
evennia.logger.log_depmsg("yesnosystem.code is deprecated. Use yesnosystem.callback.")
cmdyes.code = yescode + "\nself.caller.cmdset.delete('menucmdset')\ndel self.caller.db._menu_data"
if nocode:
evennia.logger.log_depmsg("yesnosystem.code is deprecated. Use yesnosystem.callback.")
cmdno.code = nocode + "\nself.caller.cmdset.delete('menucmdset')\ndel self.caller.db._menu_data"
# creating cmdset (this will already have look/help commands) # creating cmdset (this will already have look/help commands)
yesnocmdset = MenuCmdSet() yesnocmdset = MenuCmdSet()
yesnocmdset.add(cmdyes) yesnocmdset.add(cmdyes)
@ -481,33 +498,57 @@ def prompt_choice(caller, question="", prompts=None, choicefunc=None, force_choo
""" """
This sets up a simple choice questionnaire. Question will be This sets up a simple choice questionnaire. Question will be
asked, followed by a series of prompts. Note that this isn't asked, followed by a series of prompts. Note that this isn't
making use of the menu node system. making use of the menu node system but uses the MenuCmdSet.
Args:
caller (object): The object calling and being offered the choice
question (str, optional): Text describing the offered choice
prompts (list, optional): List of strings defining the available choises.
choicefunc (function, optional): A function called as
`choicefunc(self)` when a choice is made. Inside this function,
`self.caller` is available and `self.prompt_index` is the index
(starting with 0) matching the chosen prompt in the `prompts` list.
force_choose - force user to make a choice
Examples:
```python
def mychoice(self):
self.caller.msg("Index of choice is %s." % self.prompt_index)
prompt_choice(caller, "Make a choice:", prompts=["A","B","C"], choicefunc=mychoice)
```
When triggering the above from a command or @py prompt you get the following options:
>>> Make a choice:
[1] A
[2] B
[3] C
<<< 2
>>> Index of choice is 1.
caller - the object calling and being offered the choice
question - text describing the offered choice
prompts - list of choices
choicefunc - functions callback to be called as func(self) when
make choice (self.caller is available) The function's definition
should be like func(self, menu_node), and menu_node.key is user's
choice.
force_choose - force user to make a choice or not
""" """
# creating and defining commands # creating and defining commands
count = 0 count = 0
choices = "" choices = ""
commands = [] commands = []
for choice in utils.make_iter(prompts): for choice in utils.make_iter(prompts):
# create the available choice-commands
count += 1 count += 1
choices += "\n{lc%d{lt[%d]{le %s" % (count, count, choice) choices += "\n{lc%d{lt[%d]{le %s" % (count, count, choice)
cmdfunc = CmdMenuNode(key="%d" % count) cmdfunc = CmdMenuNode(key="%d" % count)
cmdfunc.prompt_index = count-1
if choicefunc: if choicefunc:
cmdfunc.choicefunc = choicefunc cmdfunc.choicefunc = choicefunc
def _choicefunc(self): def _choicefunc(self):
self.caller.cmdset.delete('menucmdset') self.caller.cmdset.delete('menucmdset')
del self.caller.db._menu_data del self.caller.db._menu_data
self.choicefunc(self) self.choicefunc(self)
# set a new method "callback" on cmdfunc
cmdfunc.callback = MethodType(_choicefunc, cmdfunc, CmdMenuNode) cmdfunc.callback = MethodType(_choicefunc, cmdfunc, CmdMenuNode)
commands.append(cmdfunc) commands.append(cmdfunc)
@ -517,6 +558,7 @@ def prompt_choice(caller, question="", prompts=None, choicefunc=None, force_choo
prompt = question + choices + "\nPlease choose one." prompt = question + choices + "\nPlease choose one."
# create the error-reporting command
errorcmd = CmdMenuNode(key=CMD_NOMATCH) errorcmd = CmdMenuNode(key=CMD_NOMATCH)
if force_choose: if force_choose:
def _errorcmd(self): def _errorcmd(self):
@ -531,6 +573,7 @@ def prompt_choice(caller, question="", prompts=None, choicefunc=None, force_choo
self.choicefunc(self) self.choicefunc(self)
errorcmd.callback = MethodType(_errorcmd, errorcmd, CmdMenuNode) errorcmd.callback = MethodType(_errorcmd, errorcmd, CmdMenuNode)
# create the fallback command
defaultcmd = CmdMenuNode(key=CMD_NOINPUT) defaultcmd = CmdMenuNode(key=CMD_NOINPUT)
if force_choose: if force_choose:
def _defaultcmd(self): def _defaultcmd(self):
@ -547,7 +590,8 @@ def prompt_choice(caller, question="", prompts=None, choicefunc=None, force_choo
# creating cmdset (this will already have look/help commands) # creating cmdset (this will already have look/help commands)
choicecmdset = MenuCmdSet() choicecmdset = MenuCmdSet()
for cmdfunc in commands: choicecmdset.add(cmdfunc) for cmdfunc in commands:
choicecmdset.add(cmdfunc)
choicecmdset.add(errorcmd) choicecmdset.add(errorcmd)
choicecmdset.add(defaultcmd) choicecmdset.add(defaultcmd)
choicecmdset.add(CmdMenuLook()) choicecmdset.add(CmdMenuLook())

View file

@ -4,22 +4,19 @@ Evennia Talkative NPC
Contribution - Griatch 2011 Contribution - Griatch 2011
This is a simple NPC object capable of holding a This is a simple NPC object capable of holding a simple menu-driven
simple menu-driven conversation. Create it by conversation. Create it by creating an object of typeclass
creating an object of typeclass contrib.talking_npc.TalkingNPC, contrib.talking_npc.TalkingNPC, For example using @create:
For example using @create:
@create John : contrib.talking_npc.TalkingNPC @create John : contrib.talking_npc.TalkingNPC
Walk up to it and give the talk command Walk up to it and give the talk command to strike up a conversation.
to strike up a conversation. If there are many If there are many talkative npcs in the same room you will get to
talkative npcs in the same room you will get to choose which one's talk command to call (Evennia handles this
choose which one's talk command to call (Evennia automatically).
handles this automatically).
Note that this is only a prototype class, showcasing Note that this is only a prototype class, showcasing the uses of the
the uses of the menusystem module. It is NOT a full menusystem module. It is NOT a full mob implementation.
mob implementation.
""" """

View file

@ -1,14 +1,12 @@
""" """
Example script for testing. This adds a simple timer that Example script for testing. This adds a simple timer that has your
has your character make observations and notices at irregular character make observations and notices at irregular intervals.
intervals.
To test, use To test, use
@script me = examples.bodyfunctions.BodyFunctions @script me = examples.bodyfunctions.BodyFunctions
The script will only send messages to the object it The script will only send messages to the object it is stored on, so
is stored on, so make sure to put it on yourself make sure to put it on yourself or you won't see any messages!
or you won't see any messages!
""" """
import random import random

View file

@ -66,6 +66,7 @@ class RedButton(DefaultObject):
""" """
Opens the glass lid and start the timer so it will soon close Opens the glass lid and start the timer so it will soon close
again. again.
""" """
if self.db.lid_open: if self.db.lid_open:
@ -91,6 +92,7 @@ class RedButton(DefaultObject):
Close the glass lid. This validates all scripts on the button, Close the glass lid. This validates all scripts on the button,
which means that scripts only being valid when the lid is open which means that scripts only being valid when the lid is open
will go away automatically. will go away automatically.
""" """
if not self.db.lid_open: if not self.db.lid_open:
@ -111,6 +113,9 @@ class RedButton(DefaultObject):
""" """
Breaks the lamp in the button, stopping it from blinking. Breaks the lamp in the button, stopping it from blinking.
Args:
feedback (bool): Show a message about breaking the lamp.
""" """
self.db.lamp_works = False self.db.lamp_works = False
desc = self.db.desc_lamp_broken desc = self.db.desc_lamp_broken
@ -126,7 +131,10 @@ class RedButton(DefaultObject):
def press_button(self, pobject): def press_button(self, pobject):
""" """
Someone was foolish enough to press the button! Someone was foolish enough to press the button!
pobject - the person pressing the button
Args:
pobject (Object): The person pressing the button
""" """
# deactivate the button so it won't flash/close lid etc. # deactivate the button so it won't flash/close lid etc.
self.scripts.add(scriptexamples.DeactivateButtonEvent) self.scripts.add(scriptexamples.DeactivateButtonEvent)