NPC Mission

Discussion in 'Programming & Scripting' started by Limos, Mar 10, 2014.

  1. Limos

    Limos Premium User

    Joined:
    Jun 7, 2010
    Messages:
    10,017
    Likes Received:
    2,936
    Best Answers:
    7
    [​IMG][​IMG][​IMG] [​IMG][​IMG]

    [​IMG]
    NPC Mission Tutorial

    I made this tutorial about a year ago for someone who requested it. Back then I was unable to post it because of the anti double post system.
    I've decided to post it after all, maybe it's still useful for other people.





    NPC Lua scripts have several codes that are added to every NPC. This is connected to the npcsystem and it makes the NPC work as it should.
    You can find those basic things in your default.lua which is located in data/npc/scripts.
    When you are going to make a mission or just a conversation with an NPC, you need to add a couple of other things aswell.​

    In total it will look like the script below. So you start with this in every mission NPC Lua script (You don't have to edit anything here).
    Then what the NPC should do you add where now my quote line "-- here comes what the NPC should do" is.​

    Code (Text):
    1.  
    2. local keywordHandler = KeywordHandler:new()
    3. local npcHandler = NpcHandler:new(keywordHandler)
    4. NpcSystem.parseParameters(npcHandler)
    5. local talkState = {}
    6.  
    7. function onCreatureAppear(cid)       npcHandler:onCreatureAppear(cid)       end
    8. function onCreatureDisappear(cid)     npcHandler:onCreatureDisappear(cid)       end
    9. function onCreatureSay(cid, type, msg)     npcHandler:onCreatureSay(cid, type, msg)     end
    10. function onThink()         npcHandler:onThink()             end
    11.  
    12. function creatureSayCallback(cid, type, msg)
    13.      if not npcHandler:isFocused(cid) then
    14.          return false
    15.      end
    16.  
    17.      local talkUser = NPCHANDLER_CONVBEHAVIOR == CONVERSATION_DEFAULT and 0 or cid
    18.  
    19.      -- here comes what the NPC should do
    20.  
    21.      return true
    22. end
    23.  
    24. npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
    25. npcHandler:addModule(FocusModule:new())
    26.  




    Part 1 - Let an NPC respond to certain words
    http://otland.net/threads/npc-mission.211063/#post-2022374

    Part 2 - Using talkState
    http://otland.net/threads/npc-mission.211063/#post-2022375

    Part 3 - Using storage
    http://otland.net/threads/npc-mission.211063/#post-2022376

    Part 4 - Using different greetmessages
    http://otland.net/threads/npc-mission.211063/#post-2022377

    Part 5 - Different kinds of missions
    http://otland.net/threads/npc-mission.211063/#post-2022378
     
    Last edited: Mar 10, 2014
    Rorschach2, Wason, Synnical and 21 others like this.
  2. Limos

    Limos Premium User

    Joined:
    Jun 7, 2010
    Messages:
    10,017
    Likes Received:
    2,936
    Best Answers:
    7
    Part 1 – Let an NPC respond to certain words


    [​IMG]



    When it comes to the XML part of the NPC, you can just copy any NPC of your server and edit it.
    If you don't understand things.
    http://otland.net/f481/xml-how-make-npcs-93612/

    [​IMG]

    Here is an example.
    Code (Text):
    1.  
    2. <?xml version="1.0" encoding="UTF-8"?>
    3. <npc name="NPC" script="mission.lua" speed="250" walkinterval="2000" floorchange="0">
    4.    <health now="100" max="100"/>
    5.    <look type="130" head="95" body="114" legs="118" feet="4" addons="0"/>
    6.    <parameters>
    7.      <parameter key="message_greet" value="Hello |PLAYERNAME|, I'm lost on this {mountain}."/>
    8.    </parameters>
    9. </npc>
    10.  
    Note: if you use TFS 0.2 (Mystic Spirit), the link to the lua script will look like this.
    Code (Text):
    1. script="data/npc/scripts/mission.lua"
    The message_greet is what the NPC will say when you say hi, when you use { } the word in it will become dark blue, so a player knows what to say.



    Now to let the NPC respond on mountain in the Lua script, you can use msgcontains.
    Code (Text):
    1. if msgcontains(msg, "mountain") then
    With using msgcontains, the message of the player only have to contain that word, so it will also respond if someone says: cool mountain, hbjhmountaincfg or MouNTaIn.
    If the message has to be exactly 1 word you can do
    Code (Text):
    1. if msg == "mountain" then
    Then to make the NPC say something after you say mountain, you can use selfSay.
    Code (Text):
    1. selfSay("This mountain scares me, I've been hearing all kinds of strange noises down at the waterfall. Better don't go there, I think some dangerous monster lives there.", cid)
    Every if statement has to close with "end", so add that under it.



    In total it will look like this.
    Code (Text):
    1.  
    2. local keywordHandler = KeywordHandler:new()
    3. local npcHandler = NpcHandler:new(keywordHandler)
    4. NpcSystem.parseParameters(npcHandler)
    5. local talkState = {}
    6.  
    7. function onCreatureAppear(cid)       npcHandler:onCreatureAppear(cid)       end
    8. function onCreatureDisappear(cid)     npcHandler:onCreatureDisappear(cid)       end
    9. function onCreatureSay(cid, type, msg)     npcHandler:onCreatureSay(cid, type, msg)     end
    10. function onThink()         npcHandler:onThink()           end
    11.  
    12. function creatureSayCallback(cid, type, msg)
    13.      if not npcHandler:isFocused(cid) then
    14.          return false
    15.      end
    16.  
    17.      local talkUser = NPCHANDLER_CONVBEHAVIOR == CONVERSATION_DEFAULT and 0 or cid
    18.  
    19.      if msgcontains(msg, "mountain") then
    20.          selfSay("This mountain scares me, I've been hearing all kinds of strange noises down at the waterfall. Better don't go there, I think some dangerous monster lives there.", cid)
    21.      end
    22.      return true
    23. end
    24.  
    25. npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
    26. npcHandler:addModule(FocusModule:new())
    27.  
    Ingame example.
    [​IMG]




    To let the NPC respond the same way with different words, you can use or.
    Code (Text):
    1. if msgcontains(msg, "mountain") or msgcontains(msg, "lost") then

    Or to let the NPC respond different on different words, you could do it like this.
    Code (Text):
    1.  
    2. if msgcontains(msg, "mountain") then
    3.      selfSay("This mountain scares me, I've been hearing all kinds of strange noises down at the waterfall. Better don't go there, I think some dangerous monster lives there.", cid)
    4. end
    5. if msgcontains(msg, "lost") then
    6.      selfSay("It's a really big mountain, be careful where you walk else you might get lost too.", cid)
    7. end
    8.  
    But since there is no reason to repeat if/end like this and if people say mountain and lost, the NPC will respond 2x, a more proper way would be using elseif.
    Code (Text):
    1.  
    2. if msgcontains(msg, "mountain") then
    3.      selfSay("This mountain scares me, I've been hearing all kinds of strange noises down at the waterfall. Better don't go there, I think some dangerous monster lives there.", cid)
    4. elseif msgcontains(msg, "lost") then
    5.      selfSay("It's a really big mountain, be careful where you walk else you might get lost too.", cid)
    6. end
    7.  
    Using elseif means, if the line that starts with if is not true (so in this case if a player doesn't say mountain), it will look if the line with elseif is true (if the player says lost).
    Now if you want to add more words, you can just add more lines with elseif or else if you want a respond from the NPC if all the lines above it are not true (order of words doesn't matter).
    Code (Text):
    1.  
    2. if msgcontains(msg, "mountain")  then
    3.      selfSay("This mountain scares me, I've been hearing all kinds of strange noises down at the waterfall. Better don't go there, I think some dangerous monster lives there.", cid)
    4. elseif msgcontains(msg, "lost") then -- if the player doesn't say mountain, it will look if the player says lost
    5.      selfSay("It's a really big mountain, be careful where you walk else you might get lost too.", cid)
    6. elseif msgcontains(msg, "help") then -- if the player doesn't say lost, it will look if the player says help
    7.      selfSay("I want to try to find to way back myself for next time, but thanks anyway.", cid)
    8. else -- if all lines are not true, so the player doesn't say mountain, lost or help, it will say 'What do you mean?'.
    9.      selfSay("What do you mean?", cid)
    10. end
    11.  
    Important thing to remember is that elseif and else are a part of an if statement. Only an if statement needs an end and the elseif and else parts should always be between an if statement and end.
    You also can't use elseif under else, because it will always do the else part if the lines above it are not true.
    If you want more information about if, elseif, else and end.
    http://otland.net/f481/scripting-guide-74030/#post758243



    In total
    Code (Text):
    1.  
    2. local keywordHandler = KeywordHandler:new()
    3. local npcHandler = NpcHandler:new(keywordHandler)
    4. NpcSystem.parseParameters(npcHandler)
    5. local talkState = {}
    6.  
    7. function onCreatureAppear(cid)       npcHandler:onCreatureAppear(cid)       end
    8. function onCreatureDisappear(cid)     npcHandler:onCreatureDisappear(cid)       end
    9. function onCreatureSay(cid, type, msg)     npcHandler:onCreatureSay(cid, type, msg)     end
    10. function onThink()         npcHandler:onThink()           end
    11.  
    12. function creatureSayCallback(cid, type, msg)
    13.      if not npcHandler:isFocused(cid) then
    14.          return false
    15.      end
    16.  
    17.      local talkUser = NPCHANDLER_CONVBEHAVIOR == CONVERSATION_DEFAULT and 0 or cid
    18.  
    19.      if msgcontains(msg, "mountain") then
    20.          selfSay("This mountain scares me, I've been hearing all kinds of strange noises down at the waterfall. Better don't go there, I think some dangerous monster lives there.", cid)
    21.      elseif msgcontains(msg, "lost") then
    22.          selfSay("It's a really big mountain, be careful where you walk else you might get lost too.", cid)
    23.      elseif msgcontains(msg, "help") then
    24.          selfSay("I want to try to find to way back myself for next time, but thanks anyway.", cid)
    25.      else
    26.          selfSay("What do you mean?", cid)
    27.      end
    28.      return true
    29. end
    30.  
    31. npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
    32. npcHandler:addModule(FocusModule:new())
    33.  
    Ingame example
    [​IMG]
     
    Last edited: Mar 11, 2014
  3. Limos

    Limos Premium User

    Joined:
    Jun 7, 2010
    Messages:
    10,017
    Likes Received:
    2,936
    Best Answers:
    7
    Part 2 - Using talkState
    Note: talkState and talkUser are choosen words, you can change this in local talkState and local talkUser.
    So there is a chance people call it different in their scripts, but it still works the same.

    [​IMG]




    If you want an NPC to only respond on words, when first saying other words, then you have to use talkState.
    For example you don't want people to skip a few steps and start with yes without even getting a question from the NPC.
    Unlike storage, talkState doesn't get saved and will be gone with restart or reload, so only use it to go to the next message.


    To set the talkState (You can use different numbers for different talkstates).
    Code (Text):
    1. talkState[talkUser] = 1
    To let the NPC only respond when talkState is 1 after saying yes.
    Code (Text):
    1. if msgcontains(msg, "yes") and talkState[talkUser] == 1 then
    Now you can do it like this.
    Code (Text):
    1.  
    2. if msgcontains(msg, "mountain") then
    3.      selfSay("Do you want to know more about this mountain.", cid)
    4.      talkState[talkUser] = 1
    5. elseif msgcontains(msg, "yes") and talkState[talkUser] == 1 then
    6.      selfSay("This mountain is a real legend. It is one of the most beautiful but at the same time most dangerous mountains I know.", cid)
    7.      talkState[talkUser] = 0
    8. end
    9.  
    Now someone can't say yes, before saying mountain first, because the talkstate needs to be set to 1 first and this happens when saying mountain.
    It is also good to reset the talkState by setting it to 0 after saying yes, so a person can't say yes again without getting the question first.
    This is extra important if the npc also does something else after yes besides talking, like removing/adding items or setting storage.
    Besides yes, you can also add a part if a player says no, also set talkstate to 0 there, since it's also an answer to a question and people shouldn't be able to say this without getting a question first.


    In total
    Code (Text):
    1.  
    2. local keywordHandler = KeywordHandler:new()
    3. local npcHandler = NpcHandler:new(keywordHandler)
    4. NpcSystem.parseParameters(npcHandler)
    5. local talkState = {}
    6.  
    7. function onCreatureAppear(cid)       npcHandler:onCreatureAppear(cid)       end
    8. function onCreatureDisappear(cid)     npcHandler:onCreatureDisappear(cid)       end
    9. function onCreatureSay(cid, type, msg)     npcHandler:onCreatureSay(cid, type, msg)     end
    10. function onThink()         npcHandler:onThink()           end
    11.  
    12. function creatureSayCallback(cid, type, msg)
    13.      if(not npcHandler:isFocused(cid)) then
    14.          return false
    15.      end
    16.  
    17.      local talkUser = NPCHANDLER_CONVBEHAVIOR == CONVERSATION_DEFAULT and 0 or cid
    18.  
    19.      if msgcontains(msg, "mountain") then
    20.          selfSay("Do you want to know more about this mountain?", cid)
    21.          talkState[talkUser] = 1
    22.      elseif msgcontains(msg, "yes") and talkState[talkUser] == 1 then
    23.          selfSay("This mountain is a real legend. It is one of the most beautiful but at the same time most dangerous mountains I know.", cid)
    24.          talkState[talkUser] = 0
    25.      elseif msgcontains(msg, "no") and talkState[talkUser] == 1 then
    26.          selfSay("Ok then.", cid)
    27.          talkState[talkUser] = 0
    28.      end
    29.      return true
    30. end
    31.  
    32. npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
    33. npcHandler:addModule(FocusModule:new())
    34.  
    Ingame example
    [​IMG]



    Using different talkState numbers (I skipped the "In total" here, but you still have to add the rest)

    Sometimes it can be useful to use more talkState numbers, for example when you want the player to say more things before saying yes.
    Code (Text):
    1.  
    2. if msgcontains(msg, "mountain") then
    3.      selfSay("I think there lives some dangerous {monster} down at the waterfall.", cid)
    4.      talkState[talkUser] = 1
    5. elseif msgcontains(msg, "monster") and talkState[talkUser] == 1 then -- after saying mountain talkState was set to 1
    6.      selfSay("This monster is known for killing everyone who passes by, do you think of going there?", cid)
    7.      talkState[talkUser] = 2
    8. elseif msgcontains(msg, "yes") and talkState[talkUser] == 2 then -- after saying monster the talkState was set to 2
    9.      selfSay('Well, I warned you but good luck.', cid)
    10.      talkState[talkUser] = 0
    11. end
    12.  
    Ingame example
    [​IMG]


    Or when you want the NPC to respond different on different words.
    Code (Text):
    1.  
    2. if msgcontains(msg, "mountain") then
    3.      selfSay("I think there lives some dangerous {monster} down at the waterfall.", cid)
    4.      talkState[talkUser] = 1
    5. elseif msgcontains(msg, "monster") and talkState[talkUser] == 1 then -- after saying mountain talkState was set to 1
    6.      selfSay("There are rumors this monster kills people randomly who pass by, it's really evil.", cid)
    7.      talkState[talkUser] = 0
    8. elseif msgcontains(msg, "lost") then
    9.      selfSay("There seems to be a {way} to find the way back really fast, but I'm to affrain to go there.", cid)
    10.      talkState[talkUser] = 2
    11. elseif msgcontains(msg, "way") and talkState[talkUser] == 2 then -- after saying lost the talkState was set to 2
    12.      selfSay("If you go to the east, you will find a bridge, use that to go the the other side of the mountain, then follow the path untill you see a cave, go in that cave and you will find the way to the city.", cid)
    13.      talkState[talkUser] = 0
    14. end
    15.  
    Ingame example
    [​IMG]

    Or when you want the NPC to respond different on the same word.
    Code (Text):
    1.  
    2. if msgcontains(msg, "mountain") then
    3.      selfSay("Do you want to know more about this mountain?", cid)
    4.      talkState[talkUser] = 1
    5. elseif msgcontains(msg, "lost") then
    6.      selfSay("Do you want to know how I got lost here?", cid)
    7.      talkState[talkUser] = 2
    8. elseif msgcontains(msg, "yes") then
    9.      if talkState[talkUser] == 1 then -- after saying mountain talkState was set to 1
    10.          selfSay("This mountain is a real legend. It is one of the most beautiful but at the same time most dangerous mountains I know.", cid)
    11.      elseif talkState[talkUser] == 2 then -- after saying lost the talkState was set to 2
    12.          selfSay("Well, I did bring a map, but I lost it when I run away after I heard the monster near the waterfall.", cid)
    13.      end
    14.      talkState[talkUser] = 0 -- Here is the talkState set outside any if, elseif or else part inside the yes part, so it will always be set to 0 after saying yes.
    15. end
    16.  
    Ingame example
    [​IMG]
     
    Last edited: Mar 11, 2014
  4. Limos

    Limos Premium User

    Joined:
    Jun 7, 2010
    Messages:
    10,017
    Likes Received:
    2,936
    Best Answers:
    7
    Part 3 - Using Storage

    Note: There are different functions for this, but I will use getPlayerStorageValue and setPlayerStorageValue here because it works in both Crying Damson (Because of the 100-compat.lua) and Mystic Spirit.



    If you want to make an NPC that gives a mission, you should always use storage, because the NPC has to know if a player already accepted the mission or completed it.
    Storage gets saved in the database, so this stays forever (unless you change this with a script or in the database).

    To set storage.
    Code (Text):
    1. setPlayerStorageValue(cid, 5000, 1)
    To check the value of storage.
    Code (Text):
    1. if getPlayerStorageValue(cid, 5000) == 1 then
    The 5000 here is the storage, the 1 here is the value of the storage (both the storage number and value are as example, use a storage number that is not used in an other script).
    When you set storage, you only have to change the value.

    Also something good to use.
    Code (Text):
    1. local storage = 5000
    This means if you use the word storage, it means 5000, so if you want to change the 5000 later and you use it alot in your script, you only have to change it 1x.

    When using storage, after every msgcontains line you have to check for storagevalue, because it should respond different after accepting or completing the mission.
    First time people go the the NPC, they don't have the storage yet, storagevalue -1 means the player doesn't have the storage so you can start with that.
    Code (Text):
    1.  
    2. if getPlayerStorageValue(cid, storage) == -1 then
    3.      selfSay('My daughter lost her doll, I saw a monster taking it that went in this cave, but I\'m to affraid to go there, can you get it for me?.', cid)
    4. end
    5.  

    Then you set the storagevalue to 1 after the player accepted te mission.
    Code (Text):
    1. setPlayerStorageValue(cid, storage, 1)

    Now the player won't get the 'My daughter lost her doll..' message when saying mission again, because the storagevalue is then not -1 anymore.
    Code (Text):
    1.  
    2. local storage = 5000
    3. if msgcontains(msg, "mission") then
    4.      if getPlayerStorageValue(cid, storage) == -1 then
    5.          selfSay("My daughter lost her doll, I saw a monster taking it that went in this cave, but I'm to affraid to go there, can you get it for me?", cid)
    6.          talkState[talkUser] = 1
    7.      end
    8. elseif msgcontains(msg, "yes") and talkState[talkUser] == 1 then
    9.      if getPlayerStorageValue(cid, storage) == -1 then
    10.          selfSay("Thanks alot, come back when you have it.", cid)
    11.          setPlayerStorageValue(cid, storage, 1)
    12.      end
    13.      talkState[talkUser] = 0
    14. end

    The storagevalue after accepting the mission was set to 1, so now you add a response if the storagevalue is 1 for when the player comes back.
    When the players says yes when the storagevalue is 1, you add what the NPC should do, in this case removing the item (doll) and add rewards.
    Then you can set the storage value to 2, so when the player says mission again, it won't respond on storagevalue 1 anymore.
    Code (Text):
    1.  
    2. local storage = 5000
    3. if msgcontains(msg, "mission") then
    4.      if getPlayerStorageValue(cid, storage) == -1 then -- When the player comes to the NPC for the first time or didn't accepted the mission yet.
    5.          selfSay("My daughter lost her doll, I saw a monster taking it that went in this cave, but I'm to affraid to go there, can you get it for me?", cid)
    6.          talkState[talkUser] = 1
    7.      elseif getPlayerStorageValue(cid, storage) == 1 then -- When the player comes back after accepting the mission.
    8.          selfSay("Did you find the doll?", cid)
    9.          talkState[talkUser] = 1
    10.      else
    11.          selfSay("Thanks again for finding the doll.", cid) -- The NPC will say this when the storage is not -1 or 1, so this means the player completed the mission.
    12.      end
    13. elseif msgcontains(msg, "yes") and talkState[talkUser] == 1 then
    14.      if getPlayerStorageValue(cid, storage) == -1 then
    15.          selfSay("Thanks alot, come back when you have it.", cid)
    16.          setPlayerStorageValue(cid, storage, 1)
    17.      else -- You can use else here because it's only possible to say yes here with storagevalue -1 and 1, so if it's not -1, it has to be 1.
    18.          if doPlayerRemoveItem(cid, 2110, 1) then -- You can use this to remove an item, by using if it will also look if it's possible, so it's not true if the player doesn't have the item.
    19.              selfSay("That's great, my daughter will be really happy, thanks.", cid)
    20.              doPlayerAddItem(cid, 2160, 3) -- You can add for example items as reward.
    21.              doPlayerAddExp(cid, 5000) -- or experience
    22.              setPlayerStorageValue(cid, 3843, 1) -- or storage, for example to open a quest door (I will not add this to the in total part, since most of the time you don't need it).
    23.              setPlayerStorageValue(cid, storage, 2) -- Set storage to value 2, so when the value is not -1 or 1, it will say 'Thanks again for finding the doll.'.
    24.          else -- if removing the item wasn't possible (so this means the player doesn't have it), it will do the part after else.
    25.              selfSay("You don't have it.", cid)
    26.          end
    27.      end
    28.      talkState[talkUser] = 0
    29. end


    In total
    Code (Text):
    1. local keywordHandler = KeywordHandler:new()
    2. local npcHandler = NpcHandler:new(keywordHandler)
    3. NpcSystem.parseParameters(npcHandler)
    4. local talkState = {}
    5.  
    6. function onCreatureAppear(cid)       npcHandler:onCreatureAppear(cid)       end
    7. function onCreatureDisappear(cid)     npcHandler:onCreatureDisappear(cid)       end
    8. function onCreatureSay(cid, type, msg)     npcHandler:onCreatureSay(cid, type, msg)     end
    9. function onThink()         npcHandler:onThink()           end
    10.  
    11. local storage = 5000
    12.  
    13. function creatureSayCallback(cid, type, msg)
    14.      if not npcHandler:isFocused(cid) then
    15.          return false
    16.      end
    17.  
    18.      local talkUser = NPCHANDLER_CONVBEHAVIOR == CONVERSATION_DEFAULT and 0 or cid
    19.  
    20.      if msgcontains(msg, "mission") then
    21.          if getPlayerStorageValue(cid, storage) == -1 then
    22.              selfSay("My daughter lost her doll, I saw a monster taking it that went in this cave, but I\'m to affraid to go there, can you get it for me?", cid)
    23.              talkState[talkUser] = 1
    24.          elseif getPlayerStorageValue(cid, storage) == 1 then
    25.              selfSay("Did you find the doll?", cid)
    26.              talkState[talkUser] = 1
    27.          else
    28.              selfSay("Thanks again for finding the doll.", cid)
    29.          end
    30.      elseif msgcontains(msg, "yes") and talkState[talkUser] == 1 then
    31.          if getPlayerStorageValue(cid, storage) == -1 then
    32.              selfSay("Thanks alot, come back when you have it.", cid)
    33.              setPlayerStorageValue(cid, storage, 1)
    34.          else
    35.              if(doPlayerRemoveItem(cid, 2110, 1)) then
    36.                  selfSay("That's great, my daughter will be really happy, thanks.", cid)
    37.                  doPlayerAddItem(cid, 2160, 3)
    38.                  doPlayerAddExp(cid, 5000)
    39.                  setPlayerStorageValue(cid, storage, 2)
    40.              else
    41.                  selfSay("You don't have it.", cid)
    42.              end
    43.          end
    44.          talkState[talkUser] = 0
    45.      elseif msgcontains(msg, "no") and talkState[talkUser] == 1 then
    46.          selfSay("Ok then.", cid)
    47.          talkState[talkUser] = 0
    48.      end
    49.      return true
    50. end
    51.  
    52. npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
    53. npcHandler:addModule(FocusModule:new())
    You can add local storage = 5000 above function creatureSayCallback(cid, type, msg), because it doesn't have any of the functions parameters (cid, type, msg), so it doesn't have to be in the function.

    Ingame example
    [​IMG]
    [​IMG]
     
    Last edited: Sep 4, 2014
  5. Limos

    Limos Premium User

    Joined:
    Jun 7, 2010
    Messages:
    10,017
    Likes Received:
    2,936
    Best Answers:
    7
    Part 4 - Using different greetmessages



    When you make an NPC the way I just did, it will also say the same greet message. The reason for this is this line at the bottem of the Lua script.
    Code (Text):
    1. npcHandler:addModule(FocusModule:new())
    This line makes the NPC repond on hi and bye with saying something and adding or releasing focus.
    So if you want a different greet message based on storage, you can delete this line and add responds on hi and bye in the Lua script.
    The NPC should respond to hi when it's not focused, so you can use this part.
    Code (Text):
    1.  
    2. if not npcHandler:isFocused(cid) then
    3.      return false
    4. end
    5.  

    When talking to the NPC when it's not focused, it's good to use msg == 'hi' instead of msgcontains(msg, 'hi'), because with msgcontains the NPC will also respond if the word 'hi' is in a message.
    For example if someone is talking about history when standing close to the NPC, it should not respond.
    Then, same as after the msgcontains(msg, "misson"), you can check for storage.
    Code (Text):
    1.  
    2. if not npcHandler:isFocused(cid) then
    3.      if msg == "hi" or msg == "hello" then
    4.          if getPlayerStorageValue(cid, storage) == -1 then
    5.              selfSay("My daughter lost her doll, I saw a monster taking it that went in this cave, but I'm to affraid to go there, can you get it for me?", cid)
    6.              talkState[talkUser] = 1
    7.          elseif getPlayerStorageValue(cid, storage) == 1 then
    8.              selfSay("Did you find the doll?", cid)
    9.              talkState[talkUser] = 1
    10.          else
    11.              selfSay("Thanks again for finding the doll.", cid)
    12.          end
    13.          npcHandler:addFocus(cid)
    14.      else
    15.          return false
    16.      end
    17. end
    As you can see, you also have to add focus yourself, because now you don't have this anymore: npcHandler:addModule(FocusModule:new()).
    For the rest you can use the yes part the same way, except from now you start yes with if instead of elseif, since there is no msgcontains line above.


    In total
    Code (Text):
    1.  
    2. local keywordHandler = KeywordHandler:new()
    3. local npcHandler = NpcHandler:new(keywordHandler)
    4. NpcSystem.parseParameters(npcHandler)
    5. local talkState = {}
    6.  
    7. function onCreatureAppear(cid)       npcHandler:onCreatureAppear(cid)       end
    8. function onCreatureDisappear(cid)     npcHandler:onCreatureDisappear(cid)       end
    9. function onCreatureSay(cid, type, msg)     npcHandler:onCreatureSay(cid, type, msg)     end
    10. function onThink()         npcHandler:onThink()           end
    11. local storage = 5001
    12. function creatureSayCallback(cid, type, msg)
    13.  
    14.      local talkUser = NPCHANDLER_CONVBEHAVIOR == CONVERSATION_DEFAULT and 0 or cid
    15.  
    16.      if not npcHandler:isFocused(cid) then
    17.          if msg == "hi" or msg == "hello" then
    18.              if getPlayerStorageValue(cid, storage) == -1 then
    19.                  selfSay("My daughter lost her doll, I saw a monster taking it that went in this cave, but I'm to affraid to go there, can you get it for me?", cid)
    20.                  talkState[talkUser] = 1
    21.              elseif getPlayerStorageValue(cid, storage) == 1 then
    22.                  selfSay("Did you find the doll?", cid)
    23.                  talkState[talkUser] = 1
    24.              else
    25.                  selfSay("Thanks again for finding the doll.", cid)
    26.              end
    27.              npcHandler:addFocus(cid)
    28.          else
    29.              return false
    30.          end
    31.      end
    32.  
    33.      if msgcontains(msg, "yes") and talkState[talkUser] == 1 then
    34.          if getPlayerStorageValue(cid, storage) == -1 then
    35.              selfSay("Thanks alot, come back when you have it.", cid)
    36.              setPlayerStorageValue(cid, storage, 1)
    37.          else
    38.              if doPlayerRemoveItem(cid, 2110, 1) then
    39.                  selfSay("That's great, my daughter will be really happy, thanks.", cid)
    40.                  doPlayerAddItem(cid, 2160, 3)
    41.                  doPlayerAddExp(cid, 5000)
    42.                  setPlayerStorageValue(cid, storage, 2)
    43.              else
    44.                  selfSay("You don't have it.", cid)
    45.              end
    46.          end
    47.          talkState[talkUser] = 0
    48.      elseif msgcontains(msg, "no") and talkState[talkUser] == 1 then
    49.          selfSay("Ok then.", cid)
    50.          talkState[talkUser] = 0
    51.      elseif msgcontains(msg, "bye") then
    52.          selfSay("Bye.", cid)
    53.          npcHandler:releaseFocus(cid)
    54.      end
    55.      return true
    56. end
    57. npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
    58.  
    Same as you had to add focus on hi, you also have to release focus on bye now.
    Also make sure that local talkUser is always above where you use it, because the script reads from the top to the bottom, else it won't know what talkUser is.
    Ingame example
    [​IMG]
     
    Last edited: Mar 10, 2014
    LordVissie, Exoltes, deryzeen and 8 others like this.
  6. Limos

    Limos Premium User

    Joined:
    Jun 7, 2010
    Messages:
    10,017
    Likes Received:
    2,936
    Best Answers:
    7
    Part 5 - Different kinds of missions



    Monster mission

    [​IMG]

    When you want a mission that's not about items, for example with killing monsters, it's not so much different than an items mission.
    Only now instead of checking if a player has an item, you check if a player killed monsters.

    There are functions to find out if a player has items, but there is no function to find out if a player has killed monsters, that's why you have to add storagevalues when killing the monsters, so you can check for that storagevalue in the NPC.
    This can't be done in an NPC, since an NPC has no influence on what you kill, so you have to make an extra script in creaturescripts with function onKill.
    For example you want a mission where someone has to kill 5 dragons and 3 dragons lords (if you want more or less monsters, you can just add or remove lines in local config).

    Code (Text):
    1.  
    2. local config = {
    3.      ['dragon'] = {amount = 5, storage = 19000, startstorage = 5002, startvalue = 1},
    4.      ['dragon lord'] = {amount = 3, storage = 19001, startstorage = 5002, startvalue = 1}
    5. }
    6. function onKill(cid, target)
    7.      local monster = config[getCreatureName(target):lower()]
    8.      if isPlayer(target) or not monster or isSummon(target) then
    9.          return true
    10.      end
    11.  
    12.      if (getPlayerStorageValue(cid, monster.storage)+1) < monster.amount and getPlayerStorageValue(cid, monster.startstorage) >= monster.startvalue then
    13.          setPlayerStorageValue(cid, monster.storage, getPlayerStorageValue(cid, monster.storage) + 1)
    14.          doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_ORANGE, 'Task message: '..(getPlayerStorageValue(cid, monster.storage)+1)..' of '..monster.amount..' '..getCreatureName(target)..'s killed.')
    15.      end
    16.      if (getPlayerStorageValue(cid, monster.storage)+1) == monster.amount then
    17.          doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, 'Congratulations, you have killed '..(getPlayerStorageValue(cid, monster.storage)+1)..' '..getCreatureName(target)..'s and completed the '..getCreatureName(target)..'s mission.')
    18.          setPlayerStorageValue(cid, monster.storage, getPlayerStorageValue(cid, monster.storage) + 1)
    19.      end
    20.      return true
    21. end
    The startstorage is the storage from the NPC with value 1, so this means the script will start counting after accepting the mission.
    The other storage, named storage, is to count the monsters.


    Now in the NPC, instead of checking if a player has an item, you check for the value of the storage you use used to count the monsters.
    Code (Text):
    1.  
    2. local keywordHandler = KeywordHandler:new()
    3. local npcHandler = NpcHandler:new(keywordHandler)
    4. NpcSystem.parseParameters(npcHandler)
    5. local talkState = {}
    6.  
    7. function onCreatureAppear(cid)       npcHandler:onCreatureAppear(cid)       end
    8. function onCreatureDisappear(cid)     npcHandler:onCreatureDisappear(cid)       end
    9. function onCreatureSay(cid, type, msg)     npcHandler:onCreatureSay(cid, type, msg)     end
    10. function onThink()         npcHandler:onThink()           end
    11.  
    12. local storage = 5002
    13.  
    14. function creatureSayCallback(cid, type, msg)
    15.      if not npcHandler:isFocused(cid) then
    16.          return false
    17.      end
    18.      local talkUser = NPCHANDLER_CONVBEHAVIOR == CONVERSATION_DEFAULT and 0 or cid
    19.  
    20.      if msgcontains(msg, "mission") then
    21.          if getPlayerStorageValue(cid, storage) == -1 then
    22.              selfSay("I have a mission for you to kill 5 dragons and 3 dragon lords, do you accept?", cid)
    23.              talkState[talkUser] = 1
    24.          elseif getPlayerStorageValue(cid, storage) == 1 then
    25.              selfSay("Did you kill 5 dragons and 3 dragon lords?", cid)
    26.              talkState[talkUser] = 1
    27.          else
    28.              selfSay("You already did the mission.", cid)
    29.          end
    30.      elseif msgcontains(msg, "yes") and talkState[talkUser] == 1 then
    31.          if getPlayerStorageValue(cid, storage) == -1 then
    32.              selfSay("Good, come back when you killed them.", cid)
    33.              setPlayerStorageValue(cid, storage, 1)
    34.          else
    35.              if getPlayerStorageValue(cid, 19000) == 5 and getPlayerStorageValue(cid, 19001) == 3 then
    36.                  selfSay("Good job, here is your reward.", cid)
    37.                  doPlayerAddItem(cid, 2160, 5)
    38.                  doPlayerAddExp(cid, 50000)
    39.                  setPlayerStorageValue(cid, storage, 2)
    40.              else
    41.                  selfSay("You didn't kill them all.", cid)
    42.              end
    43.          end
    44.          talkState[talkUser] = 0
    45.      elseif msgcontains(msg, "no") and talkState[talkUser] == 1 then
    46.          selfSay("Ok then.", cid)
    47.          talkState[talkUser] = 0
    48.      end
    49.      return true
    50. end
    51.  
    52. npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
    53. npcHandler:addModule(FocusModule:new())
     
    Last edited: Jul 29, 2014
  7. Limos

    Limos Premium User

    Joined:
    Jun 7, 2010
    Messages:
    10,017
    Likes Received:
    2,936
    Best Answers:
    7
    More NPCs, 1 mission

    When you are going to make more NPCs for 1 mission, it's basicly the same as when making 1 NPC, only now you use the same storage for all the NPCs from that mission.
    Every time when the NPC sends the player to an other NPC, you set the storagevalue + 1 and check for that storagevalue in the other NPC.
    I will do 2 NPCs as example.


    NPC1
    Code (Text):
    1.  
    2. local keywordHandler = KeywordHandler:new()
    3. local npcHandler = NpcHandler:new(keywordHandler)
    4. NpcSystem.parseParameters(npcHandler)
    5. local talkState = {}
    6.  
    7. function onCreatureAppear(cid)       npcHandler:onCreatureAppear(cid)       end
    8. function onCreatureDisappear(cid)     npcHandler:onCreatureDisappear(cid)       end
    9. function onCreatureSay(cid, type, msg)     npcHandler:onCreatureSay(cid, type, msg)     end
    10. function onThink()         npcHandler:onThink()           end
    11.  
    12. local storage = 5003
    13.  
    14. function creatureSayCallback(cid, type, msg)
    15.      if not npcHandler:isFocused(cid) then
    16.          return false
    17.      end
    18.  
    19.      local talkUser = NPCHANDLER_CONVBEHAVIOR == CONVERSATION_DEFAULT and 0 or cid
    20.  
    21.      if msgcontains(msg, "mission") then
    22.          if getPlayerStorageValue(cid, storage) == -1 then -- When the player comes for the first time to NPC1
    23.              selfSay("I'm feeling really sick, there is an old man called NPC2 who lives close the the west gate of this city. He makes a potion I need, but I'm to sick to go there, can you get it for me?", cid)
    24.              talkState[talkUser] = 1
    25.          elseif getPlayerStorageValue(cid, storage) == 1 then -- In NPC2 the storagevalue is set to 2, so this means the player didn't talk with NPC2 yet.
    26.              selfSay("You didn't find him? He lives in the small wooden house with the red roof.", cid)
    27.          elseif getPlayerStorageValue(cid, storage) == 2 then -- In NPC2 the storagevalue is set to 2, so this means the player talked with NPC2.
    28.              selfSay("Do you have the potion?", cid)
    29.              talkState[talkUser] = 1
    30.          else
    31.              selfSay("Thanks again for the potion, I'm feeling alot better.", cid) -- The NPC will say this when the storage is not -1, 1 or 2, so this means the player completed the mission.
    32.          end
    33.      elseif msgcontains(msg, "yes") and talkState[talkUser] == 1 then
    34.          if getPlayerStorageValue(cid, storage) == -1 then
    35.              selfSay("Thank you, tell him I need a {sacred berry potion}.", cid)
    36.              setPlayerStorageValue(cid, storage, 1)  -- Now the storagevalue here is set to 1, with storage value 1, the player can go to NPC2.
    37.          else -- You can use else here because it's only possible to say yes here with storagevalue -1 and 2, so if it's not -1, it has to be 2.
    38.              if doPlayerRemoveItem(cid, 10089, 1) then -- If the player has the potion
    39.                  selfSay("Thanks alot.", cid)
    40.                  doPlayerAddItem(cid, 2160, 3) -- You can add here rewards again
    41.                  doPlayerAddExp(cid, 5000)
    42.                  setPlayerStorageValue(cid, storage, 3) -- Now the storagevalue is 3, so the player won't get the responds from storagevalue 2 anymore
    43.              else
    44.                  selfSay("You don't have it.", cid)
    45.              end
    46.          end
    47.          talkState[talkUser] = 0
    48.      elseif msgcontains(msg, "no") and talkState[talkUser] == 1 then
    49.          selfSay("Ok then.", cid)
    50.          talkState[talkUser] = 0
    51.      end
    52.      return true
    53. end
    54.  
    55. npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
    56. npcHandler:addModule(FocusModule:new())
    NPC2
    Code (Text):
    1.  local keywordHandler = KeywordHandler:new()
    2. local npcHandler = NpcHandler:new(keywordHandler)
    3. NpcSystem.parseParameters(npcHandler)
    4. local talkState = {}
    5.  
    6. function onCreatureAppear(cid)       npcHandler:onCreatureAppear(cid)       end
    7. function onCreatureDisappear(cid)     npcHandler:onCreatureDisappear(cid)       end
    8. function onCreatureSay(cid, type, msg)     npcHandler:onCreatureSay(cid, type, msg)     end
    9. function onThink()         npcHandler:onThink()           end
    10.  
    11. local storage = 5003
    12.  
    13. function creatureSayCallback(cid, type, msg)
    14.      if not npcHandler:isFocused(cid) then
    15.          return false
    16.      end
    17.  
    18.      local talkUser = NPCHANDLER_CONVBEHAVIOR == CONVERSATION_DEFAULT and 0 or cid
    19.  
    20.      if msgcontains(msg, "sacred berry potion") then
    21.          if getPlayerStorageValue(cid, storage) == -1 then -- When the player didn't go to NPC1 first, because NPC1 sets the storagevalue to 1.
    22.              selfSay("What?", cid)
    23.          elseif getPlayerStorageValue(cid, storage) == 1 then -- In NPC1 the storagevalue is set to 1, so this means the player accepted that mission.
    24.              selfSay("Ah, I just heard about someone who needed this potion, It's to help NPC1 right?", cid)
    25.              talkState[talkUser] = 1
    26.          elseif getPlayerStorageValue(cid, storage) == 2 then -- In NPC2 the storagevalue was set to 2, when going back to NPC1 it will be set to 3, so storagevalue 2 means the player didn't go back to NPC1 yet.
    27.              selfSay("Go give the potion, he will feel alot better after drinking it.", cid)
    28.          else -- if the storagevalue is not -1, 1 or 2, so someone says 'sacred berry potion' again after giving the potion to NPC1.
    29.              selfSay("You gave NPC1 the potion? Thats great, he will feel alot better soon.", cid)
    30.          end
    31.      elseif msgcontains(msg, "yes") and talkState[talkUser] == 1 then
    32.          -- You don't need to check for storagevalue here, because there is only 1 question you can answer with yes.
    33.          selfSay("That's very kind of you, here it is.", cid)
    34.          doPlayerAddItem(cid, 10089, 1) -- to add the potion
    35.          setPlayerStorageValue(cid, storage, 2) -- set storagevalue to 2 so NPC1 knows the player talked with NPC2
    36.          talkState[talkUser] = 0
    37.      end
    38.      return true
    39. end
    40.  
    41. npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
    42. npcHandler:addModule(FocusModule:new())
    43.  
     
    Last edited: Mar 10, 2014
    Tony32, LordVissie, Exoltes and 10 others like this.
  8. Cornex

    Cornex Web Developer Staff Member Global Moderator

    Joined:
    Jun 15, 2008
    Messages:
    3,440
    Likes Received:
    1,147
    Best Answers:
    4
    Great work Limos, thanks
     
  9. Colors

    Colors

    Joined:
    Mar 22, 2013
    Messages:
    910
    Likes Received:
    251
    Best Answers:
    0
    This tutorial is just amazing, thanks!
     
  10. filipus

    filipus Member

    Joined:
    Dec 31, 2010
    Messages:
    229
    Likes Received:
    10
    Best Answers:
    0
    Wow, Wow ,Wow. Very very good. Thank you!
     
  11. Jaed Le Raep

    Jaed Le Raep ★Gaeming★

    Joined:
    Sep 3, 2007
    Messages:
    994
    Likes Received:
    196
    Best Answers:
    0
  12. Peroxide

    Peroxide Suh dud

    Joined:
    Aug 22, 2008
    Messages:
    10,325
    Likes Received:
    1,875
    Best Answers:
    0
    @Jaed Le Raep @Limos
    Stickied, this should really benefit people who are creating custom servers! :)
     
  13. Acubens

    Acubens Old Penguin

    Joined:
    May 6, 2008
    Messages:
    963
    Likes Received:
    66
    Best Answers:
    0
    Great work Limos
     
  14. Limos

    Limos Premium User

    Joined:
    Jun 7, 2010
    Messages:
    10,017
    Likes Received:
    2,936
    Best Answers:
    7
    Thanks for the comments.
     
    Last edited: Nov 5, 2014
  15. jordi10

    jordi10 New Member

    Joined:
    Jun 15, 2009
    Messages:
    11
    Likes Received:
    2
    Best Answers:
    0
    Hi Limos, this really benefits me. However, when I copy the 'monsterquest' in my server it doesn't work. There aren't any errors, but when I killed the monsters the NPC says I didn't and doesn't give me my reward(s).

    My server:
    The Forgotten Server - version 0.2.15 (Mystic Spirit)
     
    Last edited: Apr 6, 2014
  16. Limos

    Limos Premium User

    Joined:
    Jun 7, 2010
    Messages:
    10,017
    Likes Received:
    2,936
    Best Answers:
    7
    I've tested it on TFS 0.2.15
    [​IMG]

    creaturescripts.xml
    Code (Text):
    1. <event type="kill" name="Dragons" script="dragons.lua"/>
    login.lua
    Code (Text):
    1. registerCreatureEvent(cid, "Dragons")
    If you changed the amount of monsters, you also have to change it in the npc.
    Code (Text):
    1. if getPlayerStorageValue(cid, 19000) == 5 and getPlayerStorageValue(cid, 19001) == 3 then
     
    Xiver21 likes this.
  17. psychosisneamia

    psychosisneamia ~Beginner~

    Joined:
    Jun 7, 2012
    Messages:
    162
    Likes Received:
    7
    Best Answers:
    0
    Can you make it so the task mission can have more then one task? Like once you kill the dragons he asks would you like another mission {yes} or {no} and if you say yes then they ask you to kill 1 demon or something?
     
  18. Limos

    Limos Premium User

    Joined:
    Jun 7, 2010
    Messages:
    10,017
    Likes Received:
    2,936
    Best Answers:
    7
    You can do this with the storage.
    Under
    Code (Text):
    1. elseif getPlayerStorageValue(cid, storage) == 1 then
    You can make an other part for storage value 2
    Code (Text):
    1. elseif getPlayerStorageValue(cid, storage) == 2 then
    Like this you can let the npc continue giving more missions instead of letting it say "You already did the mission." after the first misson.

    If you have alot of missions, it's better to do it with a table so you don't have a huge script.
    Here is an example how you can use a table for missions.
    http://otland.net/threads/collecting-items-missions.164797/
    The numbers [1], [2] etc here are the storage values.
     
    Last edited: Apr 10, 2014
    ricotje1 and psychosisneamia like this.
  19. psychosisneamia

    psychosisneamia ~Beginner~

    Joined:
    Jun 7, 2012
    Messages:
    162
    Likes Received:
    7
    Best Answers:
    0
    PERFECT!!! Rep++
     
  20. mori2005

    mori2005 Member

    Joined:
    Feb 19, 2009
    Messages:
    68
    Likes Received:
    0
    Best Answers:
    0
    How can i put a custom questo in a quest log ??
     

Share This Page

Loading...