NPC Mission

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

  1. Limos

    Limos Premium User

    Joined:
    Jun 7, 2010
    Messages:
    10,017
    [​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:
    local keywordHandler = KeywordHandler:new()
    local npcHandler = NpcHandler:new(keywordHandler)
    NpcSystem.parseParameters(npcHandler)
    local talkState = {}
    
    function onCreatureAppear(cid)       npcHandler:onCreatureAppear(cid)       end
    function onCreatureDisappear(cid)     npcHandler:onCreatureDisappear(cid)       end
    function onCreatureSay(cid, type, msg)     npcHandler:onCreatureSay(cid, type, msg)     end
    function onThink()         npcHandler:onThink()             end
    
    function creatureSayCallback(cid, type, msg)
         if not npcHandler:isFocused(cid) then
             return false
         end
    
         local talkUser = NPCHANDLER_CONVBEHAVIOR == CONVERSATION_DEFAULT and 0 or cid
    
         -- here comes what the NPC should do
    
         return true
    end
    
    npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
    npcHandler:addModule(FocusModule:new())
    




    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
    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:
    <?xml version="1.0" encoding="UTF-8"?>
    <npc name="NPC" script="mission.lua" speed="250" walkinterval="2000" floorchange="0">
       <health now="100" max="100"/>
       <look type="130" head="95" body="114" legs="118" feet="4" addons="0"/>
       <parameters>
         <parameter key="message_greet" value="Hello |PLAYERNAME|, I'm lost on this {mountain}."/>
       </parameters>
    </npc>
    
    Note: if you use TFS 0.2 (Mystic Spirit), the link to the lua script will look like this.
    Code:
    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:
    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:
    if msg == "mountain" then
    Then to make the NPC say something after you say mountain, you can use selfSay.
    Code:
    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:
    local keywordHandler = KeywordHandler:new()
    local npcHandler = NpcHandler:new(keywordHandler)
    NpcSystem.parseParameters(npcHandler)
    local talkState = {}
    
    function onCreatureAppear(cid)       npcHandler:onCreatureAppear(cid)       end
    function onCreatureDisappear(cid)     npcHandler:onCreatureDisappear(cid)       end
    function onCreatureSay(cid, type, msg)     npcHandler:onCreatureSay(cid, type, msg)     end
    function onThink()         npcHandler:onThink()           end
    
    function creatureSayCallback(cid, type, msg)
         if not npcHandler:isFocused(cid) then
             return false
         end
    
         local talkUser = NPCHANDLER_CONVBEHAVIOR == CONVERSATION_DEFAULT and 0 or cid
    
         if msgcontains(msg, "mountain") then
             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)
         end
         return true
    end
    
    npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
    npcHandler:addModule(FocusModule:new())
    
    Ingame example.
    [​IMG]




    To let the NPC respond the same way with different words, you can use or.
    Code:
    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:
    if msgcontains(msg, "mountain") then
         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)
    end
    if msgcontains(msg, "lost") then
         selfSay("It's a really big mountain, be careful where you walk else you might get lost too.", cid)
    end
    
    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:
    if msgcontains(msg, "mountain") then
         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)
    elseif msgcontains(msg, "lost") then
         selfSay("It's a really big mountain, be careful where you walk else you might get lost too.", cid)
    end
    
    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:
    if msgcontains(msg, "mountain")  then
         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)
    elseif msgcontains(msg, "lost") then -- if the player doesn't say mountain, it will look if the player says lost
         selfSay("It's a really big mountain, be careful where you walk else you might get lost too.", cid)
    elseif msgcontains(msg, "help") then -- if the player doesn't say lost, it will look if the player says help
         selfSay("I want to try to find to way back myself for next time, but thanks anyway.", cid)
    else -- if all lines are not true, so the player doesn't say mountain, lost or help, it will say 'What do you mean?'.
         selfSay("What do you mean?", cid)
    end
    
    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:
    local keywordHandler = KeywordHandler:new()
    local npcHandler = NpcHandler:new(keywordHandler)
    NpcSystem.parseParameters(npcHandler)
    local talkState = {}
    
    function onCreatureAppear(cid)       npcHandler:onCreatureAppear(cid)       end
    function onCreatureDisappear(cid)     npcHandler:onCreatureDisappear(cid)       end
    function onCreatureSay(cid, type, msg)     npcHandler:onCreatureSay(cid, type, msg)     end
    function onThink()         npcHandler:onThink()           end
    
    function creatureSayCallback(cid, type, msg)
         if not npcHandler:isFocused(cid) then
             return false
         end
    
         local talkUser = NPCHANDLER_CONVBEHAVIOR == CONVERSATION_DEFAULT and 0 or cid
    
         if msgcontains(msg, "mountain") then
             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)
         elseif msgcontains(msg, "lost") then
             selfSay("It's a really big mountain, be careful where you walk else you might get lost too.", cid)
         elseif msgcontains(msg, "help") then
             selfSay("I want to try to find to way back myself for next time, but thanks anyway.", cid)
         else
             selfSay("What do you mean?", cid)
         end
         return true
    end
    
    npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
    npcHandler:addModule(FocusModule:new())
    
    Ingame example
    [​IMG]
     
    Last edited: Mar 11, 2014
  3. Limos

    Limos Premium User

    Joined:
    Jun 7, 2010
    Messages:
    10,017
    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:
    talkState[talkUser] = 1
    To let the NPC only respond when talkState is 1 after saying yes.
    Code:
    if msgcontains(msg, "yes") and talkState[talkUser] == 1 then
    Now you can do it like this.
    Code:
    if msgcontains(msg, "mountain") then
         selfSay("Do you want to know more about this mountain.", cid)
         talkState[talkUser] = 1
    elseif msgcontains(msg, "yes") and talkState[talkUser] == 1 then
         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)
         talkState[talkUser] = 0
    end
    
    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:
    local keywordHandler = KeywordHandler:new()
    local npcHandler = NpcHandler:new(keywordHandler)
    NpcSystem.parseParameters(npcHandler)
    local talkState = {}
    
    function onCreatureAppear(cid)       npcHandler:onCreatureAppear(cid)       end
    function onCreatureDisappear(cid)     npcHandler:onCreatureDisappear(cid)       end
    function onCreatureSay(cid, type, msg)     npcHandler:onCreatureSay(cid, type, msg)     end
    function onThink()         npcHandler:onThink()           end
    
    function creatureSayCallback(cid, type, msg)
         if(not npcHandler:isFocused(cid)) then
             return false
         end
    
         local talkUser = NPCHANDLER_CONVBEHAVIOR == CONVERSATION_DEFAULT and 0 or cid
    
         if msgcontains(msg, "mountain") then
             selfSay("Do you want to know more about this mountain?", cid)
             talkState[talkUser] = 1
         elseif msgcontains(msg, "yes") and talkState[talkUser] == 1 then
             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)
             talkState[talkUser] = 0
         elseif msgcontains(msg, "no") and talkState[talkUser] == 1 then
             selfSay("Ok then.", cid)
             talkState[talkUser] = 0
         end
         return true
    end
    
    npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
    npcHandler:addModule(FocusModule:new())
    
    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:
    if msgcontains(msg, "mountain") then
         selfSay("I think there lives some dangerous {monster} down at the waterfall.", cid)
         talkState[talkUser] = 1
    elseif msgcontains(msg, "monster") and talkState[talkUser] == 1 then -- after saying mountain talkState was set to 1
         selfSay("This monster is known for killing everyone who passes by, do you think of going there?", cid)
         talkState[talkUser] = 2
    elseif msgcontains(msg, "yes") and talkState[talkUser] == 2 then -- after saying monster the talkState was set to 2
         selfSay('Well, I warned you but good luck.', cid)
         talkState[talkUser] = 0
    end
    
    Ingame example
    [​IMG]


    Or when you want the NPC to respond different on different words.
    Code:
    if msgcontains(msg, "mountain") then
         selfSay("I think there lives some dangerous {monster} down at the waterfall.", cid)
         talkState[talkUser] = 1
    elseif msgcontains(msg, "monster") and talkState[talkUser] == 1 then -- after saying mountain talkState was set to 1
         selfSay("There are rumors this monster kills people randomly who pass by, it's really evil.", cid)
         talkState[talkUser] = 0
    elseif msgcontains(msg, "lost") then
         selfSay("There seems to be a {way} to find the way back really fast, but I'm to affrain to go there.", cid)
         talkState[talkUser] = 2
    elseif msgcontains(msg, "way") and talkState[talkUser] == 2 then -- after saying lost the talkState was set to 2
         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)
         talkState[talkUser] = 0
    end
    
    Ingame example
    [​IMG]

    Or when you want the NPC to respond different on the same word.
    Code:
    if msgcontains(msg, "mountain") then
         selfSay("Do you want to know more about this mountain?", cid)
         talkState[talkUser] = 1
    elseif msgcontains(msg, "lost") then
         selfSay("Do you want to know how I got lost here?", cid)
         talkState[talkUser] = 2
    elseif msgcontains(msg, "yes") then
         if talkState[talkUser] == 1 then -- after saying mountain talkState was set to 1
             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)
         elseif talkState[talkUser] == 2 then -- after saying lost the talkState was set to 2
             selfSay("Well, I did bring a map, but I lost it when I run away after I heard the monster near the waterfall.", cid)
         end
         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.
    end
    
    Ingame example
    [​IMG]
     
    Last edited: Mar 11, 2014
  4. Limos

    Limos Premium User

    Joined:
    Jun 7, 2010
    Messages:
    10,017
    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:
    setPlayerStorageValue(cid, 5000, 1)
    To check the value of storage.
    Code:
    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:
    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:
    if getPlayerStorageValue(cid, storage) == -1 then
         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)
    end
    

    Then you set the storagevalue to 1 after the player accepted te mission.
    Code:
    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:
    local storage = 5000
    if msgcontains(msg, "mission") then
         if getPlayerStorageValue(cid, storage) == -1 then
             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)
             talkState[talkUser] = 1
         end
    elseif msgcontains(msg, "yes") and talkState[talkUser] == 1 then
         if getPlayerStorageValue(cid, storage) == -1 then
             selfSay("Thanks alot, come back when you have it.", cid)
             setPlayerStorageValue(cid, storage, 1)
         end
         talkState[talkUser] = 0
    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:
    local storage = 5000
    if msgcontains(msg, "mission") then
         if getPlayerStorageValue(cid, storage) == -1 then -- When the player comes to the NPC for the first time or didn't accepted the mission yet.
             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)
             talkState[talkUser] = 1
         elseif getPlayerStorageValue(cid, storage) == 1 then -- When the player comes back after accepting the mission.
             selfSay("Did you find the doll?", cid)
             talkState[talkUser] = 1
         else
             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.
         end
    elseif msgcontains(msg, "yes") and talkState[talkUser] == 1 then
         if getPlayerStorageValue(cid, storage) == -1 then
             selfSay("Thanks alot, come back when you have it.", cid)
             setPlayerStorageValue(cid, storage, 1)
         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.
             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.
                 selfSay("That's great, my daughter will be really happy, thanks.", cid)
                 doPlayerAddItem(cid, 2160, 3) -- You can add for example items as reward.
                 doPlayerAddExp(cid, 5000) -- or experience
                 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).
                 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.'.
             else -- if removing the item wasn't possible (so this means the player doesn't have it), it will do the part after else.
                 selfSay("You don't have it.", cid)
             end
         end
         talkState[talkUser] = 0
    end


    In total
    Code:
    local keywordHandler = KeywordHandler:new()
    local npcHandler = NpcHandler:new(keywordHandler)
    NpcSystem.parseParameters(npcHandler)
    local talkState = {}
    
    function onCreatureAppear(cid)       npcHandler:onCreatureAppear(cid)       end
    function onCreatureDisappear(cid)     npcHandler:onCreatureDisappear(cid)       end
    function onCreatureSay(cid, type, msg)     npcHandler:onCreatureSay(cid, type, msg)     end
    function onThink()         npcHandler:onThink()           end
    
    local storage = 5000
    
    function creatureSayCallback(cid, type, msg)
         if not npcHandler:isFocused(cid) then
             return false
         end
    
         local talkUser = NPCHANDLER_CONVBEHAVIOR == CONVERSATION_DEFAULT and 0 or cid
    
         if msgcontains(msg, "mission") then
             if getPlayerStorageValue(cid, storage) == -1 then
                 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)
                 talkState[talkUser] = 1
             elseif getPlayerStorageValue(cid, storage) == 1 then
                 selfSay("Did you find the doll?", cid)
                 talkState[talkUser] = 1
             else
                 selfSay("Thanks again for finding the doll.", cid)
             end
         elseif msgcontains(msg, "yes") and talkState[talkUser] == 1 then
             if getPlayerStorageValue(cid, storage) == -1 then
                 selfSay("Thanks alot, come back when you have it.", cid)
                 setPlayerStorageValue(cid, storage, 1)
             else
                 if(doPlayerRemoveItem(cid, 2110, 1)) then
                     selfSay("That's great, my daughter will be really happy, thanks.", cid)
                     doPlayerAddItem(cid, 2160, 3)
                     doPlayerAddExp(cid, 5000)
                     setPlayerStorageValue(cid, storage, 2)
                 else
                     selfSay("You don't have it.", cid)
                 end
             end
             talkState[talkUser] = 0
         elseif msgcontains(msg, "no") and talkState[talkUser] == 1 then
             selfSay("Ok then.", cid)
             talkState[talkUser] = 0
         end
         return true
    end
    
    npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
    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
    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:
    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:
    if not npcHandler:isFocused(cid) then
         return false
    end
    

    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:
    if not npcHandler:isFocused(cid) then
         if msg == "hi" or msg == "hello" then
             if getPlayerStorageValue(cid, storage) == -1 then
                 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)
                 talkState[talkUser] = 1
             elseif getPlayerStorageValue(cid, storage) == 1 then
                 selfSay("Did you find the doll?", cid)
                 talkState[talkUser] = 1
             else
                 selfSay("Thanks again for finding the doll.", cid)
             end
             npcHandler:addFocus(cid)
         else
             return false
         end
    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:
    local keywordHandler = KeywordHandler:new()
    local npcHandler = NpcHandler:new(keywordHandler)
    NpcSystem.parseParameters(npcHandler)
    local talkState = {}
    
    function onCreatureAppear(cid)       npcHandler:onCreatureAppear(cid)       end
    function onCreatureDisappear(cid)     npcHandler:onCreatureDisappear(cid)       end
    function onCreatureSay(cid, type, msg)     npcHandler:onCreatureSay(cid, type, msg)     end
    function onThink()         npcHandler:onThink()           end
    local storage = 5001
    function creatureSayCallback(cid, type, msg)
    
         local talkUser = NPCHANDLER_CONVBEHAVIOR == CONVERSATION_DEFAULT and 0 or cid
    
         if not npcHandler:isFocused(cid) then
             if msg == "hi" or msg == "hello" then
                 if getPlayerStorageValue(cid, storage) == -1 then
                     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)
                     talkState[talkUser] = 1
                 elseif getPlayerStorageValue(cid, storage) == 1 then
                     selfSay("Did you find the doll?", cid)
                     talkState[talkUser] = 1
                 else
                     selfSay("Thanks again for finding the doll.", cid)
                 end
                 npcHandler:addFocus(cid)
             else
                 return false
             end
         end
    
         if msgcontains(msg, "yes") and talkState[talkUser] == 1 then
             if getPlayerStorageValue(cid, storage) == -1 then
                 selfSay("Thanks alot, come back when you have it.", cid)
                 setPlayerStorageValue(cid, storage, 1)
             else
                 if doPlayerRemoveItem(cid, 2110, 1) then
                     selfSay("That's great, my daughter will be really happy, thanks.", cid)
                     doPlayerAddItem(cid, 2160, 3)
                     doPlayerAddExp(cid, 5000)
                     setPlayerStorageValue(cid, storage, 2)
                 else
                     selfSay("You don't have it.", cid)
                 end
             end
             talkState[talkUser] = 0
         elseif msgcontains(msg, "no") and talkState[talkUser] == 1 then
             selfSay("Ok then.", cid)
             talkState[talkUser] = 0
         elseif msgcontains(msg, "bye") then
             selfSay("Bye.", cid)
             npcHandler:releaseFocus(cid)
         end
         return true
    end
    npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
    
    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
    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:
    local config = {
         ['dragon'] = {amount = 5, storage = 19000, startstorage = 5002, startvalue = 1},
         ['dragon lord'] = {amount = 3, storage = 19001, startstorage = 5002, startvalue = 1}
    }
    function onKill(cid, target)
         local monster = config[getCreatureName(target):lower()]
         if isPlayer(target) or not monster or isSummon(target) then
             return true
         end
    
         if (getPlayerStorageValue(cid, monster.storage)+1) < monster.amount and getPlayerStorageValue(cid, monster.startstorage) >= monster.startvalue then
             setPlayerStorageValue(cid, monster.storage, getPlayerStorageValue(cid, monster.storage) + 1)
             doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_ORANGE, 'Task message: '..(getPlayerStorageValue(cid, monster.storage)+1)..' of '..monster.amount..' '..getCreatureName(target)..'s killed.')
         end
         if (getPlayerStorageValue(cid, monster.storage)+1) == monster.amount then
             doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, 'Congratulations, you have killed '..(getPlayerStorageValue(cid, monster.storage)+1)..' '..getCreatureName(target)..'s and completed the '..getCreatureName(target)..'s mission.')
             setPlayerStorageValue(cid, monster.storage, getPlayerStorageValue(cid, monster.storage) + 1)
         end
         return true
    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:
    local keywordHandler = KeywordHandler:new()
    local npcHandler = NpcHandler:new(keywordHandler)
    NpcSystem.parseParameters(npcHandler)
    local talkState = {}
    
    function onCreatureAppear(cid)       npcHandler:onCreatureAppear(cid)       end
    function onCreatureDisappear(cid)     npcHandler:onCreatureDisappear(cid)       end
    function onCreatureSay(cid, type, msg)     npcHandler:onCreatureSay(cid, type, msg)     end
    function onThink()         npcHandler:onThink()           end
    
    local storage = 5002
    
    function creatureSayCallback(cid, type, msg)
         if not npcHandler:isFocused(cid) then
             return false
         end
         local talkUser = NPCHANDLER_CONVBEHAVIOR == CONVERSATION_DEFAULT and 0 or cid
    
         if msgcontains(msg, "mission") then
             if getPlayerStorageValue(cid, storage) == -1 then
                 selfSay("I have a mission for you to kill 5 dragons and 3 dragon lords, do you accept?", cid)
                 talkState[talkUser] = 1
             elseif getPlayerStorageValue(cid, storage) == 1 then
                 selfSay("Did you kill 5 dragons and 3 dragon lords?", cid)
                 talkState[talkUser] = 1
             else
                 selfSay("You already did the mission.", cid)
             end
         elseif msgcontains(msg, "yes") and talkState[talkUser] == 1 then
             if getPlayerStorageValue(cid, storage) == -1 then
                 selfSay("Good, come back when you killed them.", cid)
                 setPlayerStorageValue(cid, storage, 1)
             else
                 if getPlayerStorageValue(cid, 19000) == 5 and getPlayerStorageValue(cid, 19001) == 3 then
                     selfSay("Good job, here is your reward.", cid)
                     doPlayerAddItem(cid, 2160, 5)
                     doPlayerAddExp(cid, 50000)
                     setPlayerStorageValue(cid, storage, 2)
                 else
                     selfSay("You didn't kill them all.", cid)
                 end
             end
             talkState[talkUser] = 0
         elseif msgcontains(msg, "no") and talkState[talkUser] == 1 then
             selfSay("Ok then.", cid)
             talkState[talkUser] = 0
         end
         return true
    end
    
    npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
    npcHandler:addModule(FocusModule:new())
     
    Last edited: Jul 29, 2014
  7. Limos

    Limos Premium User

    Joined:
    Jun 7, 2010
    Messages:
    10,017
    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:
    local keywordHandler = KeywordHandler:new()
    local npcHandler = NpcHandler:new(keywordHandler)
    NpcSystem.parseParameters(npcHandler)
    local talkState = {}
    
    function onCreatureAppear(cid)       npcHandler:onCreatureAppear(cid)       end
    function onCreatureDisappear(cid)     npcHandler:onCreatureDisappear(cid)       end
    function onCreatureSay(cid, type, msg)     npcHandler:onCreatureSay(cid, type, msg)     end
    function onThink()         npcHandler:onThink()           end
    
    local storage = 5003
    
    function creatureSayCallback(cid, type, msg)
         if not npcHandler:isFocused(cid) then
             return false
         end
    
         local talkUser = NPCHANDLER_CONVBEHAVIOR == CONVERSATION_DEFAULT and 0 or cid
    
         if msgcontains(msg, "mission") then
             if getPlayerStorageValue(cid, storage) == -1 then -- When the player comes for the first time to NPC1
                 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)
                 talkState[talkUser] = 1
             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.
                 selfSay("You didn't find him? He lives in the small wooden house with the red roof.", cid)
             elseif getPlayerStorageValue(cid, storage) == 2 then -- In NPC2 the storagevalue is set to 2, so this means the player talked with NPC2.
                 selfSay("Do you have the potion?", cid)
                 talkState[talkUser] = 1
             else
                 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.
             end
         elseif msgcontains(msg, "yes") and talkState[talkUser] == 1 then
             if getPlayerStorageValue(cid, storage) == -1 then
                 selfSay("Thank you, tell him I need a {sacred berry potion}.", cid)
                 setPlayerStorageValue(cid, storage, 1)  -- Now the storagevalue here is set to 1, with storage value 1, the player can go to NPC2.
             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.
                 if doPlayerRemoveItem(cid, 10089, 1) then -- If the player has the potion
                     selfSay("Thanks alot.", cid)
                     doPlayerAddItem(cid, 2160, 3) -- You can add here rewards again
                     doPlayerAddExp(cid, 5000)
                     setPlayerStorageValue(cid, storage, 3) -- Now the storagevalue is 3, so the player won't get the responds from storagevalue 2 anymore
                 else
                     selfSay("You don't have it.", cid)
                 end
             end
             talkState[talkUser] = 0
         elseif msgcontains(msg, "no") and talkState[talkUser] == 1 then
             selfSay("Ok then.", cid)
             talkState[talkUser] = 0
         end
         return true
    end
    
    npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
    npcHandler:addModule(FocusModule:new())
    NPC2
    Code:
     local keywordHandler = KeywordHandler:new()
    local npcHandler = NpcHandler:new(keywordHandler)
    NpcSystem.parseParameters(npcHandler)
    local talkState = {}
    
    function onCreatureAppear(cid)       npcHandler:onCreatureAppear(cid)       end
    function onCreatureDisappear(cid)     npcHandler:onCreatureDisappear(cid)       end
    function onCreatureSay(cid, type, msg)     npcHandler:onCreatureSay(cid, type, msg)     end
    function onThink()         npcHandler:onThink()           end
    
    local storage = 5003
    
    function creatureSayCallback(cid, type, msg)
         if not npcHandler:isFocused(cid) then
             return false
         end
    
         local talkUser = NPCHANDLER_CONVBEHAVIOR == CONVERSATION_DEFAULT and 0 or cid
    
         if msgcontains(msg, "sacred berry potion") then
             if getPlayerStorageValue(cid, storage) == -1 then -- When the player didn't go to NPC1 first, because NPC1 sets the storagevalue to 1.
                 selfSay("What?", cid)
             elseif getPlayerStorageValue(cid, storage) == 1 then -- In NPC1 the storagevalue is set to 1, so this means the player accepted that mission.
                 selfSay("Ah, I just heard about someone who needed this potion, It's to help NPC1 right?", cid)
                 talkState[talkUser] = 1
             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.
                 selfSay("Go give the potion, he will feel alot better after drinking it.", cid)
             else -- if the storagevalue is not -1, 1 or 2, so someone says 'sacred berry potion' again after giving the potion to NPC1.
                 selfSay("You gave NPC1 the potion? Thats great, he will feel alot better soon.", cid)
             end
         elseif msgcontains(msg, "yes") and talkState[talkUser] == 1 then
             -- You don't need to check for storagevalue here, because there is only 1 question you can answer with yes.
             selfSay("That's very kind of you, here it is.", cid)
             doPlayerAddItem(cid, 10089, 1) -- to add the potion
             setPlayerStorageValue(cid, storage, 2) -- set storagevalue to 2 so NPC1 knows the player talked with NPC2
             talkState[talkUser] = 0
         end
         return true
    end
    
    npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
    npcHandler:addModule(FocusModule:new())
    
     
    Last edited: Mar 10, 2014
    LordVissie, Exoltes, Shackal and 9 others like this.
  8. Cornex

    Cornex Web Developer Staff Member Global Moderator

    Joined:
    Jun 15, 2008
    Messages:
    3,407
    Great work Limos, thanks
     
  9. Colors

    Colors

    Joined:
    Mar 22, 2013
    Messages:
    904
    This tutorial is just amazing, thanks!
     
  10. filipus

    filipus Member

    Joined:
    Dec 31, 2010
    Messages:
    229
    Wow, Wow ,Wow. Very very good. Thank you!
     
  11. Jaed Le Raep

    Jaed Le Raep ★Gaeming★

    Joined:
    Sep 3, 2007
    Messages:
    988
  12. Peroxide

    Peroxide Bangin'

    Joined:
    Aug 22, 2008
    Messages:
    10,236
    @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:
    954
    Great work Limos
     
  14. Limos

    Limos Premium User

    Joined:
    Jun 7, 2010
    Messages:
    10,017
    Thanks for the comments.
     
    Last edited: Nov 5, 2014
  15. jordi10

    jordi10 New Member

    Joined:
    Jun 15, 2009
    Messages:
    11
    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
    I've tested it on TFS 0.2.15
    [​IMG]

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

    psychosisneamia ~Beginner~

    Joined:
    Jun 7, 2012
    Messages:
    162
    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
    You can do this with the storage.
    Under
    Code:
    elseif getPlayerStorageValue(cid, storage) == 1 then
    You can make an other part for storage value 2
    Code:
    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
    PERFECT!!! Rep++
     
  20. mori2005

    mori2005 Member

    Joined:
    Feb 19, 2009
    Messages:
    68
    How can i put a custom questo in a quest log ??
     

Share This Page