diff --git a/contrib/extended_room.py b/contrib/extended_room.py index 926f34f7cb..2b6f9ef82f 100644 --- a/contrib/extended_room.py +++ b/contrib/extended_room.py @@ -44,9 +44,17 @@ will also look through the available details at the current location if applicable. An extended @desc command is used to set details. -Installation: +4) Extra commands -1) Add CmdExtendedLook and CmdExtendedDesc from this module to the default cmdset (see wiki how to do this). + CmdExtendedLook - look command supporting room details + CmdExtendedDesc - @desc command allowing to add seasonal descs and details, + as well as listing them + CmdGameTime - A simple "time" command, displaying the current time and season. + + +Installation/testing: + +1) Add CmdExtendedLook, CmdExtendedDesc and CmdGameTime to the default cmdset (see wiki how to do this). 2) @dig a room of type contrib.extended_room.ExtendedRoom (or make it the default room type) 3) Use @desc and @detail to customize the room, then play around! @@ -126,7 +134,6 @@ class ExtendedRoom(Room): elif DAY_BOUNDARIES[2] <= timeslot < DAY_BOUNDARIES[3]: curr_timeslot = "afternoon" else: curr_timeslot = "evening" - print "season:%s, timeslot:%s" % (curr_season, curr_timeslot) return curr_season, curr_timeslot def replace_timeslots(self, raw_desc, curr_time): @@ -283,9 +290,9 @@ class CmdExtendedDesc(default_cmds.CmdDesc): def func(self): "Define extended command" caller = self.caller + location = caller.location if self.cmdstring == '@detail': # switch to detailing mode. This operates only on current location - location = self.caller.location if not location: caller.msg("No location to detail!") return @@ -308,20 +315,28 @@ class CmdExtendedDesc(default_cmds.CmdDesc): del location.db.detail caller.msg("Detail %s deleted, if it existed." % self.lhs) return - # setting a detail location.db.details[self.lhs] = self.rhs caller.msg("Set Detail %s to '%s'." % (self.lhs, self.rhs)) return else: # we are doing a @desc call + if not self.args: + if location: + string = "{wDescriptions on %s{n:\n" % location.key + string += " {wspring:{n %s\n" % location.db.spring_desc + string += " {wsummer:{n %s\n" % location.db.summer_desc + string += " {wautumn:{n %s\n" % location.db.autumn_desc + string += " {wwinter:{n %s\n" % location.db.winter_desc + string += " {wgeneral:{n %s" % location.db.general_desc + caller.msg(string) + return if self.switches and self.switches[0] in ("spring", "summer", "autumn", "winter"): # a seasonal switch was given if self.rhs: caller.msg("Seasonal descs only works with rooms, not objects.") return switch = self.switches[0] - location = caller.location if not location: caller.msg("No location was found!") return @@ -346,3 +361,29 @@ class CmdExtendedDesc(default_cmds.CmdDesc): obj.db.desc = self.args # compatability caller.msg("General description was set on %s." % obj.key) +# Simple command to view the current time and season + +class CmdGameTime(default_cmds.MuxCommand): + """ + Check the game time + + Usage: + time + + Shows the current in-game time and season. + """ + key = "time" + locks = "cmd:all()" + help_category = "General" + + def func(self): + "Reads time info from current room" + location = self.caller.location + if not location or not hasattr(location, "get_time_and_season"): + self.caller.msg("No location available - you are outside time.") + else: + season, timeslot = location.get_time_and_season() + prep = "a" + if season == "autumn": + prep = "an" + self.caller.msg("It's %s %s day, in the %s." % (prep, season, timeslot)) diff --git a/contrib/lineeditor.py b/contrib/lineeditor.py index f22623abc9..9993fce8ec 100644 --- a/contrib/lineeditor.py +++ b/contrib/lineeditor.py @@ -8,12 +8,12 @@ This implements an advanced line editor for editing longer texts in-game. The editor mimics the command mechanisms of the VI editor as far as possible. -Features of the editor: - undo/redo +Features of the editor: + undo/redo edit/replace on any line of the buffer search&replace text anywhere in buffer formatting of buffer, or selection, to certain width + indentations - allow to echo the input or not depending on your client. + allow to echo the input or not depending on your client. """ import re @@ -34,23 +34,23 @@ class CmdEditorBase(Command): help_entry = "LineEditor" code = None - editor = None + editor = None def parse(self): """ Handles pre-parsing - Editor commands are on the form + Editor commands are on the form :cmd [li] [w] [txt] - Where all arguments are optional. + Where all arguments are optional. li - line number (int), starting from 1. This could also be a range given as : w - word(s) (string), could be encased in quotes. txt - extra text (string), could be encased in quotes """ linebuffer = [] - if self.editor: + if self.editor: linebuffer = self.editor.buffer.split("\n") nlines = len(linebuffer) @@ -63,39 +63,39 @@ class CmdEditorBase(Command): arglist = [part for part in RE_GROUP.findall(self.args) if part] temp = [] for arg in arglist: - # we want to clean the quotes, but only one type, in case we are nesting. + # we want to clean the quotes, but only one type, in case we are nesting. if arg.startswith('"'): arg.strip('"') elif arg.startswith("'"): arg.strip("'") temp.append(arg) - arglist = temp + arglist = temp # A dumb split, without grouping quotes words = self.args.split() - - # current line number - cline = nlines - 1 + + # current line number + cline = nlines - 1 # the first argument could also be a range of line numbers, on the # form :. Either of the ends could be missing, to - # mean start/end of buffer respectively. - + # mean start/end of buffer respectively. + lstart, lend = cline, cline + 1 - linerange = False - if arglist and ':' in arglist[0]: + linerange = False + if arglist and ':' in arglist[0]: part1, part2 = arglist[0].split(':') - if part1 and part1.isdigit(): + if part1 and part1.isdigit(): lstart = min(max(0, int(part1)) - 1, nlines) linerange = True if part2 and part2.isdigit(): lend = min(lstart + 1, int(part2)) + 1 - linerange = True + linerange = True elif arglist and arglist[0].isdigit(): - lstart = min(max(0, int(arglist[0]) - 1), nlines) - lend = lstart + 1 - linerange = True + lstart = min(max(0, int(arglist[0]) - 1), nlines) + lend = lstart + 1 + linerange = True if linerange: arglist = arglist[1:] @@ -107,7 +107,7 @@ class CmdEditorBase(Command): lstr = "lines %i-%i" % (lstart + 1, lend) - # arg1 and arg2 is whatever arguments. Line numbers or -ranges are never included here. + # arg1 and arg2 is whatever arguments. Line numbers or -ranges are never included here. args = " ".join(arglist) arg1, arg2 = "", "" if len(arglist) > 1: @@ -124,30 +124,30 @@ class CmdEditorBase(Command): self.lstart = lstart self.lend = lend self.linerange = linerange - self.lstr = lstr - self.words = words - self.args = args + self.lstr = lstr + self.words = words + self.args = args self.arg1 = arg1 self.arg2 = arg2 - + def func(self): "Implements the Editor commands" - pass - + pass + class CmdLineInput(CmdEditorBase): """ No command match - Inputs line of text into buffer. """ - key = CMD_NOMATCH + key = CMD_NOMATCH aliases = [CMD_NOINPUT] def func(self): "Adds the line without any formatting changes." - # add a line of text - if not self.editor.buffer: + # add a line of text + if not self.editor.buffer: buf = self.args else: - buf = self.editor.buffer + "\n%s" % self.args + buf = self.editor.buffer + "\n%s" % self.args self.editor.update_buffer(buf) if self.editor.echo_mode: self.caller.msg("%02i| %s" % (self.cline + 1, self.args)) @@ -164,18 +164,18 @@ class CmdEditorGroup(CmdEditorBase): def func(self): """ This command handles all the in-editor :-style commands. Since each command - is small and very limited, this makes for a more efficient presentation. + is small and very limited, this makes for a more efficient presentation. """ - caller = self.caller - editor = self.editor - linebuffer = self.linebuffer - lstart, lend = self.lstart, self.lend - cmd = self.cmdstring - echo_mode = self.editor.echo_mode + caller = self.caller + editor = self.editor + linebuffer = self.linebuffer + lstart, lend = self.lstart, self.lend + cmd = self.cmdstring + echo_mode = self.editor.echo_mode string = "" if cmd == ":": - # Echo buffer + # Echo buffer if self.linerange: buf = linebuffer[lstart:lend] string = editor.display_buffer(buf=buf, offset=lstart) @@ -189,9 +189,9 @@ class CmdEditorGroup(CmdEditorBase): else: string = editor.display_buffer(linenums=False) self.caller.msg(string, data={"raw":True}) - return + return elif cmd == ":::": - # Insert single colon alone on a line + # Insert single colon alone on a line editor.update_buffer(editor.buffer + "\n:") if echo_mode: string = "Single ':' added to buffer." @@ -212,25 +212,25 @@ class CmdEditorGroup(CmdEditorBase): yescode = "self.caller.ndb._lineeditor.save_buffer()\nself.caller.ndb._lineeditor.quit()", nocode = "self.caller.msg(self.caller.ndb._lineeditor.quit())", default="Y") else: - string = editor.quit() + string = editor.quit() elif cmd == ":q!": # force quit, not checking saving string = editor.quit() elif cmd == ":u": - # undo + # undo string = editor.update_undo(-1) elif cmd == ":uu": - # redo + # redo string = editor.update_undo(1) elif cmd == ":UU": # reset buffer editor.update_buffer(editor.pristine_buffer) string = "Reverted all changes to the buffer back to original state." elif cmd == ":dd": - # :dd - delete line + # :dd - delete line buf = linebuffer[:lstart] + linebuffer[lend:] editor.update_buffer(buf) - string = "Deleted %s." % (self.lstr) + string = "Deleted %s." % (self.lstr) elif cmd == ":dw": # :dw - delete word in entire buffer # :dw delete word only on line(s) @@ -241,7 +241,7 @@ class CmdEditorGroup(CmdEditorBase): lstart = 0 lend = self.cline + 1 string = "Removed %s for lines %i-%i." % (self.arg1, lstart + 1 , lend + 1) - else: + else: string = "Removed %s for %s." % (self.arg1, self.lstr) sarea = "\n".join(linebuffer[lstart:lend]) sarea = re.sub(r"%s" % self.arg1.strip("\'").strip('\"'), "", sarea, re.MULTILINE) @@ -250,16 +250,16 @@ class CmdEditorGroup(CmdEditorBase): elif cmd == ":DD": # clear buffer editor.update_buffer("") - string = "Cleared %i lines from buffer." % self.nlines + string = "Cleared %i lines from buffer." % self.nlines elif cmd == ":y": # :y - yank line(s) to copy buffer cbuf = linebuffer[lstart:lend] - editor.copy_buffer = cbuf + editor.copy_buffer = cbuf string = "%s, %s yanked." % (self.lstr.capitalize(), cbuf) elif cmd == ":x": - # :x - cut line to copy buffer + # :x - cut line to copy buffer cbuf = linebuffer[lstart:lend] - editor.copy_buffer = cbuf + editor.copy_buffer = cbuf buf = linebuffer[:lstart] + linebuffer[lend:] editor.update_buffer(buf) string = "%s, %s cut." % (self.lstr.capitalize(), cbuf) @@ -272,16 +272,16 @@ class CmdEditorGroup(CmdEditorBase): editor.update_buffer(buf) string = "Copied buffer %s to %s." % (editor.copy_buffer, self.lstr) elif cmd == ":i": - # :i - insert new line + # :i - insert new line new_lines = self.args.split('\n') if not new_lines: string = "You need to enter a new line and where to insert it." else: - buf = linebuffer[:lstart] + new_lines + linebuffer[lstart:] + buf = linebuffer[:lstart] + new_lines + linebuffer[lstart:] editor.update_buffer(buf) string = "Inserted %i new line(s) at %s." % (len(new_lines), self.lstr) elif cmd == ":r": - # :r - replace lines + # :r - replace lines new_lines = self.args.split('\n') if not new_lines: string = "You need to enter a replacement string." @@ -290,7 +290,7 @@ class CmdEditorGroup(CmdEditorBase): editor.update_buffer(buf) string = "Replaced %i line(s) at %s." % (len(new_lines), self.lstr) elif cmd == ":I": - # :I - insert text at beginning of line(s) + # :I - insert text at beginning of line(s) if not self.args: string = "You need to enter text to insert." else: @@ -314,17 +314,17 @@ class CmdEditorGroup(CmdEditorBase): lstart = 0 lend = self.cline + 1 string = "Search-replaced %s -> %s for lines %i-%i." % (self.arg1, self.arg2, lstart + 1 , lend) - else: - string = "Search-replaced %s -> %s for %s." % (self.arg1, self.arg2, self.lstr) + else: + string = "Search-replaced %s -> %s for %s." % (self.arg1, self.arg2, self.lstr) sarea = "\n".join(linebuffer[lstart:lend]) - + regex = r"%s|^%s(?=\s)|(?<=\s)%s(?=\s)|^%s$|(?<=\s)%s$" regarg = self.arg1.strip("\'").strip('\"') if " " in regarg: - regarg = regarg.replace(" ", " +") + regarg = regarg.replace(" ", " +") sarea = re.sub(regex % (regarg, regarg, regarg, regarg, regarg), self.arg2.strip("\'").strip('\"'), sarea, re.MULTILINE) buf = linebuffer[:lstart] + sarea.split("\n") + linebuffer[lend:] - editor.update_buffer(buf) + editor.update_buffer(buf) elif cmd == ":f": # :f flood-fill buffer or lines of buffer. width = 78 @@ -332,7 +332,7 @@ class CmdEditorGroup(CmdEditorBase): lstart = 0 lend = self.cline + 1 string = "Flood filled lines %i-%i." % (lstart + 1 , lend) - else: + else: string = "Flood filled %s." % self.lstr fbuf = "\n".join(linebuffer[lstart:lend]) fbuf = utils.fill(fbuf, width=width) @@ -345,7 +345,7 @@ class CmdEditorGroup(CmdEditorBase): lstart = 0 lend = self.cline + 1 string = "Indented lines %i-%i." % (lstart + 1 , lend) - else: + else: string = "Indented %s." % self.lstr fbuf = [indent + line for line in linebuffer[lstart:lend]] buf = linebuffer[:lstart] + fbuf + linebuffer[lend:] @@ -356,7 +356,7 @@ class CmdEditorGroup(CmdEditorBase): lstart = 0 lend = self.cline + 1 string = "Removed left margin (dedented) lines %i-%i." % (lstart + 1 , lend) - else: + else: string = "Removed left margin (dedented) %s." % self.lstr fbuf = "\n".join(linebuffer[lstart:lend]) fbuf = utils.dedent(fbuf) @@ -365,7 +365,7 @@ class CmdEditorGroup(CmdEditorBase): elif cmd == ":echo": # set echoing on/off editor.echo_mode = not editor.echo_mode - string = "Echo mode set to %s" % editor.echo_mode + string = "Echo mode set to %s" % editor.echo_mode caller.msg(string) @@ -378,7 +378,7 @@ class LineEditor(object): """ This defines a line editor object. It creates all relevant commands and tracks the current state of the buffer. It also cleans up after - itself. + itself. """ def __init__(self, caller, loadcode="", savecode="", key=""): @@ -388,11 +388,11 @@ class LineEditor(object): savecode - code to execute in order to save the result key = an optional key for naming this session (such as which attribute is being edited) """ - self.key = key + self.key = key self.caller = caller self.caller.ndb._lineeditor = self self.buffer = "" - self.unsaved = False + self.unsaved = False if loadcode: try: exec(loadcode) @@ -410,12 +410,12 @@ class LineEditor(object): editor_cmdset = EditorCmdSet() editor_cmdset.add(cmd1) editor_cmdset.add(cmd2) - self.caller.cmdset.add(editor_cmdset) + self.caller.cmdset.add(editor_cmdset) - # store the original version - self.pristine_buffer = self.buffer + # store the original version + self.pristine_buffer = self.buffer - self.savecode = savecode + self.savecode = savecode self.sep = "-" # undo operation buffer @@ -425,9 +425,9 @@ class LineEditor(object): # copy buffer self.copy_buffer = [] - + # echo inserted text back to caller - self.echo_mode = False + self.echo_mode = False # show the buffer ui self.caller.msg(self.display_buffer()) @@ -435,15 +435,15 @@ class LineEditor(object): def update_buffer(self, buf): """ This should be called when the buffer has been changed somehow. - It will handle unsaved flag and undo updating. + It will handle unsaved flag and undo updating. """ if utils.is_iter(buf): buf = "\n".join(buf) if buf != self.buffer: - self.buffer = buf + self.buffer = buf self.update_undo() - self.unsaved = True + self.unsaved = True def quit(self): "Cleanly exit the editor." @@ -456,7 +456,7 @@ class LineEditor(object): if self.unsaved: try: exec(self.savecode) - self.unsaved = False + self.unsaved = False return "Buffer saved." except Exception, e: return "%s\n{rSave code gave an error. Buffer not saved." % e @@ -466,30 +466,30 @@ class LineEditor(object): def update_undo(self, step=None): """ This updates the undo position. - - """ + + """ if step and step < 0: if self.undo_pos <= 0: return "Nothing to undo." self.undo_pos = max(0, self.undo_pos + step) - self.buffer = self.undo_buffer[self.undo_pos] - return "Undo." + self.buffer = self.undo_buffer[self.undo_pos] + return "Undo." elif step and step > 0: if self.undo_pos >= len(self.undo_buffer) - 1 or self.undo_pos + 1 >= self.undo_max: return "Nothing to redo." self.undo_pos = min(self.undo_pos + step, min(len(self.undo_buffer), self.undo_max) - 1) self.buffer = self.undo_buffer[self.undo_pos] - return "Redo." - if not self.undo_buffer or (self.undo_buffer and self.buffer != self.undo_buffer[self.undo_pos]): + return "Redo." + if not self.undo_buffer or (self.undo_buffer and self.buffer != self.undo_buffer[self.undo_pos]): self.undo_buffer = self.undo_buffer[:self.undo_pos + 1] + [self.buffer] self.undo_pos = len(self.undo_buffer) - 1 - + def display_buffer(self, buf=None, offset=0, linenums=True): """ This displays the line editor buffer, or selected parts of it. - If buf is set and is not the full buffer, offset should define - the starting line number, to get the linenum display right. + If buf is set and is not the full buffer, offset should define + the starting line number, to get the linenum display right. """ if buf == None: buf = self.buffer @@ -504,32 +504,32 @@ class LineEditor(object): sep = self.sep header = "{n" + sep * 10 + "Line Editor [%s]" % self.key + sep * (78-25-len(self.key)) footer = "{n" + sep * 10 + "[l:%02i w:%03i c:%04i]" % (nlines, nwords, nchars) + sep * 12 + "(:h for help)" + sep * 23 - if linenums: + if linenums: main = "\n".join("{b%02i|{n %s" % (iline + 1 + offset, line) for iline, line in enumerate(lines)) else: main = "\n".join(lines) string = "%s\n%s\n%s" % (header, main, footer) - return string + return string def display_help(self): """ - Shows the help entry for the editor. + Shows the help entry for the editor. """ string = self.sep*78 + """ - any non-command is appended to the end of the buffer. : - view buffer or only line :: - view buffer without line numbers or other parsing ::: - print a ':' as the only character on the line... -:h - this help. +:h - this help. :w - saves the buffer (don't quit) :wq - save buffer and quit :q - quits (will be asked to save if buffer was changed) -:q! - quit without saving, no questions asked +:q! - quit without saving, no questions asked :u - (undo) step backwards in undo history :uu - (redo) step forward in undo history -:UU - reset all changes back to initial +:UU - reset all changes back to initial :dd - delete line :dw - delete word or regex in entire buffer or on line @@ -539,7 +539,7 @@ class LineEditor(object): :x - cut line and store it in the copy buffer :p - put (paste) previously copied line directly after :i - insert new text at line . Old line will be shifted down -:r - replace line with text +:r - replace line with text :I - insert text at the beginning of line :A - append text after the end of line @@ -547,17 +547,17 @@ class LineEditor(object): :f - flood-fill entire buffer or line :fi - indent entire buffer or line -:fd - de-indent entire buffer or line +:fd - de-indent entire buffer or line :echo - turn echoing of the input on/off (helpful for some clients) - Legend: + Legend: - line numbers, or range lstart:lend, e.g. '3:7'. - one word or several enclosed in quotes. - - longer string, usually not needed to be enclosed in quotes. + - longer string, usually not needed to be enclosed in quotes. """ + self.sep * 78 - return string - + return string + @@ -566,15 +566,15 @@ class LineEditor(object): # class CmdEditor(Command): - """ + """ start editor - Usage: - @editor / - + Usage: + @editor / + This will start Evennia's powerful line editor, which has a host of commands on its own. Use :h for a list - of commands. + of commands. """ @@ -588,23 +588,23 @@ class CmdEditor(Command): if not self.args or not '/' in self.args: self.caller.msg("Usage: @editor /") - return + return objname, attrname = [part.strip() for part in self.args.split("/")] obj = self.caller.search(objname) if not obj: - return + return - # the load/save codes define what the editor shall do when wanting to + # the load/save codes define what the editor shall do when wanting to # save the result of the editing. The editor makes self.buffer and # self.caller available for this code - self.buffer holds the editable text. loadcode = "obj = self.caller.search('%s')\n" % obj.id loadcode += "if obj.db.%s: self.buffer = obj.db.%s" % (attrname, attrname) - + savecode = "obj = self.caller.search('%s')\n" % obj.id savecode += "obj.db.%s = self.buffer" % attrname - + editor_key = "%s/%s" % (objname, attrname) - # start editor, it will handle things from here. + # start editor, it will handle things from here. LineEditor(self.caller, loadcode=loadcode, savecode=savecode, key=editor_key) diff --git a/contrib/talking_npc.py b/contrib/talking_npc.py index 8d82322ddc..0128d0b722 100644 --- a/contrib/talking_npc.py +++ b/contrib/talking_npc.py @@ -1,17 +1,17 @@ """ -Evennia Talkative NPC +Evennia Talkative NPC 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 conversation. Create it by creating an object of typeclass contrib.talking_npc.TalkingNPC, -For example using @create: +For example using @create: @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. If there are many talkative npcs in the same room you will get to choose which one's talk command to call (Evennia @@ -19,18 +19,18 @@ handles this automatically). Note that this is only a prototype class, showcasing the uses of the menusystem module. It is NOT a full -mob implementation. +mob implementation. """ -from contrib import menusystem -from game.gamesrc.objects.baseobjects import Object +from contrib import menusystem +from game.gamesrc.objects.baseobjects import Object from game.gamesrc.commands.basecmdset import CmdSet from game.gamesrc.commands.basecommand import MuxCommand # -# The talk command +# The talk command # class CmdTalk(MuxCommand): @@ -42,32 +42,32 @@ class CmdTalk(MuxCommand): This command is only available if a talkative non-player-character (NPC) is actually present. It will strike up a conversation with that NPC - and give you options on what to talk about. + and give you options on what to talk about. """ key = "talk" locks = "cmd:all()" help_category = "General" - + def func(self): "Implements the command." - # self.obj is the NPC this is defined on + # self.obj is the NPC this is defined on obj = self.obj self.caller.msg("(You walk up and talk to %s.)" % self.obj.key) # conversation is a dictionary of keys, each pointing to a dictionary defining - # the keyword arguments to the MenuNode constructor. - conversation = obj.db.conversation + # the keyword arguments to the MenuNode constructor. + conversation = obj.db.conversation if not conversation: self.caller.msg("%s says: 'Sorry, I don't have time to talk right now.'" % (self.obj.key)) - return - - # build all nodes by loading them from the conversation tree. + return + + # build all nodes by loading them from the conversation tree. menu = menusystem.MenuTree(self.caller) for key, kwargs in conversation.items(): menu.add(menusystem.MenuNode(key, **kwargs)) - menu.start() + menu.start() class TalkingCmdSet(CmdSet): "Stores the talk command." @@ -102,22 +102,22 @@ CONV = {"START":{"text": "Hello there, how can I help you?", "code":None}, "info3":{"text":"Well ... I'm sort of busy so, have to go. NPC business. Important stuff. You wouldn't understand.", "links":["END", "info2"], - "linktexts":["Oookay ... I won't keep you. Bye.", + "linktexts":["Oookay ... I won't keep you. Bye.", "Wait, why don't you tell me your name first?"], "keywords":None, "code":None}, } - + class TalkingNPC(Object): """ - This implements a simple Object using the talk command and using the - conversation defined above. . + This implements a simple Object using the talk command and using the + conversation defined above. . """ - + def at_object_creation(self): - "This is called when object is first created." + "This is called when object is first created." # store the conversation. self.db.conversation = CONV self.db.desc = "This is a talkative NPC." - # assign the talk command to npc + # assign the talk command to npc self.cmdset.add_default(TalkingCmdSet, permanent=True)