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

Error with firestorm Revscript

richardestro

New Member
Joined
Oct 23, 2023
Messages
16
Reaction score
2
Hey buddies, I'm trying to use this Revscript for firestorm event:
LUA:
--[[

    Credits: Sarah Wesker
    Version: 1.2
    Compat: TFS 1.3
    Create: December 2020

]]--

local config = {
    miscellaneous = {
        name = "FireStorm", -- event name
        talkaction = "!firestorm", -- Talkaction words
        -->> These two functions are compatible with newer versions of TFS Eyes o.o <<-
        canPushPlayers = false, -- Allow players within the event to push each other?
        canMoveItems = false, -- Allow moving items within the event?
        enterOnlyInPzTile = true
    },
    start = {
        time = "20:02:10", -- time to start event each day
        waiting = '30s' -- wait player to enter event, examples: 10s -> 10 seconds | 2m -> 2 minutes
    },
    area = {
        center = Position(3111, 1862, 8), -- center room event
        radius = {
            x = 17, -- radius X
            y = 16 -- radius Y
        }
    },
    players = {
        min = 2, -- min required
        max = 10, -- max players in the evento
        corpseId = 6325, -- corpse id :v
        storage = 7777 -- storage, to remove players from the event in case they get trapped! (it should never happen)
    },
    state = {
        type = 'stoped', -- no edit!
        debug = false -- not use in production
    },
    waves = { -- Wave properties in the event
        interval = 3000, -- event speed
        dificulty = 1, -- default start dificulty
        distanceEffect = CONST_ANI_FIRE, -- rain effect
        impactEffect = CONST_ME_FIREAREA, -- impact effect
        warningEffect = CONST_ME_HITBYFIRE, -- warning effect
        iDWhenWaves = { waves = 5, increase = 0.2 } -- every 5 waves, increase the difficulty 0.2
    },
    rewardBag = 1992, -- Reward bag id
    rewards = { -- In this table, you can add the rewards, as are the example:
        { itemId = 2160, count = 100, chance = 100 },
        { itemId = 7591, count = 100, chance = 70 },
        { itemId = 7590, count = 100, chance = 70 }
    },
    cache = { -- This table should not be modified for anything
        eventIds = {},
        players = {},
        tiles = {},
        tileCount = 0,
        waves = 0,
        dificulty = 0
    }
}

_FSE = {}

function _FSE.isWalkable(x, y, z)
    local tile = Tile(x, y, z)
    if not tile or tile:hasFlag(TILESTATE_FLOORCHANGE) 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 tile
end

local function shuffle(tbl)
    for i = #tbl, 2, -1 do
        local j = math.random(i)
        tbl[i], tbl[j] = tbl[j], tbl[i]
    end
end

local function loadTiles()
    config.cache.tiles = {}
    config.cache.tileCount = 0
    for x = config.area.center.x - config.area.radius.x, config.area.center.x + config.area.radius.x do
        for y = config.area.center.y - config.area.radius.y, config.area.center.y + config.area.radius.y do
            local tile = _FSE.isWalkable(x, y, config.area.center.z)
            if tile then
                table.insert(config.cache.tiles, tile)
                config.cache.tileCount = config.cache.tileCount +1
            end
        end
    end
    shuffle(config.cache.tiles)
end

if not Container.getItems then
    function Container.getItems(self, array)
        local array = array or {}
        for slot = 0, self:getCapacity() - 1 do
            local item = self:getItem(slot)
            if item and item:isContainer() then
                array = item:getItems(array)
            elseif item then
                array[#array +1] = item
            end
        end
        return array
    end
end

local function formatTime(seconds)
    if seconds <= 0 then return '0s' end
    local days = math.floor(seconds / 86400)
    seconds = (seconds % 86400)
    local hours = math.floor(seconds / 3600)
    seconds = (seconds % 3600)
    local minutes = math.floor(seconds / 60)
    seconds = (seconds % 60)
    local result = ''
    if days >= 1 then result = string.format("%s%u days", result, days) end
    if hours >= 1 then result = string.format("%s%s%u hours", result, (days > 0 and ' ' or ''), hours) end
    if minutes >= 1 then result = string.format("%s%s%u minutes", result, (hours > 0 and ' ' or ''), minutes) end
    if seconds >= 1 then result = string.format("%s%s%u seconds", result, (minutes > 0 and ' ' or ''), seconds) end
    return result
end

local function getTime(str)
    local seconds = str:match('(%d+)s') or 0
    local minutes = str:match('(%d+)m') or 0
    local hours = str:match('(%d+)h') or 0
    return seconds, minutes, hours
end

function _FSE.debug(message, player)
    local errorMsg = string.format("[%s - Debug] %s", config.miscellaneous.name, message)
    if player then
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, errorMsg)
    end
end

function _FSE.eventSay(message)
    Game.broadcastMessage(string.format("%s says:\n%s", config.miscellaneous.name, message), MESSAGE_EVENT_ADVANCE)
end

function _FSE.sayToPlayers(message)
    for _, playerId in pairs(config.cache.players) do
        local player = Player(playerId)
        if player then
            player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("%s says:\n%s", config.miscellaneous.name, message))
        end
    end
end

function _FSE.removeCachePlayer(player)
    local playerId = player:getId()
    for index, pid in pairs(config.cache.players) do
        if pid == playerId then
            table.remove(config.cache.players, index)
            return true
        end
    end
    return false
end

function _FSE.finish()
    local winner = Player(config.cache.players[1])
    if winner then
        stopEvent(config.cache.eventIds['looping'])
        stopEvent(config.cache.eventIds['warningLooping'])
        winner:getPosition():sendMagicEffect(CONST_ME_TUTORIALSQUARE)
        winner:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Congratulations, you are the winner!")
        _FSE.eventSay(string.format("The player %s is the winner!", winner:getName()))
        config.cache.eventIds['winner'] = addEvent(_FSE.sendRewards, 3000)
    else
        _FSE.close()
    end
end

function _FSE.sendRewards()
    local winner = Player(config.cache.players[1])
    if not winner then
        _FSE.close()
        return
    end
    local bag = Game.createItem(config.rewardBag, 1)
    if bag then
        for _, reward in pairs(config.rewards) do
            if reward.chance >= math.random(1, 100) then
                bag:addItem(reward.itemId, reward.count, INDEX_WHEREEVER, FLAG_NOLIMIT)
            end
        end
        local inbox = winner:getInbox()
        if inbox then
            local description, items = "You rewards: ", bag:getItems()
            for _, item in pairs(items) do
                description = string.format("%s%d %s%s", description, item:getCount(), item:getName(), (_ == #items and '.' or ', '))
            end
            inbox:addItemEx(bag, INDEX_WHEREEVER, FLAG_NOLIMIT)
            winner:sendTextMessage(MESSAGE_INFO_DESCR, description..'\nCheck your depot inbox.')
        end
    end
    _FSE.close()
end

function _FSE.killPlayer(player)
    if not _FSE.removeCachePlayer(player) and config.state.debug then
        _FSE.debug(string.format("The player %s could not be removed from the cache, this is weird.", player:getName()))
    end
    local playerPos = player:getPosition()
    player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You are burned.")
    _FSE.resetPlayer(player)
    local corpse = Game.createItem(config.players.corpseId, 1, playerPos)
    if corpse then
        corpse:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, string.format("%s ashes.", player:getName()))
    end
end

function _FSE.resetPlayer(player)
    player:setStorageValue(config.players.storage, -1)
    local town = player:getTown()
    if not town then town = Town(1) end
    player:getPosition():sendMagicEffect(CONST_ME_POFF)
    local townPos = town:getTemplePosition()
    player:teleportTo(townPos)
    townPos:sendMagicEffect(CONST_ME_TELEPORT)
end

function _FSE.cleanCorpses()
    for _, tile in pairs(config.cache.tiles) do
        local corpse = tile:getItemById(config.players.corpseId)
        if corpse then
            corpse:remove()
        end
        local creatures = tile:getCreatures()
        for _, creature in pairs(creatures) do
            if creature:isPlayer() then
                _FSE.resetPlayer(creature)
            end
        end
    end
end

function _FSE.checkWaiting()
    if #config.cache.players >= config.players.min then
        _FSE.eventPreparing()
        return
    end
    config.state.type = "stoped"
    _FSE.eventSay("The event has been closed due to lack of participants.")
    _FSE.kickPlayers()
end

function _FSE.kickPlayers()
    for _, playerId in pairs(config.cache.players) do
        local player = Player(playerId)
        if player then
            _FSE.resetPlayer(player)
        end
    end
    config.cache.players = {}
end

function _FSE.kickPlayer(player)
    local index = _FSE.isPlayerExist(player)
    if not index then
        return false
    end
    _FSE.resetPlayer(player)
    table.remove(config.cache.players, index)
    return true
end

function _FSE.isPlayerExist(player)
    local playerId = player:getId()
    for _, pid in pairs(config.cache.players) do
        if playerId == pid then
            return _
        end
    end
    return false
end

function _FSE.joinPlayer(player)
    table.insert(config.cache.players, player:getId())
    player:setStorageValue(config.players.storage, 1)
    player:getPosition():sendMagicEffect(CONST_ME_POFF)
    player:teleportTo(config.area.center)
    config.area.center:sendMagicEffect(CONST_ME_TELEPORT)
    if #config.cache.players >= config.players.max then
        local waitingEventId = config.cache.eventIds['waiting']
        if waitingEventId then
            stopEvent(waitingEventId)
            config.cache.eventIds['waiting'] = nil
        end
        _FSE.eventPreparing()
    end
    return true
end

function _FSE.eventPreparing()
    _FSE.eventSay("We are ready to start, get ready...")
    config.state.type = "preparing"
    config.cache.eventIds['warningLooping'] = addEvent(_FSE.eventWaveWarningLooping, 6000)
end


function _FSE.eventWaveWarningLooping()
    if config.cache.dificulty == 0 then
        config.state.type = "started"
        config.cache.dificulty = config.waves.dificulty
        if config.state.debug then
            _FSE.debug(string.format("load default dificulty: %d", config.cache.dificulty))
        end
    elseif #config.cache.players == 1 then
        _FSE.finish()
        return
    end
    local affectedTileCount = math.min(config.cache.tileCount, (config.cache.tileCount / 6) * config.cache.dificulty)
    local affectedTileList = {}
    local start = os.mtime()
    while affectedTileCount > 0 do
        if os.mtime() - start > 20 then
            break
        end
        local rNumber = math.random(1, config.cache.tileCount)
        if not table.contains(affectedTileList, rNumber) then
            table.insert(affectedTileList, rNumber)
            affectedTileCount = affectedTileCount -1
        end
    end
    for _, tNumber in pairs(affectedTileList) do
        local tile = config.cache.tiles[tNumber]
        tile:getPosition():sendMagicEffect(config.waves.warningEffect)
    end
    local interval = math.max(500, config.waves.interval / config.cache.dificulty)
    config.cache.eventIds['looping'] = addEvent(_FSE.eventWaveLooping, interval, affectedTileList, interval)
end

function _FSE.eventWaveLooping(affectedTileList, interval)
    for _, tNumber in pairs(affectedTileList) do
        local tile = config.cache.tiles[tNumber]
        local position = tile:getPosition()
        local fromPos = Position(position.x -5, position.y -5, position.z)
        fromPos:sendDistanceEffect(position, config.waves.distanceEffect)
        position:sendMagicEffect(config.waves.impactEffect)
        local creatures = tile:getCreatures()
        for _, creature in pairs(creatures) do
            if creature:isPlayer() then
                _FSE.killPlayer(creature)
                if #config.cache.players == 1 then
                    _FSE.finish()
                    return
                end
            end
        end
    end
    -- increase wave count and dificulty
    if config.cache.waves > 0 and (config.cache.waves % config.waves.iDWhenWaves.waves == 0) then
        config.cache.dificulty = config.cache.dificulty + config.waves.iDWhenWaves.increase
        _FSE.sayToPlayers("The difficulty has increased!")
    end
    config.cache.waves = config.cache.waves +1
    config.cache.eventIds['warningLooping'] = addEvent(_FSE.eventWaveWarningLooping, interval)
end

function _FSE.init()
    if config.cache.tileCount == 0 then
        loadTiles()
        if config.state.debug then
            _FSE.debug(string.format("loaded %d tiles.", config.cache.tileCount))
        end
    end
    _FSE.cleanCorpses()
    local s, m ,h = getTime(config.start.waiting)
    local tseconds = s+(m*60)+(h*60*60)
    _FSE.eventSay(string.format("The event will start in %s, use the command %s to enter.", formatTime(tseconds), config.miscellaneous.talkaction))
    config.cache.eventIds['waiting'] = addEvent(_FSE.checkWaiting, tseconds * 1000)
    config.state.type = "open"
    return true
end

function _FSE.close()
    for _, eventId in pairs(config.cache.eventIds) do
        stopEvent(eventId)
    end
    config.cache.waves = 0
    config.cache.dificulty = 0
    config.cache.eventIds = {}
    _FSE.kickPlayers()
    config.state.type = "close"
end

local global = GlobalEvent("FireStorm")
global.onTime = _FSE.init
global:time(config.start.time)
global:register()

local talk = TalkAction(config.miscellaneous.talkaction)
function talk.onSay(player, words, param)
    if player:getGroup():getAccess() then
        if param == "open" then
            if table.contains({'open', 'started', 'preparing'}, config.state.type) then
                player:sendCancelMessage("The event is now open.")
            else
                _FSE.init()
            end
            return false
        elseif param == "close" then
            if table.contains({'close', 'stoped'}, config.state.type) then
                player:sendCancelMessage("The event is already closed.")
            else
                _FSE.close()
            end
            return false
        end
    end
    if config.miscellaneous.enterOnlyInPzTile then
        if not Tile(player:getPosition()):hasFlag(TILESTATE_PROTECTIONZONE) then
            player:sendCancelMessage("You can enter only in PZ.")
            return false
        end
    end
    if table.contains({'back', 'exit'}, param:lower()) then
        if table.contains({'preparing', 'started'}, config.state.type) then
            player:sendCancelMessage("Sorry, but the event already started.")
        elseif not _FSE.kickPlayer(player) then
            player:sendCancelMessage("Sorry, but you are not at the event.")
        else
            player:sendCancelMessage("You have left the event.")
        end
        return false
    end
    if _FSE.isPlayerExist(player) then
        player:sendCancelMessage("Sorry, you're already inside the event.")
        return false
    end
    if config.state.type == "open" then
        if not _FSE.joinPlayer(player) then
            player:sendCancelMessage("Sorry, there are no more spaces.")
        end
    elseif table.contains({'close', 'stoped'}, config.state.type) then
        player:sendCancelMessage("Sorry, but the event is closed.")
    else
        player:sendCancelMessage("Sorry, but the event is running.")
    end
    return false
end

talk:separator(" ")
talk:register()

local cEvent = CreatureEvent("FireStorm")
function cEvent.onLogin(player)
    if player:getStorageValue(config.players.storage) == 1 then
        _FSE.resetPlayer(player)
    end
    return true
end
cEvent:register()

local cEvent = CreatureEvent("FireStormLogout")
function cEvent.onLogout(player)
    if player:getStorageValue(config.players.storage) == 1 then
        player:sendCancelMessage("You cannot logout, because you are at the event.")
        return false
    end
    return true
end
cEvent:register()


if EventCallback then
    local ec = EventCallback
    function ec.onMoveCreature(player, creature, fromPosition, toPosition)
        if not player:getGroup():getAccess() then
            if not config.miscellaneous.canPushPlayers then
                if player:getStorageValue(config.players.storage) == 1 then
                    player:sendCancelMessage("It is not allowed to move players in the event.")
                    return false
                end
            end
        end
        return true
    end
    ec:register(-1)

    local ec = EventCallback
    function ec.onMoveItem(player, item, count, fromPosition, toPosition, fromCylinder, toCylinder)
        if not player:getGroup():getAccess() then
            if not config.miscellaneous.canMoveItems then
                if toPosition.x ~= CONTAINER_POSITION or fromPosition.x ~= CONTAINER_POSITION then
                    if player:getStorageValue(config.players.storage) == 1 then
                        player:sendCancelMessage("It is not allowed to move items in the event.")
                        return false
                    end
                end
            end
        end
        return true
    end
    ec:register(-1)

elseif config.state.debug then
    _FSE.debug("Your current version does not support EventCallback functionalities.")
end

Original code thread: [TFS 1.3] EventCallback: Firestorm Event (https://otland.net/threads/tfs-1-3-eventcallback-firestorm-event.274028/)

But when i want to execute the event with "!firestorm open" or I wait it to start normally with the day time i'm getting this error:
Code:
Lua Script Error: [Scripts Interface]
D:\Escritorio\Basureron't\Pruebas del canaot\Canas Ote\Toxic Tibia\data\scripts\firestorm.lua:callback
... canaot\Canas Ote\Toxic Tibia\data\scripts\firestorm.lua:249: bad argument #1 to 'pairs' (table expected, got nil)
stack traceback:
        [C]: at 0x7ff7402320d0
        [C]: in function 'pairs'
        ... canaot\Canas Ote\Toxic Tibia\data\scripts\firestorm.lua:249: in function 'cleanCorpses'
        ... canaot\Canas Ote\Toxic Tibia\data\scripts\firestorm.lua:387: in function 'init'
        ... canaot\Canas Ote\Toxic Tibia\data\scripts\firestorm.lua:419: in function <... canaot\Canas Ote\Toxic Tibia\data\scripts\firestorm.lua:413>

I'm using TFS 1.3, thanks in advance :D
 
Changes lines 248-253
LUA:
local creatures = tile:getCreatures()
for _, creature in pairs(creatures) do
    if creature:isPlayer() then
        _FSE.resetPlayer(creature)
    end
end
to
LUA:
local creatures = tile:getCreatures()
if creatures then
    for _, creature in pairs(creatures) do
        if creature:isPlayer() then
            _FSE.resetPlayer(creature)
        end
    end
end
 
Changes lines 248-253
LUA:
local creatures = tile:getCreatures()
for _, creature in pairs(creatures) do
    if creature:isPlayer() then
        _FSE.resetPlayer(creature)
    end
end
to
LUA:
local creatures = tile:getCreatures()
if creatures then
    for _, creature in pairs(creatures) do
        if creature:isPlayer() then
            _FSE.resetPlayer(creature)
        end
    end
end
That worked to start the event,; event started got 1 wave and then the console shows this error lol:
LUA:
Lua Script Error: [Main Interface]
in a timer event called from:
(Unknown scriptfile)
... canaot\Canas Ote\Toxic Tibia\data\scripts\firestorm.lua:363: bad argument #1 to 'pairs' (table expected, got nil)
stack traceback:
        [C]: at 0x7ff73d5420d0
        [C]: in function 'pairs'
        ... canaot\Canas Ote\Toxic Tibia\data\scripts\firestorm.lua:363: in function <... canaot\Canas Ote\Toxic Tibia\data\scripts\firestorm.lua:355>

:(
 
Changes
LUA:
function _FSE.cleanCorpses()
    for _, tile in pairs(config.cache.tiles) do
        local corpse = tile:getItemById(config.players.corpseId)
        if corpse then
            corpse:remove()
        end
        local creatures = tile:getCreatures()
        for _, creature in pairs(creatures) do
            if creature:isPlayer() then
                _FSE.resetPlayer(creature)
            end
        end
    end
end
to.
LUA:
function _FSE.cleanCorpses()
    if not config.cache.tiles then
        if config.state.debug then
            _FSE.debug("The cache tiles table is empty or nil.")
        end
        return
    end

    for _, tile in pairs(config.cache.tiles) do
        if tile then
            local corpse = tile:getItemById(config.players.corpseId)
            if corpse then
                corpse:remove()
            end
            local creatures = tile:getCreatures()
            for _, creature in pairs(creatures) do
                if creature:isPlayer() then
                    _FSE.resetPlayer(creature)
                end
            end
        end
    end
end
 
same thing...

lines 360-369
LUA:
local creatures = tile:getCreatures()
for _, creature in pairs(creatures) do
    if creature:isPlayer() then
        _FSE.killPlayer(creature)
        if #config.cache.players == 1 then
            _FSE.finish()
            return
        end
    end
end
to
LUA:
local creatures = tile:getCreatures()
if creatures then
    for _, creature in pairs(creatures) do
        if creature:isPlayer() then
            _FSE.killPlayer(creature)
            if #config.cache.players == 1 then
                _FSE.finish()
                return
            end
        end
    end
end
 
Back
Top