User Tools

Site Tools


cfdialog

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
cfdialog [2018/03/30 08:35]
karl moved to :cfpython: namespace
— (current)
Line 1: Line 1:
-====== CFDialog Helper Classes ====== 
  
-===== What is this about ? ===== 
- 
-This is a small set of [[cfpython|Python]] utility classes, to help you creating 
-complex dialogs. It is made for those who do not want to 
-bother about complex programming,​ but just want to make a few 
-dialogs that are better than the @match system used in the 
-server. 
- 
-=====What is CFDialog?​===== 
- 
-This is a small set of utility classes, to help you create complex dialogs. ​ It is made for those who do not want to bother about complex programming,​ but just want to make a few dialogs that are better than the @match system used in the server. 
- 
-=====How to use CFDialog===== 
- 
-First, create a script that imports the DialogRule and Dialog classes. Add the following line at the beginning of your script: 
- 
-  from CFDialog import DialogRule, Dialog 
- 
-Next, build the dialog by creating a sequence of several rules made up of keywords, answers, preconditions,​ and postconditions. ​ Optionally, define prefunctions or postfunctions to enhance the capabilities of the rule. 
- 
-  * **Keywords** are what the rule answers to.  For example, if you want a rule to trigger when the player says ''​hi'',​ then ''//​hi//''​ must appear in the keyword list.  One or more keywords are specified in a string list in the form **["​keyword1",​ "​keyword2"​ ...]**. ​ A ''​*''​ character is a special keyword that means: "match everything",​ and is useful to create rules that provide generic answers no matter what the player character says. 
-    * **NOTE:​** ​ Like the @match system, CFDialog converts both keywords and the things the player says to lowercase before checking for a match, so it is never necessary to include multiple keywords that only differ in case. 
- 
-  * **Answers** are what the rule will respond, or say, to the player when it is triggered. ​ This is what the NPC replies to the player. Answers are stored in a list of one or more strings in the form **["​Answer1",​ "​Answer2"​ ...]**. ​ When there is more than one answer in that list, each time the rule is triggered, a single random reply will be selected from the list. 
-    *NOTE: ​ Answers may contain line breaks. ​ To insert one, use "​\n"​. 
- 
-  * **Preconditions** are flags that must match specific values in order for a rule to be triggered. These flags persist across gaming sessions and are useful for tracking the state of a conversation with an NPC.  Because of this, it is possible for the same word to elicit different NPC responses depending on how flags have been set.  If dialogs are set to use identical locations, the flags and preconditions can be used by other NPC dialogs so that other NPCs can detect that the player heard specific information from another NPC.  The flags can also be used to help an individual NPC remember what he has said to the player in the past.  Flag settings are stored in the player file, so they persist as long as the character exists in the game.  Each rule contains a list of one or more preconditions,​ and each of the individual preconditions is itself a list of a flag name and one or more values in the following format: **<​nowiki>​[["​flag1",​ "​value1",​ "​value2"​ ...], ["​flag2",​ "​value3"​] ...]</​nowiki>​** where **...** indicates that the pattern may be repeated. The flag name is always the first item in a precondition list.  ":"​ and ";"​ characters are forbidden in the flag names and values. ​ For a rule to be triggered, all its preconditions must be satisfied by settings in the player file.  To satisfy a precondition,​ one of the its values must match the identified flag setting in the player file. The default value of any precondition that has not been specifically set in the player file is "​0"​. ​ If one of the precondition values is set to "​*",​ a match is not required. 
- 
-  * **Postconditions** are state changes to apply to the player file flags after the rule triggers. The postcondition is a nested list that has the same format as the precondition list except that each postcondition list only contains one value. ​ This is because the other main difference is that whereas a precondition checks a player file to see if a flag has a certain value, the postcondition causes a value to be stored into the player file, and it does not make sense to store more than one value into a single flag.  A value of "​*"​ means that the player file flag will not be changed. 
- 
-  * **Prefunctions** are an optional callback function that will be called when a rule's preconditions are all matched, but before the rule is validated. ​ The callback can do additional tests, and should return 1 to allow the rule to be selected, or 0 to block the rule.  The function arguments are the player and the actual rule being tested. 
- 
-  * **Postfunctions** are an optional callback that is called when a rule has been applied, and after the message is said. It can do additional custom processing. The function arguments are the player and the actual rule having been used. 
- 
-Once the rules are all defined, assemble them into a dialog. ​ Each dialog involves somebody who triggers it, somebody who answers, and also a unique name so it cannot be confused with other dialogs. 
- 
-Typically, the "one who triggers"​ will be the player, and the "one who answers"​ is an NPC the player was taking to. You are free to choose whatever you want for the dialog name, as long as it contains no whitespace or special characters, and as long as it is not used by another dialog. ​ You can then add the rules you created to the dialog. Rules are parsed in a given order, so you must add the most generic answer last. 
- 
-===== A simple example ===== 
- 
-If I want to create a dialog for an old man, I might want him to respond to "​hello"​ or "​hi"​ differently the first time the player meets the NPC, and differently for subsequent encounters. In this example, grandpa greets the player cordially the first time, but grumbles subequent times (because he's like that, you know :)). This example grandpa also has a generic answer for what ever else is said to him.  In the example, the player is stored in '​player',​ and the old man in '​grandpa',​ and the player said is in '​message'​. 
- 
-To illustrate the setup and make use of this plugin, a previously inanimate NPC in Goths Tavern will be modified. ​ The order of the following steps is not important, but they will take you through every step required to get an NPC that uses the plugin. 
- 
-  * Edit the map file in scorn/​taverns/​goths as follows. 
- 
-  Index: goths 
-  =================================================================== 
-  --- goths       ​(revision 6971) 
-  +++ goths       ​(working copy) 
-  @@ -974,10 +974,15 @@ 
-   y 23 
-   end 
-   arch man 
-  +name grandpa 
-   x 3 
-   y 23 
-   ​friendly 1  
-  +arch event_say 
-  +title Python 
-  +slaying /​python/​maps/​scorn-goths-grandpa_say.py 
-   end 
-  +end 
-   arch woodfloor 
-   x 3 
-   y 24 
- 
- * Create a new file in the maps directory python/​maps/​scorn-goths-grandpa_say.py and place the following code in the file. 
- 
-  import Crossfire 
-  import string 
-  from CFDialog import Dialog, DialogRule 
-  # 
-  player=Crossfire.WhoIsActivator() 
-  grandpa=Crossfire.WhoAmI() 
-  message=Crossfire.WhatIsMessage() 
-  ​ 
-  # Dialog creation: 
-  speech = Dialog(player,​ grandpa, "​test_grandpa_01"​) 
-  ​ 
-  # The first rule is the "​hello"​ answer, so we place it at index 0 of the 
-  # rules list. The precondition is that we never said hello before. The 
-  # postcondition saves a value of "​1"​ into a player file flag named "​hello"​ 
-  # so grandpa remembers he has already met this player before. 
-  ​ 
-  prer = [["​hello","​0"​]] 
-  postr = [["​hello",​ "​1"​]] 
-  rmsg = ["​Hello,​ lad!","​Hi,​ young fellow!","​Howdy!"​] 
-  speech.addRule(DialogRule(["​hello","​hi"​],​ prer, rmsg, postr),0) 
-  ​ 
-  # The second rule is the answer to a greeting if he as already met the player 
-  # before. ​ Notice that "​*"​ is used for the postcondition value, meaning that 
-  # the flag will remain set as it was prior to the rule triggering. 
-  ​ 
-  prer = [["​hello","​1"​]] 
-  postr = [["​hello",​ "​*"​]] 
-  rmsg = ["​I'​ve heard, you know, I'm not deaf *grmbl*"​] 
-  speech.addRule(DialogRule(["​hello","​hi"​],​ prer, rmsg, postr),1) 
-  ​ 
-  # Finally, the generic answer is written. This is the last rule of the list. 
-  # We don't need to match any condition, and don't need to change any flags, 
-  # so we use "​*"​ in both cases this time. 
-  ​ 
-  prer = [["​hello","​*"​]] 
-  postr = [["​hello",​ "​*"​]] 
-  rmsg = ["What ?", "Huh ?", "What do you want ?"] 
-  speech.addRule(DialogRule(["​*"​],​ prer, rmsg, postr),2) 
-  ​ 
-  # We only have to let the old man speak now: 
-  speech.speak(message) 
- 
-  * In this example, the player is stored in '​player',​ and the old man in '​grandpa'​. What the player said is in '​message'​. 
- 
-  * Start the crossfire server, login, then enter Goths tavern and walk up to the man now named "​grandpa"​ in the left-hand room.  He is in the top left-hand corner of the room. 
- 
-  * Say hello two times. 
- 
-  Player says: hello 
-  grandpa says: Hello, lad! 
-  Player says: hello 
-  grandpa says: I've heard, you know, I'm not deaf *grmbl* 
- 
-  * The conversation state is stored in your player file.  For example: 
- 
-  $ grep -ri test_grandpa var 
-  var/​crossfire/​players/​Player/​Player.pl:​dialog_test_grandpa_01 hello:1 
- 
-=====A more complex example===== 
- 
-A **/​python/​misc/​npc_dialog.py** script has been written that uses CFDialog, but allows the dialog data to be written in a slightly different format. ​ **/​scorn/​kar/​gork.msg** is an example that uses multiple keywords and multiple precondition values. ​ Whereas the above example has a linear and predicable conversation paths, note how a conversation with Gork can fork, merge, and loop back on itself. ​ The example also illustrates how CFDialog can allow dialogs to affect how other NPCs react to a player. ​ **/​scorn/​kar/​mork.msg** is a completely different dialog, but it is part of a quest that requires the player to interact with both NPCs in a specific way before the quest prize can be obtained. ​ With the @match system, once the player knew the key words, he could short-circuit the conversation the map designer intended to occur. ​ CFDialog constrains the player to follow the proper conversation thread to qualify to receive the quest reward. 
- 
-=====Related Resources===== 
- 
-  *  [[http://​crossfire.svn.sourceforge.net/​viewvc/​crossfire/​maps/​trunk/​python/​CFDialog.py?​view=log|CFDialog.py]] in SVN. 
- 
-====== Derivative CFDialog Scripts ====== 
- 
-CFDialog Helper Classes provide basic functionality that can be used in scripts. ​ Some CFDialog derivatives are: 
- 
-=====NPC Dialog===== 
- 
-This is a simple script that make use of CFDialog.py and receives parameters from a [[http://​www.json.org/​|JSON]] inside the event message. Alternatively,​ the JSON parameters, if >= 4096 characters, can be stored in a separate file. Use the '​name'​ script parameter to specify relative location to the dialog file. 
- 
-====Map Example==== 
- 
-An example of a map file entry is: 
- 
-  arch guildmaster 
-  name Sigmund 
-  msg 
-  ​ 
-  endmsg 
-  x 11 
-  y 7 
-  resist_physical 100 
-  resist_magic 100 
-  weight 50000000 
-  friendly 1 
-  stand_still 1 
-  arch event_say 
-  name start/​sigmund.msg 
-  title Python 
-  slaying /​python/​misc/​npc_dialog.py 
-  end 
-  end 
- 
-====Dialog Example==== 
- 
-An example of a JSON dialog similar to the one described [[cfdialog#​A simple example|above]] is: 
- 
-  { 
-    "​location"​ : "​test_grandpa_01",​ 
-    "​rules":​ [ 
-    { 
-      "​match"​ : ["​hello","​hi"​],​ 
-      "​pre"​ : [["​hello","​0"​]],​ 
-      "​post"​ : [["​hello","​1"​]],​ 
-      "​msg"​ : ["​Hello,​ lad!","​Hi,​ young fellow!","​Howdy!"​] 
-    }, 
-    { 
-      "​match":​ ["​hello","​hi"​],​ 
-      "​pre"​ :​[["​hello","​1"​]],​ 
-      "​post"​ :​[["​hello",​ "​*"​]],​ 
-      "​msg"​ : ["​I'​ve heard, you know, I'm not deaf *grmbl*"​] 
-    }, 
-    { 
-      "​match"​ : ["​*"​],​ 
-      "​pre"​ : [["​hello","​*"​]],​ 
-      "​post"​ : [["​hello",​ "​*"​]],​ 
-      "​msg"​ : ["What ?", "Huh ?", "What do you want ?"] 
-    } 
-  ]} 
- 
-For detailed descriptions of the match, pre, post, and msg formats, see the above CFDialog.py documentation. 
- 
-**match** is a list of keyword strings, and corresponds to what the player says that the dialog will respond to. 
- 
-In the above example, the first rule is applied if the player/​character says "​hello"​ or "​hi"​ and if the "​hello"​ flag is set to "​0"​ (default). ​ When the rule is applied, the "​hello"​ flag is then set to "​1"​. 
- 
-**pre** is a list of preconditions that identifies flags that must be set to a particular value in order to trigger a response if a match is detected. 
- 
-**post** is a list of postconditions that specify flags that are to be set if a response is triggered. 
- 
-All of the rule values are lists, and must be enclosed by square braces, but pre and post are lists of lists, so the double square braces (<​nowiki>​[[]]</​nowiki>​) are required. 
- 
-**msg** defines one or more responses that will be given if the rule triggers. ​ When more than one "​msg"​ value is set up, the NPC randomly selects which one to say each time the rule is applied. 
- 
-**replies** optionally defines suggestion to the player if this rule is executed. This is used first to let the player know what options are available, and secondly to alter the text that the player will actually say. This should be a list of items in the form ''<​nowiki>​[word,​ text, type]</​nowiki>'',​ with ''​word''​ the actual word the player should say, ''​text''​ the text the player will actually say if she says the word, ''​type''​ an optional integer to specify if the text is a regular sentence (0), a reply (1) or a question to ask (2). 
- 
-A relatively complex example of an npc_dialog.py dialog is given in the Gork treasure room quest. ​ See **/​scorn/​kar/​gork.msg** in particular as it demonstrates how multiple precondition flag values may be exploited to produce non-linear and variable-path conversations that are less likely to frustrate a player. ​ Refer also to **/​scorn/​kar/​mork.msg** to see how more than one dialog can reference the same dialog flags. 
- 
-====Related Resources==== 
- 
-  * [[http://​crossfire.svn.sourceforge.net/​viewvc/​crossfire/​maps/​trunk/​python/​misc/​npc_dialog.py?​view=log|npc_dialog.py]] in SVN. 
-  * Some actual in-game dialogs supported by npc_dialog.py are: 
-    - Mork and Gork mini-quest in Scorn 
-    - Sigmund in Newbies House 
cfdialog.1522416936.txt.gz ยท Last modified: 2018/03/30 08:35 by karl