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

Action [TFS 1.3] Advanced Annihilator System

Yea its edited by ninja i think 50% of people are using his version
Well 50% of people are using a outdated, bugged server. I suggest you try nekiros 1.3 8.6 version since it is still maintained. But if you want to troubleshoot your own distro your issue most likely lies here:

Lua:
    local tile = Tile(position)
    local item = tile and tile:getItemById(config.pulled_id)
    if not item then
        return
    end
 
Well 50% of people are using a outdated, bugged server. I suggest you try nekiros 1.3 8.6 version since it is still maintained. But if you want to troubleshoot your own distro your issue most likely lies here:

Lua:
    local tile = Tile(position)
    local item = tile and tile:getItemById(config.pulled_id)
    if not item then
        return
    end
Yea its true. Well its kinda late to move on new tfs :D Im not sure what might be different in this part maybe local tile = Tile(toPosition) but it doesnt solve anything
 
Yea its true. Well its kinda late to move on new tfs :D Im not sure what might be different in this part maybe local tile = Tile(toPosition) but it doesnt solve anything
Try changing the part in the addEvent at the bottom of the onUse func. Change toPosition to fromPosition.
 
Last edited:
Annihilator_rewards, recognizes the uniqueids as the rewards that be awarded.
Idk what you're doing wrong but it works as intended.
Lua:
player:addItem(reward.reward_id, reward.reward_count, false, 1, CONST_SLOT_WHEREEVER)
You can see here that it is adding the reward_id and not the chest_uid like you claim. Are you sure you are not just getting these mixed up?
 
No. I can show you in live.
I would guess it's because your quest.lua is overriding this script and adding the item based on the uid.

Make sure you are setting the uid's to the annihilator_chest.lua in actions.xml, otherwise it will be using quest.lua to determine what to do with the chest because that action script is set by the item id of the chest.

Example:
XML:
<action fromuid="5306" touid="5309" script="quests/annihilator_chest.lua"/>

Also if you still have issues, I would suggest using a uid greater than 30000 to avoid this. Otherwise if you can't get it figured out I sent you my discord in pms.
 
Features:
  • Supports multiple different creatures
  • Supports custom participant amount
  • Automatic reset
  • Fully customizable
  • Error notifications
  • Should work for other TFS 1.X servers

annihilator.lua
Lua:
local config = {
    duration = 30, -- time till reset, in minutes
    level_req = 100, -- minimum level to do quest
    min_players = 4, -- minimum players to join quest
    lever_id = 1945, -- id of lever before pulled
    pulled_id = 1946 -- id of lever after pulled
}

local player_positions = {
    [1] = {fromPos = Position(1000, 1000, 7), toPos = Position(1000, 1000, 7)},
    [2] = {fromPos = Position(1000, 1000, 7), toPos = Position(1000, 1000, 7)},
    [3] = {fromPos = Position(1000, 1000, 7), toPos = Position(1000, 1000, 7)},
    [4] = {fromPos = Position(1000, 1000, 7), toPos = Position(1000, 1000, 7)}
}

local monsters = {
    [1] = {pos = Position(1000, 1000, 7), name = "Demon"},
    [2] = {pos = Position(1000, 1000, 7), name = "Demon"},
    [3] = {pos = Position(1000, 1000, 7), name = "Demon"},
    [4] = {pos = Position(1000, 1000, 7), name = "Demon"},
    [5] = {pos = Position(1000, 1000, 7), name = "Demon"},
    [6] = {pos = Position(1000, 1000, 7), name = "Demon"}
}

function doResetAnnihilator(uid)
    local item = Item(uid)
    if not item then
        return
    end

    local monster_names = {}
    for key, value in pairs(monsters) do
        if not isInArray(monster_names, value.name) then
            monster_names[monster_names + 1] = value.name
        end
    end

    for i = 1, #monsters do
        local creatures = Tile(monsters[i].pos):getCreatures()
        for key, creature in pairs(creatures) do
            if isInArray(monster_names, creature:getName()) then
                creature:remove()
            end
        end
    end

    for i = 1, #player_positions do
        local creatures = Tile(player_positions[i].toPos):getCreatures()
        for key, creature in pairs(creatures) do
            if isInArray(monster_names, creature:getName()) then
                creature:remove()
            end
        end
    end

    item:transform(config.lever_id)
end

function onUse(player, item, fromPosition, target, toPosition, isHotkey)
    if item.itemid ~= config.lever_id then
        return player:sendCancelMessage("The quest is currently in use. Cooldown is " .. config.duration .. " minutes.")
    end

    local participants, pull_player = {}, false
    for i = 1, #player_positions do
        local fromPos = player_positions[i].fromPos
        local tile = Tile(fromPos)
        if not tile then
            print(">> ERROR: Annihilator tile does not exist for Position(" .. fromPos.x .. ", " .. fromPos.y .. ", " .. fromPos.z .. ").")
            return player:sendCancelMessage("There is an issue with this quest. Please contact an administrator.")
        end

        local creature = tile:getBottomCreature()
        if creature then
            local participant = creature:getPlayer()
            if not participant then
                return player:sendCancelMessage(participant:getName() .. " is not a valid participant.")
            end

            if participant:getLevel() < config.level_req then
                return player:sendCancelMessage(participant:getName() .. " is not the required level.")
            end

            if participant.uid == player.uid then
                pull_player = true
            end

            participants[#participants + 1] = {participant = participant, toPos = player_positions[i].toPos}
        end
    end

    if #participants < config.min_players then
        return player:sendCancelMessage("You do not have the required amount of participants.")
    end

    if not pull_player then
        return player:sendCancelMessage("You are in the wrong position.")
    end

    for i = 1, #monsters do
        local toPos = monsters[i].pos
        if not Tile(toPos) then
            print(">> ERROR: Annihilator tile does not exist for Position(" .. toPos.x .. ", " .. toPos.y .. ", " .. toPos.z .. ").")
            return player:sendCancelMessage("There is an issue with this quest. Please contact an administrator.")
        end
        Game.createMonster(monsters[i].name, monsters[i].pos, false, true)
    end

    for i = 1, #participants do
        participants[i].participant:teleportTo(participants[i].toPos)
        participants[i].toPos:sendMagicEffect(CONST_ME_TELEPORT)
    end

    item:transform(config.pulled_id)
    addEvent(doResetAnnihilator, config.duration * 60 * 1000, item.uid)
    return true
end

annihilator_chest.lua
Lua:
local storage_id = 2000

local rewards = {
    [1] = {chest_uid = 2001, reward_id = 2494, reward_count = 1},
    [2] = {chest_uid = 2002, reward_id = 2400, reward_count = 1},
    [3] = {chest_uid = 2003, reward_id = 2431, reward_count = 1},
    [4] = {chest_uid = 2004, reward_id = 2421, reward_count = 1}
}

function onUse(player, item, fromPosition, target, toPosition, isHotkey)
    local storage = player:getStorageValue(storage_id)
    if storage > 0 then
        return player:sendTextMessage(MESSAGE_INFO_DESCR, "It is empty.")
    end

    local reward
    for i = 1, #rewards do
        if rewards[i].chest_uid == item.uid then
            reward = rewards[i]
            break
        end
    end

    local reward_type = ItemType(reward.reward_id)
    if reward_type then
        if player:addItem(reward.reward_id, reward.reward_count, false, 1, CONST_SLOT_WHEREEVER) then
            player:sendTextMessage(MESSAGE_INFO_DESCR, "You have found a " .. reward_type:getName():lower() .. ".")
            player:setStorageValue(storage_id, 1)
        else
            local weight = reward_type:getWeight()
            player:sendTextMessage(MESSAGE_INFO_DESCR, 'You have found an item weighing ' .. weight / 100 .. ' oz it\'s too heavy or you do not have enough room.')
        end
    end
    return true
end

Why you're using table with indexes if you don't need these indexes? Maybe better avoid foor loop if you don't need it and use something like this:

Lua:
local rewards = {
    [2001] = {reward_id = 2494, reward_cound = 1},
    [2002] = {reward_id = 2400, reward_cound = 1},
    [2003] = {reward_id = 2431, reward_cound = 1},
    [2004] = {reward_id = 2421, reward_cound = 1}
}

if(rewards[item.uid]) then
    --script
 
Why you're using table with indexes if you don't need these indexes? Maybe better avoid foor loop if you don't need it and use something like this:

Lua:
local rewards = {
    [2001] = {reward_id = 2494, reward_cound = 1},
    [2002] = {reward_id = 2400, reward_cound = 1},
    [2003] = {reward_id = 2431, reward_cound = 1},
    [2004] = {reward_id = 2421, reward_cound = 1}
}

if(rewards[item.uid]) then
    --script
Yeah, you're right. When I was creating the script I was trying to make it as easy as possible to understand where to put the values, since a script like this would only be used by members with limited to no experience. But maybe it is better to show the optimal way to do things instead and add a comment on the side. If I do a rewrite I'll definitely consider changing it.
 
lever only reseting after server reset

Lua Script Error: [Main Interface]
in a timer event called from:
(Unknown scriptfile)
data/actions/scripts/orshani/orshani.lua:39: attempt to perform arithmetic on local 'monster_names' (a table value)
stack traceback:
[C]: in function '__add'
data/actions/scripts/orshani/orshani.lua:39: in function <data/actions/scripts/orshani/orshani.lua:29>
 
lever only reseting after server reset

Lua Script Error: [Main Interface]
in a timer event called from:
(Unknown scriptfile)
data/actions/scripts/orshani/orshani.lua:39: attempt to perform arithmetic on local 'monster_names' (a table value)
stack traceback:
[C]: in function '__add'
data/actions/scripts/orshani/orshani.lua:39: in function <data/actions/scripts/orshani/orshani.lua:29>
Same it resets only after server rests. I see person more above had same issue
 
lever only reseting after server reset

Lua Script Error: [Main Interface]
in a timer event called from:
(Unknown scriptfile)
data/actions/scripts/orshani/orshani.lua:39: attempt to perform arithmetic on local 'monster_names' (a table value)
stack traceback:
[C]: in function '__add'
data/actions/scripts/orshani/orshani.lua:39: in function <data/actions/scripts/orshani/orshani.lua:29>
If you are using something other than TFS base release (ninjas 8.6 won't work), or you haven't used the latest script (check later pages in this thread for updates) then you likely will have problems.

I've tested the latest update multiple times and I know it works.
 
lever only reseting after server reset

Lua Script Error: [Main Interface]
in a timer event called from:
(Unknown scriptfile)
data/actions/scripts/orshani/orshani.lua:39: attempt to perform arithmetic on local 'monster_names' (a table value)
stack traceback:
[C]: in function '__add'
data/actions/scripts/orshani/orshani.lua:39: in function <data/actions/scripts/orshani/orshani.lua:29>
Same it resets only after server rests. I see person more above had same issue
Change
Lua:
monster_names[monster_names + 1] = value.name
to
Lua:
monster_names[#monster_names + 1] = value.name
 
Im sorry to say this. I am not trying to start anything but, this doesn't seem like an advanced system. It is just the same system coded better.
 
Im sorry to say this. I am not trying to start anything but, this doesn't seem like an advanced system. It is just the same system coded better.
MIuplvE2

7dnPvjAS

Ironic :rolleyes:
 
That task NPC is actually legit though. It has some minor work needed just because of how coding works but all in all there is so much that can be done with it. The other npc quest system was outdated and doesn't have even close to the same abilities. I don't think that's ironic at all.

How about an anni that can use the groups level average and change the monsters depending on how strong they are, even the rewards.

Anyway, @Apollos you should make the zone nologout then add all the pariticpants to a table so you don't need to check fromPos toPos to kick them, ect. Save some memory usage.
Code:
local tmpParticipants = {}

function onUse()
codeToGetPartiticpants
tmpParticipants[#tmpParticipants + 1] = player:getName()

timedCodeToKickPlayers
end

function timedCodeToKickPlayers()
   for i = 1, #tmpParticipants do
         local player = Player(tmpParticipants[i])
             if player then
                kickPlayer
                tmpParticipants[i] = nil
             end
    end
openAnniCode
end
 
Last edited:
That task NPC is actually legit though. It has some minor work needed just because of how coding works but all in all there is so much that can be done with it. The other npc quest system was outdated and doesn't have even close to the same abilities. I don't think that's ironic at all.

How about an anni that can use the groups level average and change the monsters depending on how strong they are, even the rewards.

Anyway, @Apollos you should make the zone nologout then add all the pariticpants to a table so you don't need to check fromPos toPos to kick them, ect. Save some memory usage.
Code:
local tmpParticipants = {}

function onUse()
codeToGetPartiticpants
tmpParticipants[#tmpParticipants + 1] = player:getName()

timedCodeToKickPlayers
end

function timedCodeToKickPlayers()
   for i = 1, #tmpParticipants do
         local player = Player(tmpParticipants[i])
             if player then
                kickPlayer
                tmpParticipants[i] = nil
             end
    end
openAnniCode
end
I don't think my choice of words for the title is in need for debate. In my mind it's advanced because it's much more customizable than other scripts, if you disagree that's fine but I can't nor will change it now.

I don't see your point with the no logout part. If someone can't logout in the area yet still chooses to stay until the script is reset then you still need to check if they are in range to remove them. Otherwise you will either be not moving anyone at all or moving those inside or outside of the quest area. Also even if you were right isInRange is extremely lightweight of a function, it's not a big deal to use.
 
in a timer event called from:
(Unknown scriptfile)
data/actions/scripts/quests/annih.lua:58: attempt to call global 'isInRange' (a nil value)
stack traceback:
[C]: in function 'isInRange'
data/actions/scripts/quests/annih.lua:58: in function <data/actions/scripts/quests/annih.lua:24>
Post automatically merged:

in a timer event called from:
(Unknown scriptfile)
data/actions/scripts/quests/annih.lua:58: attempt to call global 'isInRange' (a nil value)
stack traceback:
[C]: in function 'isInRange'
data/actions/scripts/quests/annih.lua:58: in function <data/actions/scripts/quests/annih.lua:24>

solved it by adding

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

into data/lib/compat
 
Last edited:
Back
Top