• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

Advanced Task NPC

Itutorial

Excellent OT User
Joined
Dec 23, 2014
Messages
2,307
Solutions
68
Reaction score
982
This is a pretty advanced task npcs set up. I did test it but only enough to know that it works as intended. There could be small bugs I didn't think about or find during the testing.

Here is a list of what is in the code.

--EDIT: I have created this code in Modal Windows aswell. It is a few posts down the page.

1) Npc can give: Monster killing tasks, and item collecting tasks. You can choose to have just one or both of them.
2) The npc is set up to have many different messages depending on how far into the tasks the player is
3) Almost everything is configurable in the NPC including greetings and goodbyes.
4) Missions can require other missions to be completed first.
5) The NPC can tell a story the first time a player talks to it.
6) The player can use multiple messages to get the task
7) The NPC will tell the player what is needed left for the task to be complete
8) The player will be notified each time he kills a task monster and updated what is left.
9) The player can be rewarded with items, exp, and can have other storages set (eg. to allow a player through a specific door)

NPC FILE
Code:
local keywordHandler = KeywordHandler:new()
local npcHandler = NpcHandler:new(keywordHandler)
NpcSystem.parseParameters(npcHandler)

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

npcHandler:addModule(FocusModule:new())

local npcMissions = {
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    [1] = {name = "Rat Killer", -- Kill monsters only
        arrayName = {"Rat", "rat", "Rats", "rats", "Pest", "pest", "Pests", "pests", "rat killer", "Rat killer", "Rat Killer"},
        storageMission = 43000, -- Mission storage should be unique even in different NPC files.
        storageRequired = nil, -- Use this to require different tasks to be done complete before this task can be started.
        messageTaskInfo = "I need you to kill 10 rats and 10 cave rats.", -- This message is said to the player when he starts the task.
        messageTaskComplete = "Thank you! Those pests have ruined my store. unfortunately they will be back. The rats aren't the main {problem}", -- This message is said to the player when he finishs the task.
        monsters = { -- Monster task
            [1] = {name = "Rat", amount = 10, storage = 41000}, -- Monster storages should be unique even in different NPC files.
            [2] = {name = "Cave Rat", amount = 10, storage = 41001}
        },
        --Rewards--
        exp = 1000, -- Exp given to the player for completing the task.
        rewardItems = { -- Items given to the player for completing the task.
            [1] = {itemid = 2390, amount = 1}
        },
        setStorage = nil -- Use this to set any storages you want to when the player completes the task excluding storageNpc and storageMission
    },
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    [2] = {name = "Troll Hunter",
        arrayName = {"troll hunter", "Troll Hunter", "Troll", "troll", "Trolls", "trolls"},
        storageMission = 43001,
        storageRequired = {43000},
        messageTaskInfo = "I need you to kill 20 trolls.",
        messageTaskComplete = "You are truley a legend. There is one more thing I need. Could you {help} me one more time?",
        monsters = {
            [1] = {name = "Troll", amount = 20, storage = 41002},
        },
        collectItems = { -- Collect Items task
            [1] = {itemid = 1111, amount = 1}
        },
        --Rewards--
        exp = 5000,
        rewardItems = {
            [1] = {itemid = 2390, amount = 1}
        },
        setStorage = {
            [1] = {41532, 1}
        }
    },
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    [3] = {name = "The Collector", -- Collect Items only
        arrayName = {"collector", "Collector", "collect", "Collect", "cheese", "Cheese"},
        storageMission = 43003,
        storageRequired = {43001, 43002},
        messageTaskInfo = "I need you to collect 10 cheese.",
        messageTaskComplete = "You are truley a legend. I do not need anymore help.",
        collectItems = { -- Collect Items task
            [1] = {itemid = 1111, amount = 1}
        },
        --Rewards--
        exp = 5000,
        rewardItems = {
            [1] = {itemid = 2390, amount = 1}
        },
        setStorage = nil
    }
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
}

local npcStory = { -- This story is told when the player first talks to the npc.
    "Not now adventurer. I have to deal with these pests.",
    "Honey! Get my broom, these things are everywhere. They aren't even scared of me!?",
    "If this continues we will have to close down our business!",
    "Please god send us some {help}!"
}

local MESSAGES_GREET = { -- Messages are based on the npcStorage for the player. This table handles all greeting interaction between the npc and player.
  --Storage / message
  -- The player gets his storage for the npc set as soon as he talks to the npc
  -- Everytime a player accepts and completes one of the tasks/missions his npcStorage is increased by 1. That is how the code keeps track of how the npc should respond to the player.
    [1] = "Hello again |PLAYERNAME|. Do you want to {help} me yet?",
    [2] = "I am glad you decided to help. Are those {rats} giving you a problem?",
    [3] = "Thanks for killing those rats. I have another {task} I need {help} with.",
    [4] = "The trolls are powerful. Be sure to take strong gear with you.",
    [5] = "Thank you for all your help."
}

local MESSAGES_GOODBYE = { -- This works the same as MESSAGES_GREET. Depending on how many tasks the player has done for the npc. The npc will say different things when he says goodbye to the player.
    [1] = "Thanks for nothing |PLAYERNAME|.",
    [2] = "Thank you for your help. Goodbye.",
    [3] = "You have helped so much. I will be sure to tell other of you."
}

local arrayGreetings = {"hi", "Hi", "hello", "Hello", "hey", "Hey", "oi", "Oi", "hola", "Hola"}
local arrayFarewell = {"bye", "Bye", "goodbye", "Goodbye", "good-bye", "Good-Bye", "Good-bye", "cya", "adios", "Adios"}

local messageHearStoryAgain = "story"
local messageCheckTasks = {"help", "Help", "task", "Task", "tasks", "Tasks"}
local messageShowTasks = "Here is what I need help with:"

local messageCompletedAllTasks = "Thank you for all you have done. I dont need anymore help."

local npcStorage = 45000

function onCreatureSay(cid, type, msg)
    local player = Player(cid)
    local playerGreetMessage = MESSAGES_GREET[player:getStorageValue(npcStorage)]
    local playerGoodbyeMessage = MESSAGES_GOODBYE[player:getStorageValue(npcStorage)]
  
    if isInArray(arrayGreetings, msg) and not npcHandler:isFocused(cid) and player:getStorageValue(npcStorage) == -1 then
        npcHandler:addFocus(cid)
        npcHandler:say(npcStory, cid, false, true, 4000)
        player:setStorageValue(npcStorage, 1)
  
    elseif isInArray(arrayGreetings, msg) and not npcHandler:isFocused(cid) then
        npcHandler:addFocus(cid)
        if string.find(playerGreetMessage, "|PLAYERNAME|") then
            local newMsg = string.gsub(playerGreetMessage, "|PLAYERNAME|", player:getName())
            selfSay(newMsg, cid)
        else
            selfSay(playerGreetMessage, cid)
        end
      
    elseif isInArray(arrayFarewell, msg) and npcHandler:isFocused(cid) then
        if string.find(playerGoodbyeMessage, "|PLAYERNAME|") then
            local newMsg = string.gsub(playerGoodbyeMessage, "|PLAYERNAME|", player:getName())
            selfSay(newMsg, cid)
        else
            selfSay(playerGoodbyeMessage, cid)
        end
        npcHandler:releaseFocus(cid)
      
    elseif isInArray(messageCheckTasks, msg) and npcHandler:isFocused(cid) then
        local text = messageShowTasks
        local tmpTable = {}
      
        for i = 1, #npcMissions do
            if player:getStorageValue(npcMissions[i].storageMission) < 2 then
                if npcMissions[i].storageRequired then
                local showTask = true
                    for x = 1, #npcMissions[i].storageRequired do
                        if player:getStorageValue(npcMissions[i].storageRequired[x]) ~= 2 then
                            showTask = false
                            break
                        end
                    end
                  
                    if showTask then
                        tmpTable[#tmpTable + 1] = npcMissions[i].name
                    end
                else
                    tmpTable[#tmpTable + 1] = npcMissions[i].name
                end
            end
        end
      
        if #tmpTable > 0 then
            for i = 1, #tmpTable do
                if i == #tmpTable then
                    text = text.." {"..tmpTable[i].."}"
                else
                    text = text.." {"..tmpTable[i].."},"
                end
            end
            selfSay(text, cid)
        else
            selfSay(messageCompletedAllTasks, cid)
        end
  
    elseif msg and npcHandler:isFocused(cid) then
        local MISSION = nil
        for i = 1, #npcMissions do
            if isInArray(npcMissions[i].arrayName, msg) then
                MISSION = npcMissions[i]
                break
            end
        end
      
        if MISSION == nil then
            return false
        end
      
        if player:getStorageValue(MISSION.storageMission) == 2 then
            selfSay("You have already completed {"..MISSION.name.."}.", cid)
            return false
        end

        local canDoTask = true
        if MISSION.storageRequired then
            for i = 1, #MISSION.storageRequired do
                if player:getStorageValue(MISSION.storageRequired) ~= 2 then
                    canDoTask = false
                    break
                end
            end
        end
      
        if not canDoTask then
            selfSay("You are not ready for {"..MISSION.name.."}.", cid)
        return false
        end
      
        if player:getStorageValue(MISSION.storageMission) == 1 then
            local isTaskDone = true
            if MISSION.monsters then
                for i = 1, #MISSION.monsters do
                    if player:getStorageValue(MISSION.monsters[i].storage) ~= MISSION.monsters[i].amount then
                        isTaskDone = false
                        break
                    end
                end
            end
          
            if MISSION.collectItems then
                for i = 1, #MISSION.collectItems do
                    if player:getItemCount(MISSION.collectItems[i].itemid) < MISSION.collectItems[i].amount then
                        isTaskDone = false
                        break
                    end
                end
            end
          
            if isTaskDone then
                if MISSION.monsters then
                    for i = 1, #MISSION.monsters do
                        player:setStorageValue(MISSION.monsters[i].storage, -1)
                    end
                end
              
                if MISSION.collectItems then
                    for i = 1, #MISSION.collectItems do
                        player:removeItem(MISSION.collectItems[i].itemid, MISSION.collectItems[i].amount)
                    end
                end
              
                if MISSION.exp then
                    player:addExperience(MISSION.exp)
                end
              
                if MISSION.rewardItems then
                    for i = 1, #MISSION.rewardItems do
                        player:addItem(MISSION.rewardItems[i].itemid, MISSION.rewardItems[i].amount, true)
                    end
                end
              
                selfSay(MISSION.messageTaskComplete, cid)
                player:setStorageValue(npcStorage, player:getStorageValue(npcStorage) + 1)
                player:setStorageValue(MISSION.storageMission, 2)
                return false
            end
      
            local text = "["..MISSION.name.."]: "
            if MISSION.monsters then
                for i = 1, #MISSION.monsters do
                    if i == #MISSION.monsters then
                        text = text.."("..player:getStorageValue(MISSION.monsters[i].storage).."/"..MISSION.monsters[i].amount..") "..MISSION.monsters[i].name.." "
                    else
                        text = text.."("..player:getStorageValue(MISSION.monsters[i].storage).."/"..MISSION.monsters[i].amount..") "..MISSION.monsters[i].name..", "
                    end
                end
            end
          
            if MISSION.collectItems then
                for i = 1, #MISSION.collectItems do
                    if i == #MISSION.collectItems then
                        text = text.."("..player:getItemCount(MISSION.collectItems[i].itemid).."/"..MISSION.collectItems[i].amount..") "..ItemType(MISSION.collectItems[i].itemid):getName().." "
                    else
                        text = text.."("..player:getItemCount(MISSION.collectItems[i].itemid).."/"..MISSION.collectItems[i].amount..") "..ItemType(MISSION.collectItems[i].itemid):getName()..", "
                    end
                end
            end
            selfSay(text, cid)
            return false
        end
          
          
        selfSay(MISSION.messageTaskInfo, cid)
        player:setStorageValue(MISSION.storageMission, 1)
        player:setStorageValue(npcStorage, player:getStorageValue(npcStorage) + 1)

        if MISSION.monsters then
            for i = 1, #MISSION.monsters do
                player:setStorageValue(MISSION.monsters[i].storage, 0)
            end
        end
    end
return true
end

CREATURESCRIPT
Code:
local missions = { -- Use missionStorage to link the monsters to each mission. It is set up this way so multiple missions can have the same monsters.
    [1] = {name = "Rat", amount = 10, missionStorage = 43000, storage = 41000},
    [2] = {name = "Cave Rat", amount = 10, missionStorage = 43000, storage = 41001},
    [3] = {name = "Troll", amount = 10, missionStorage = 43001, storage = 41002}
}

function onKill(creature, target)
    if isPlayer(creature) and isMonster(target) then
        for i = 1, #missions do
            if target:getName() == missions[i].name then
                if creature:getStorageValue(missions[i].missionStorage) == 1 then
                    if creature:getStorageValue(missions[i].storage) < missions[i].amount then
                        creature:setStorageValue(missions[i].storage, creature:getStorageValue(missions[i].storage) + 1)
                        creature:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "You have killed "..creature:getStorageValue(missions[i].storage).."/"..missions[i].amount.." "..missions[i].name.."('s)")
                    end
                end
            end
        end
    end
    return true
end

If there is enough interest in it, I will convert this code into modal windows.
 
Last edited:
@Itutorial
Code:
Lua Script Error: [Npc interface]
data/npc/scripts/Task.lua:onCreatureSay
LuaScriptInterface::luaAddEvent(). Argument #7 is unsafe

Code:
Lua Script Error: [Npc interface]
data/npc/scripts/Task.lua:onCreatureSay
data/npc/scripts/Task.lua:136: bad argument #1 to 'find' (string expected, got nil)
 
Ty for sharing, will definitely give it a test :]
 
@Lucifer The second error do you know what you said to the npc? When exactly did that error happen?

@ernaix can you pm me the luascript.cpp of that distro

to fix the first error change line 124:
Code:
npcHandler:say(npcStory, player:getId(), false, true, 4000)
 
Last edited:
Converted to Modal Windows

Creaturescript.xml
Code:
<event type="modalwindow" name="TaskSystemWindow" script="TaskSystemWindow.lua" />
<event type="kill" name="TaskSystemKill" script="TaskSystemKill.lua" />

add creaturescript: TaskSystemWindow.lua
Code:
local npcMissions = {
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    [1] = {name = "Rat Killer", -- Kill monsters only
        arrayName = {"Rat", "rat", "Rats", "rats", "Pest", "pest", "Pests", "pests", "rat killer", "Rat killer", "Rat Killer"},
        storageMission = 43000, -- Mission storage should be unique even in different NPC files.
        storageRequired = nil, -- Use this to require different tasks to be done complete before this task can be started.
        messageTaskInfo = "I need you to kill 10 rats and 10 cave rats.", -- This message is said to the player when he starts the task.
        messageTaskComplete = "Thank you! Those pests have ruined my store. unfortunately they will be back. The rats aren't the main {problem}", -- This message is said to the player when he finishs the task.
        windowMsg = "Kill rats in the basement.", -- This text is shown under the title of the modal window
        monsters = { -- Monster task
            [1] = {name = "Rat", amount = 10, storage = 41000}, -- Monster storages should be unique even in different NPC files.
            [2] = {name = "Cave Rat", amount = 10, storage = 41001}
        },
        --Rewards--
        exp = 1000, -- Exp given to the player for completing the task.
        rewardItems = { -- Items given to the player for completing the task.
            [1] = {itemid = 2390, amount = 1}
        },
        setStorage = nil -- Use this to set any storages you want to when the player completes the task excluding storageNpc and storageMission
    },
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    [2] = {name = "Troll Hunter",
        arrayName = {"troll hunter", "Troll Hunter", "Troll", "troll", "Trolls", "trolls"},
        storageMission = 43001,
        storageRequired = {43000},
        messageTaskInfo = "I need you to kill 20 trolls.",
        messageTaskComplete = "You are truley a legend. There is one more thing I need. Could you {help} me one more time?",
        windowMsg = "Kill trolls in the forst to the east. Also, collect x items",
        monsters = {
            [1] = {name = "Troll", amount = 20, storage = 41002},
        },
        collectItems = { -- Collect Items task
            [1] = {itemid = 1111, amount = 1}
        },
        --Rewards--
        exp = 5000,
        rewardItems = {
            [1] = {itemid = 2390, amount = 1}
        },
        setStorage = {
            [1] = {41532, 1}
        }
    },
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    [3] = {name = "The Collector", -- Collect Items only
        arrayName = {"collector", "Collector", "collect", "Collect", "cheese", "Cheese"},
        storageMission = 43003,
        storageRequired = {43001, 43002},
        messageTaskInfo = "I need you to collect 10 cheese.",
        messageTaskComplete = "You are truley a legend. I do not need anymore help.",
        windowMsg = "Collect x items",
        collectItems = { -- Collect Items task
            [1] = {itemid = 1111, amount = 1}
        },
        --Rewards--
        exp = 5000,
        rewardItems = {
            [1] = {itemid = 2390, amount = 1}
        },
        setStorage = nil
    }
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
}

function onModalWindow(player, modalWindowId, buttonId, choiceId)
    if modalWindowId == 0x10 then
        if buttonId == 0x01 then
            local TASK = npcMissions[choiceId]
            
            if not TASK then print('Internal Error finding Task') return false end
            if player:getStorageValue(TASK.storageMission) == 2 then
                local window = ModalWindow(0x12, TASK.name, TASK.windowMsg)
                window:addChoice(0x00, "You have already completed this task.")
                window:addButton(0x00, "Exit")
                window:setDefaultEnterButton(0x00)
                window:sendToPlayer(player)
                return true
            elseif player:getStorageValue(TASK.storageMission) == 1 then
                local window = ModalWindow(0x12, TASK.name, TASK.windowMsg)
                local count = 1
                if TASK.monsters then
                    for i = 1, #TASK.monsters do
                        window:addChoice(count, "("..player:getStorageValue(TASK.monsters[i].storage).."/"..TASK.monsters[i].amount.." "..TASK.monsters[i].name..".")
                        count = count + 1
                    end
                end
                
                if TASK.collectItems then
                    for i = 1, #TASK.collectItems do
                        window:addChoice(count, "("..player:getItemCount(TASK.collectItems[i].itemid).."/"..TASK.collectItems[i].amount.." "..ItemType(TASK.collectItems[i].itemid):getName()..".")
                        count = count + 1
                    end
                end
                
                window:addButton(0x00, "Exit")
                window:addButton(choiceId, "Turn-In")
                window:setDefaultEnterButton(0x01)
                window:sendToPlayer(player)
                return true
                
            elseif player:getStorageValue(TASK.storageMission) == -1 then
                local window = ModalWindow(0x11, TASK.name, TASK.windowMsg)
                local count = 1
                if TASK.monsters then
                    for i = 1, #TASK.monsters do
                        window:addChoice(count, "Kill: "..TASK.monsters[i].amount.." "..TASK.monsters[i].name..".")
                        count = count + 1
                    end
                end
                
                if TASK.collectItems then
                    for i = 1, #TASK.collectItems do
                        window:addChoice(count, "Collect: "..TASK.collectItems[i].amount.." "..ItemType(TASK.collectItems[i].itemid):getName()..".")
                        count = count + 1
                    end
                end
                
                window:addButton(0x00, "Exit")
                window:addButton(choiceId, "Accept")
                window:setDefaultEnterButton(0x01)
                window:sendToPlayer(player)
                return true
            end
            return true
        end
    
    elseif modalWindowId == 0x11 then
            local TASK = npcMissions[buttonId]
        
            if not TASK then print('Internal Error finding Task') return false end
        
            player:setStorageValue(TASK.storageMission, 1)
        
            if TASK.monsters then
                for i = 1, #TASK.monsters do
                    player:setStorageValue(TASK.monsters[i].storage, 0)
                end
            end
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "You have started: "..TASK.name.."!")
            return true
            
    elseif modalWindowId == 0x12 then
            local TASK = npcMissions[buttonId]
            
            if not TASK then print('Internal Error finding Task') return false end
            
            local canTurnIn = true
            
            if TASK.monsters then
                for i = 1, #TASK.monsters do
                    if player:getStorageValue(TASK.monsters[i].storage) < TASK.monsters[i].amount then
                        canTurnIn = false
                    end
                end
            end
            
            if TASK.collectItems then
                for i = 1, #TASK.collectItems do
                    if player:getItemCount(TASK.collectItems[i].itemid) < TASK.collectItems[i].amount then
                        canTurnIn = false
                    end
                end
            end
            
            if canTurnIn then
                if TASK.monsters then
                    for i = 1, #TASK.monsters do
                        player:setStorageValue(TASK.monsters[i].storage, -1)
                    end
                end
            
                if TASK.collectItems then
                    for i = 1, #TASK.collectItems do
                        player:removeItem(TASK.collectItems[i].itemid, TASK.collectItems[i].amount)
                    end
                end
                
                player:setStorageValue(TASK.storageMission, 2)
                
                if TASK.exp then
                    player:addExperience(TASK.exp)
                end
                
                if TASK.rewardItems then
                    for i = 1, #TASK.rewardItems do
                        player:addItem(TASK.rewardItems[i].itemid, TASK.rewardItems[i].amount, true)
                    end
                end
                
                player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "You have completed: "..TASK.name.."!")
            else
                player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "You are not ready to turn in this task.")
            end
        end
    return true
end

add creaturescript: TaskSystemKill.lua
Code:
local missions = { -- Use missionStorage to link the monsters to each mission. It is set up this way so multiple missions can have the same monsters.
    [1] = {name = "Rat", amount = 10, missionStorage = 43000, storage = 41000},
    [2] = {name = "Cave Rat", amount = 10, missionStorage = 43000, storage = 41001},
    [3] = {name = "Troll", amount = 10, missionStorage = 43001, storage = 41002}
}

function onKill(creature, target)
    if isPlayer(creature) and isMonster(target) then
        for i = 1, #missions do
            if target:getName() == missions[i].name then
                if creature:getStorageValue(missions[i].missionStorage) == 1 then
                    if creature:getStorageValue(missions[i].storage) < missions[i].amount then
                        creature:setStorageValue(missions[i].storage, creature:getStorageValue(missions[i].storage) + 1)
                        creature:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "You have killed "..creature:getStorageValue(missions[i].storage).."/"..missions[i].amount..") "..missions[i].name.."('s)")
                    end
                end
            end
        end
    end
    return true
end

NPC file
Code:
local keywordHandler = KeywordHandler:new()
local npcHandler = NpcHandler:new(keywordHandler)
NpcSystem.parseParameters(npcHandler)

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

npcHandler:addModule(FocusModule:new())

local npcMissions = {
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    [1] = {name = "Rat Killer", -- Kill monsters only
        arrayName = {"Rat", "rat", "Rats", "rats", "Pest", "pest", "Pests", "pests", "rat killer", "Rat killer", "Rat Killer"},
        storageMission = 43000, -- Mission storage should be unique even in different NPC files.
        storageRequired = nil, -- Use this to require different tasks to be done complete before this task can be started.
        messageTaskInfo = "I need you to kill 10 rats and 10 cave rats.", -- This message is said to the player when he starts the task.
        messageTaskComplete = "Thank you! Those pests have ruined my store. unfortunately they will be back. The rats aren't the main {problem}", -- This message is said to the player when he finishs the task.
        windowMsg = "Kill rats in the basement.", -- This text is shown under the title of the modal window
        monsters = { -- Monster task
            [1] = {name = "Rat", amount = 10, storage = 41000}, -- Monster storages should be unique even in different NPC files.
            [2] = {name = "Cave Rat", amount = 10, storage = 41001}
        },
        --Rewards--
        exp = 1000, -- Exp given to the player for completing the task.
        rewardItems = { -- Items given to the player for completing the task.
            [1] = {itemid = 2390, amount = 1}
        },
        setStorage = nil -- Use this to set any storages you want to when the player completes the task excluding storageNpc and storageMission
    },
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    [2] = {name = "Troll Hunter",
        arrayName = {"troll hunter", "Troll Hunter", "Troll", "troll", "Trolls", "trolls"},
        storageMission = 43001,
        storageRequired = {43000},
        messageTaskInfo = "I need you to kill 20 trolls.",
        messageTaskComplete = "You are truley a legend. There is one more thing I need. Could you {help} me one more time?",
        windowMsg = "Kill trolls in the forst to the east. Also, collect x items",
        monsters = {
            [1] = {name = "Troll", amount = 20, storage = 41002},
        },
        collectItems = { -- Collect Items task
            [1] = {itemid = 1111, amount = 1}
        },
        --Rewards--
        exp = 5000,
        rewardItems = {
            [1] = {itemid = 2390, amount = 1}
        },
        setStorage = {
            [1] = {41532, 1}
        }
    },
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    [3] = {name = "The Collector", -- Collect Items only
        arrayName = {"collector", "Collector", "collect", "Collect", "cheese", "Cheese"},
        storageMission = 43003,
        storageRequired = {43001, 43002},
        messageTaskInfo = "I need you to collect 10 cheese.",
        messageTaskComplete = "You are truley a legend. I do not need anymore help.",
        windowMsg = "Collect x items",
        collectItems = { -- Collect Items task
            [1] = {itemid = 1111, amount = 1}
        },
        --Rewards--
        exp = 5000,
        rewardItems = {
            [1] = {itemid = 2390, amount = 1}
        },
        setStorage = nil
    }
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
}

local npcStory = { -- This story is told when the player first talks to the npc.
    [1] = "Not now adventurer. I have to deal with these pests.",
    [2] = "Honey! Get my broom, these things are everywhere. They aren't even scared of me!?",
    [3] = "If this continues we will have to close down our business!",
    [4] = "Please god send us some {help}!"
}

local MESSAGES_GREET = { -- Messages are based on the npcStorage for the player. This table handles all greeting interaction between the npc and player.
  --Storage / message
  -- The player gets his storage for the npc set as soon as he talks to the npc
  -- The players stoarge is set to 0. This table is accessed by MESSAGES[player:getStorageValue(npcStorage) - 1]
  -- Everytime a player accepts and completes one of the tasks/missions his npcStorage is increased by 1. That is how the code keeps track of how the npc should respond to the player.
    [1] = "Hello again |PLAYERNAME|. Do you want to {help} me yet?",
    [2] = "I am glad you decided to help. Are those {rats} giving you a problem?",
    [3] = "Thanks for killing those rats. I have another {task} I need {help} with.",
    [4] = "The trolls are powerful. Be sure to take strong gear with you.",
    [5] = "Thank you for all your help."
}

local MESSAGES_GOODBYE = { -- This works the same as MESSAGES_GREET. Depending on how many tasks the player has done for the npc. The npc will say different things when he says goodbye to the player.
    [1] = "Thanks for nothing |PLAYERNAME|.",
    [2] = "Thank you for your help. Goodbye.",
    [3] = "You have helped so much. I will be sure to tell other of you."
}

local arrayGreetings = {"hi", "Hi", "hello", "Hello", "hey", "Hey", "oi", "Oi", "hola", "Hola"}
local arrayFarewell = {"bye", "Bye", "goodbye", "Goodbye", "good-bye", "Good-Bye", "Good-bye", "cya", "adios", "Adios"}

local messageHearStoryAgain = "story"
local messageCheckTasks = {"help", "Help", "task", "Task", "tasks", "Tasks"}
local messageShowTasks = "Here is what I need help with."

local messageCompletedAllTasks = "Thank you for all you have done. I dont need anymore help."
local taskWindowTitle = "Title of Modal Window"
local taskWindowText = "Tasks"

local npcStorage = 45000

function onCreatureSay(cid, type, msg)
    local player = Player(cid)
    local playerGreetMessage = MESSAGES_GREET[player:getStorageValue(npcStorage)]
    local playerGoodbyeMessage = MESSAGES_GOODBYE[player:getStorageValue(npcStorage)]
 
    if isInArray(arrayGreetings, msg) and not npcHandler:isFocused(cid) and player:getStorageValue(npcStorage) == -1 then
        npcHandler:addFocus(cid)
        npcHandler:say(npcStory, player:getId(), false, true, 4000)
        player:setStorageValue(npcStorage, 1)
 
    elseif isInArray(arrayGreetings, msg) and not npcHandler:isFocused(cid) then
        npcHandler:addFocus(cid)
        if string.find(playerGreetMessage, "|PLAYERNAME|") then
            local newMsg = string.gsub(playerGreetMessage, "|PLAYERNAME|", player:getName())
            selfSay(newMsg, cid)
        else
            selfSay(playerGreetMessage, cid)
        end
     
    elseif isInArray(arrayFarewell, msg) and npcHandler:isFocused(cid) then
        if string.find(playerGoodbyeMessage, "|PLAYERNAME|") then
            local newMsg = string.gsub(playerGoodbyeMessage, "|PLAYERNAME|", player:getName())
            selfSay(newMsg, cid)
        else
            selfSay(playerGoodbyeMessage, cid)
        end
        player:setStorageValue(npcStorage, -1)
        npcHandler:releaseFocus(cid)
     
    elseif isInArray(messageCheckTasks, msg) and npcHandler:isFocused(cid) then
        local text = messageShowTasks
        local window = ModalWindow(0x10, taskWindowTitle, taskWindowText)
        local hasMissions = false
        for i = 1, #npcMissions do
            if player:getStorageValue(npcMissions[i].storageMission) < 2 then
                if npcMissions[i].storageRequired then
                local showTask = true
                    for x = 1, #npcMissions[i].storageRequired do
                        if player:getStorageValue(npcMissions[i].storageRequired[x]) ~= 2 then
                            showTask = false
                            break
                        end
                    end
                 
                    if showTask then
                        window:addChoice(i, npcMissions[i].name)
                        hasMissions = true
                    end
                else
                    window:addChoice(i, npcMissions[i].name)
                    hasMissions = true
                end
            end
        end
     
        if hasMissions then
            window:addButton(0x00, "Exit")
            window:addButton(0x01, "Select")
            window:setDefaultEnterButton(0x01)
            window:sendToPlayer(player)
            return true
        else
            selfSay(messageCompletedAllTasks, cid)
            return true
        end
    end
return true
end
 
Last edited:
Converted to Modal Windows

Creaturescript.xml
Code:
<event type="modalwindow" name="TaskSystemWindow" script="TaskSystemWindow.lua" />
<event type="kill" name="TaskSystemKill" script="TaskSystemKill.lua" />

add creaturescript: TaskSystemWindow.lua
Code:
local npcMissions = {
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    [1] = {name = "Rat Killer", -- Kill monsters only
        arrayName = {"Rat", "rat", "Rats", "rats", "Pest", "pest", "Pests", "pests", "rat killer", "Rat killer", "Rat Killer"},
        storageMission = 43000, -- Mission storage should be unique even in different NPC files.
        storageRequired = nil, -- Use this to require different tasks to be done complete before this task can be started.
        messageTaskInfo = "I need you to kill 10 rats and 10 cave rats.", -- This message is said to the player when he starts the task.
        messageTaskComplete = "Thank you! Those pests have ruined my store. unfortunately they will be back. The rats aren't the main {problem}", -- This message is said to the player when he finishs the task.
        windowMsg = "Kill rats in the basement.", -- This text is shown under the title of the modal window
        monsters = { -- Monster task
            [1] = {name = "Rat", amount = 10, storage = 41000}, -- Monster storages should be unique even in different NPC files.
            [2] = {name = "Cave Rat", amount = 10, storage = 41001}
        },
        --Rewards--
        exp = 1000, -- Exp given to the player for completing the task.
        rewardItems = { -- Items given to the player for completing the task.
            [1] = {itemid = 2390, amount = 1}
        },
        setStorage = nil -- Use this to set any storages you want to when the player completes the task excluding storageNpc and storageMission
    },
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    [2] = {name = "Troll Hunter",
        arrayName = {"troll hunter", "Troll Hunter", "Troll", "troll", "Trolls", "trolls"},
        storageMission = 43001,
        storageRequired = {43000},
        messageTaskInfo = "I need you to kill 20 trolls.",
        messageTaskComplete = "You are truley a legend. There is one more thing I need. Could you {help} me one more time?",
        windowMsg = "Kill trolls in the forst to the east. Also, collect x items",
        monsters = {
            [1] = {name = "Troll", amount = 20, storage = 41002},
        },
        collectItems = { -- Collect Items task
            [1] = {itemid = 1111, amount = 1}
        },
        --Rewards--
        exp = 5000,
        rewardItems = {
            [1] = {itemid = 2390, amount = 1}
        },
        setStorage = {
            [1] = {41532, 1}
        }
    },
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    [3] = {name = "The Collector", -- Collect Items only
        arrayName = {"collector", "Collector", "collect", "Collect", "cheese", "Cheese"},
        storageMission = 43003,
        storageRequired = {43001, 43002},
        messageTaskInfo = "I need you to collect 10 cheese.",
        messageTaskComplete = "You are truley a legend. I do not need anymore help.",
        windowMsg = "Collect x items",
        collectItems = { -- Collect Items task
            [1] = {itemid = 1111, amount = 1}
        },
        --Rewards--
        exp = 5000,
        rewardItems = {
            [1] = {itemid = 2390, amount = 1}
        },
        setStorage = nil
    }
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
}

function onModalWindow(player, modalWindowId, buttonId, choiceId)
    if modalWindowId == 0x10 then
        if buttonId == 0x01 then
            local TASK = npcMissions[choiceId]
           
            if not TASK then print('Internal Error finding Task') return false end
            if player:getStorageValue(TASK.storageMission) == 2 then
                local window = ModalWindow(0x12, TASK.name, TASK.windowMsg)
                window:addChoice(0x00, "You have already completed this task.")
                window:addButton(0x00, "Exit")
                window:setDefaultEnterButton(0x00)
                window:sendToPlayer(player)
                return true
            elseif player:getStorageValue(TASK.storageMission) == 1 then
                local window = ModalWindow(0x12, TASK.name, TASK.windowMsg)
                local count = 1
                if TASK.monsters then
                    for i = 1, #TASK.monsters do
                        window:addChoice(count, "("..player:getStorageValue(TASK.monsters[i].storage).."/"..TASK.monsters[i].amount.." "..TASK.monsters[i].name..".")
                        count = count + 1
                    end
                end
               
                if TASK.collectItems then
                    for i = 1, #TASK.collectItems do
                        window:addChoice(count, "("..player:getItemCount(TASK.collectItems[i].itemid).."/"..TASK.collectItems[i].amount.." "..ItemType(TASK.collectItems[i].itemid):getName()..".")
                        count = count + 1
                    end
                end
               
                window:addButton(0x00, "Exit")
                window:addButton(choiceId, "Turn-In")
                window:setDefaultEnterButton(0x01)
                window:sendToPlayer(player)
                return true
               
            elseif player:getStorageValue(TASK.storageMission) == -1 then
                local window = ModalWindow(0x11, TASK.name, TASK.windowMsg)
                local count = 1
                if TASK.monsters then
                    for i = 1, #TASK.monsters do
                        window:addChoice(count, "Kill: "..TASK.monsters[i].amount.." "..TASK.monsters[i].name..".")
                        count = count + 1
                    end
                end
               
                if TASK.collectItems then
                    for i = 1, #TASK.collectItems do
                        window:addChoice(count, "Collect: "..TASK.collectItems[i].amount.." "..ItemType(TASK.collectItems[i].itemid):getName()..".")
                        count = count + 1
                    end
                end
               
                window:addButton(0x00, "Exit")
                window:addButton(choiceId, "Accept")
                window:setDefaultEnterButton(0x01)
                window:sendToPlayer(player)
                return true
            end
            return true
        end
   
    elseif modalWindowId == 0x11 then
            local TASK = npcMissions[buttonId]
       
            if not TASK then print('Internal Error finding Task') return false end
       
            player:setStorageValue(TASK.storageMission, 1)
       
            if TASK.monsters then
                for i = 1, #TASK.monsters do
                    player:setStorageValue(TASK.monsters[i].storage, 0)
                end
            end
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "You have started: "..TASK.name.."!")
            return true
           
    elseif modalWindowId == 0x12 then
            local TASK = npcMissions[buttonId]
           
            if not TASK then print('Internal Error finding Task') return false end
           
            local canTurnIn = true
           
            if TASK.monsters then
                for i = 1, #TASK.monsters do
                    if player:getStorageValue(TASK.monsters[i].storage) < TASK.monsters[i].amount then
                        canTurnIn = false
                    end
                end
            end
           
            if TASK.collectItems then
                for i = 1, #TASK.collectItems do
                    if player:getItemCount(TASK.collectItems[i].itemid) < TASK.collectItems[i].amount then
                        canTurnIn = false
                    end
                end
            end
           
            if canTurnIn then
                if TASK.monsters then
                    for i = 1, #TASK.monsters do
                        player:setStorageValue(TASK.monsters[i].storage, -1)
                    end
                end
           
                if TASK.collectItems then
                    for i = 1, #TASK.collectItems do
                        player:removeItem(TASK.collectItems[i].itemid, TASK.collectItems[i].amount)
                    end
                end
               
                player:setStorageValue(TASK.storageMission, 2)
               
                if TASK.exp then
                    player:addExperience(TASK.exp)
                end
               
                if TASK.rewardItems then
                    for i = 1, #TASK.rewardItems do
                        player:addItem(TASK.rewardItems[i].itemid, TASK.rewardItems[i].amount, true)
                    end
                end
               
                player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "You have completed: "..TASK.name.."!")
            else
                player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "You are not ready to turn in this task.")
            end
        end
    return true
end

add creaturescript: TaskSystemKill.lua
Code:
local missions = { -- Use missionStorage to link the monsters to each mission. It is set up this way so multiple missions can have the same monsters.
    [1] = {name = "Rat", amount = 10, missionStorage = 43000, storage = 41000},
    [2] = {name = "Cave Rat", amount = 10, missionStorage = 43000, storage = 41001},
    [3] = {name = "Troll", amount = 10, missionStorage = 43001, storage = 41002}
}

function onKill(creature, target)
    if isPlayer(creature) and isMonster(target) then
        for i = 1, #missions do
            if target:getName() == missions[i].name then
                if creature:getStorageValue(missions[i].missionStorage) == 1 then
                    if creature:getStorageValue(missions[i].storage) < missions[i].amount then
                        creature:setStorageValue(missions[i].storage, creature:getStorageValue(missions[i].storage) + 1)
                        creature:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "You have killed "..creature:getStorageValue(missions[i].storage).."/"..missions[i].amount..") "..missions[i].name.."('s)")
                    end
                end
            end
        end
    end
    return true
end

NPC file
Code:
local keywordHandler = KeywordHandler:new()
local npcHandler = NpcHandler:new(keywordHandler)
NpcSystem.parseParameters(npcHandler)

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

npcHandler:addModule(FocusModule:new())

local npcMissions = {
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    [1] = {name = "Rat Killer", -- Kill monsters only
        arrayName = {"Rat", "rat", "Rats", "rats", "Pest", "pest", "Pests", "pests", "rat killer", "Rat killer", "Rat Killer"},
        storageMission = 43000, -- Mission storage should be unique even in different NPC files.
        storageRequired = nil, -- Use this to require different tasks to be done complete before this task can be started.
        messageTaskInfo = "I need you to kill 10 rats and 10 cave rats.", -- This message is said to the player when he starts the task.
        messageTaskComplete = "Thank you! Those pests have ruined my store. unfortunately they will be back. The rats aren't the main {problem}", -- This message is said to the player when he finishs the task.
        windowMsg = "Kill rats in the basement.", -- This text is shown under the title of the modal window
        monsters = { -- Monster task
            [1] = {name = "Rat", amount = 10, storage = 41000}, -- Monster storages should be unique even in different NPC files.
            [2] = {name = "Cave Rat", amount = 10, storage = 41001}
        },
        --Rewards--
        exp = 1000, -- Exp given to the player for completing the task.
        rewardItems = { -- Items given to the player for completing the task.
            [1] = {itemid = 2390, amount = 1}
        },
        setStorage = nil -- Use this to set any storages you want to when the player completes the task excluding storageNpc and storageMission
    },
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    [2] = {name = "Troll Hunter",
        arrayName = {"troll hunter", "Troll Hunter", "Troll", "troll", "Trolls", "trolls"},
        storageMission = 43001,
        storageRequired = {43000},
        messageTaskInfo = "I need you to kill 20 trolls.",
        messageTaskComplete = "You are truley a legend. There is one more thing I need. Could you {help} me one more time?",
        windowMsg = "Kill trolls in the forst to the east. Also, collect x items",
        monsters = {
            [1] = {name = "Troll", amount = 20, storage = 41002},
        },
        collectItems = { -- Collect Items task
            [1] = {itemid = 1111, amount = 1}
        },
        --Rewards--
        exp = 5000,
        rewardItems = {
            [1] = {itemid = 2390, amount = 1}
        },
        setStorage = {
            [1] = {41532, 1}
        }
    },
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    [3] = {name = "The Collector", -- Collect Items only
        arrayName = {"collector", "Collector", "collect", "Collect", "cheese", "Cheese"},
        storageMission = 43003,
        storageRequired = {43001, 43002},
        messageTaskInfo = "I need you to collect 10 cheese.",
        messageTaskComplete = "You are truley a legend. I do not need anymore help.",
        windowMsg = "Collect x items",
        collectItems = { -- Collect Items task
            [1] = {itemid = 1111, amount = 1}
        },
        --Rewards--
        exp = 5000,
        rewardItems = {
            [1] = {itemid = 2390, amount = 1}
        },
        setStorage = nil
    }
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
}

local npcStory = { -- This story is told when the player first talks to the npc.
    [1] = "Not now adventurer. I have to deal with these pests.",
    [2] = "Honey! Get my broom, these things are everywhere. They aren't even scared of me!?",
    [3] = "If this continues we will have to close down our business!",
    [4] = "Please god send us some {help}!"
}

local MESSAGES_GREET = { -- Messages are based on the npcStorage for the player. This table handles all greeting interaction between the npc and player.
  --Storage / message
  -- The player gets his storage for the npc set as soon as he talks to the npc
  -- The players stoarge is set to 0. This table is accessed by MESSAGES[player:getStorageValue(npcStorage) - 1]
  -- Everytime a player accepts and completes one of the tasks/missions his npcStorage is increased by 1. That is how the code keeps track of how the npc should respond to the player.
    [1] = "Hello again |PLAYERNAME|. Do you want to {help} me yet?",
    [2] = "I am glad you decided to help. Are those {rats} giving you a problem?",
    [3] = "Thanks for killing those rats. I have another {task} I need {help} with.",
    [4] = "The trolls are powerful. Be sure to take strong gear with you.",
    [5] = "Thank you for all your help."
}

local MESSAGES_GOODBYE = { -- This works the same as MESSAGES_GREET. Depending on how many tasks the player has done for the npc. The npc will say different things when he says goodbye to the player.
    [1] = "Thanks for nothing |PLAYERNAME|.",
    [2] = "Thank you for your help. Goodbye.",
    [3] = "You have helped so much. I will be sure to tell other of you."
}

local arrayGreetings = {"hi", "Hi", "hello", "Hello", "hey", "Hey", "oi", "Oi", "hola", "Hola"}
local arrayFarewell = {"bye", "Bye", "goodbye", "Goodbye", "good-bye", "Good-Bye", "Good-bye", "cya", "adios", "Adios"}

local messageHearStoryAgain = "story"
local messageCheckTasks = {"help", "Help", "task", "Task", "tasks", "Tasks"}
local messageShowTasks = "Here is what I need help with."

local messageCompletedAllTasks = "Thank you for all you have done. I dont need anymore help."
local taskWindowTitle = "Title of Modal Window"
local taskWindowText = "Tasks"

local npcStorage = 45000

function onCreatureSay(cid, type, msg)
    local player = Player(cid)
    local playerGreetMessage = MESSAGES_GREET[player:getStorageValue(npcStorage)]
    local playerGoodbyeMessage = MESSAGES_GOODBYE[player:getStorageValue(npcStorage)]

    if isInArray(arrayGreetings, msg) and not npcHandler:isFocused(cid) and player:getStorageValue(npcStorage) == -1 then
        npcHandler:addFocus(cid)
        npcHandler:say(npcStory, player:getId(), false, true, 4000)
        player:setStorageValue(npcStorage, 1)

    elseif isInArray(arrayGreetings, msg) and not npcHandler:isFocused(cid) then
        npcHandler:addFocus(cid)
        if string.find(playerGreetMessage, "|PLAYERNAME|") then
            local newMsg = string.gsub(playerGreetMessage, "|PLAYERNAME|", player:getName())
            selfSay(newMsg, cid)
        else
            selfSay(playerGreetMessage, cid)
        end
    
    elseif isInArray(arrayFarewell, msg) and npcHandler:isFocused(cid) then
        if string.find(playerGoodbyeMessage, "|PLAYERNAME|") then
            local newMsg = string.gsub(playerGoodbyeMessage, "|PLAYERNAME|", player:getName())
            selfSay(newMsg, cid)
        else
            selfSay(playerGoodbyeMessage, cid)
        end
        player:setStorageValue(npcStorage, -1)
        npcHandler:releaseFocus(cid)
    
    elseif isInArray(messageCheckTasks, msg) and npcHandler:isFocused(cid) then
        local text = messageShowTasks
        local window = ModalWindow(0x10, taskWindowTitle, taskWindowText)
        local hasMissions = false
        for i = 1, #npcMissions do
            if player:getStorageValue(npcMissions[i].storageMission) < 2 then
                if npcMissions[i].storageRequired then
                local showTask = true
                    for x = 1, #npcMissions[i].storageRequired do
                        if player:getStorageValue(npcMissions[i].storageRequired[x]) ~= 2 then
                            showTask = false
                            break
                        end
                    end
                
                    if showTask then
                        window:addChoice(i, npcMissions[i].name)
                        hasMissions = true
                    end
                else
                    window:addChoice(i, npcMissions[i].name)
                    hasMissions = true
                end
            end
        end
    
        if hasMissions then
            window:addButton(0x00, "Exit")
            window:addButton(0x01, "Select")
            window:setDefaultEnterButton(0x01)
            window:sendToPlayer(player)
            return true
        else
            selfSay(messageCompletedAllTasks, cid)
            return true
        end
    end
return true
end
could convert to tfs 1.3 i am having too many errors thanks again
 
This system was tested on tfs 1.3 could you show errors?
 
When I talk to this npc and say help, I get the window with the first task "Rat Killer" but when I click on select nothing happens.
I don't get any errors or anything.
Any suggestions?
Thanks in advance!
 
If you don't know how to add files to your server correctly, you probably won't be able to get this to work. Both codes have been tested. Look around on the forum on information for linking lua files to your xml files.
 
Ive tested the script and it works fine. Remember to add the script to Login.lua as well for it to count.

I have an issue tho. Ive copied the scripts and added everything correctly so that the missions starts, the killing is counted and the npc gives rewards etc. The only issue is, it that no matter what what the amount of "Rats" i want the player to kill, the update in the Local Chat says the max is 10.
So 1/10, 2/10 etc even tho Ive copied the scripts and the only thing ive changed is the amount of Rats needed to be killed, from 10 to 20. Both in the NPC script, and the CREATURESCRIPT.

Ill add the scripts below in case ive missed something.


Lua:
local keywordHandler = KeywordHandler:new()
local npcHandler = NpcHandler:new(keywordHandler)
NpcSystem.parseParameters(npcHandler)

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

npcHandler:addModule(FocusModule:new())

local npcMissions = {
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    [1] = {name = "Rat Killer", -- Kill monsters only
        arrayName = {"Rat", "rat", "Rats", "rats", "Pest", "pest", "Pests", "pests", "rat killer", "Rat killer", "Rat Killer"},
        storageMission = 43000, -- Mission storage should be unique even in different NPC files.
        storageRequired = nil, -- Use this to require different tasks to be done complete before this task can be started.
        messageTaskInfo = "I need you to kill 10 rats and 10 cave rats.", -- This message is said to the player when he starts the task.
        messageTaskComplete = "Thank you! Those pests have ruined my store. unfortunately they will be back. The rats aren't the main {problem}", -- This message is said to the player when he finishs the task.
        monsters = { -- Monster task
            [1] = {name = "Rat", amount = 20, storage = 41000}, -- Monster storages should be unique even in different NPC files.
            [2] = {name = "Cave Rat", amount = 20, storage = 41001}
        },
        --Rewards--
        exp = 1000, -- Exp given to the player for completing the task.
        rewardItems = { -- Items given to the player for completing the task.
            [1] = {itemid = 2390, amount = 1}
        },
        setStorage = nil -- Use this to set any storages you want to when the player completes the task excluding storageNpc and storageMission
    },
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    [2] = {name = "Troll Hunter",
        arrayName = {"troll hunter", "Troll Hunter", "Troll", "troll", "Trolls", "trolls"},
        storageMission = 43001,
        storageRequired = {43000},
        messageTaskInfo = "I need you to kill 20 trolls.",
        messageTaskComplete = "You are truley a legend. There is one more thing I need. Could you {help} me one more time?",
        monsters = {
            [1] = {name = "Troll", amount = 20, storage = 41002},
        },
        collectItems = { -- Collect Items task
            [1] = {itemid = 1111, amount = 1}
        },
        --Rewards--
        exp = 5000,
        rewardItems = {
            [1] = {itemid = 2390, amount = 1}
        },
        setStorage = {
            [1] = {41532, 1}
        }
    },
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    [3] = {name = "The Collector", -- Collect Items only
        arrayName = {"collector", "Collector", "collect", "Collect", "cheese", "Cheese"},
        storageMission = 43003,
        storageRequired = {43001, 43002},
        messageTaskInfo = "I need you to collect 10 cheese.",
        messageTaskComplete = "You are truley a legend. I do not need anymore help.",
        collectItems = { -- Collect Items task
            [1] = {itemid = 1111, amount = 1}
        },
        --Rewards--
        exp = 5000,
        rewardItems = {
            [1] = {itemid = 2390, amount = 1}
        },
        setStorage = nil
    }
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
}

local npcStory = { -- This story is told when the player first talks to the npc.
    "Not now adventurer. I have to deal with these pests.",
    "Honey! Get my broom, these things are everywhere. They aren't even scared of me!?",
    "If this continues we will have to close down our business!",
    "Please god send us some {help}!"
}

local MESSAGES_GREET = { -- Messages are based on the npcStorage for the player. This table handles all greeting interaction between the npc and player.
  --Storage / message
  -- The player gets his storage for the npc set as soon as he talks to the npc
  -- Everytime a player accepts and completes one of the tasks/missions his npcStorage is increased by 1. That is how the code keeps track of how the npc should respond to the player.
    [1] = "Hello again |PLAYERNAME|. Do you want to {help} me yet?",
    [2] = "I am glad you decided to help. Are those {rats} giving you a problem?",
    [3] = "Thanks for killing those rats. I have another {task} I need {help} with.",
    [4] = "The trolls are powerful. Be sure to take strong gear with you.",
    [5] = "Thank you for all your help."
}

local MESSAGES_GOODBYE = { -- This works the same as MESSAGES_GREET. Depending on how many tasks the player has done for the npc. The npc will say different things when he says goodbye to the player.
    [1] = "Thanks for nothing |PLAYERNAME|.",
    [2] = "Thank you for your help. Goodbye.",
    [3] = "You have helped so much. I will be sure to tell other of you."
}

local arrayGreetings = {"hi", "Hi", "hello", "Hello", "hey", "Hey", "oi", "Oi", "hola", "Hola"}
local arrayFarewell = {"bye", "Bye", "goodbye", "Goodbye", "good-bye", "Good-Bye", "Good-bye", "cya", "adios", "Adios"}

local messageHearStoryAgain = "story"
local messageCheckTasks = {"help", "Help", "task", "Task", "tasks", "Tasks"}
local messageShowTasks = "Here is what I need help with:"

local messageCompletedAllTasks = "Thank you for all you have done. I dont need anymore help."

local npcStorage = 45000

function onCreatureSay(cid, type, msg)
    local player = Player(cid)
    local playerGreetMessage = MESSAGES_GREET[player:getStorageValue(npcStorage)]
    local playerGoodbyeMessage = MESSAGES_GOODBYE[player:getStorageValue(npcStorage)]

    if isInArray(arrayGreetings, msg) and not npcHandler:isFocused(cid) and player:getStorageValue(npcStorage) == -1 then
        npcHandler:addFocus(cid)
        npcHandler:say(npcStory, cid, false, true, 4000)
        player:setStorageValue(npcStorage, 1)

    elseif isInArray(arrayGreetings, msg) and not npcHandler:isFocused(cid) then
        npcHandler:addFocus(cid)
        if string.find(playerGreetMessage, "|PLAYERNAME|") then
            local newMsg = string.gsub(playerGreetMessage, "|PLAYERNAME|", player:getName())
            selfSay(newMsg, cid)
        else
            selfSay(playerGreetMessage, cid)
        end
    
    elseif isInArray(arrayFarewell, msg) and npcHandler:isFocused(cid) then
        if string.find(playerGoodbyeMessage, "|PLAYERNAME|") then
            local newMsg = string.gsub(playerGoodbyeMessage, "|PLAYERNAME|", player:getName())
            selfSay(newMsg, cid)
        else
            selfSay(playerGoodbyeMessage, cid)
        end
        npcHandler:releaseFocus(cid)
    
    elseif isInArray(messageCheckTasks, msg) and npcHandler:isFocused(cid) then
        local text = messageShowTasks
        local tmpTable = {}
    
        for i = 1, #npcMissions do
            if player:getStorageValue(npcMissions[i].storageMission) < 2 then
                if npcMissions[i].storageRequired then
                local showTask = true
                    for x = 1, #npcMissions[i].storageRequired do
                        if player:getStorageValue(npcMissions[i].storageRequired[x]) ~= 2 then
                            showTask = false
                            break
                        end
                    end
                
                    if showTask then
                        tmpTable[#tmpTable + 1] = npcMissions[i].name
                    end
                else
                    tmpTable[#tmpTable + 1] = npcMissions[i].name
                end
            end
        end
    
        if #tmpTable > 0 then
            for i = 1, #tmpTable do
                if i == #tmpTable then
                    text = text.." {"..tmpTable[i].."}"
                else
                    text = text.." {"..tmpTable[i].."},"
                end
            end
            selfSay(text, cid)
        else
            selfSay(messageCompletedAllTasks, cid)
        end

    elseif msg and npcHandler:isFocused(cid) then
        local MISSION = nil
        for i = 1, #npcMissions do
            if isInArray(npcMissions[i].arrayName, msg) then
                MISSION = npcMissions[i]
                break
            end
        end
    
        if MISSION == nil then
            return false
        end
    
        if player:getStorageValue(MISSION.storageMission) == 2 then
            selfSay("You have already completed {"..MISSION.name.."}.", cid)
            return false
        end

        local canDoTask = true
        if MISSION.storageRequired then
            for i = 1, #MISSION.storageRequired do
                if player:getStorageValue(MISSION.storageRequired) ~= 2 then
                    canDoTask = false
                    break
                end
            end
        end
    
        if not canDoTask then
            selfSay("You are not ready for {"..MISSION.name.."}.", cid)
        return false
        end
    
        if player:getStorageValue(MISSION.storageMission) == 1 then
            local isTaskDone = true
            if MISSION.monsters then
                for i = 1, #MISSION.monsters do
                    if player:getStorageValue(MISSION.monsters[i].storage) ~= MISSION.monsters[i].amount then
                        isTaskDone = false
                        break
                    end
                end
            end
        
            if MISSION.collectItems then
                for i = 1, #MISSION.collectItems do
                    if player:getItemCount(MISSION.collectItems[i].itemid) < MISSION.collectItems[i].amount then
                        isTaskDone = false
                        break
                    end
                end
            end
        
            if isTaskDone then
                if MISSION.monsters then
                    for i = 1, #MISSION.monsters do
                        player:setStorageValue(MISSION.monsters[i].storage, -1)
                    end
                end
            
                if MISSION.collectItems then
                    for i = 1, #MISSION.collectItems do
                        player:removeItem(MISSION.collectItems[i].itemid, MISSION.collectItems[i].amount)
                    end
                end
            
                if MISSION.exp then
                    player:addExperience(MISSION.exp)
                end
            
                if MISSION.rewardItems then
                    for i = 1, #MISSION.rewardItems do
                        player:addItem(MISSION.rewardItems[i].itemid, MISSION.rewardItems[i].amount, true)
                    end
                end
            
                selfSay(MISSION.messageTaskComplete, cid)
                player:setStorageValue(npcStorage, player:getStorageValue(npcStorage) + 1)
                player:setStorageValue(MISSION.storageMission, 2)
                return false
            end
    
            local text = "["..MISSION.name.."]: "
            if MISSION.monsters then
                for i = 1, #MISSION.monsters do
                    if i == #MISSION.monsters then
                        text = text.."("..player:getStorageValue(MISSION.monsters[i].storage).."/"..MISSION.monsters[i].amount..") "..MISSION.monsters[i].name.." "
                    else
                        text = text.."("..player:getStorageValue(MISSION.monsters[i].storage).."/"..MISSION.monsters[i].amount..") "..MISSION.monsters[i].name..", "
                    end
                end
            end
        
            if MISSION.collectItems then
                for i = 1, #MISSION.collectItems do
                    if i == #MISSION.collectItems then
                        text = text.."("..player:getItemCount(MISSION.collectItems[i].itemid).."/"..MISSION.collectItems[i].amount..") "..ItemType(MISSION.collectItems[i].itemid):getName().." "
                    else
                        text = text.."("..player:getItemCount(MISSION.collectItems[i].itemid).."/"..MISSION.collectItems[i].amount..") "..ItemType(MISSION.collectItems[i].itemid):getName()..", "
                    end
                end
            end
            selfSay(text, cid)
            return false
        end
        
        
        selfSay(MISSION.messageTaskInfo, cid)
        player:setStorageValue(MISSION.storageMission, 1)
        player:setStorageValue(npcStorage, player:getStorageValue(npcStorage) + 1)

        if MISSION.monsters then
            for i = 1, #MISSION.monsters do
                player:setStorageValue(MISSION.monsters[i].storage, 0)
            end
        end
    end
return true
end


And:

Lua:
local missions = { -- Use missionStorage to link the monsters to each mission. It is set up this way so multiple missions can have the same monsters.
    [1] = {name = "Rat", amount = 20, missionStorage = 43000, storage = 41000},
    [2] = {name = "Cave Rat", amount = 20, missionStorage = 43000, storage = 41001},
    [3] = {name = "Troll", amount = 10, missionStorage = 43001, storage = 41002}
}

function onKill(creature, target)
    if isPlayer(creature) and isMonster(target) then
        for i = 1, #missions do
            if target:getName() == missions[i].name then
                if creature:getStorageValue(missions[i].missionStorage) == 1 then
                    if creature:getStorageValue(missions[i].storage) < missions[i].amount then
                        creature:setStorageValue(missions[i].storage, creature:getStorageValue(missions[i].storage) + 1)
                        creature:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "You have killed "..creature:getStorageValue(missions[i].storage).."/"..missions[i].amount.." "..missions[i].name.."('s)"),
                    end
                end
            end
        end
    end
    return true
end


I cant seem to figure out why it doesnt change the 10 to 20 when you kill a rat.
I will also add a screenshot to show what i mean.

( I edited the 5/10 rats shown in the picture from another screenshot where I just killed a rat. It will ofcourse show up in the Local chat and not NPC-chat. But as I said, I just added it into ONE screenshot to make it easier)

Hope someone can help me with this, because I have NO idea where the script get the number 10 from when ive changed all the numbers from 10 to 20, and the NPC clearly know its 20. Its just the counter that still thinks its 10 for some reason. Something iam missing? :)

After testing it further i found this issue:
Since it says the maximum amout of rats are 10 (even if it says 20 in both scripts), the counter stops counting at 10 even if i kill more then 10.
And if I change lets say the Cave Rat to Snake (in both scripts), it wount recognize that Ive killed a snake. It simply says 0/20 when i talk to the NPC, and it doest register at all in the counting (No red text coming up in the Local chat, as it does with the rats).
 

Attachments

Last edited:
So.. my problem is that I am trying to create just a monster task npc and after i complete the first task, it will not let me accept the second task. It just keep reiterating that "You are not ready for" line:

Code:
 local canDoTask = true
        if MISSION.storageRequired then
            for i = 1, #MISSION.storageRequired do
                if player:getStorageValue(MISSION.storageRequired) ~= 2 then
                    canDoTask = false
                    break
                end
            end
        end
    
        if not canDoTask then
            selfSay("You are not ready for {"..MISSION.name.."}.", cid)
        return false
        end

And here is what I changed apart from your script shown:

Code:
 local npcMissions = {
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    [1] = {name = "Rats", -- Kill monsters only
        arrayName = {"Rat", "rat", "Rats", "rats"},
        storageMission = 43000, -- Mission storage should be unique even in different NPC files.
        storageRequired = nil, -- Use this to require different tasks to be done complete before this task can be started.
        messageTaskInfo = "Hmph. Well then, prove to me you are capable. Go and slay 10 rat.", -- This message is said to the player when he starts the task.
        messageTaskComplete = "Well well, you made it out in one piece. Ready for your next {task}?", -- This message is said to the player when he finishs the task.
        monsters = { -- Monster task
            [1] = {name = "Rat", amount = 10, storage = 41000}, -- Monster storages should be unique even in different NPC files.
        },
        --Rewards--
        exp = 150, -- Exp given to the player for completing the task.
        setStorage = nil    -- Use this to set any storages you want to when the player completes the task excluding storageNpc and storageMission
    },
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    [2] = {name = "Wolves",
        arrayName = {"wolf", "Wolf", "wolves", "Wolves"},
        storageMission = 43001,
        storageRequired = {43000},
        messageTaskInfo = "You still haven't completely convinced me. Your next task is to go out into the wild and slay 8 wolves. I hope you're ready.",
        messageTaskComplete = "Very impressive. I suppose you are worthy of training. If you are looking for another {task}, just ask and I will be happy to give you one.",
        monsters = {
            [1] = {name = "Wolf", amount = 8, storage = 41001},
        },
        --Rewards--
        exp = 350,
        setStorage = nil
    },
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    [3] = {name = "Bears",
        arrayName = {"Bear", "bear", "Bears", "bears"},
        storageMission = 43002,
        storageRequired = {43001},
        messageTaskInfo = "I must admit, you have it in you. Well then, we must continue our training. I heard there were some bears near by, and I need you to take them out. I need you to extinguish 15 and report back.",
        messageTaskComplete = "Well done young one. But we have further more to be sure our town is safe. Come back when you are ready for another {task}.",
        monsters = {
            [1] = {name = "Bear", amount = 15, storage = 41002},
        },
        --Rewards--
        exp = 700,
        setStorage = nil
    },
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
        [4] = {name = "Trolls",
        arrayName = {"Troll", "Trolls", "troll", "trolls"},
        storageMission = 43003,
        storageRequired = {43002},
        messageTaskInfo = "For your next task, I need you to find the Troll lair and extinguish them. You can find their lair to the north east of town. Although, their cave is very well hidden. Be sure to bring the proper supplies before you leave!",
        messageTaskComplete = "And another one bites the dust. That's what I like to see.",
        monsters = {
            [1] = {name = "Troll", amount = 20, storage = 41003},
        },
        --Rewards--
        exp = 1000,
        setStorage = nil
    }
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------
 
I have the same problem as Charlyhustle
When I talk to this npc and say help, I get the window with the first task "Rat Killer" but when I click on select nothing happens.
I don't get any errors or anything.
Any suggestions?
Thanks in advance!

I`m using 1.3 TFS and the pop up does indeed show but the buttons (exit and select) do not work, as in do not trigger the next event.
EDIT
I added in the login script

Lua:
    player:registerEvent("TaskSystemWindow")
    player:registerEvent("TaskSystemKill")

Now the modals work but for some reason the NPC will not end the conversation and modal will be able to show up everywhere once you talk in npc channel
 
Last edited:
I am having this error below when I press the "exit" button

1584379250676.png
1584379335916.png

Would I like a button option to return to choose the initial tasks would also be good.
could anyone help?
 
Does anyone use this system?
I realized that when I talk to the npc and if I relog or climb stairs or walk away it doesn't end the conversation.
When i press the button to exit the npc also does not finish the corverse. I'm almost giving up on using it. Someone help?
 
Back
Top