NPC Mission

Limos

Premium User
Joined
Jun 7, 2010
Messages
10,017
Reaction score
2,952
Location
Netherlands



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:
OP
Limos

Limos

Premium User
Joined
Jun 7, 2010
Messages
10,017
Reaction score
2,952
Location
Netherlands
Part 1 – Let an NPC respond to certain words





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/



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.





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
 
Last edited:
OP
Limos

Limos

Premium User
Joined
Jun 7, 2010
Messages
10,017
Reaction score
2,952
Location
Netherlands
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.






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




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



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


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
 
Last edited:
OP
Limos

Limos

Premium User
Joined
Jun 7, 2010
Messages
10,017
Reaction score
2,952
Location
Netherlands
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

 
Last edited:
OP
Limos

Limos

Premium User
Joined
Jun 7, 2010
Messages
10,017
Reaction score
2,952
Location
Netherlands
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
 
Last edited:
OP
Limos

Limos

Premium User
Joined
Jun 7, 2010
Messages
10,017
Reaction score
2,952
Location
Netherlands
Part 5 - Different kinds of missions



Monster mission



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:
OP
Limos

Limos

Premium User
Joined
Jun 7, 2010
Messages
10,017
Reaction score
2,952
Location
Netherlands
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:

Cornex

Web Developer
Staff member
Global Moderator
Joined
Jun 15, 2008
Messages
3,446
Reaction score
1,051
Location
Sweden
Great work Limos, thanks
 
OP
Limos

Limos

Premium User
Joined
Jun 7, 2010
Messages
10,017
Reaction score
2,952
Location
Netherlands
Thanks for the comments.
 
Last edited:

jordi10

New Member
Joined
Jun 15, 2009
Messages
11
Reaction score
2
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:
OP
Limos

Limos

Premium User
Joined
Jun 7, 2010
Messages
10,017
Reaction score
2,952
Location
Netherlands
I've tested it on TFS 0.2.15


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
 

psychosisneamia

~Beginner~
Joined
Jun 7, 2012
Messages
162
Reaction score
7
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?
 
OP
Limos

Limos

Premium User
Joined
Jun 7, 2010
Messages
10,017
Reaction score
2,952
Location
Netherlands
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:
Top