Multiplayer Interactive-Fiction Game-Design Blog


Conversation trees – cConvTree



Download 8.87 Mb.
Page133/151
Date02.02.2017
Size8.87 Mb.
#15199
1   ...   129   130   131   132   133   134   135   136   ...   151

15 Conversation trees – cConvTree


How to create pre-scripted conversations between two or more NPCs.


Conversation trees - cConvTree

Conversation trees are a tride-and-true method for talking to NPCs,

and the standard NPC conversation system for most CRPGs. For those

of you not familiar with the term, you'll be familiar with the

scenario: The NPC says something like "What's your favorite color?", and

the player is given a menu of choices, such as "Red", "Green", or "Blue".

Once the player selects the color, the NPC makes a statement and (often)

a new question is asked by the NPC.


While Circumreality doesn't rely on conversation trees (most CRPGs do), it does

allow for them.


Creating a conversation-tree object

To create a conversation tree (combination of question, player answers,

and NPC's responses to the answers), you need to create a conversation-tree

object. To create a conversation-tree object:





  1. Create an object, as normal, but base it off

    of cConvTree. For this example, call

    it oConvTreeFavoriteColor.





  2. The object should not be contained in anything.





  3. Fill pConvTreeQuestion with the question that the NPC

    asks, such as "What's your favorite color?"





  4. pConvTreeAnswers should be filled in with a list of

    answers to the NPC's question, such as ["Red", "Green", "Blue"].







  5. pConvTreeResponses is a list containing the responses

    to each of the player's answers, such as ["Red? I hate red.",

    "Green is nice.", "Blue is my favorite color."]



  6. If you wish one or more answers to lead to another conversation

    tree, then set pConvTreeResponsesNext to indicate

    subsequent conversation trees.





  7. Since the player's answers may change the NPC or other parts of

    the game, you'll probably need to write your

    own ConvTreeResponse() to handle and coding.

    For example: If the player likes red, the NPC might give them

    a red riding cloak; this needs to be handled by code.






You may also wish to set the following properties and methods:





  • Set pConvTreeMustAnswer to TRUE if the NPC requires

    an answer from the player and won't be distracted by attempts

    to change the subject.







  • pConvTreeQuestionRepeat is spoken if the player

    doesn't answer the NPC's question properly and the NPC has to

    repeat the question.



  • If you wish to customize pConvTreeQuestion or

    pConvTreeQuestionRepeat based upon the current situation,

    you can write your own ConvTreeQuestion() method.





  • You can allow for alternate phrasings of the answers, such as

    "I like red best." by proving

    a pConvTreeAnswersParse property.





  • Answers won't always be valid every time a conversation tree

    pops up. For example: If the NPC asks the player, "What inn are

    you staying at?", you could include answers for all the inns in

    town. However, if the player character didn't know about some of

    the inns, you might want to invalidate and hide some of the

    answers. To do this, write your

    own ConvTreeAnswerIsValid() method.







  • pConvTreeResponseNotAnswer controls what the NPC says

    if the player tries to change the subject and avoid answering

    the question.





  • pConvTreeResponseNotAnswerLike controls how much

    the NPC's like/trust of the PC changes when the player tries

    to change the subject.





  • pConvTreeImportantDecision will cause all the context-menu

    options to be flagged so the player knows they're making an important decision.





  • If you invoke the converation tree using AIConversationConvTreeAdd()

    then you may wish to write a ConvTreeIsValid() method

    so that the conversation tree won't be brought up in the conversation

    if it's no longer valid.





  • pConvElementConversationState is used when you call

    AIConversationConvTreeAdd() to determine the conversation state after

    the conversation tree finishes.







Activating a conversation tree

There are several ways that you can activate a conversation tree:





  • Call oNPC.AIConversationConvTreeSet() to start the conversation

    tree immediately.





  • oNPC.AIConversationConvTreeAdd() will add the conversation tree

    to the NPC's list of "questions" that the NPC might choose to bring up, depending

    upon where the conversation takes it. Unless you set the priority to a very

    large value (greater than 1.0), there's no guarantee that the conversation

    tree will be run immediately.


  • If you're providing a oNPC.AIConversationQuestionFill() method

    for your NPC, which controls what questions the NPC asks, you can

    also call AIConversationConvTreeAdd().

  • You can have a cFactoid invoke a conversation tree by

    setting oFactoid.pFactoidConvTree.







Easy conversation trees

Often, you'll want NPCs to ask a series of questions to players as they get

to know them. The questions will start out easy, like "Who do you want to win the

world series?" and gradually work up to something more contentious, like

"Do you want the elves or the orcs to win the war?"

Creating this question ladder is easy, just fill in oNPC.pAIConvTrees with

the series of questions and acceptable responses.

16 Conversation stories – cConvStory


Code that allows NPCs to tell stories to player.


Conversation stories - cConvStory

An important part of Circumreality gameplay is for non-player characters to

tell stories to the players. The stories not only provide knowledge

(cKnowledge) to the player characters, but they provide clues

about the NPC's personality. These clues help the player determine

what needs to be done to get on the NPC's good side.





Creating a story object

Getting NPCs to speak stories is pretty easy, and involves the creation

of an object for each story:



  1. Create an object, as normal, but base it off

    of cConvStory. For this example, call

    it oConvStoryFishingTrip.





  2. The object should not be contained in anything.





  3. Fill pConvStorySpeak with the <CutScene> resource

    for the story. If you just wish the NPC to speak the story, you

    can also use a <SpeakScript> resource, or even a string.

    If pConvStorySpeak is a list, then the story will

    be multi-part, and require the player to speak,

    "Please continue" after each part.





  4. pConvStoryAskSpeak should be filled with a unique string

    that the player can speak to the NPC and re-hear the story.

    For example: "Could you tell me about your fishing trip to the Catskills?"





  5. pConvStoryIsRumor should be set to TRUE if the story

    is actually a rumor.







  6. pConvElementConversationState is a list of lower-case

    strings that described what the story is about. For example:

    ["fishing", "catskills", "holiday"]. Every story needs this

    property since it allows the NPC to determine which stories to use

    given the conversation context.


  7. If your code access pConvElementConversationState, it should

    call ConvElementConversationState() instead, since this

    method appends "story" or "rumor" to the list based on

    pConvStoryIsRumor.


  8. You either need to sub-class your story object from cKnowledge

    as well as cConvStory or,

    set pConvStoryKnowledge to the associated knowledge

    object. This means that every time a player hears a story/rumor, they

    will be able to access it from their "What do I know?" list, as well

    as tell it to other NPCs in story/rumor swaps.




You may also wish to fill in the following properties or methods:







  • ConvStorySpeak() and ConvStoryNumParts() can

    be overridden so that the story changes depending upon the player.







  • pConvStoryWontSpeak is the text that the NPC speaks

    if it isn't willing to tell the player the story just yet, perhaps

    because the NPC doesn't like the player enough.



  • You can write your own ConvStoryWontSpeak() to decide

    when the NPC will speak the story to the player. The

    default method tests the properties: pConvStoryWontSpeakLike,

    pConvStoryWontSpeakFaction, pConvStoryWontSpeakFracture,

    and pConvStoryWontSpeakQuest.







  • pConvStoryAskSpeakParse provides for other ways to

    ask the pConvStoryAskSpeak question.







  • pConvStoryAskSpeakAlwaysShow causes the pConvStoryAskSpeak

    question to always be listed in the NPC's context menu, even if the

    player hasn't heard the story.





  • pConvElementPriorty affects how badly the NPC wishes

    to tell the story. Values range from 1.0 (very badly) to 0.0 (doesn't

    wish to tell).



  • If pConvStoryExpectPCToReciprocate then after the NPC

    finishes with the story/rumor, it will expect the player to follow

    with their own story/rumor. Players will be shown their, "What do I know?"

    list and be able to select a story to speak back.



  • Some NPCs just like to tell stories to NPCs. If

    you set pConvStorySpeakLike then every time the NPC

    gets to tell a story/rumor to a character, it will like/trust the

    character more. Use this to create NPCs that just want a shoulder

    to cry on, or someone to brag to.







Speaking a story

There are several ways that you can have a NPC speak a story:





  • Call oNPC.AIConversationConvStorySet() to speak the story immediately.





  • Call oNPC.AIConversationConvStoryAdd() will add the story

    to the NPC's list of "questions" that the NPC might choose to bring up, depending

    upon where the conversation takes it. Unless you set the priority to a very

    large value (greater than 1.0), there's no guarantee that the story

    will be spoken immediately.




  • If you're providing a oNPC.AIConversationQuestionFill() method

    for your NPC, which controls what questions the NPC asks, you can

    also call AIConversationConvTreeAdd(). You probably won't need to; see below.

  • For each cCharacter and cFaction, you can

    set pAIConvStories to a list of stories that the character (or

    faction) knows. Characters will automatically know stories assigned to

    their factions.


  • Using pAIConvStories will automatically add stories to the list of "questions"

    when oNPC.AIConversationQuestionFill() is called, as well as provide

    context menu options for the stories.

  • A call to ConvStorySelect() will select a story that seems

    most appropriate to the NPC, given the current conversation state and

    stories that the NPC has already spoken to the player.









Properties associates with NPCs

You may wish to set the following properites for every NPC:







  • pAIConvStories, as previously stated, controls the stories

    that the NPC knows. (It also knows the cFaction.pAIConvStories of any

    factions to which it belongs.)





  • pAIConvStoryPreferred is a list of the topics (aka: a conversation

    state) in stories that the NPC prefers to speak, as well as hear.

    For example: If a NPC's pAIConvStoryPreferred is ["story", "fishing", "cars",

    "holiday"], then the NPC will prefer to tell stories about fishing, cars,

    and/or holidays. Likewise, they will prefer to hear such stories.

    This property is very important because it defines much of

    the NPC's personality.


  • A more flexible alternative to pAIConvStoryPreferred, it to

    override the method, AIConvStoryPreferred(). The default

    behavior just refers to pAIConversationStoryPreferred. However, if

    you write your own code, you can specify that all dwarves like to hear

    about "mining" and "gems", or that NPC's who are sad wish to hear "sad" stories.



  • pAIConvStoryStickToPreferred controls how much the NPC

    sticks to its pAIConvStoryPreferred list during conversations. If this

    is 1.0, the NPC will keep returning to fishing, cars, and holidays. If

    this is 0.0, the NPC will easily follow the player's lead.





  • By default, if the NPC knows any stories or rumors, it will include

    the context menu of "Do you know any good rumors/stories?" to save

    players some typing. However, this context menu reveals to the player

    the fact that the NPC knows some stories/rumors. If you wish

    to hide the menu, then

    set pAIConvStoryHideRumorMenu to TRUE.





Conversation states

As stated, the pConvElementConversationState property is very important for

stories, since it controls when a NPC will speak a story, and what

the NPC thinks about stories told to it by the player. The property

is a list of lower-case strings that describe the story, such as ["story",

"fishing", "holiday", "catskills"].


The NPC's AI determines which stories follow one another best by

calling ConvStoryConversationIntersect(), comparing

the existing conversation state to the story's conversation state.


Some converation state topics are opposites of one another. For example:

"rumor" is the opposite of story, and "work" is the opposite of "holiday".

NPCs take extra precaution to not speak a follow-on story with opposites

to the current conversation state. For example: A story about a holiday

followed by a story about work (holiday's opposite) is a social mistake

that's to be avoided. Players need to work within the "avoid opposites"

framework too.


To determine a conversation state's

opposites, ConversationStateOpposites() is called.

17 Conversation messages


Easy way for NPCs to relay messages to players.


Conversation messages

Often times, you'll want NPCs to relay messages to PCs, such as

"Your mother called me and wants you to go home and eat dinner now,"

or "[Other PC] has been spreading bad rumors about you."


The hard way to provide this functionality is to write your

own AIConversationQuestionFill() that calls AIConversationQuestionAdd().

The easy way is to:





  1. Call AIConversationMessageAdd() when the NPC learns of

    a message that it wants to tell the player.



  2. That's it.





If you want to remove a message from a NPC,

then call AIConversationMessageRemove(). For example:

When the player returns home from, then all the mothers in the neighborhood

that had been called with AIConversationMessageAdd() should

receive an AIConversationMessageRemove() call with the same

message string.

18 Commands, statements, and questions


How to easily allow players to give NPC commands, ask them questions, or state facts to them.


Commands, statements, and questions

There is yet another way to make NPCs more conversive; this way is

designed so that you'll find it easy to create commands (that players can

give to the NPCs), questions (that players can ask), and

statements (that players can speak to NPCs). In most cases, you may

find this easier than using factoids (cFactoid), conversation

trees (cConvTree), or conversation stories (cConvStory).


The easiest way

The easiest way for a NPC to answer questions is to:





  1. Fill in pAIQuestionsAndAnswers with the questions and

    answers that the NPC knows.


  2. Optionally, write your own AIQuestionsAndAnswers() method.

Unfortuntely, this method is somewhat limit, so you'll

need something more flexible.


A more flexible technique

To get a NPC to respond to a custom command, answer a specific question, or

understand a statement:





  1. Invent a lower-case alphabetic-only string that described

    the phrase, such as "singsong" if you want players to ask NPCs

    to sing a song.



  2. Add a prefix to the string. Use "cmd" if it's a command,

    "quest" for a question, or "state" for a statement. For the singing

    example, this would create a name of "cmdsingsong".





  3. Add AIListenForEnum() to the NPC. You'll need to

    write this code so this appends the appropriate phrases (commands, questions,

    or statements) to the list. For the singing example, this might

    append, ["cmdsingsong", "Sing a song", "sing [a] song [to %1]"].

    "cmdsingsong" is the name. "Sing a song" will appear on the NPC's context

    menu so that players know they can ask the NPC to sing. "sing [a] song [to %1]"

    is an additional parse that instructs the NPC to sing their song to

    a specific character.


  4. If you don't include the second element in the list, "Sing a song", then

    the command won't appear on the NPC's context menu, requiring

    users to type it in.

  5. You'll need to add AIListenForIsValid() to validate

    that the pameters for the %1 wildcard is correct. In this case,

    if the (PhraseID === "cmdsingsong") then the method should verify that

    there are no parameters, in the case of "Sing a song", or that there is

    one parameter and it is another character, for "Sing a song to %1".





  6. Finally, write a AIListenForAct() to actually

    have the NPC sing a song.



  7. If you only want the NPC to speak a phrase in reply, such as to the

    question, "What is the meaning of life?", then you can get away with

    only an AIListenForEnum() that fills the list in with ["questmeaningoflife",

    "What is the meaning of life?", "[*4] meaning [of] life [*5]", "The meaning

    of life is 42."]. The fourth parameter will be spoken if the question

    is asked. The third parameter, "[*4] meaning [of] life [*5]", accepts

    any sentence as long as it has "meaning of life" or "meaning life" in it.

    Using this approach you won't need an AIListenForIsValid() or

    AIListenForAct().







Advanced tips



  • You can also add AIListenForEnum(), AIListenForIsValid(),

    and AIListenForAct() to the NPC's cFaction, allowing

    all members of the faction to respond to the phrase.





  • Alternatively, adding AIListenForEnum(), AIListenForIsValid(),

    and AIListenForAct() to different NPC classes, such as cNPCSinger

    or cNPCPhilisopher,

    will enable all members of the class to react to the command, question,

    or statement. The methods are all layerable, so a NPC derived

    from cNPCSinger and cNPCPhilosopher will be able to

    both sing and answer the meaning of life.







Give/offer (default command)

If a player gives an object to a NPC, this is translated into a "cmdoffer"

ParseID that's passed into AIListenForIsValid() and AIListenForAct().

Only one parameter is used, the object that's being given.


The default implimentation checks to see the real value of the gift, along with

the NPC's personal valuation (since some NPCs like specific types of objects,

such as chocolates or collectables). If the real valuation is too high for

the NPC, or the player have given the NPC too many gifts recently, then the

NPC will decline the gift. Otherwise, the gift will be accepted, and the

NPC's opinion of the player will be improved based upon the NPC's valuation

of the gift (not the real value).


Some properties that you might wish to modify are:







  • pAIOfferCanBribe - If this is TRUE then the player can give

    money to the NPC without offending the NPC. By default, this is FALSE.







  • pAIOfferEffectiveness - The amount that a NPC's like/trust

    go up when it's given a gift is based upon the NPC's personal valuation of

    the gift combined with the values in pAIOfferEffectiveness. Higher numbers

    will make the NPC more susceptible to gift-giving and bribery.







  • pAIOfferForget - See below for details, but this controls

    how frequently players are able to give NPCs gifts.







  • pAIOfferMaxAmount - This is the maximum (real) value of gifts

    that the player can give the NPC per "week". (pAIOfferForget controls how

    long a "week" is.) The value is tempered by how much the NPC likes the

    PC; with smaller amounts being used by NPCs that like the player less.

    If the player tries to give a gift that's more expensive

    than this then the NPC will reject the gift.






Because this behaviour is customized so frequently, there's a special

method that you may wish to modify, AIListenForCmdOffer().


Show (default command)

If a player shows an object to a NPC, this is translated into a "cmdshow"

ParseID that's passed into AIListenForIsValid() and AIListenForAct().

Only one parameter is used, the object that's being given.


The default implimentation checks to see

the NPC's personal valuation of the object (since some NPCs like specific types of objects,

such as chocolates or collectables).

The NPC then responds based on its personal valuation.

Some properties that you might wish to modify are:







  • pAIOfferCanBribe - NPCs that can be bribed will find

    money more interesting that those that can't be bribed.







  • pAIOfferEffectiveness - This is used for "cmdoffer" (above),

    and to determine how interested the NPC is in the object.






Because this behaviour is customized so frequently, there's a special

method that you may wish to modify, AIListenForCmdShow().


Can I have your OBJECT? (default command/question)

Players can ask NPCs to give them objects that the NPCs are carrying, by typing,

"Can I have your OBJECT".

To specify what objects the NPCs are willing to give:





  1. Set pAIGivable to the list of objects that the NPC will give. To ensure

    that the NPC will have the objects for all players that come through a multiplayer

    game, objects in pAIGivable will (by default) be automatically created in the NPC's

    possession.





Default questions

NPCs understand dozens of default questions, such as "What race are you?",

"What guilds are you a member of?", etc.

You can fine-tune how readily the NPC answers these questions by

setting:





  • pAIInfoReveal controls how tight-lipped the NPC is about

    information. If you don't set this, the value will be randomly generated.







  • AIInfoRevealValue() is a layered method to see how tight lipped the AI

    is about a specific type of information, such as the languages that it

    speaks or its hobbies. This is called by AIInfoReveal() (see below) to

    see whether a NPC will reveal specific information.





  • While you won't modify this, AIInfoReveal() is

    a method to see how tight lipped the AI

    is about a specific type of information, such as the languages that it

    speaks or its hobbies. Call this to see if the AI would be willing

    to reveal some information to the player.





You may also wish to override some of the default responses:


You can add a layer to AIListenForAct(), as described above.

This will cause, for example, a specific question to a specific NPC

to be trapped. If you ask Fred, "Is Mike a member of any guilds?", Fred

will have a unique answer.

If you wish to have all NPCs that know Mike to provide

a custom response to, "Is Mike a member of any guilds?", then...



  • Write you own AIInfoSpeak() associated with the object about

    which the question is being asked. For this example, you would

    write oMike.AIInfoSpeak().





  • And/or, set oMike.pAIInfoSpeakFuturePlansNPC to provide a default

    response to the question, "What are Mike's future plans?"



  • And/or, set oMike.pAIInfoSpeakHowDoingNPC to provide a default

    response to the question, "How is Mike doing?" or "How are you doing?"



  • And/or, set oMike.pAIInfoSpeakKnowAboutNPC to provide a default

    response to the question, "What do you know about Mike?"



  • And/or, set oMike.pAIInfoSpeakOccupationNPC to provide a default

    response to the question, "What is Mike's occupation?" or "What is your occupation?"



  • And/or, set oMike.pAIInfoSpeakWhereFromNPC to provide a default

    response to the question, "Where is Mike from?"



  • And/or, set oMike.pAIInfoSpeakWhereLiveNPC to provide a default

    response to the question, "Where does Mike live?" or "Where do you live?"



  • And/or, set oMike.pAIInfoSpeakWhyHereNPC to provide a default

    response to the question, "Where is Mike here?"


  • The default location for where the character lives is

    generated from pAIScheduleLocationLive.


  • And/or, set oMike.pAIInfoSpeakWhereWorkNPC do provide a default

    response to the question, "Where does Mike work?" or "Where do you work?"


  • The default location for where the character works is

    generated from pAIScheduleLocationWork.


  • Fill pAIInfoSpeakAdvice with the NPC's answer to "Do you

    have any advice for me?"



  • Fill pAIInfoSpeakHappening with the NPC's answer to "What's

    going on here?"






19 Chatter-bot functionality


How to easily incorporate chatter-bot functionality into your NPCs.


Chatterbot functionality

So far, all the NPC conversation tools that you've been shown are designed

to (a) create procedural responses to questions, like "Where is fred?", and

(b) create NPC-specific responses, such as "Why are you here?"


Sometimes, though, you may just need basic chatter-bot functionality.

The player types in a statement or question, and the NPC responds with

a statement (or a statement that ends in a question mark). In general,

the same response is provided by all NPCs, or at least, all NPCs

of a specific class (such as all elves).


To create a chatterbot:





  1. For a class of NPC, such as cElf, write a cElf.AIConvChatter() method.

    This will fill in a list with [ResourceName, Resource]. In the case

    of elves, this might be ["elf", rNLPRuleSetElfChatter].




  2. Alternatively, you could fill in pAIConvChatter, but this isn't

    recommended for classes of NPCs, because one class might overwrite another

    class's pAIConvChatter if a NPC based on two classes, each with their own

    chatter-bot rules.




  3. Create a <NLPRuleSet> resource named rNLPRuleSetElfChatter (or

    whatever you called it above.





  4. In the rule set, create rules that convert sentences the player may speak

    into parsed sentences of the format, "`speak WHATTOSPEAK".


  5. For example: If you want players to ask, "What is the meaning of life?",

    then create a rule that converts "what is the meaning of life" to

    "`speak The meaning of life? 42, of course.".

That's it. From now on, players will be able to ask all elves, "What is the

meaning of life?", and all elves will reply with the exact same answer,

"The meaning of life? 42, of course."


This simple method has several limitations: (a) You can't assign transplanted

prosody to the response, (b) You can't assign a <SpeakScript> to the response.

And (c) you can't customize it according to the NPC, PC, and situation.


A more sophisticated approach is:







  1. Create a rule that converts the english sentence to a parsed

    sentence with the form, "`chatter `RESOURCENAME `SENTENCENAME". RESOURCENAME

    is the name you used for the resource, above, "elfchatter". SENTENCENAME

    uniquely identified the response for "What is the meaning of life?"




  2. For example: Your rule set would convert "what is the meaning of life" to

    "`chatter `elf `whatmeaningoflife".




  3. Write a cElf.AIConvChatterAct() method. In the method, first

    test that (ResourceName == "elf"), returning if it isn't. Then, test

    that (SentenceName == "whatmeaningoflife"), returning "The meaning of life?

    42, of course." if there's a match.





  4. If the method returns the resource sAIConvChatterWhatMeaningOfLife,

    then you can record transplanted prosody.



  5. Or, the method could return a <SpeakScript> resource.





  6. Or, the method could run any code it wishes, and have the elf break into

    song, and then return TRUE to indicate that the AIConvChatterAct() method

    handled all the speaking.






20 Merchants – cMerchant


The cMerchant class makes it easy to add a merchant to your world.


Merchants - cMerchant

Merchants are a common fixture in most worlds.

The cMerchant class was created to make implimenting

merchants easy.


Merchants can perform the following functionality:







  • Sell equipment to player characters.





  • Buy equipment from player characters.





  • Change currency from one form to another (gold to copper).





  • Repair equipment.





  • Identify equipment.





  • Teach player characters skills.





  • Heal charaters.





  • Other actions; whatever the author thinks up.




To create a merchant:







  1. Create your NPC as usual.



  2. Add an extra "Super-class", cMerchant, on top

    of (and with higher priority than) the NPC's basic class.





  3. Specific properties are required depending upon exactly what

    the NPC will sell. See below.









Selling equipment

If you wish your merchant to sell equipment:





  1. Fill in pMerchantForSale with a list of items that

    the merchant is selling. For each item, an object class, the

    number of items in stock, the restock rate, and the maximum

    stock are stored. You can also customize the price for an item,

    making it more or less expensive for a particular merchant.




  2. You can leave this blank if you fill in pMerchantForSaleInit; see

    below.

  3. Since many merchants sell the same basic goods, such as bandages,

    you can fill in pMerchantForSaleInit with a series

    of callback functions that indicate "classes" of objects that

    are sold. The fantasy library containes some

    default callback functions, MerchantForSaleInitArmor(),

    MerchantForSaleInitWeapons(), MerchantForSaleInitFirstAid(),

    MerchantForSaleInitLighting(),

    and MerchantForSaleInitContainers() which include default

    armor, weapons, firt-aid equipment, lighting, and containers.

    For each of these callbacks, you can scale the number of goods

    a merchant stocks, how quickly the stock arrives, and how much

    the merchant charges for the stock.




  4. You can write your own callbacks if you wish.

You might wish to change some other properties of the merchant:







  • pAIValueObject will cause the merchant to value some

    items more or less than others. This is another way of increasing

    or decreasing the amount the merchant will charge.





  • gMerchantCurrencyError affects the amount that the

    merchant will round a price to. The default is 2 (percent).

    See CurrencyIAToCoin()





  • gMerchantCurrencyMax controls how many currencies the

    merchant will include in a price. The default is 2, allowing

    for prices like "2 gp, 1 sp", not "2 gp, 1 sp, 3 cp".

    See CurrencyIAToCoin()







  • pMerchantLikeScale affects how much the merchant will

    adjust his price based on how much the merchant likes (or dislikes) the

    character he is selling to.





  • pMerchantLikeThreshhold will cause merchants to refuse

    to sell (or do any transaction with) characters the merchant doesn't

    like.





Buying equipment

If you wish the merchant to buy equipment from players:





  1. Set pMerchantBuyBack to TRUE so the merchant is allowed

    to purchase used equipment.



You may also wish to set the following values:







  • pMerchantBuyBackNotNormallyStocked affects the price

    the merchant is willing to pay for items that the merchant doesn't

    normally stocked. If not specified, the number is randomly

    generated.







  • pMerchantCash specified how much money the merchant

    has, and ultimately how expensive of items the merchant can buy.







  • gMerchantBuyBackScale controls the price

    that all merchants are willing to pay for used equipment.

    This global will help you control your world's economy, affecting

    how much money is introduced into the world by merchants.





  • When the merchant purchases an item that isn't normally

    stocked, gMerchantBuyBackSellDown determines how long

    the item will remain on the merchant's list before being "sold off"

    and removed from the list.





  • pMerchantForSale, pMerchantLikeScale, pAIValueObject,

    and pMerchantLikeThreshhold will also affect the merchant's

    purchasing of used equipment.






Making change

By default, merchants are willing to exchange coins for larger

or smaller denominations with players. If you wish to

disable this:





  1. Set pMerchantChange to FALSE to prevent a merchant from

    making change.




You may also wish to set the following values:







  • pMerchantLikeThreshhold will affect whether the merchant

    will make change for the player.








Repairing objects

To allow a merchant to repair objects:





  1. Set pMerchantRepair to TRUE.





  2. Modify the NPC's pSkills property so it includes the

    appropriate repair skills. For example: A blacksmith would

    have oSkillBlacksmith, while a leather worker would have oSkillLeatherWorking.





  3. Make sure all the tools that the merchant needs to

    repair are either held by the NPC or located in the same room. Merchants

    can't repair without tools. They can get buy without

    materials though.




You may also wish to set the following values:







  • pMerchantLikeThreshhold will affect whether the merchant

    will make change for the player.







  • pMerchantCash affects how much cash the merchant has

    on hand, and (more importantly) how much patronage will affect

    how much the merchant likes the player's character. Spending lots

    of money with a merchant make the merchant like the player character better.








Identifying objects

To allow a merchant to identify objects:





  1. Set pMerchantIdentify to TRUE.





  2. Modify the NPC's pSkills property so it includes the

    appropriate identification skills. For example: A botonist might

    have oSkillBotony, while a magical scholar might have oSkillMagicHistory.






You may also wish to set the following values:







  • pMerchantLikeThreshhold will affect whether the merchant

    will make change for the player.







  • pMerchantCash affects how much cash the merchant has

    on hand, and (more importantly) how much patronage will affect

    how much the merchant likes the player's character. Spending lots

    of money with a merchant make the merchant like the player character better.









Bankers

To allow a merchant to be a banker:





  1. Set pMerchantBanker to 1. If you use a higher value then

    the merchant will charge more for deposits than normal. Lower values (more

    than 0) will resulting in cheaper banking.






You may also wish to set the following values:







  • gBankerPricePerVolume is the "average" that a banker

    charges to store 1 liter of object volume.







  • gBankerMaxDeposit is the maximum number of items a

    character can store in their bank accounts. This ensures that characters

    don't hoard too much equipment.





  • pMerchantLikeThreshhold will affect whether the merchant

    will take deposits from the character..







  • pMerchantCash affects how much cash the merchant has

    on hand, and (more importantly) how much patronage will affect

    how much the merchant likes the player's character. Spending lots

    of money with a merchant make the merchant like the player character more.








Teach skills

To allow a merchant to teach skills to a player:





  1. Set pMerchantSkills to a list of skills that the merchant

    teaches, along with the cost to learn the skill.




You may also wish to set the following values:







  • pMerchantLikeThreshhold will affect whether the merchant

    will teach the character.







  • pMerchantCash affects how much cash the merchant has

    on hand, and (more importantly) how much patronage will affect

    how much the merchant likes the player's character. Spending lots

    of money with a merchant make the merchant like the player character more.









Healer

To allow a merchant to heal the player's wounds:





  1. Use the cMerchantHealer class instead of the

    cMerchant class.



  2. You may want to adjust pMerchantHealer to control

    how expensive/cheap the merchant is.





  3. pSkills can be changed so the merchant has

    a higher or lower oSkillFirstAid, which affects what kind of

    healing is possible as well as the speed at which the healing

    occurs.





You may also wish to set the following values:







  • gHealerPriceBandage, gHealerPriceSplint,

    and gHealerPriceRegrow affect the typical cost of

    getting an injury healed.





  • pMerchantLikeThreshhold will affect whether the merchant

    will heal the character.







  • pMerchantCash affects how much cash the merchant has

    on hand, and (more importantly) how much patronage will affect

    how much the merchant likes the player's character. Spending lots

    of money with a merchant make the merchant like the player character more.








Miscellanrous services

Healers are really based on the merchant's "Miscellaneous" services

option. If you wanted to create a barber, for example, you'd write

some code to allow for miscellaneous services:





  1. Write a MerchantMiscEnum() method for the merchant. This

    method enumerates all the miscellaneous services the merchant offers.



  2. Write a MerchantMiscDo() method, causing the merchant

    to act on a purchased service.



  3. Optionally, set pMerchantMiscCommand to the phrase

    displayed in the merchant's context menu. The default is,

    "Do you provide any services?". You could change this to,

    "I want my hair cut."


  4. For this to work, you must include the parser rules in the merchant

    for "I want my hair cut" to either "`merchantforsale `misc" or

    "`merchantforsale `hair". (See below.)

  5. If you use the "`merchantforsale `hair" option (or another custom

    tag), you'll need

    to set pMerchantMiscCommandTag to "`hair".






You may also wish to set the following values:







  • pMerchantLikeThreshhold will affect whether the merchant

    will provide a service for the character.







  • pMerchantCash affects how much cash the merchant has

    on hand, and (more importantly) how much patronage will affect

    how much the merchant likes the player's character. Spending lots

    of money with a merchant make the merchant like the player character more.








Miscellanrous notes

You may also wish to modify the following:







  • MerchantIsOpen() can be overridden to see if the

    merchant is open for business.







21 Relationships – cRelationship


Creating relationships between your NPCs.


Relationships - cRelationship

In Circumreality, NPCs can have relationships with other NPCs. These might

include being a spouse, child, coworker, friend, etc. The relationshps

a NPC has affects how much one NPC knows about another, as well as how

much the NPC is willing to say about the other NPC.



cRelationship object

Before specifying what relationships a NPC has, you must first specify

the types of relationships: spouses, parents, children, enemies, friends, etc.

The following relationships are already built into Circumreality, and handled by



the listed object:




























































































































































































































Major relationship Minor relationship Object Inverse
"auntuncle" None oRelationshipAuntUncle "niecenephew"
"child" None oRelationshipChild "parent"
"coworker" None oRelationshipCoWorker "coworker"
"employee" None oRelationshipEmployee "employer"
"employer" None oRelationshipEmployer "employee"
"enemy" None oRelationshipEnemy "enemy"
"friend" None oRelationshipFriend "friend"
"friend" "good" oRelationshipFriendGood "friend"
"girlboyfriend" None oRelationshipGirlBoyFriend "girlboyfriend"
"girlboyfriend" "hidden" oRelationshipGirlBoyFriendHidden "girlboyfriend", "hidden"
"grandchild" None oRelationshipGrandChild "grandparent"
"grandparent" None oRelationshipGrandParent "grandchild"
"niecenephew" None oRelationshipNieceNephew "auntuncle"
"parent" None oRelationshipParent "child"
"rival" None oRelationshipRival "rival"
"sibling" None oRelationshipSibling "sibline"
"spouse" None oRelationshipSpouse "spouse"



cRelationship properties

Every relationship object (associated with a relationship) has a list

of properties that describe the "typical" relationship:





  • pNLPNounName and pNLPParseName are used to

    name the relationship, such as "grandmother".







  • pRelationshipInfluence simulates the NPC telling its

    relations about what a good/bad guy the player is. If this is a large number

    then any like/trust impression that the player character makes on the

    NPC also has a large impact on the NPC's relations. Thus, a "spouse" relationship

    would have a much larger impact than a "coworker" relationship. This

    number can be negative for an "enemy" relationship where making friends with

    one NPC causes the NPC's enemies to become unfriendly towards the player.





  • pRelationshipInverse indicates the opposite relationship.

    Thus, if NPC A is a "employer" to NPC B, then NPC B must be an

    "employee" to NPC A.





  • pRelationshipLike indicates how much the NPC likes/trusts his

    relations. "friend" might have a [1, 1] indicating theat if NPC A is a friend

    of NPC B then, in general, NPC A's like/trust of NPC B is +1.0 higher than

    one would normally expect. Conversely, "enemy" might be [-6, -6], indicating

    a default like/trust of 6-points below.





  • pRelationshipString" is the lower-case string for the relationship,

    such as "spouse".







  • pRelationshipTalkAbout controls how willing the NPC is to divulge

    personal information about the relation. A husband may be wary about divulging

    personal information about his wife, and have high values, like [4, 4]. However,

    NPCs are willing to say anything (bad) they know about enemies,

    with a [-4, -4], score.



  • Setting pHidden to TRUE will cause the relationship to

    be secret. It won't be spoken of. The only way the player can learn it is

    by observation.







Minor relationships

So far, I've just been discussing "major" relationships. Although no

"minor" relationships are built in, you may wish to add some. Minor

relationships are variations on the major ones. For example: You could

have a "loving" (minor relationship) "spouse" (major relationship). The loving

spouse might have a higher pRelationshipLike.


As you may have noticed, there is an important naming schemes for

relationship objects. All relationship objects are named with a prefix

of "oRelationship", followed by the major relationship, and then followed

by the minior relationship, if one exists. Thus, the spouse relationship

object is "oRelationshipSpouse", while the loving-spouse object

is "oRelationshipSpouseLoving".

This naming scheme is critical since it's used

by RelationshipToObject() to convert a major (and minor)

relationship string into a relationship object.





Creating your own relationship object

To create your own relationship object, such as that of a "priest" to

his "follower"(s):



  1. Create an object based off cRelationship.





  2. The object should not be contained in any other object.





  3. Make sure to name it "oRelationshipXXXYYY", where XXX is the

    major relationship, and YYY is the minor relationship, if there is one.

    Thus, your new relationship object would be "oRelationshipPriest".





  4. Make sure to fill in all the relationship properties, as listed

    above.



  5. If the relationship has an inverse, create

    the inverse object too. Thus, if you created

    oRelationshipPriest, you'd also need to create oRelationshipFollower.





  6. If you have created a minor relationship, such as oRelationshipFollowerFanatic,

    then make sure you also have an object that will handle

    the major relationship, such as oRelationshipFollower.









Assigning relationships to a NPC

When you want to assign one or more relationships to a NPC, do the following:





  • Set the NPC's pAIRelationships to indicate what relationships

    the NPC has. For example: [ [oNPCWife, "spouse"], [oNPCChild, "child"] ] causes

    the NPC to be married and have one child.




  • Make sure that the NPC's inverse relationship is included in

    the pAIRelationships of the NPC's relations. For example: You would need

    to set oNPCChild.pAIRelationships to [[oNPCFather, "parent"]].

  • Additionally, you could write your

    own AIRelationships() method. This might be useful to ensure

    that all members of a town have a relationship with the town mayor, etc.





  • You can also create relationship by adding a pAIRelationships

    to cFaction objects.







Inquiring about NPC relationships

If you're writing code and wish to find out what kinds of relationships

a NPC has with other NPCs, you can do the following:



  • Call the NPC's AIRelationships() and search through the list

    yourself.



  • Call the NPC's AIRelationshipExists() to see if the NPC

    has a relationship with a specific NPC. If the NPC does, then a list of the

    relationship objects will be returned. Alternatively, the NPC may merely

    know the other NPC because they're from the same town, causing a number (in

    this case 2.0) to be returned.




  • The NPC's "home town" is automatically determined based on the NPC's

    creation room. If you wish to specify a specific map (or maps) then

    set pAIHomeMap, or write your

    own AIHomeMap() property.





Player database NPC relationships

As playes meet NPCs, a database about the NPC is automatically saved, allowing

the player to then view relationship graphs of the NPC. Most of the

relationship information is automatically stored, so you won't need to

worry about it. However, in the event that you do need to modify the

relationshipd database:





  • The database is stored in the player

    character's pKnowledgeRelationships property. You shouldn't

    need to access this directly.





  • You can find remembered relationship information specific to a NPC

    by calling the PC's KnowledgeRelationshipsGet().



  • To have the player's character remember relationship information,

    call KnowledgeRelationshipsSet(). You can store information

    about the fact that a relationship exists between two NPCs, the

    NPC's last like/trust for the player, and the NPC's last known location.





Automatic conversations between relationships

You can have automatic conversations happen between two NPCs given a specific

relationship. This is handy to have friends say hello, enemies sneer at one

another, etc.


To do this, your relationship object should have:







  • pAIConvScripts filled in with one or more conversation scripts

    that might occur.





Each cConvScript specific to the relationship needs to have:







  • pConvScriptAutoRelationship set to the relationship object.





  • pConvScriptNPCs set to 2, since relationships only exist

    between two NPCs.







  • pConvScriptAutoPriority should be lowered (below 50) so that

    other non-autmatic conversation scripts will take priority.





  • Other cConvScript properties as appropriate.





  • You may wish to modify the pConvScriptResource resource so

    that the relationship between the two NPCs is automatically exposed.

    That way, players that see the conversation happen will learn of

    the relationship.




22 Favors


Favors, the conversational-game equivalent of "loot".


Favors

Once players have gotten successfully friendly with NPCs (by giving them

gifts, doing quests for them, etc.), the PCs can ask the NPCs

for favors.


"Favors" are the equivalent to "loot" in the "kill 10 rats" game. It acts

as an incentive/reward, and also enables new gameplay. NPCs can grant

a variety of favors:







  • goodword - The NPC puts in a good word to one of its

    relations, providing the player with a "leg up" when interacting with

    the relation (NPC).





  • knowledge - The NPC will tell the player a tasty rumor,

    which the player can then use to get procede elsewhere in the game.







  • money - This is a bit mundane, but players can ask

    for a bit of extra cash.







  • object - The NPC gives the player an object, like a key (that

    provides access to another part of the world), and a letter of reference

    to another NPC.





  • reputation - The PC's reputation (such as how heroic the PC is)

    can be enhanced, affecting how other NPCs react to the PC.







  • skill - The NPC trains the PC in a new skill, such as a long-forgotten

    language that's needed to translate an important document.







  • speak - The NPC speaks some words of wisdom (such as hints) to

    the player.







  • story - The NPC tells the PC a special story.





  • custom - Whatever you wish to code.



To ask for a favor, the player types "Can you do me a favor?"

This automatically appears on the NPC's context menu.

After asking for a favor, the player will be presented with a list of favors,

and will be able to choose one.

After the favor is granted, the player won't be able to ask

for another favor for aroud half a day (of real time), and until the player

has rebuilt some of the like/trust lost by asking for the favor.



Coding

To get a NPC to provide favors, you need to do the following:







  1. Nothing! Some defaults already exist for:


  2. "goodword" favors are automatically created for all of the NPC's positive (non-enemy)

    relations so long as the player knows about these relations. (See KnowledgeRelationshipsGet()).

    A "money" favor is automatically created that defaults to giving pAIFavorMoney inflation-adjusted

    units.


    Some objects, based on pAIGivable, are automatically added by AIFavor().
  3. Having said that, you'll probably want to modify pAIFavor to provide some

    custom favors.



You may also wish to modify:







  • pAIFavorAsk is the question that the player asks the NPC to

    be granted a favor. This defaults to "Could you do me a favor?"







  • pAIFavorLike controls how many favors a NPC will grant, as well as how

    much the NPC must like/trust the player to grant them.







  • pAIFavorLikeCost affects how much the NPC's like/trust goes down

    after a favor has been asked.







  • pAIFavorQuestion is what the NPC speaks after the player

    has asked for a favor.







  • pAIFavorTime specified the number of real days that the player must

    wait before being able to ask the NPC for another favor.







  • AIFavorDoCustom() can be used to write your own custom favor

    behaviors, such as, "Can you quack like a duck for the next two hours?"





If you wish a NPC to grant a favor outside of the default context menu,

then you may wish to call AIFavorDo() and AIFavor().


Download 8.87 Mb.

Share with your friends:
1   ...   129   130   131   132   133   134   135   136   ...   151




The database is protected by copyright ©ininet.org 2024
send message

    Main page