• 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!

Spike Tasks Quest

hodleo

Formerly cbrm -Crypto enthusiast, Retired scripter
Staff member
Global Moderator
Joined
Jan 6, 2009
Messages
6,598
Solutions
3
Reaction score
955
Location
Caribbean Sea
Spike Tasks Quest
Help the Gnomes to fight their enemies around The Spike!
Originally scripted on TFS 1.2, 10.77. Also tested on 10.95
http://tibia.wikia.com/wiki/Spike_Tasks_Quest/Spoiler


393
363


Initial Setup
You need to have common sense, patience and at least an average ot knowledge to install this quest on your real-map server, so you can know why an error occurs while you do it. This has been thoroughly tested to avoid bugs and errors, and written efficiently with Sublime Text 3. I'm open to discussion and suggestions for code improvement and storyline similarity.

Add @data/lib/core/constants.lua
Code:
SPIKE_FAME_POINTS = 27890

SPIKE_UPPER_PACIFIER_MAIN = 27891
SPIKE_UPPER_PACIFIER_DAILY = 27892
SPIKE_UPPER_MOUND_MAIN = 27893
SPIKE_UPPER_MOUND_DAILY = 27894
SPIKE_UPPER_TRACK_MAIN = 27895
SPIKE_UPPER_TRACK_DAILY = 27896
SPIKE_UPPER_KILL_MAIN = 27897
SPIKE_UPPER_KILL_DAILY = 27898

SPIKE_MIDDLE_CHARGE_MAIN = 27899
SPIKE_MIDDLE_CHARGE_DAILY = 27900
SPIKE_MIDDLE_MUSHROOM_MAIN = 27901
SPIKE_MIDDLE_MUSHROOM_DAILY = 27902
SPIKE_MIDDLE_NEST_MAIN = 27903
SPIKE_MIDDLE_NEST_DAILY = 27904
SPIKE_MIDDLE_KILL_MAIN = 27905
SPIKE_MIDDLE_KILL_DAILY = 27906

SPIKE_LOWER_PARCEL_MAIN = 27907
SPIKE_LOWER_PARCEL_DAILY = 27908
SPIKE_LOWER_UNDERCOVER_MAIN = 27909
SPIKE_LOWER_UNDERCOVER_DAILY = 27910
SPIKE_LOWER_LAVA_MAIN = 27911
SPIKE_LOWER_LAVA_DAILY = 27912
SPIKE_LOWER_KILL_MAIN = 27913
SPIKE_LOWER_KILL_DAILY = 27914
ESAEFj.png
Make sure these storage keys are not in use in your server, otherwise feel free to replace these ones with others that you don't use.
Add @data/lib/core/player.lua
Code:
function Player.setExhaustion(self, value, time)
    return self:setStorageValue(value, time + os.time())
end

function Player.getExhaustion(self, value)
    local storage = self:getStorageValue(value)
    if storage <= 0 then
        return 0
    end
    return storage - os.time()
end

function Player.addFamePoint(self)
    local points = self:getStorageValue(SPIKE_FAME_POINTS)
    local current = math.max(0, points)
    self:setStorageValue(SPIKE_FAME_POINTS, current + 1)
    self:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "You have received a fame point.")
end

function Player.getFamePoints(self)
    local points = self:getStorageValue(SPIKE_FAME_POINTS)
    return math.max(0, points)
end

function Player.removeFamePoints(self, amount)
    local points = self:getStorageValue(SPIKE_FAME_POINTS)
    local current = math.max(0, points)
    self:setStorageValue(SPIKE_FAME_POINTS, current - amount)
end

Add @data/lib/core/position.lua
Code:
function Position:compare(position)
    return self.x == position.x and self.y == position.y and self.z == position.z
end

function Position:isInRange(fromPosition, toPosition)
    return (self.x >= fromPosition.x and self.y >= fromPosition.y and self.z >= fromPosition.z
        and self.x <= toPosition.x and self.y <= toPosition.y and self.z <= toPosition.z)
end

function Position:isWalkable()
    local tile = Tile(self)
    if not tile then
          return false
    end

    local ground = tile:getGround()
    if not ground or ground:hasProperty(CONST_PROP_BLOCKSOLID) then
        return false
    end

    local items = tile:getItems()
    for i = 1, tile:getItemCount() do
        local item = items[i]
        local itemType = item:getType()
        if itemType:getType() ~= ITEM_TYPE_MAGICFIELD and not itemType:isMovable() and item:hasProperty(CONST_PROP_BLOCKSOLID) then
            return false
        end
    end
    return true
end

function getFreePosition(from, to)
    local result, tries = Position(from.x, from.y, from.z), 0
    repeat
        local x, y, z = math.random(from.x, to.x), math.random(from.y, to.y), math.random(from.z, to.z)
        result = Position(x, y, z)
        tries = tries + 1
        if tries >= 20 then
            return result
        end                                            
    until result:isWalkable()
    return result
end

function getFreeSand()
    local from, to = ghost_detector_area.from, ghost_detector_area.to
    local result, tries = Position(from.x, from.y, from.z), 0
    repeat
        local x, y, z = math.random(from.x, to.x), math.random(from.y, to.y), math.random(from.z, to.z)
        result = Position(x, y, z)
        tries = tries + 1
        if tries >= 50 then
            return result
        end                                            
    until result:isWalkable() and Tile(result):getGround():getName() == "grey sand"
    return result
end

Add @data/lib/core/string.lua
Code:
string.diff = function(diff)
    local format = {
        {'day', diff / 60 / 60 / 24},
        {'hour', diff / 60 / 60 % 24},
        {'minute', diff / 60 % 60},
        {'second', diff % 60}
    }

    local out = {}
    for k, t in ipairs(format) do
        local v = math.floor(t[2])
        if(v > 0) then
            table.insert(out, (k < #format and (#out > 0 and ', ' or '') or ' and ') .. v .. ' ' .. t[1] .. (v ~= 1 and 's' or ''))
        end
    end
    local ret = table.concat(out)
    if ret:len() < 16 and ret:find('second') then
        local a, b = ret:find(' and ')
        ret = ret:sub(b+1)
    end
    return ret
end

Create folder data/actions/scripts/spike tasks

Add @data/actions/actions.xml
Code:
<!-- Spike Tasks Quest -->
<action itemid="21553" script="spike tasks/spirit shovel.lua"/>
<action itemid="21554" script="spike tasks/tuning fork.lua"/>
<action itemid="21555" script="spike tasks/ghost detector.lua"/>
<action itemid="21556" script="spike tasks/thermometer.lua"/>
<action itemid="21557" script="spike tasks/lodestone.lua"/>
<action itemid="21559" script="spike tasks/nests.lua"/>
<action itemid="21564" script="spike tasks/fertilizer.lua"/>
<action itemid="21566" script="spike tasks/lodestone.lua"/>
<action itemid="21568" script="spike tasks/lodestone.lua"/>

Create folder data/creaturescripts/scripts/spike tasks

Add @data/creaturescripts/creaturescripts.xml
Code:
<!-- Spike Tasks Quest -->
<event type="kill" name="UpperSpikeKill" script="spike tasks/upperspikekill.lua"/>
<event type="kill" name="MiddleSpikeKill" script="spike tasks/middlespikekill.lua"/>
<event type="kill" name="LowerSpikeKill" script="spike tasks/lowerspikekill.lua"/>

Register @data/creaturescripts/scripts/login.lua
Code:
player:registerEvent("UpperSpikeKill")
player:registerEvent("MiddleSpikeKill")
player:registerEvent("LowerSpikeKill")

Add @data/items/items.xml
Code:
<item id="21559" article="a" name="monster nest" />
<item id="21560" article="a" name="destroyed monster nest">
        <attribute key="decayTo" value="21559" />
        <attribute key="duration" value="120" />
</item>
<item id="21561" article="an" name="ominous mound" />
<item id="21562" article="an" name="opened ominous mound">
        <attribute key="decayTo" value="21561" />
        <attribute key="duration" value="120" />
</item>
<item id="21564" article="a" name="flask of mushroom fertilizer">
        <attribute key="weight" value="180" />
        <attribute key="description" value="It holds a liquid concentrate developed by the gnomes to fertilise mushrooms." />
</item>    
<item id="21565" article="a" name="gardener mushroom"/>
<item id="21566" article="a" name="partically charged lodestone">
        <attribute key="weight" value="300" />
</item>    
<item id="21567" article="a" name="magnetic monolith"/>
<item id="21568" article="a" name="highly charged lodestone">
        <attribute key="weight" value="300" />
</item>            
<item id="21713" article="a" name="chargeless monolith">
        <attribute key="decayTo" value="21567" />
        <attribute key="duration" value="120" />
</item>
ESAEFj.png
Replace the old items if you have any of these already.

Create @data/npc/Gnommander.xml
Code:
<?xml version="1.0" encoding="UTF-8"?>
<npc name="Gnommander" script="Gnommander.lua" walkinterval="2000" floorchange="0">
      <health now="100" max="100" />
      <look type="493" head="59" body="57" legs="39" feet="38" addons="0" />
      <parameters>
          <parameter key="message_greet" value="Hi there! Welcome to the spike." />
    </parameters>    
</npc>

Create @data/npc/Gnomux.xml
Code:
<?xml version="1.0" encoding="UTF-8"?>
<npc name="Gnomux" script="Gnomux.lua" walkinterval="2000" floorchange="0">
      <health now="100" max="100" />
      <look type="493" head="12" body="82" legs="39" feet="114" addons="0" />
    <parameters>
        <parameter key="message_greet" value="Hi!" />
    </parameters>
</npc>

Create @data/npc/scripts/Gnommander.lua
Code:
local keywordHandler = KeywordHandler:new()
local npcHandler = NpcHandler:new(keywordHandler)
NpcSystem.parseParameters(npcHandler)
local talkState = {}
local speech = {
    "I'm the operating commander of the Spike, the latest great accomplishment of the gnomish race.",
    "The Spike is a crystal structure, created by our greatest crystal experts. It has grown from a crystal the size of my fist to the structure you see here and now.",
    "Of course this did not happen from one day to the other. It's the fruit of the work of several gnomish generations. Its purpose has changed in the course of time.",
    "At first it was conceived as a fast growing resource node. Then it was planned to become the prototype of a new type of high security base.",
    "Now it has become a military base and a weapon. With our foes occupied elsewhere, we can prepare our strike into the depths of the earth.",
    "This crystal can withstand extreme pressure and temperature, and it's growing deeper and deeper even as we speak.",
    "The times of the fastest growth have come to an end, however, and we have to slow down in order not to risk the structural integrity of the Spike. But we are on our way and have to do everything possible to defend the Spike."
}

function onCreatureAppear(cid)                npcHandler:onCreatureAppear(cid)            end
function onCreatureDisappear(cid)            npcHandler:onCreatureDisappear(cid)            end
function onCreatureSay(cid, type, msg)        npcHandler:onCreatureSay(cid, type, msg)    end
function onThink()                            npcHandler:onThink()                        end

function creatureSayCallback(cid, type, msg)
    if not npcHandler:isFocused(cid) then
        return false
    end
    local player = Player(cid)

    if msgcontains(msg, 'commander') then
        return npcHandler:say('I\'m responsible for the security and reward heroes to our cause. If you are looking for missions, talk to Gnomilly, Gnombold and Gnomagery.', cid)
    end

    if msgcontains(msg, 'reward') then
        return npcHandler:say('I can sell special outfit parts. If your fame is high enough, you might be {worthy} of such a reward.', cid)
    end

    if msgcontains(msg, 'spike') then
        return npcHandler:say(speech, cid)
    end

    if msgcontains(msg, 'worthy') then
        if player:getFamePoints() < 100 then
            return npcHandler:say('You are not worthy of a special reward yet.', cid)
        end

        talkState[cid] = 'worthy'
        return npcHandler:say('You can acquire the {basic} outfit for 1000 Gold, the {first} addon for 2000 gold and the {second} addon for 3000 gold. Which do you want to buy?', cid)
    end

    if talkState[cid] == 'worthy' then
        if msgcontains(msg, 'basic') then
            if getPlayerLevel(cid) < 25 then
                talkState[cid] = nil
                return npcHandler:say('You do not have enough level yet.', cid)
            end
       
            if player:hasOutfit(player:getSex() == 0 and 575 or 574) then
                talkState[cid] = nil
                return npcHandler:say('You already have that outfit.', cid)
            end

            talkState[cid] = 'basic'
            return npcHandler:say('Do you want to buy the basic outfit for 1000 Gold?', cid)
        elseif msgcontains(msg, 'first') then
            if getPlayerLevel(cid) < 50 then
                talkState[cid] = nil
                return npcHandler:say('You do not have enough level yet.', cid)
            end
       
            if not player:hasOutfit(player:getSex() == 0 and 575 or 574) then
                talkState[cid] = nil
                return npcHandler:say('You do not have the Cave Explorer outfit.', cid)
            end

            if player:hasOutfit(player:getSex() == 0 and 575 or 574, 1) then
                talkState[cid] = nil
                return npcHandler:say('You already have that addon.', cid)
            end

            talkState[cid] = 'first'
            return npcHandler:say('Do you want to buy the first addon for 2000 Gold?', cid)
        elseif msgcontains(msg, 'second') then
            if getPlayerLevel(cid) < 80 then
                talkState[cid] = nil
                return npcHandler:say('You do not have enough level yet.', cid)
            end
       
            if not player:hasOutfit(player:getSex() == 0 and 575 or 574) then
                talkState[cid] = nil
                return npcHandler:say('You do not have the Cave Explorer outfit.', cid)
            end

            if player:hasOutfit(player:getSex() == 0 and 575 or 574, 2) then
                talkState[cid] = nil
                return npcHandler:say('You already have that addon.', cid)
            end

            talkState[cid] = 'second'
            return npcHandler:say('Do you want to buy the second addon for 3000 Gold?', cid)
        end
    end

    if talkState[cid] == 'basic' then
        if msgcontains(msg, 'yes') then
            if not player:removeMoney(1000) then
                talkState[cid] = nil
                return npcHandler:say('You do not have that money.', cid)
            end
        end
        player:removeFamePoints(100)
        player:addOutfit(player:getSex() == 0 and 575 or 574)
        talkState[cid] = nil
        return npcHandler:say('Here it is.', cid)
    elseif talkState[cid] == 'first' then
        if msgcontains(msg, 'yes') then
            if not player:removeMoney(2000) then
                talkState[cid] = nil
                return npcHandler:say('You do not have that money.', cid)
            end
        end
        player:removeFamePoints(100)
        player:addOutfitAddon(player:getSex() == 0 and 575 or 574, 1)
        talkState[cid] = nil
        return npcHandler:say('Here it is.', cid)
    elseif talkState[cid] == 'second' then
        if msgcontains(msg, 'yes') then
            if not player:removeMoney(3000) then
                talkState[cid] = nil
                return npcHandler:say('You do not have that money.', cid)
            end
        end
        player:removeFamePoints(100)
        player:addOutfitAddon(player:getSex() == 0 and 575 or 574, 2)
        talkState[cid] = nil
        return npcHandler:say('Here it is.', cid)    
    end
    return true
end

npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
npcHandler:addModule(FocusModule:new())

Create @data/npc/scripts/Gnomux.lua
Code:
local keywordHandler = KeywordHandler:new()
local npcHandler = NpcHandler:new(keywordHandler)
NpcSystem.parseParameters(npcHandler)
local talkState = {}

local spike_items = {
    [21564] = {250, 4, SPIKE_MIDDLE_MUSHROOM_MAIN},
    [21555] = {150, 3, SPIKE_UPPER_TRACK_MAIN},
    [21569] = {100, 4, SPIKE_LOWER_PARCEL_MAIN},
    [21557] = {250, 1, SPIKE_MIDDLE_CHARGE_MAIN},
    [21553] = {150, 4, SPIKE_UPPER_MOUND_MAIN},
    [21556] = {500, 1, SPIKE_LOWER_LAVA_MAIN},
    [21554] = {150, 7, SPIKE_UPPER_PACIFIER_MAIN}
}

local onBuy = function(cid, item, subType, amount, ignoreCap, inBackpacks)
    if not doPlayerRemoveMoney(cid, spike_items[item][1]*amount) then
        selfSay("You don't have enough money.", cid)
    else
        doPlayerAddItem(cid, item, amount)
        selfSay("Here you are!", cid)
    end
    return true
end

function onCreatureAppear(cid)                npcHandler:onCreatureAppear(cid)            end
function onCreatureDisappear(cid)            npcHandler:onCreatureDisappear(cid)            end
function onCreatureSay(cid, type, msg)        npcHandler:onCreatureSay(cid, type, msg)    end
function onThink()                            npcHandler:onThink()                        end

function creatureSayCallback(cid, type, msg)

    if not npcHandler:isFocused(cid) then
        return false
    end

    local player, canBuy, shopWindow = Player(cid), false, {}
    for itemid, data in pairs(spike_items) do
        if not isInArray({-1, data[2]}, player:getStorageValue(data[3])) then
            canBuy = true
            table.insert(shopWindow, {id = itemid, subType = 0, buy = data[1], sell = 0, name = ItemType(itemid):getName()})
        end
    end

    if msgcontains(msg, 'trade') then
        if canBuy then
            openShopWindow(cid, shopWindow, onBuy, onSell)
            return npcHandler:say("Here you are.", cid)
        else
            return npcHandler:say("Sorry, there's nothing for you right now.", cid)
        end
        return true
    end

    if msgcontains(msg, 'job') then
        npcHandler:say("I'm responsible for resupplying foolish adventurers with equipment that they may have lost. If you're one of them, just ask me about a {trade}. ", cid)
    end

    if msgcontains(msg, 'gnome') then
        npcHandler:say("What could I say about gnomes that anyone would not know? I mean, we're interesting if not fascinating, after all.", cid)
    end

    if msgcontains(msg, 'spike') then
        npcHandler:say({"I came here as a crystal farmer and know the Spike all the way back to when it was a little baby crystal. I admit I feel a little fatherly pride in how big and healthy it has become.","When most other crystal experts left for new assignments, I decided to stay and help here a bit."}, cid)
    end
    return true
end

npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
npcHandler:addModule(FocusModule:new())

Map positions for NPCs
Gnommander @32246, 32603, 8
Gnomilly @32243, 32598, 9
Gnombold @32242, 32598, 11
Gnomux @32242, 32611, 13
Gnomargery @32243, 32597, 14
Gnome Trooper @32321, 32586, 13
Gnome Trooper @32166, 32642, 13
Gnome Trooper @32289, 32507, 14
Gnome Trooper @32175, 32654, 14
Gnome Trooper @32157, 32515, 15
A Drillworm @32232, 32684, 13
A Nightmare Scion @32334, 32523, 13
A Vulcongra @32132, 32562, 13
A Dragon Lord @32273, 32629, 14
A Lost Basher @32171, 32630, 14
A Lost Thrower @32306, 32546, 14
A Behemoth @32306, 32582, 15
A Lost Husher @32209, 32531, 15
A Wyrm @32174, 32598, 15

Create magic forcefield with actionid 1000 @32242, 32611, 11
 
Upper Spike Tasks

Create @data/actions/scripts/spike tasks/tuning fork.lua
Lua:
function onUse(player, item, fromPosition, target, toPosition, isHotkey)
    if isInArray({-1, 7}, player:getStorageValue(SPIKE_UPPER_PACIFIER_MAIN)) then
        return player:sendCancelMessage(RETURNVALUE_NOTPOSSIBLE)
    end

    if (target == nil) or not target:isItem() or (target:getId() ~= 21558) then
        return player:sendCancelMessage(RETURNVALUE_NOTPOSSIBLE)
    end

    local sum = player:getStorageValue(SPIKE_UPPER_PACIFIER_MAIN) + 1
    player:setStorageValue(SPIKE_UPPER_PACIFIER_MAIN, sum)

    if sum == 7 then
        item:remove()
        player:sendTextMessage(MESSAGE_INFO_DESCR, "Report the task to Gnomilly.")
    end

    target:transform(21563)
    target:decay()
    toPosition:sendMagicEffect(24)
    return true
end

Add @data/monsters/monsters.xml
Lua:
<monster name="Enraged Soul" file="ghosts/enraged soul.xml"/>

Create @data/monsters/ghosts/enraged soul.lua
XML:
<?xml version="1.0" encoding="UTF-8"?>
<monster name="Enraged Soul" nameDescription="an enraged soul" race="undead" experience="120" speed="150">
    <health now="150" max="150"/>
    <look type="568" corpse="21368"/>
    <targetchange interval="4000" chance="0"/>
    <flags>
        <flag summonable="0"/>
        <flag attackable="1"/>
        <flag hostile="1"/>
        <flag illusionable="1"/>
        <flag convinceable="0"/>
        <flag pushable="0"/>
        <flag canpushitems="1"/>
        <flag canpushcreatures="0"/>
        <flag targetdistance="1"/>
        <flag staticattack="90"/>
        <flag runonhealth="0"/>
    </flags>
    <attacks>
        <attack name="melee" interval="2000" skill="40" attack="50"/>
        <attack name="lifedrain" interval="2000" chance="15" range="1" min="-30" max="-55">
            <attribute key="areaEffect" value="redshimmer"/>
        </attack>
    </attacks>
    <immunities>
        <immunity physical="1"/>
        <immunity drown="1"/>
        <immunity earth="1"/>
        <immunity death="1"/>
        <immunity lifedrain="1"/>
        <immunity paralyze="1"/>
    </immunities>
    <voices interval="5000" chance="10">
        <voice sentence="Huh!"/>
        <voice sentence="Shhhhhh"/>
        <voice sentence="Buuuuuh"/>
    </voices>
    <loot>
        <item id="2404" chance="7002"/><!-- combat knife -->
        <item id="2654" chance="8800"/><!-- cape -->
        <item id="5909" chance="1940"/><!-- white piece of cloth -->
    </loot>
</monster>

Create @data/actions/scripts/spike tasks/spirit shovel.lua
Lua:
local chance = {
    {90, "You unearthed a spirit\'s anger!!!", "Enraged Soul"},
    {80, "Your crude digging has angered some ancient ghost.", "Ghost"},
    {70, "You unearthed some not-so-death creature.", "Demon Skeleton"},
    {50, "You unearthed some not-so-death creature.", "Zombie"},
    {1, "You've found nothing special."}
}

function onUse(player, item, fromPosition, target, toPosition, isHotkey)
    if isInArray({-1, 4}, player:getStorageValue(SPIKE_UPPER_MOUND_MAIN)) then
        return player:sendCancelMessage(RETURNVALUE_NOTPOSSIBLE)
    end

    if (target == nil) or not target:isItem() or (target:getId() ~= 21561) then
        return player:sendCancelMessage(RETURNVALUE_NOTPOSSIBLE)
    end

    target:transform(21562)
    target:decay()
    local luck = math.random(100)
    for i, result in ipairs(chance) do
        if luck >= result[1] then
            player:sendTextMessage(MESSAGE_INFO_DESCR, result[2])
            if result[3] then
                Game.createMonster(result[3], toPosition)
            end
            if i == 1 then
                local sum = player:getStorageValue(SPIKE_UPPER_MOUND_MAIN) + 1
                player:setStorageValue(SPIKE_UPPER_MOUND_MAIN, sum)
                if sum == 4 then
                    item:remove()
                    player:sendTextMessage(MESSAGE_INFO_DESCR, "Report the task to Gnomilly.")
                end
            end
            break
        end
    end
    return toPosition:sendMagicEffect(35)
end

Create @data/actions/scripts/spike tasks/ghost detector.lua
Lua:
if not GHOST_DETECTOR_MAP then
    GHOST_DETECTOR_MAP = {}
end

ghost_detector_area = {
    from = Position(32008, 32522, 8),
    to = Position(32365, 32759, 10)
}

local function getSearchString(fromPos, toPos)
    local distance = 0
    local direction = 0
    local level = 0

    local dx = fromPos.x - toPos.x
    local dy = fromPos.y - toPos.y
    local dz = fromPos.z - toPos.z

    level = (dz > 0) and 0 or (dz < 0) and 1 or 2

    if math.abs(dx) < 5 and math.abs(dy) < 5 then
        distance = 0
    else
        local tmp = dx * dx + dy * dy
        distance = (tmp < 10000) and 1 or (tmp < 75625) and 2 or 3
    end

    local tang = (dx ~= 0) and dy / dx or 10
    if math.abs(tang) < 0.4142 then
        direction = (dx > 0) and 3 or 2
    elseif math.abs(tang) < 2.4142 then
        direction = (tang > 0) and ((dy > 0) and 5 or 6) or ((dx > 0) and 7 or 4)
    else
        direction = (dy > 0) and 0 or 1
    end

    local text = {
        [0] = {
            [0] = "above you",
            [1] = "below you",
            [2] = "next to you"
        },
        [1] = {
            [0] = "on a higher level to the ",
            [1] = "on a lower level to the ",
            [2] = "to the "
        },
        [2] = "far to the ",
        [3] = "very far to the "
    }

    local dirs = {
        [0] = "north",
        [1] = "south",
        [2] = "east",
        [3] = "west",
        [4] = "north-east",
        [5] = "north-west",
        [6] = "south-east",
        [7] = "south-west"
    }

    return ((type(text[distance]) == "table") and text[distance][level] or text[distance]) .. ((distance ~= 0) and dirs[direction] or '')
end

function onUse(player, item, fromPosition, target, toPosition, isHotkey)
    local stat = player:getStorageValue(SPIKE_UPPER_TRACK_MAIN)
 
    if isInArray({-1, 3}, stat) then
        return player:sendCancelMessage(RETURNVALUE_NOTPOSSIBLE)
    end

    local current = GHOST_DETECTOR_MAP[player:getGuid()]
    if not current then
        local random = getFreeSand()
        GHOST_DETECTOR_MAP[player:getGuid()] = random
        current = random
    end

    if player:getPosition():compare(current) then    
        if stat == 2 then
            item:remove()
            GHOST_DETECTOR_MAP[player:getGuid()] = nil
            player:sendTextMessage(MESSAGE_INFO_DESCR, "Report the task to Gnomilly.")
            player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You found a malignant presence, the glowing detector signals that it does not need any further data.")
        else
            GHOST_DETECTOR_MAP[player:getGuid()] = getFreeSand()        
            player:sendTextMessage(MESSAGE_EVENT_ADVANCE, 'You found a malignant presence, the glowing detector signals another presence nearby.')
        end
        player:setStorageValue(SPIKE_UPPER_TRACK_MAIN, stat+1)
    else
        player:sendTextMessage(MESSAGE_EVENT_ADVANCE, 'The detector points ' .. getSearchString(player:getPosition(), current) .. '.')
    end
    return true
end

Create @data/creaturescripts/scripts/spike tasks/upperspikekill.lua
Lua:
local range = { -- Only the Demon Skeletons killed on this area count
    from = Position(32008, 32522, 8),
    to = Position(32365, 32759, 10)
}

function onKill(creature, target)
    if not isInArray({-1, 7}, creature:getStorageValue(SPIKE_UPPER_KILL_MAIN)) then
        if creature:getPosition():isInRange(range.from, range.to) then
            if target:isMonster() and (target:getMaster() == nil) and (target:getName() == "Demon Skeleton") then
                local sum = creature:getStorageValue(SPIKE_UPPER_KILL_MAIN) + 1
                creature:setStorageValue(SPIKE_UPPER_KILL_MAIN, sum)
                creature:sendTextMessage(MESSAGE_EVENT_ADVANCE, 'You have slayed ' .. sum .. ' out of 7 Demon Skeletons.')
                if sum == 7 then
                    creature:sendTextMessage(MESSAGE_INFO_DESCR, "Report the task to Gnomilly.")
                end
            end
        end
    end
end

Create @data/npc/Gnomilly.xml
XML:
<?xml version="1.0" encoding="UTF-8"?>
<npc name="Gnomilly" script="Gnomilly.lua" walkinterval="2000" floorchange="0" speechbubble="1">
      <health now="100" max="100" />
      <look type="507" head="14" body="15" legs="91" feet="92" addons="0" />
      <parameters>
          <parameter key="message_greet" value="Hi!" />
    </parameters>   
</npc>

Create @data/npc/scripts/Gnomilly.lua
Lua:
local keywordHandler = KeywordHandler:new()
local npcHandler = NpcHandler:new(keywordHandler)
NpcSystem.parseParameters(npcHandler)
local talkState = {}
local levels = {25, 49}

function onCreatureAppear(cid)                npcHandler:onCreatureAppear(cid)            end
function onCreatureDisappear(cid)            npcHandler:onCreatureDisappear(cid)            end
function onCreatureSay(cid, type, msg)        npcHandler:onCreatureSay(cid, type, msg)    end
function onThink()                            npcHandler:onThink()                        end

function creatureSayCallback(cid, type, msg)
    if not npcHandler:isFocused(cid) then
        return false
    end
 
    local player = Player(cid)

    if msgcontains(msg, 'job') then
        return npcHandler:say('I\'m the officer responsible for this area. I give out missions, accept mission reports and oversee our defences.', cid)
    end

    if msgcontains(msg, 'gnome') then
        return npcHandler:say('We are the only protectors of the world against the enemies below. With small stature comes great responsibilities, as they say.', cid)
    end

    if msgcontains(msg, 'area') then
        return npcHandler:say({
            "On these levels we found evidence of some monumental battle that has taken place here centuries ago. We also found some grave sites, but oddly enough no clues of any form of settlement. ...",
            "Some evidence we have found suggests that at least one of the battles here was fought for many, many years. People came here, lived here, fought here and died here. ...",
            "The battles continued until someone or something literally ploughed through the battlefields, turning everything upside down. All this killing and death soaked the area with negative energy. ...",
            "Necromantic forces are running wild all over the place and we are hard-pressed to drive all these undead, spirits and ghosts, away from the Spike. ...",
            "Unless we can secure that area somehow, the Spike operation is threatened to become crippled by the constant attacks of the undead. ...",
            "The whole growing downwards could come to a halt, leaving us exposed to even more attacks, counter attacks, and giving the enemy time to prepare their defences. There's a lot to do for aspiring adventurers."
        }, cid)
    end
 
    if msgcontains(msg, 'mission') then
        if player:getLevel() > levels[2] then
            npcHandler:say('Sorry, but no! Your expertise could be put to better use elsewhere. Here awaits you no challenge. You are desperately needed in the deeper levels of the Spike. Report there immediately. ', cid)
        else
            npcHandler:say('I can offer you several missions: to recharge our ghost {pacifiers}, to {release} the spiritual anger, to {track} an evil presence and to {kill} some demon skeletons.', cid)
        end
        return
    end

    if msgcontains(msg, 'report') then
        talkState[cid] = 'report'
        return npcHandler:say('What mission do you want to report about: recharging the ghost {pacifiers}, the {release} of the spiritual anger, about {tracking} an evil presence and the {killing} of demon skeletons?', cid)
    end

    if talkState[cid] == 'report' then
        if msgcontains(msg, 'pacifiers') then
            if player:getStorageValue(SPIKE_UPPER_PACIFIER_MAIN) == -1 then
                npcHandler:say('You have not started that mission.', cid)
            elseif player:getStorageValue(SPIKE_UPPER_PACIFIER_MAIN) == 7 then
                npcHandler:say('You have done well. Here, take your reward.', cid)
                player:addFamePoint()
                player:addExperience(1000, true)
                player:setStorageValue(SPIKE_UPPER_PACIFIER_MAIN, -1)
                player:setExhaustion(SPIKE_UPPER_PACIFIER_DAILY, 86400)
            else
                npcHandler:say('Gnowful! Take the resonance charger and use it on seven of the pacifiers in the cave.', cid)
            end
        elseif msgcontains(msg, 'release') then
            if player:getStorageValue(SPIKE_UPPER_MOUND_MAIN) == -1 then
                npcHandler:say('You have not started that mission.', cid)
            elseif player:getStorageValue(SPIKE_UPPER_MOUND_MAIN) == 4 then
                npcHandler:say('You have done well. Here, take your reward.', cid)
                player:addFamePoint()
                player:addExperience(1000, true)
                player:setStorageValue(SPIKE_UPPER_MOUND_MAIN, -1)
                player:setExhaustion(SPIKE_UPPER_MOUND_DAILY, 86400)
            else
                npcHandler:say('Gnowful! Take the spirit shovel use it on four graves in the cave system.', cid)
            end
        elseif msgcontains(msg, 'tracking') then
            if player:getStorageValue(SPIKE_UPPER_TRACK_MAIN) == -1 then
                npcHandler:say('You have not started that mission.', cid)
            elseif player:getStorageValue(SPIKE_UPPER_TRACK_MAIN) == 3 then
                npcHandler:say('You have done well. Here, take your reward.', cid)
                player:addFamePoint()
                player:addExperience(1000, true)
                player:setStorageValue(SPIKE_UPPER_TRACK_MAIN, -1)
                player:setExhaustion(SPIKE_UPPER_TRACK_DAILY, 86400)
            else
                npcHandler:say('Gnowful! Take the tracking device in the caves and locate the residual spirit energy.', cid)
            end
        elseif msgcontains(msg, 'killing') then
            if player:getStorageValue(SPIKE_UPPER_KILL_MAIN) == -1 then
                npcHandler:say('You have not started that mission.', cid)
            elseif player:getStorageValue(SPIKE_UPPER_KILL_MAIN) == 7 then
                npcHandler:say('You have done well. Here, take your reward.', cid)
                player:addFamePoint()
                player:addExperience(1000, true)
                player:setStorageValue(SPIKE_UPPER_KILL_MAIN, -1)
                player:setExhaustion(SPIKE_UPPER_KILL_DAILY, 86400)
            else
                npcHandler:say('Gnowful! Just go out to the caves and kill at least seven demon skeletons.', cid)
            end
        else
            npcHandler:say('That\'s not a valid mission name.', cid)
        end
        talkState[cid] = nil
        return
    end

    --[[///////////////////
    ////GHOST PACIFIERS////
    /////////////////////]]
    if msgcontains(msg, 'pacifiers') then
        if player:getExhaustion(SPIKE_UPPER_PACIFIER_DAILY) > 0 then
            return npcHandler:say('Sorry, you have to wait ' .. string.diff(player:getStorageValue(SPIKE_UPPER_PACIFIER_DAILY)-os.time()) .. ' before this task gets available again.', cid)
        end

        if (player:getLevel() < levels[1]) or (player:getLevel() > levels[2]) then
            return npcHandler:say('Sorry, you are not on the required range of levels [' .. levels[1] ..'-' .. levels[2] ..'].', cid)
        end

        if player:getStorageValue(SPIKE_UPPER_PACIFIER_MAIN) == -1 then   
            npcHandler:say({'We need you to recharge our ghost pacifiers. They are placed at several strategic points in the caves around us and should be easy to find. Your mission would be to charge seven of them.', 'If you are interested, I can give you some more {information} about it. Are you willing to accept this mission?'}, cid)
            talkState[cid] = 'pacifiers'
        else
            npcHandler:say('You have already started that mission.', cid)
        end
    end

    if talkState[cid] == 'pacifiers' then
        if msgcontains(msg, 'yes') then
            player:addItem(21554, 1)
            player:setStorageValue(SPIKE_UPPER_PACIFIER_MAIN, 0)
            npcHandler:say('Gnometastic! Take this resonance charger and use it on seven of the pacifiers in the cave. If you lose the charger, you\'ll have to bring your own. Gnomux sells all the equipment that is required for our missions.', cid)
            talkState[cid] = nil
        elseif msgcontains(msg, 'no') then
            npcHandler:say('Ok then.', cid)
            talkState[cid] = nil
        end
    end

    --[[///////////////////
    ////SPIRIT RELEASE/////
    /////////////////////]]
    if msgcontains(msg, 'release') then
        if player:getExhaustion(SPIKE_UPPER_MOUND_DAILY) > 0 then
            return npcHandler:say('Sorry, you have to wait ' .. string.diff(player:getStorageValue(SPIKE_UPPER_MOUND_DAILY)-os.time()) .. ' before this task gets available again.', cid)
        end

        if (player:getLevel() < levels[1]) or (player:getLevel() > levels[2]) then
            return npcHandler:say('Sorry, you are not on the required range of levels [' .. levels[1] ..'-' .. levels[2] ..'].', cid)
        end

        if player:getStorageValue(SPIKE_UPPER_MOUND_MAIN) == -1 then   
            npcHandler:say('Your task would be to use a spirit shovel to release some spirit\'s anger from graves that can be found all around here. If you are interested, I can give you some more information about it. Are you willing to accept this mission?', cid)
            talkState[cid] = 'release'
        else
            npcHandler:say('You have already started that mission.', cid)
        end
    end

    if talkState[cid] == 'release' then
        if msgcontains(msg, 'yes') then
            player:addItem(21553, 1)
            player:setStorageValue(SPIKE_UPPER_MOUND_MAIN, 0)
            npcHandler:say('Gnometastic! Take this spirit shovel and use it on four graves in the cave system. If you lose the shovel you\'ll have to bring your own. Gnomux sells all the equipment that is required for our missions.', cid)
            talkState[cid] = nil
        elseif msgcontains(msg, 'no') then
            npcHandler:say('Ok then.', cid)
            talkState[cid] = nil
        end
    end

    --[[/////////////////
    ////TRACK GHOSTS/////
    ///////////////////]]
    if msgcontains(msg, 'track') then
        if player:getExhaustion(SPIKE_UPPER_TRACK_DAILY) > 0 then
            return npcHandler:say('Sorry, you have to wait ' .. string.diff(player:getStorageValue(SPIKE_UPPER_TRACK_DAILY)-os.time()) .. ' before this task gets available again.', cid)
        end

        if (player:getLevel() < levels[1]) or (player:getLevel() > levels[2]) then
            return npcHandler:say('Sorry, you are not on the required range of levels [' .. levels[1] ..'-' .. levels[2] ..'].', cid)
        end

        if player:getStorageValue(SPIKE_UPPER_TRACK_MAIN) == -1 then   
            npcHandler:say({'You\'d be given the highly important task to track down an enormously malevolent spiritual presence in the cave system. Use your tracking device to find out how close you are to the presence.','Use that information to find the residual energy and use the tracker there. If you are interested, I can give you some more information about it. Are you willing to accept this mission?'}, cid)
            talkState[cid] = 'track'
        else
            npcHandler:say('You have already started that mission.', cid)
        end
    end

    if talkState[cid] == 'track' then
        if msgcontains(msg, 'yes') then
            GHOST_DETECTOR_MAP[player:getGuid()] = getFreeSand()
            player:addItem(21555, 1)
            player:setStorageValue(SPIKE_UPPER_TRACK_MAIN, 0)
            npcHandler:say('Gnometastic! Use this tracking device in the caves and locate the residual spirit energy. If you lose the tracking device, you\'ll have to bring your own. Gnomux sells all the equipment that is required for our missions.', cid)
            talkState[cid] = nil
        elseif msgcontains(msg, 'no') then
            npcHandler:say('Ok then.', cid)
            talkState[cid] = nil
        end
    end

    --[[/////////
    ////KILL/////
    ///////////]]
    if msgcontains(msg, 'kill') then
        if player:getExhaustion(SPIKE_UPPER_KILL_DAILY) > 0 then
            return npcHandler:say('Sorry, you have to wait ' .. string.diff(player:getStorageValue(SPIKE_UPPER_KILL_DAILY)-os.time()) .. ' before this task gets available again.', cid)
        end

        if (player:getLevel() < levels[1]) or (player:getLevel() > levels[2]) then
            return npcHandler:say('Sorry, you are not on the required range of levels [' .. levels[1] ..'-' .. levels[2] ..'].', cid)
        end

        if player:getStorageValue(SPIKE_UPPER_KILL_MAIN) == -1 then   
            npcHandler:say('We need someone to reduce the steadily growing number of demon skeletons in the caves. If you are interested, I can give you some more information about it. Are you willing to accept this mission?', cid)
            talkState[cid] = 'kill'
        else
            npcHandler:say('You have already started that mission.', cid)
        end
    end

    if talkState[cid] == 'kill' then
        if msgcontains(msg, 'yes') then
            player:setStorageValue(SPIKE_UPPER_KILL_MAIN, 0)
            npcHandler:say('Gnometastic! Just go out and kill them. You should find more of them than you like.', cid)
            talkState[cid] = nil
        elseif msgcontains(msg, 'no') then
            npcHandler:say('Ok then.', cid)
            talkState[cid] = nil
        end
    end
    return true
end

npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
npcHandler:addModule(FocusModule:new())
 
Middle Spike Tasks

Add @data/movements/movements.xml
Lua:
<!-- Spike Tasks Quest -->
<movevent event="StepIn" actionid="1000" script="middlespike.lua"/>

Create @data/movements/scripts/middlespike.lua
Lua:
local condition = createConditionObject(CONDITION_OUTFIT)
setConditionParam(condition, CONDITION_PARAM_TICKS, 120000)
addOutfitCondition(condition, 0, 307, 0, 0, 0, 0)

function onStepIn(creature, item, position, fromPosition)
    local player = creature:getPlayer()
    if (player == nil) or player:isInGhostMode() then
        creature:teleportTo(fromPosition)
        return true
    end

    local tasksLoaded = {}
    if not isInArray({-1, 8}, player:getStorageValue(SPIKE_MIDDLE_NEST_MAIN)) then
        tasksLoaded["NEST"] = true
    end
    if player:getStorageValue(SPIKE_MIDDLE_CHARGE_MAIN) == 1 then
        tasksLoaded["CHARGE"] = true
    end

    if not tasksLoaded["NEST"] and not tasksLoaded["CHARGE"] then
        player:teleportTo(fromPosition)
        return true
    end

    if tasksLoaded["NEST"] then
        if player:getCondition(CONDITION_OUTFIT) or (player:getOutfit().lookType == 307) then
            player:teleportTo(fromPosition)
            return true
        end
        player:addCondition(condition)
        player:getPosition():sendMagicEffect(11)
    end
 
    if tasksLoaded["CHARGE"] then
        player:getPosition():sendMagicEffect(12)
        player:setStorageValue(SPIKE_MIDDLE_CHARGE_MAIN, 2)
        player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have charged your body with geomantic energy and can report about it.")
    end
    return true
end

Create @data/actions/scripts/spike tasks/nests.lua
Lua:
local summon = {"Spider", "Larva", "Scarab", "Tarantula"}

function onUse(player, item, fromPosition, target, toPosition, isHotkey)
    if isInArray({-1, 8}, player:getStorageValue(SPIKE_MIDDLE_NEST_MAIN)) then
        return false
    end

    if player:getOutfit().lookType ~= 307 then
        return false
    end

    local sum = player:getStorageValue(SPIKE_MIDDLE_NEST_MAIN) + 1
    player:setStorageValue(SPIKE_MIDDLE_NEST_MAIN, sum)
    player:sendTextMessage(MESSAGE_STATUS_SMALL, "You have destroyed a monster nest.")

    if sum == 8 then
        player:sendTextMessage(MESSAGE_INFO_DESCR, "Report the task to Gnombold.")
    end

    if math.random(100) > 60 then
        Game.createMonster(summon[math.random(#summon)], player:getPosition())
    end

    item:transform(21560)
    item:decay()
    toPosition:sendMagicEffect(17)
    return true
end

Create @data/actions/scripts/spike tasks/fertilizer.lua
Lua:
if not FERTILIZED_MUSHROOMS then
    FERTILIZED_MUSHROOMS = {}
end

function onUse(player, item, fromPosition, target, toPosition, isHotkey)
    if isInArray({-1, 4}, player:getStorageValue(SPIKE_MIDDLE_MUSHROOM_MAIN)) then
        return false
    end

    if (target == nil) or not target:isItem() or (target:getId() ~= 21565) then
        return false
    end

    if not FERTILIZED_MUSHROOMS[player:getGuid()] then
        FERTILIZED_MUSHROOMS[player:getGuid()] = {}
    end

    local mushPos = Position(toPosition.x, toPosition.y, toPosition.z)
    if isInArray(FERTILIZED_MUSHROOMS[player:getGuid()], mushPos) then
        return player:sendCancelMessage("You have already fertilised this mushroom.")
    end
 
    table.insert(FERTILIZED_MUSHROOMS[player:getGuid()], mushPos)
    local sum = player:getStorageValue(SPIKE_MIDDLE_MUSHROOM_MAIN) + 1
    player:setStorageValue(SPIKE_MIDDLE_MUSHROOM_MAIN, sum)

    if sum == 4 then
        item:remove()
        player:sendTextMessage(MESSAGE_INFO_DESCR, "Report the task to Gnombold.")
    end
    return toPosition:sendMagicEffect(46)
end

Create @data/actions/scripts/spike tasks/lodestone.lua
Lua:
local transformTo = {
    [21557] = 21566,
    [21566] = 21568,
}

local area = { --area where to teleport
    Position(32152, 32502, 11), Position(32365, 32725, 12)
}

function onUse(player, item, fromPosition, target, toPosition, isHotkey)
    if player:getStorageValue(SPIKE_MIDDLE_CHARGE_MAIN) ~= 0 then
        return false
    end

    if (target == nil) or not target:isItem() or (target:getId() ~= 21567) then
        return false
    end

    target:transform(21713)
    target:decay()
    if item:getId() == 21568 then
        player:setStorageValue(SPIKE_MIDDLE_CHARGE_MAIN, 1)
        player:getPosition():sendMagicEffect(12)
        player:say('Your tinkering caused some kind of magnetic storm that caused you to get disorientated.', TALKTYPE_MONSTER_SAY)
        item:remove()
    else
        item:transform(transformTo[item:getId()])
        if math.random(100) > 60 then
            player:teleportTo(getFreePosition(area[1], area[2]))
            player:getPosition():sendMagicEffect(11)    
        end
    end
    return toPosition:sendMagicEffect(12)
end

Create @data/creaturescripts/scripts/spike tasks/middlespikekill.lua
Lua:
local range = { -- Only the Crystalcrushers killed on this area count
    from = Position(32100, 32470, 11),
    to = Position(32380, 32725, 12)
}

function onKill(creature, target)
    if not isInArray({-1, 7}, creature:getStorageValue(SPIKE_MIDDLE_KILL_MAIN)) then
        if creature:getPosition():isInRange(range.from, range.to) then
            if target:isMonster() and (target:getMaster() == nil) and (target:getName() == "Crystalcrusher") then
                local sum = creature:getStorageValue(SPIKE_MIDDLE_KILL_MAIN) + 1
                creature:setStorageValue(SPIKE_MIDDLE_KILL_MAIN, sum)
                creature:sendTextMessage(MESSAGE_EVENT_ADVANCE, 'You have slayed ' .. sum .. ' out of 7 crystalcrushers.')
                if sum == 7 then
                    creature:sendTextMessage(MESSAGE_INFO_DESCR, "Report the task to Gnombold.")
                end
            end
        end
    end
end

Create @data/npc/Gnombold.xml
XML:
<?xml version="1.0" encoding="UTF-8"?>
<npc name="Gnombold" script="Gnombold.lua" walkinterval="2000" floorchange="0" speechbubble="1">
      <health now="100" max="100" />
      <look type="493" head="40" body="81" legs="101" feet="57" addons="0" />
      <parameters>
          <parameter key="message_greet" value="Hi!" />
    </parameters>    
</npc>

Create @data/npc/scripts/Gnombold.lua
Lua:
local keywordHandler = KeywordHandler:new()
local npcHandler = NpcHandler:new(keywordHandler)
NpcSystem.parseParameters(npcHandler)
local talkState = {}
local levels = {50, 79}

function onCreatureAppear(cid)                npcHandler:onCreatureAppear(cid)            end
function onCreatureDisappear(cid)            npcHandler:onCreatureDisappear(cid)            end
function onCreatureSay(cid, type, msg)        npcHandler:onCreatureSay(cid, type, msg)    end
function onThink()                            npcHandler:onThink()                        end

function creatureSayCallback(cid, type, msg)
    if not npcHandler:isFocused(cid) then
        return false
    end
 
    local player = Player(cid)

    if msgcontains(msg, 'job') then
        return npcHandler:say('I\'m the officer responsible for this area. I give out missions, accept mission reports and oversee our defences.', cid)
    end

    if msgcontains(msg, 'gnome') then
        return npcHandler:say('Gnomes have lived autonomous for so long that it still feels odd to work with strangers for many of us.', cid)
    end

    if msgcontains(msg, 'area') then
        return npcHandler:say({
            "The levels around us are... well, they are strange. We are still not entirely sure how they were created. It seems obvious that they are artificial, but they seem not to be burrowed or the like. ... ",
            "We found strange stone formations that were not found on other layers around the Spike, but there is no clue at all if they are as natural as they look. It seems someone used some geomantic force to move the earth. ...",
            "For what reason this has been done we can't tell as we found no clues of colonisation. ...",
            "There are theories that the caves are some kind of burrow of some extinct creature or even creatures that are still around us, but exist as some form of invisible energy; but those theories are far-fetched and not supported by any discoveries. ...",
            "Be that as it may, whatever those caves were meant for, these days they are crawling with creatures of different kinds and all are hostile towards us. The competition for food is great down here, and everything is seen as prey by the cave dwellers. ...",
            "Some would like to feast on the crystal of the Spike, others would prefer a diet of gnomes. What they have in common is that they are a threat. If we can't keep them under control their constant attacks and raids on the Spike will wear us down. ...",
            "That's where adventurers fit in to save the day. ",
        }, cid)
    end
 
    if msgcontains(msg, 'mission') then
        if player:getLevel() > levels[2] then
            npcHandler:say('Sorry, but no! Your expertise could be put to better use elsewhere. Here awaits you no challenge. You are desperately needed in the deeper levels of the Spike. Report there immediately. ', cid)
        else
            npcHandler:say(' I can offer you several missions: to gather geomantic {charges}, to {fertilise} the mushroom caves, to destroy monster {nests} and to {kill} some crystal crushers.', cid)
        end
        return
    end

    if msgcontains(msg, 'report') then
        talkState[cid] = 'report'
        return npcHandler:say('What mission do you want to report about: gathering the geomantic {charges}, the {fertilisation} of the mushroom caves, about destroying monster {nests} and the {killing} of crystal crushers?', cid)
    end

    if talkState[cid] == 'report' then
        if msgcontains(msg, 'charges') then
            if player:getStorageValue(SPIKE_MIDDLE_CHARGE_MAIN) == -1 then
                npcHandler:say('You have not started that mission.', cid)
            elseif player:getStorageValue(SPIKE_MIDDLE_CHARGE_MAIN) == 2 then
                npcHandler:say('You have done well. Here, take your reward.', cid)
                player:addFamePoint()
                player:addExperience(2000, true)
                player:setStorageValue(SPIKE_MIDDLE_CHARGE_MAIN, -1)
                player:setExhaustion(SPIKE_MIDDLE_CHARGE_DAILY, 86400)
            else
                npcHandler:say('Gnowful! Charge this magnet at three monoliths in the cave system. With three charges, the magnet will disintegrate and charge you with its gathered energies. Step on the magnetic extractor here to deliver the charge to us, then report to me.', cid)
            end
        elseif msgcontains(msg, 'fertilisation') then
            if player:getStorageValue(SPIKE_MIDDLE_MUSHROOM_MAIN) == -1 then
                npcHandler:say('You have not started that mission.', cid)
            elseif player:getStorageValue(SPIKE_MIDDLE_MUSHROOM_MAIN) == 4 then
                npcHandler:say('You have done well. Here, take your reward.', cid)
                player:addFamePoint()
                player:addExperience(2000, true)
                player:setStorageValue(SPIKE_MIDDLE_MUSHROOM_MAIN, -1)
                player:setExhaustion(SPIKE_MIDDLE_MUSHROOM_DAILY, 86400)
            else
                npcHandler:say('Gnowful! Use the fertiliser on four gardener mushroom in the caves.', cid)
            end
        elseif msgcontains(msg, 'nests') then
            if player:getStorageValue(SPIKE_MIDDLE_NEST_MAIN) == -1 then
                npcHandler:say('You have not started that mission.', cid)
            elseif player:getStorageValue(SPIKE_MIDDLE_NEST_MAIN) == 8 then
                npcHandler:say('You have done well. Here, take your reward.', cid)
                player:addFamePoint()
                player:addExperience(2000, true)
                player:setStorageValue(SPIKE_MIDDLE_NEST_MAIN, -1)
                player:setExhaustion(SPIKE_MIDDLE_NEST_DAILY, 86400)
            else
                npcHandler:say('Gnowful! Step into the transformer and destroy eight monster nests.', cid)
            end
        elseif msgcontains(msg, 'killing') then
            if player:getStorageValue(SPIKE_MIDDLE_KILL_MAIN) == -1 then
                npcHandler:say('You have not started that mission.', cid)
            elseif player:getStorageValue(SPIKE_MIDDLE_KILL_MAIN) == 7 then
                npcHandler:say('You have done well. Here, take your reward.', cid)
                player:addFamePoint()
                player:addExperience(2000, true)
                player:setStorageValue(SPIKE_MIDDLE_KILL_MAIN, -1)
                player:setExhaustion(SPIKE_MIDDLE_KILL_DAILY, 86400)
            else
                npcHandler:say('Gnowful! Just go out to the caves and kill at least seven crystalcrushers.', cid)
            end
        else
            npcHandler:say('That\'s not a valid mission name.', cid)
        end
        talkState[cid] = nil
        return
    end

    --[[/////////////////////
    ////GEOMANTIC CHARGES////
    ///////////////////////]]
    if msgcontains(msg, 'charges') then
        if player:getExhaustion(SPIKE_MIDDLE_CHARGE_DAILY) > 0 then
            return npcHandler:say('Sorry, you have to wait ' .. string.diff(player:getStorageValue(SPIKE_MIDDLE_CHARGE_DAILY)-os.time()) .. ' before this task gets available again.', cid)
        end

        if (player:getLevel() < levels[1]) or (player:getLevel() > levels[2]) then
            return npcHandler:say('Sorry, you are not on the required range of levels [' .. levels[1] ..'-' .. levels[2] ..'].', cid)
        end

        if player:getStorageValue(SPIKE_MIDDLE_CHARGE_MAIN) == -1 then    
            npcHandler:say({'Our mission for you is to use a magnet on three different monoliths in the cave system here. After the magnet evaporates on the last charge, enter the magnetic extractor here to deliver your charge.', 'If you are interested, I can give you some more {information} about it. Are you willing to accept this mission?'}, cid)
            talkState[cid] = 'charges'
        else
            npcHandler:say('You have already started that mission.', cid)
        end
    end

    if talkState[cid] == 'charges' then
        if msgcontains(msg, 'yes') then
            player:addItem(21557, 1)
            player:setStorageValue(SPIKE_MIDDLE_CHARGE_MAIN, 0)
            npcHandler:say({'Gnometastic! Charge this magnet at three monoliths in the cave system. With three charges, the magnet will disintegrate and charge you with its gathered energies. Step on the magnetic extractor here to deliver the charge to us, then report to me.','If you lose the magnet you\'ll have to bring your own. Gnomux sells all the equipment that is required for our missions.'}, cid)
            talkState[cid] = nil
        elseif msgcontains(msg, 'no') then
            npcHandler:say('Ok then.', cid)
            talkState[cid] = nil
        end
    end

    --[[/////////////
    ////FERTILISE////
    ///////////////]]
    if msgcontains(msg, 'fertilise') then
        if player:getExhaustion(SPIKE_MIDDLE_MUSHROOM_DAILY) > 0 then
            return npcHandler:say('Sorry, you have to wait ' .. string.diff(player:getStorageValue(SPIKE_MIDDLE_MUSHROOM_DAILY)-os.time()) .. ' before this task gets available again.', cid)
        end

        if (player:getLevel() < levels[1]) or (player:getLevel() > levels[2]) then
            return npcHandler:say('Sorry, you are not on the required range of levels [' .. levels[1] ..'-' .. levels[2] ..'].', cid)
        end

        if player:getStorageValue(SPIKE_MIDDLE_MUSHROOM_MAIN) == -1 then    
            npcHandler:say('Your mission would be to seek out gardener mushrooms in the caves and use some fertiliser on them. If you are interested, I can give you some more information about it. Are you willing to accept this mission?', cid)
            talkState[cid] = 'fertilise'
        else
            npcHandler:say('You have already started that mission.', cid)
        end
    end

    if talkState[cid] == 'fertilise' then
        if msgcontains(msg, 'yes') then
            player:addItem(21564)
            player:setStorageValue(SPIKE_MIDDLE_MUSHROOM_MAIN, 0)
            npcHandler:say('Gnometastic! And here is your fertiliser - use it on four gardener mushroom in the caves. If you lose the fertiliser you\'ll have to bring your own. Gnomux sells all the equipment that is required for our missions.', cid)
            talkState[cid] = nil
        elseif msgcontains(msg, 'no') then
            npcHandler:say('Ok then.', cid)
            talkState[cid] = nil
        end
    end

    --[[//////////////////
    ////DESTROY NESTS/////
    ////////////////////]]
    if msgcontains(msg, 'nests') then
        if player:getExhaustion(SPIKE_MIDDLE_NEST_DAILY) > 0 then
            return npcHandler:say('Sorry, you have to wait ' .. string.diff(player:getStorageValue(SPIKE_MIDDLE_NEST_DAILY)-os.time()) .. ' before this task gets available again.', cid)
        end

        if (player:getLevel() < levels[1]) or (player:getLevel() > levels[2]) then
            return npcHandler:say('Sorry, you are not on the required range of levels [' .. levels[1] ..'-' .. levels[2] ..'].', cid)
        end

        if player:getStorageValue(SPIKE_MIDDLE_NEST_MAIN) == -1 then    
            npcHandler:say('Our mission for you is to step into the gnomish transformer and then destroy eight monster nests in the caves. If you are interested, I can give you some more information about it. Are you willing to accept this mission?', cid)
            talkState[cid] = 'nests'
        else
            npcHandler:say('You have already started that mission.', cid)
        end
    end

    if talkState[cid] == 'nests' then
        if msgcontains(msg, 'yes') then
            player:setStorageValue(SPIKE_MIDDLE_NEST_MAIN, 0)
            npcHandler:say('Gnometastic! Don\'t forget to step into the transformer before you go out and destroy five monster nests. If your transformation runs out, return to the transformer to get another illusion.', cid)
            talkState[cid] = nil
        elseif msgcontains(msg, 'no') then
            npcHandler:say('Ok then.', cid)
            talkState[cid] = nil
        end
    end

    --[[/////////
    ////KILL/////
    ///////////]]
    if msgcontains(msg, 'kill') then
        if player:getExhaustion(SPIKE_MIDDLE_KILL_DAILY) > 0 then
            return npcHandler:say('Sorry, you have to wait ' .. string.diff(player:getStorageValue(SPIKE_MIDDLE_KILL_DAILY)-os.time()) .. ' before this task gets available again.', cid)
        end

        if (player:getLevel() < levels[1]) or (player:getLevel() > levels[2]) then
            return npcHandler:say('Sorry, you are not on the required range of levels [' .. levels[1] ..'-' .. levels[2] ..'].', cid)
        end

        if player:getStorageValue(SPIKE_MIDDLE_KILL_MAIN) == -1 then    
            npcHandler:say('This mission will require you to kill some crystal crushers for us. If you are interested, I can give you some more information about it. Are you willing to accept this mission?', cid)
            talkState[cid] = 'kill'
        else
            npcHandler:say('You have already started that mission.', cid)
        end
    end

    if talkState[cid] == 'kill' then
        if msgcontains(msg, 'yes') then
            player:setStorageValue(SPIKE_MIDDLE_KILL_MAIN, 0)
            npcHandler:say('Gnometastic! You should have no trouble to find enough crystal crushers. Killing seven of them should be enough.', cid)
            talkState[cid] = nil
        elseif msgcontains(msg, 'no') then
            npcHandler:say('Ok then.', cid)
            talkState[cid] = nil
        end
    end
    return true
end

npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
npcHandler:addModule(FocusModule:new())
 
Lower Spike Tasks

Create @data/npc/Gnome Trooper.xml
XML:
<?xml version="1.0" encoding="UTF-8"?>
<npc name="Gnome Trooper" script="Gnome Trooper.lua" walkinterval="2000" floorchange="0">
      <health now="100" max="100" />
      <look type="493" head="59" body="20" legs="39" feet="95" addons="0"/>
    <parameters>
        <parameter key="message_greet" value="Do you have {something} to deliver?"/>
    </parameters>
</npc>

Create @data/npc/scripts/Gnome Trooper.lua
Lua:
local keywordHandler = KeywordHandler:new()
local npcHandler = NpcHandler:new(keywordHandler)
NpcSystem.parseParameters(npcHandler)
local response = {
    [0] = "It's a pipe! What can be more relaxing for a gnome than to smoke his pipe after a day of duty at the front. At least it's a chance to do something really dangerous after all!",
    [1] = "Ah, a letter from home! Oh - I had no idea she felt that way! This is most interesting!",
    [2] = "It's a model of the gnomebase Alpha! For self-assembly! With toothpicks...! Yeeaah...! I guess.",
    [3] = "A medal of honour! At last they saw my true worth!"
}

if not DELIVERED_PARCELS then
    DELIVERED_PARCELS = {}
end

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 greetCallback(cid)
    local player = Player(cid)
    if isInArray({-1, 4}, player:getStorageValue(SPIKE_LOWER_PARCEL_MAIN)) then
        return false
    end
    if isInArray(DELIVERED_PARCELS[player:getGuid()], Creature(getNpcCid()):getId()) then
        return false
    end
    return true
end

function creatureSayCallback(cid, type, msg)
    if not npcHandler:isFocused(cid) then
        return false
    end
  
    local player = Player(cid)
    local status = player:getStorageValue(SPIKE_LOWER_PARCEL_MAIN)

    if not DELIVERED_PARCELS[player:getGuid()] then
        DELIVERED_PARCELS[player:getGuid()] = {}
    end

    if msgcontains(msg, 'something') and not isInArray({-1, 4}, status) then
        if isInArray(DELIVERED_PARCELS[player:getGuid()], Creature(getNpcCid()):getId()) then
            return true
        end

        if not player:removeItem(21569, 1) then
            npcHandler:say("But you don't have it...", cid)
            return npcHandler:releaseFocus(cid)
        end
  
        npcHandler:say(response[player:getStorageValue(SPIKE_LOWER_PARCEL_MAIN)], cid)
        player:setStorageValue(SPIKE_LOWER_PARCEL_MAIN, status + 1)
        table.insert(DELIVERED_PARCELS[player:getGuid()], Creature(getNpcCid()):getId())
        npcHandler:releaseFocus(cid)
    end
    return true
end

npcHandler:setCallback(CALLBACK_GREET, greetCallback)
npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
npcHandler:addModule(FocusModule:new())

Create @data/npc/A Behemoth.xml
XML:
<?xml version="1.0" encoding="UTF-8"?>
<npc name="A Behemoth" script="undercover.lua" walkinterval="2000" floorchange="0">
      <health now="100" max="100" />
      <look type="55"/>
</npc>

Create @data/npc/A Dragon Lord.xml
XML:
<?xml version="1.0" encoding="UTF-8"?>
<npc name="A Dragon Lord" script="undercover.lua" walkinterval="2000" floorchange="0">
      <health now="100" max="100" />
      <look type="39"/>
</npc>

Create @data/npc/A Drillworm.xml
XML:
<?xml version="1.0" encoding="UTF-8"?>
<npc name="A Drillworm" script="undercover.lua" walkinterval="2000" floorchange="0">
      <health now="100" max="100" />
      <look type="527"/>
</npc>

Create @data/npc/A Lost Basher.xml
XML:
<?xml version="1.0" encoding="UTF-8"?>
<npc name="A Lost Basher" script="undercover.lua" walkinterval="2000" floorchange="0">
      <health now="100" max="100" />
      <look type="538"/>
</npc>

Create @data/npc/A Lost Husher.xml
XML:
<?xml version="1.0" encoding="UTF-8"?>
<npc name="A Lost Husher" script="undercover.lua" walkinterval="2000" floorchange="0">
      <health now="100" max="100" />
      <look type="537"/>
</npc>

Create @data/npc/A Lost Thrower.xml
XML:
<?xml version="1.0" encoding="UTF-8"?>
<npc name="A Lost Thrower" script="undercover.lua" walkinterval="2000" floorchange="0">
      <health now="100" max="100" />
      <look type="539"/>
</npc>

Create @data/npc/A Nightmare Scion.xml
XML:
<?xml version="1.0" encoding="UTF-8"?>
<npc name="A Nightmare Scion" script="undercover.lua" walkinterval="2000" floorchange="0">
      <health now="100" max="100" />
      <look type="321"/>
</npc>

Create @data/npc/A Vulcongra.xml
XML:
<?xml version="1.0" encoding="UTF-8"?>
<npc name="A Vulcongra" script="undercover.lua" walkinterval="2000" floorchange="0">
      <health now="100" max="100" />
      <look type="509"/>
</npc>

Create @data/npc/A Wyrm.xml
XML:
<?xml version="1.0" encoding="UTF-8"?>
<npc name="A Wyrm" script="undercover.lua" walkinterval="2000" floorchange="0">
      <health now="100" max="100" />
      <look type="291"/>
</npc>

Create @data/npc/scripts/undercover.lua
Lua:
local keywordHandler = KeywordHandler:new()
local npcHandler = NpcHandler:new(keywordHandler)
NpcSystem.parseParameters(npcHandler)

if not UNDERCOVER_CONTACTED then
    UNDERCOVER_CONTACTED = {}
end

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 greetCallback(cid)
    local player = Player(cid)
    local status = player:getStorageValue(SPIKE_LOWER_UNDERCOVER_MAIN)

    if isInArray({-1, 3}, status) then
        return false
    end

    if not UNDERCOVER_CONTACTED[player:getGuid()] then
        UNDERCOVER_CONTACTED[player:getGuid()] = {}
    end
  
    if isInArray(UNDERCOVER_CONTACTED[player:getGuid()], Creature(getNpcCid()):getId()) then
        return false
    end

    player:setStorageValue(SPIKE_LOWER_UNDERCOVER_MAIN, status + 1)
    table.insert(UNDERCOVER_CONTACTED[player:getGuid()], Creature(getNpcCid()):getId())
    npcHandler:releaseFocus(cid)
    return npcHandler:say("Pssst! Keep it down! <gives you an elaborate report on monster activity>", cid)
end

npcHandler:setCallback(CALLBACK_GREET, greetCallback)
npcHandler:addModule(FocusModule:new())

Create @data/actions/scripts/spike tasks/thermometer.lua
Lua:
hot_lava_pools = { --all range areas of the 9 lava pools
    {Position(32263, 32481, 13), Position(32274, 32490, 13)},
    {Position(32163, 32558, 13), Position(32173, 32567, 13)},
    {Position(32201, 32667, 13), Position(32211, 32672, 13)},
    {Position(32135, 32606, 14), Position(32143, 32614, 14)},
    {Position(32330, 32519, 14), Position(32339, 32521, 14)},
    {Position(32260, 32697, 14), Position(32272, 32705, 14)},
    {Position(32176, 32493, 15), Position(32186, 32502, 15)},
    {Position(32341, 32577, 15), Position(32347, 32586, 15)},
    {Position(32220, 32643, 15), Position(32223, 32652, 15)},
}

if not SPIKE_LOWER_HOTTEST_POOL then
    SPIKE_LOWER_HOTTEST_POOL = math.random(#hot_lava_pools)
end

function onUse(player, item, fromPosition, target, toPosition, isHotkey)
    local status = player:getStorageValue(SPIKE_LOWER_LAVA_MAIN)
  
    if isInArray({-1, 1}, status) then
        return player:sendCancelMessage(RETURNVALUE_NOTPOSSIBLE)
    end

    if player:getPosition():isInRange(hot_lava_pools[SPIKE_LOWER_HOTTEST_POOL][1], hot_lava_pools[SPIKE_LOWER_HOTTEST_POOL][2]) then
        item:remove()
        player:getPosition():sendMagicEffect(16)
        player:setStorageValue(SPIKE_LOWER_LAVA_MAIN, 1)
        player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Whew! That was that hot, it melted the thermometer! At least you've found the hot spot!")
    else
        player:sendTextMessage(MESSAGE_EVENT_ADVANCE, 'This is not the hot spot!')
    end
    return true
end

Create @data/creaturescripts/scripts/spike tasks/lowerspikekill.lua
Lua:
local range = { -- Only the Drillworms killed on this area count
    from = Position(32120, 32470, 13),
    to = Position(32345, 32710, 15)
}

function onKill(creature, target)
    if not isInArray({-1, 7}, creature:getStorageValue(SPIKE_LOWER_KILL_MAIN)) then
        if creature:getPosition():isInRange(range.from, range.to) then
            if target:isMonster() and (target:getMaster() == nil) and (target:getName() == "Drillworm") then
                local sum = creature:getStorageValue(SPIKE_LOWER_KILL_MAIN) + 1
                creature:setStorageValue(SPIKE_LOWER_KILL_MAIN, sum)
                creature:sendTextMessage(MESSAGE_EVENT_ADVANCE, 'You have slayed ' .. sum .. ' out of 7 Drillworms.')
                if sum == 7 then
                    creature:sendTextMessage(MESSAGE_INFO_DESCR, "Report the task to Gnomargery.")
                end
            end
        end
    end
end

Create @data/npc/Gnomargery.xml
XML:
<?xml version="1.0" encoding="UTF-8"?>
<npc name="Gnomargery" script="Gnomargery.lua" walkinterval="2000" floorchange="0" speechbubble="1">
      <health now="100" max="100" />
      <look type="507" head="96" body="92" legs="96" feet="114" addons="0" />
      <parameters>
          <parameter key="message_greet" value="Hi!" />
    </parameters>      
</npc>

Create @data/npc/scripts/Gnomargery.lua
Lua:
local keywordHandler = KeywordHandler:new()
local npcHandler = NpcHandler:new(keywordHandler)
NpcSystem.parseParameters(npcHandler)
local talkState = {}
local level = 80

function onCreatureAppear(cid)                npcHandler:onCreatureAppear(cid)            end
function onCreatureDisappear(cid)            npcHandler:onCreatureDisappear(cid)            end
function onCreatureSay(cid, type, msg)        npcHandler:onCreatureSay(cid, type, msg)    end
function onThink()                            npcHandler:onThink()                        end

function creatureSayCallback(cid, type, msg)
    if not npcHandler:isFocused(cid) then
        return false
    end
  
    local player = Player(cid)

    if msgcontains(msg, 'job') then
        return npcHandler:say('I\'m the officer responsible for this area. I give out missions, accept mission reports and oversee our defences.', cid)
    end

    if msgcontains(msg, 'gnome') then
        return npcHandler:say('It\'s good to be a gnome for sure!', cid)
    end

    if msgcontains(msg, 'area') then
        return npcHandler:say({
            "On the levels outside, we encountered the first serious resistance of our true enemy. As evidenced by the unnatural heat in an area with little volcanic activity, there is 'something' strange going on here. ...",
            "Even the lava pools we have found here are not actually lava, but rock that was molten pretty much recently without any reasonable connection to some natural heat source. And for all we can tell, the heat is growing, slowly but steadily. ...",
            "This is the first time ever that we can witness our enemy at work. Here we can learn a lot about its operations. ...",
            "How they work, and possibly how to stop them. But therefore expeditions into the depths are necessary. The areas around us are highly dangerous, and a lethal threat to us and the Spike as a whole. ... ",
            "Our first object is to divert the forces of the enemy and weaken them as good as we can while gathering as much information as possible about them and their movements. Only highly skilled adventurers stand a chance to help us down here. ..."
        }, cid)
    end

    if msgcontains(msg, 'spike') then
        return npcHandler:say('Now that\'s gnomish ingenuity given shape! Who but a gnome would come up with such a plan to defeat our enemies. ', cid)
    end

    if msgcontains(msg, 'mission') then
        if player:getLevel() < level then  
            npcHandler:say('Sorry, but no! Your expertise could be put to better use elsewhere. You are desperately needed in the upper levels of the Spike. Report there immediately. ', cid)
        else
            npcHandler:say('I can offer you several missions: to {deliver} parcels to our boys and girls in the battlefield, to get reports from our {undercover} gnomes, to do some {temperature} measuring and to {kill} some drillworms.', cid)
        end
        return
    end

    if msgcontains(msg, 'report') then
        talkState[cid] = 'report'
        return npcHandler:say(' What mission do you want to report about: the {delivery} of parcels, the {undercover} reports, the {temperature} measuring or {kill} of drillworms?', cid)
    end

    if talkState[cid] == 'report' then
        if msgcontains(msg, 'delivery') then
            if player:getStorageValue(SPIKE_LOWER_PARCEL_MAIN) == -1 then
                npcHandler:say('You have not started that mission.', cid)
            elseif player:getStorageValue(SPIKE_LOWER_PARCEL_MAIN) == 4 then
                npcHandler:say('You have done well. Here, take your reward.', cid)
                player:addFamePoint()
                player:addExperience(3500, true)
                player:setStorageValue(SPIKE_LOWER_PARCEL_MAIN, -1)
                player:setExhaustion(SPIKE_LOWER_PARCEL_DAILY, 86400)
            else
                npcHandler:say('Gnowful! Deliver the four parcels to some of our far away outposts in the caverns.', cid)
            end
        elseif msgcontains(msg, 'undercover') then
            if player:getStorageValue(SPIKE_LOWER_UNDERCOVER_MAIN) == -1 then
                npcHandler:say('You have not started that mission.', cid)
            elseif player:getStorageValue(SPIKE_LOWER_UNDERCOVER_MAIN) == 3 then
                npcHandler:say('You have done well. Here, take your reward.', cid)
                player:addFamePoint()
                player:addExperience(3500, true)
                player:setStorageValue(SPIKE_LOWER_UNDERCOVER_MAIN, -1)
                player:setExhaustion(SPIKE_LOWER_UNDERCOVER_DAILY, 86400)
            else
                npcHandler:say('Gnowful! Get three reports from our undercover agents posing as monsters in the caves around us.', cid)
            end
        elseif msgcontains(msg, 'temperature') then
            if player:getStorageValue(SPIKE_LOWER_LAVA_MAIN) == -1 then
                npcHandler:say('You have not started that mission.', cid)
            elseif player:getStorageValue(SPIKE_LOWER_LAVA_MAIN) == 1 then
                npcHandler:say('You have done well. Here, take your reward.', cid)
                player:addFamePoint()
                player:addExperience(3500, true)
                player:setStorageValue(SPIKE_LOWER_LAVA_MAIN, -1)
                player:setExhaustion(SPIKE_LOWER_LAVA_DAILY, 86400)
            else
                npcHandler:say('Gnowful! Use the gnomish temperature measurement device to locate the hottest spot at the lava pools in the cave.', cid)
            end
        elseif msgcontains(msg, 'kill') then
            if player:getStorageValue(SPIKE_LOWER_KILL_MAIN) == -1 then
                npcHandler:say('You have not started that mission.', cid)
            elseif player:getStorageValue(SPIKE_LOWER_KILL_MAIN) == 7 then
                npcHandler:say('You have done well. Here, take your reward.', cid)
                player:addFamePoint()
                player:addExperience(3500, true)
                player:setStorageValue(SPIKE_LOWER_KILL_MAIN, -1)
                player:setExhaustion(SPIKE_LOWER_KILL_DAILY, 86400)
            else
                npcHandler:say('Gnowful! Just go out to the caves and kill at least seven drillworms.', cid)
            end
        else
            npcHandler:say('That\'s not a valid mission name.', cid)
        end
        talkState[cid] = nil
        return
    end

    --[[///////////////////
    ////PARCEL DELIVERY////
    /////////////////////]]
    if msgcontains(msg, 'deliver') then
        if player:getExhaustion(SPIKE_LOWER_PARCEL_DAILY) > 0 then
            return npcHandler:say('Sorry, you have to wait ' .. string.diff(player:getStorageValue(SPIKE_LOWER_PARCEL_DAILY)-os.time()) .. ' before this task gets available again.', cid)
        end

        if player:getLevel() < level then
            return npcHandler:say('Sorry, you are not on the required minimum level [' .. level ..'].', cid)
        end

        if player:getStorageValue(SPIKE_LOWER_PARCEL_MAIN) == -1 then      
            npcHandler:say('We need someone to bring four parcels to some of our far away outposts in the caverns. If you are interested, I can give you some more {information} about it. Are you willing to accept this mission?', cid)
            talkState[cid] = 'delivery'
        else
            npcHandler:say('You have already started that mission.', cid)
        end
    end

    if talkState[cid] == 'delivery' then
        if msgcontains(msg, 'yes') then
            player:addItem(21569, 4)
            player:setStorageValue(SPIKE_LOWER_PARCEL_MAIN, 0)
            npcHandler:say({'Gnometastic! Here are the parcels. Regrettably, the labels got lost during transport; but I guess those lonely gnomes won\'t mind as long as they get ANY parcel at all.','If you lose the parcels, you\'ll have to get new ones. Gnomux sells all the equipment that is required for our missions.'}, cid)
            talkState[cid] = nil
        elseif msgcontains(msg, 'no') then
            npcHandler:say('Ok then.', cid)
            talkState[cid] = nil
        end
    end

    --[[//////////////
    ////UNDERCOVER////
    ////////////////]]
    if msgcontains(msg, 'undercover') then
        if player:getExhaustion(SPIKE_LOWER_UNDERCOVER_DAILY) > 0 then
            return npcHandler:say('Sorry, you have to wait ' .. string.diff(player:getStorageValue(SPIKE_LOWER_UNDERCOVER_DAILY)-os.time()) .. ' before this task gets available again.', cid)
        end

        if player:getLevel() < level then
            return npcHandler:say('Sorry, you are not on the required minimum level [' .. level ..'].', cid)
        end

        if player:getStorageValue(SPIKE_LOWER_UNDERCOVER_MAIN) == -1 then      
            npcHandler:say('Someone is needed to get three reports from our undercover agents posing as monsters in the caves around us. If you are interested, I can give you some more {information} about it. Are you willing to accept this mission?', cid)
            talkState[cid] = 'undercover'
        else
            npcHandler:say('You have already started that mission.', cid)
        end
    end

    if talkState[cid] == 'undercover' then
        if msgcontains(msg, 'yes') then
            player:setStorageValue(SPIKE_LOWER_UNDERCOVER_MAIN, 0)
            npcHandler:say('Gnometastic! Get three reports from our agents. You can find them anywhere in the caves around us. Just keep looking for monsters that behave strangely and give you a wink.', cid)
            talkState[cid] = nil
        elseif msgcontains(msg, 'no') then
            npcHandler:say('Ok then.', cid)
            talkState[cid] = nil
        end
    end

    --[[////////////////
    ////TEMPERATURE/////
    //////////////////]]
    if msgcontains(msg, 'temperature') then
        if player:getExhaustion(SPIKE_LOWER_LAVA_DAILY) > 0 then
            return npcHandler:say('Sorry, you have to wait ' .. string.diff(player:getStorageValue(SPIKE_LOWER_LAVA_DAILY)-os.time()) .. ' before this task gets available again.', cid)
        end

        if player:getLevel() < level then
            return npcHandler:say('Sorry, you are not on the required minimum level [' .. level ..'].', cid)
        end

        if player:getStorageValue(SPIKE_LOWER_LAVA_MAIN) == -1 then
            npcHandler:say('Your task would be to use a gnomish temperature measurement device - short GTMD - to locate the hottest spot at the lava pools in the caves. If you are interested, I can give you some more information about it. Are you willing to accept this mission?', cid)
            talkState[cid] = 'temperature'
        else
            npcHandler:say('You have already started that mission.', cid)
        end
    end

    if talkState[cid] == 'temperature' then
        if msgcontains(msg, 'yes') then
            player:addItem(21556, 1)
            player:setStorageValue(SPIKE_LOWER_LAVA_MAIN, 0)
            npcHandler:say('Gnometastic! Find the hottest spot of the lava pools in the caves. If you lose the GTMD before you find the hot spot, you\'ll have to get yourself a new one. Gnomux sells all the equipment that is required for our missions.', cid)
            talkState[cid] = nil
        elseif msgcontains(msg, 'no') then
            npcHandler:say('Ok then.', cid)
            talkState[cid] = nil
        end
    end

    --[[/////////
    ////KILL/////
    ///////////]]
    if msgcontains(msg, 'kill') then
        if player:getExhaustion(SPIKE_LOWER_KILL_DAILY) > 0 then
            return npcHandler:say('Sorry, you have to wait ' .. string.diff(player:getStorageValue(SPIKE_LOWER_KILL_DAILY)-os.time()) .. ' before this task gets available again.', cid)
        end

        if player:getLevel() < level then
            return npcHandler:say('Sorry, you are not on the required minimum level [' .. level ..'].', cid)
        end

        if player:getStorageValue(SPIKE_LOWER_KILL_MAIN) == -1 then      
            npcHandler:say('This mission will require you to kill some drillworms for us. If you are interested, I can give you some more {information} about it. Are you willing to accept this mission?', cid)
            talkState[cid] = 'kill'
        else
            npcHandler:say('You have already started that mission.', cid)
        end
    end

    if talkState[cid] == 'kill' then
        if msgcontains(msg, 'yes') then
            player:setStorageValue(SPIKE_LOWER_KILL_MAIN, 0)
            npcHandler:say('Gnometastic! You should have no trouble finding enough drillworms.', cid)
            talkState[cid] = nil
        elseif msgcontains(msg, 'no') then
            npcHandler:say('Ok then.', cid)
            talkState[cid] = nil
        end
    end  
    return true
end

npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
npcHandler:addModule(FocusModule:new())
 
Anniversary bump. Don't miss this jewel of script in your servers.
 
Actually is a really good script, it has some minor bugs which need to be solved but works ok.

But if you have fixed them or atleast found them why not post them so they can be fixed.
 
Havent fixed them yet so far. Just a player on my OT reported some errors.

Well you can still post what the problems are insted of saying that the code has bugs in it xD
 
Spikes Spikes

Just missing quest log, also there is an error on one NPC named Gnomme Tropper for the task of level 80+ is not answering hi/something/something/something in order to deliver Gnomish Supplies and finally A Nightmare Scion NPC is not answering the respective dialog for its task.
 
Back
Top