dofile('data/lib/tasklib.lua')
local keywordHandler = KeywordHandler:new()
local npcHandler = NpcHandler:new(keywordHandler)
NpcSystem.parseParameters(npcHandler)
-- OTServ event handling functions start
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 onPlayerCloseChannel(cid) npcHandler:onPlayerCloseChannel(cid) end
local skillNames = {
[1] = "Fist",
[2] = "Club",
[3] = "Sword",
[4] = "Axe",
[5] = "Distance",
[6] = "Shield",
[7] = "Fishing",
}
-- pre-processing to use in ordered task iterator
local config = {}
local first = 14020 -- first task
local last = first
for i in pairs(Tasks) do
if i > last then
last = i
end
end
config.first = first
config.last = last
function Player:giveRewards(task)
local taskname = Tasks[task].name and Tasks[task].name or Tasks[task].creatures[1]
local reply = "For the ".. taskname .." task you have received:\n\n"
local rewards = Tasks[task].rewards
local count = 0
for _ in pairs(rewards) do count = count + 1 end -- easist solution for the message problem.
local ncount = count
for option, value in pairs(rewards) do
count = count - 1
if option == 'addons' then -- Addons
for _, v in ipairs(value) do
local tpe = self:getSex() == 0 and v.female or v.male
self:addOutfitAddon(tpe[1], tpe[2])
end
reply = reply.."- a new addon\n"
elseif option == 'outfits' then -- Outfits
for _, v in ipairs(value) do
local tpe = self:getSex() == 0 and v.female or v.male
self:addOutfit(tpe)
end
reply = reply.."- a new outfit\n"
elseif option == 'storages' then -- Storages
for _, v in ipairs(value) do
self:setStorageValue(v.storage, v.value and v.value or 1)
end
reply = reply.."- new missions unblocked\n"
elseif option == 'mounts' then -- Mounts
for _, v in ipairs(value) do
self:addMount(v)
end
reply = reply.."- a new mount\n"
elseif option == 'skills' then -- Skills
for _, v in ipairs(value) do
self:addSkillTries(v.skillid, v.value)
end
reply = reply.."- "..v.value.." points of "..skillNames[v.skillid]:lower().." skill\n"
elseif option == 'experience' then -- Experience
local quantity = 1
if type(value) == 'number' then
quantity = value
elseif value.relative then
local t_task = Tasks[task]
local mob = MonsterType(string.lower(t_task.creatures[1]))
if mob then
quantity = mob:getExperience()
else
print(t_task.creatures[1].. " is not determined in the game.")
end
quantity = quantity * t_task.count
if value.stages then
local level = self:getLevel()
local experience = self:getExperience()
local nextLevelExperience = getExperienceForLevel(level + 1)
if experience > nextLevelExperience then
nextLevelExperience = experience
end
level = level + (experience/nextLevelExperience)
local multiplier = getMultiplier(level, Game.getExperienceStage(self:getLevel()), 1)
quantity = quantity * multiplier
if value.percent then
quantity = math.ceil((quantity * value.percent)/100)
end
end
end
self:addExperience(quantity, true)
reply = reply.."- "..quantity.." points of experience\n"
elseif option == 'items' then -- Items
reply = reply.."- The follow item"..(#value > 1 and 's' or '').." sent to mailbox:\n"
local parcel = Container(doCreateItemEx(value.container and value.container or 2596))
if value.random then
local v = value[math.random(1, #value)]
local newItem = nil
if type(v[1]) == 'number' then
newItem = Item(doCreateItemEx(v[1], v[2] or 1))
else
local item = ItemType(v[1])
if item then
newItem = Item(doCreateItemEx(item:getId(), v[2] or 1))
end
end
if v.actionid then
newItem:setActionId(v.actionid)
end
reply = reply.."--> "..(v[2] > 1 and v[2] or "").." "..ItemType(v[1]):getName()..'\n'
parcel:addItemEx(newItem)
else
for i, v in ipairs(value) do
if v.chance and v.chance < math.random(1, 100) then goto continue end
if v.vocation and v.vocation ~= self:getVocation():getId() then goto continue end
local newItem = nil
if type(v[1]) == 'number' then
newItem = Item(doCreateItemEx(v[1], v[2] or 1))
else -- if the name is written as string
local item = ItemType(v[1])
if item then
newItem = Item(doCreateItemEx(item:getId(), v[2] or 1))
end
end
if v.actionid then
newItem:setActionId(v.actionid)
end
local itemcount = v[2] and v[2] or 1
reply = reply.."--> "..(itemcount > 1 and itemcount.." "..ItemType(v[1]):getPluralName() or ItemType(v[1]):getName()).."\n"
parcel:addItemEx(newItem)
::continue::
end
end
local packagename = (taskname):lower()..' task package'
parcel:setAttribute(ITEM_ATTRIBUTE_NAME, packagename)
local inbox = self:getInbox()
inbox:addItemEx(parcel, INDEX_WHEREEVER, FLAG_NOLIMIT)
elseif option == 'choose' then
end
end
local answer = self:getTaskMessage(Tasks, task, "Deliver")
if answer then
reply = reply.."\n"..answer
else
reply = reply.."\nEnjoy your ".. (ncount > 1 and 'prizes' or 'prize') .."!"
end
return reply
end
function Player:meetRequirements (i)
if Tasks[i] and self:getLevel() >= Tasks[i].level and (not self:getStorageValue(i) or self:getStorageValue(i) < 0) then
if Tasks[i].requirements then
for i, k in ipairs(Tasks[i].requirements) do
if k.storage and self:getStorageValue(k.storage) and self:getStorageValue(k.storage) < (k.value and k.value or 1) then
return false
end
end
end
return true
end
return false
end
function Player:updateStorages(task, sttask)
self:setStorageValue(task, 1) -- set task as complete
self:setStorageValue(Task_storages.ranking, math.max(self:getStorageValue(Task_storages.ranking), 0) + 1) -- Ranking on site
self:setStorageValue(sttask.task, -1) -- clear storage task
self:setStorageValue(sttask.count, 0) -- clear storage count
local ttype = Tasks[task].type
if self:getStorageValue(TaskTypes[ttype].storage) < 0 then
self:setStorageValue(TaskTypes[ttype].storage, 0) -- set as 0 the storage of tasktype (meaning we already cleared at least one of the tasks, allowing us to proceed)
end
return true
end
function Player:getInProgressTasks()
local doing = {}
for i, v in ipairs(Task_storages) do
if self:getStorageValue(v.task) > 0 then
doing[#doing + 1] = {self:getStorageValue(v.task), i}
end
end
return doing
end
function Player:getFreeSlot()
for i, v in ipairs(Task_storages) do
if not self:getStorageValue(v.task) or self:getStorageValue(v.task) <= 0 then
return i
end
end
return false
end
function creatureSayCallback(cid, typee, msg)
if (not npcHandler:isFocused(cid)) then
return false
end
local player = Player(cid)
local window = ModalWindow {}
window:addButton('Cancel')
window:setDefaultEscapeButton('Cancel')
if (msgcontains(msg, 'task') or msgcontains(msg, 'hunting')) then
local maxTasks = #Task_storages
local doing = player:getInProgressTasks()
if #doing < maxTasks then
local challenges = 0
for ttype, tb in ipairs(TaskTypes) do
local i = 1
repeat
if tb.tasks[i] then
local task = tb.tasks[i]
if player:meetRequirements(task) then
local choice = window:addChoice(tb.name)
choice.correct = tb.tasks
challenges = challenges + 1
i = -1
end
end
i = i + 1
until (i == 0 or i > (config.last - config.first))
end
-- Check if at least one task is available
if challenges > 0 then
local npc = Npc(getNpcCid())
window.title = 'Choosing the task group'
window.message = 'Which group of tasks are you interested to see?'
local function ExpandTaskType(button1, choice1)
local NewWindow = ModalWindow {
title = 'Choosing the task',
message = "Inside the ".. choice1.text .." group there are this options. \nChoose the one that fits you better."
}
NewWindow:addButton('Cancel',
function(button2, choice2)
npc:say("No interest in making a task? Ok then.", TALKTYPE_PRIVATE_NP, false, player, npc:getPosition())
end
)
NewWindow:setDefaultEscapeButton('Cancel')
NewWindow:addButton('Back',
function(button2, choice2)
window:sendToPlayer(player)
npc:say("You are back to the group selection screen.", TALKTYPE_PRIVATE_NP, false, player, npc:getPosition())
end
)
NewWindow:addButton('Choose',
function(button2, choice2)
local taskid = choice2.correct
local freeSlot = player:getFreeSlot()
if freeSlot then
player:setStorageValue(Task_storages[freeSlot].task, taskid)
player:setStorageValue(Task_storages[freeSlot].count, 0)
player:setStorageValue(taskid, 0)
-- Trigger accepted messages
local answer = player:getTaskMessage(Tasks, choice2.correct, "Accept")
if answer then
npc:say(answer, TALKTYPE_PRIVATE_NP, false, player, npc:getPosition())
else
npc:say("You have choosen {".. choice2.text.. "} task and I want you to kill {".. Tasks[taskid].count .."} of them. Come talk to me again once you have finished it.", TALKTYPE_PRIVATE_NP, false, player, npc:getPosition())
end
local reply = "The following creatures will count towards your task count:\n\n"
for i, mob in pairs(Tasks[taskid].creatures) do
reply = reply.."- "..mob.."\n"
end
reply = reply.."\nAny of these creatures will count when killed. If you're in party with shared exp active and in the same screen of the killed creatured it will count regardless of who in the party did the last blow."
player:showTextDialog(13831, reply)
end
end
)
NewWindow:setDefaultEnterButton('Choose')
local tasks = choice1.correct
local doing = player:getInProgressTasks()
for _, t in ipairs(tasks) do
if player:meetRequirements(t) then
local name = Tasks[t].name and Tasks[t].name or Tasks[t].creatures[1]
local choice = NewWindow:addChoice(name)
choice.correct = t
end
end
NewWindow:sendToPlayer(player)
npc:say("In this group I have the following tasks.", TALKTYPE_PRIVATE_NP, false, player, npc:getPosition())
return true
end
window:addButton('Select', ExpandTaskType)
window:setDefaultEnterButton('Select')
window:setDefaultCallback(
function(button, choice)
npc:say("Not interest in starting a task? It's fine, I know this isn't for everyone...", TALKTYPE_PRIVATE_NP, false, player, npc:getPosition())
end
)
window:sendToPlayer(player)
npcHandler:say("I have many kinds of tasks, choose the group that fits you best.", cid)
else
npcHandler:say("Actually things are going pretty calm these days and I don't really need your help in any hunt.", cid)
end
else
npcHandler:say("As I told you before, you can have only {".. maxTasks .."} active "..(maxTasks > 1 and 'tasks' or 'task').." at time. Tell me if you need to {cancel} one.", cid)
end
---------- cancel ------------
elseif msgcontains(msg, 'cancel') then
local doing = player:getInProgressTasks()
if #doing == 0 then
npcHandler:say("You are not doing any task and therefore can't cancel a task, why don't you start one? (say {task} to begin)", cid)
else
local npc = Npc(getNpcCid())
window.title = 'Cancel Task'
window.message = 'Which task do you want to cancel?'
for _, t in ipairs(doing) do
local task = t[1]
local name = Tasks[task].name and Tasks[task].name or Tasks[task].creatures[1]
local choice = window:addChoice(name)
choice.correct = t
end
local function MakeSure(button1, choice1)
local task = choice1.correct[1]
local sttask = Task_storages[choice1.correct[2]]
--player:updateStorages(task, sttask)
local NewWindow = ModalWindow {
title = 'Cancel Task',
message = "Are you sure you want to cancel ".. choice1.text .." task?"
}
NewWindow:addButton('Cancel',
function(button2, choice2)
npc:say("Ok then, we can do this later.", TALKTYPE_PRIVATE_NP, false, player, npc:getPosition())
end
)
NewWindow:setDefaultEscapeButton('Cancel')
NewWindow:addButton('Yes',
function(button2, choice2)
player:setStorageValue(task, -1)
player:setStorageValue(sttask.task, -1)
player:setStorageValue(sttask.count, -1)
npc:say("You are no longer doing the ".. choice1.text.." task.", TALKTYPE_PRIVATE_NP, false, player, npc:getPosition())
end
)
NewWindow:setDefaultEnterButton('Yes')
NewWindow:sendToPlayer(player)
npc:say("Please confirm your will.", TALKTYPE_PRIVATE_NP, false, player, npc:getPosition())
end
window:addButton('Choose', MakeSure)
window:setDefaultEnterButton('Choose')
window:setDefaultCallback(
function(button, choice)
npc:say("Ok then, we can do this later.", TALKTYPE_PRIVATE_NP, false, player, npc:getPosition())
end
)
window:sendToPlayer(player)
npcHandler:say("Choose the task you want to cancel and press \"Choose\" button.", cid)
end
---------- rewards ------------
elseif msgcontains(msg, 'reward') then
local doing = player:getInProgressTasks()
if #doing == 0 then
npcHandler:say("You are not even doing any task... If you are so avid for a reward, let's start one!", cid)
else
local finished = {}
for i = 1, #doing do
if player:getStorageValue(Task_storages[doing[i][2]].count) >= Tasks[doing[i][1]].count then
finished[#finished + 1] = doing[i]
end
end
if #finished == 0 then
npcHandler:say("You didn't finished any task yet, check your questlog for more details on your progress.", cid)
else
local npc = Npc(getNpcCid())
window.title = 'Collect Rewards'
window.message = 'For which task do you want to collect your rewards?'
for _, t in ipairs(finished) do
local task = t[1]
local name = Tasks[task].name and Tasks[task].name or Tasks[task].creatures[1]
local choice = window:addChoice(name)
choice.correct = t
end
local function ProcessCompletion(button, choice)
local task = choice.correct[1]
local sttask = Task_storages[choice.correct[2]]
player:updateStorages(task, sttask)
local reply = player:giveRewards(task)
player:showTextDialog(2596, reply)
npc:say("Here's your prize!", TALKTYPE_PRIVATE_NP, false, player, npc:getPosition())
end
window:addButton('Claim', ProcessCompletion)
window:setDefaultCallback(
function(button, choice)
npc:say("Ok then, we can do this later.", TALKTYPE_PRIVATE_NP, false, player, npc:getPosition())
end
)
window:setDefaultEnterButton('Claim')
window:sendToPlayer(player)
npcHandler:say("Choose the task you want to finish.", cid)
end
end
end
return true
end
npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
npcHandler:addModule(FocusModule:new())