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

Solved zombie event, tfs 1.4 players don't get kicked or attacked

johnsamir

Advanced OT User
Joined
Oct 13, 2009
Messages
1,064
Solutions
6
Reaction score
183
Location
Nowhere
As title says player don't get attacked or kicked out after the zombie has aproached the player

1.png
get thsi error in the console:

Lua:
Lua Script Error: [Event Interface]
data/events/scripts/creature.lua:Creature@onTargetCombat
data/events/scripts/creature.lua:50: attempt to call global 'hasEventCallback' (a nil value)
stack traceback:
        data/events/scripts/creature.lua:50: in function <data/events/scripts/creature.lua:40>

this is the script
Code:
local function secondsToReadable(s)
    local hours   = math.floor(s / 3600)
    local minutes = math.floor(math.fmod(s, 3600)/60)
    local seconds = math.floor(math.fmod(s, 60))
    return (hours   > 0 and (hours   .. ' hour'   .. (hours   > 1 and 's ' or ' ')) or '') ..
           (minutes > 0 and (minutes .. ' minute' .. (minutes > 1 and 's ' or ' ')) or '') ..
           (seconds > 0 and (seconds .. ' second' .. (seconds > 1 and 's ' or ' ')) or '')
end
local zombie = {}
-- keeps track of players & zombies
zombie.players = {}
zombie.zombies = {}
--#
zombie.config  = {
    startTime = {
        time = '23:08:00', -- Hours:minutes:seconds
        days = { -- set to "false" to disable specific days
            ["monday"] = true,
            ["tuesday"] = true,
            ["wednesday"] = true,
            ["thursday"] = true,
            ["friday"] = true,
            ["saturday"] = true,
            ["sunday"] = true,
        },
    },
    -- How many players needed to start the event.
       minimumPlayers = 2,
    -- How many players can enter at most.
    maximumPlayers = 10,
    -- %chance of a player dying from zombie attack
    playerDeathChance = 20, -- %
    -- How many zombies should spawn in the beginning?
    zombieStartAmount = 2,
    -- Name of the monster to be spawned
    zombieName = 'zombski',
    -- This is used to check if zombie event has started.
    storageEventStarted = 191817,
    -- Position for the teleport which is going..
    -- ..to send players to the waiting room.
    teleportSpawnPosition = Position(32340, 32218, 7),
    waitingRoom = {
        topLeft     = Position(32231, 32182, 7),
        bottomRight = Position(32243, 32191, 7)
    },
    -- How long players will wait in the waiting room.
    waitingTime = 10, -- 10 seconds
    teleportId = 1387, -- ID of teleport item
    teleportActionId = 56783, -- action ID used on the teleport for detecting players
    -- Zombie arena; Where players will try to survive
    arena = {
        topLeft     = Position(32216, 32203, 8),
        bottomRight = Position(32264, 32234, 8)
    },
    -- set to `true` if you want the rewards..
    -- ..to be given randomly instead of all at once.
    randomReward = false,
    rewardBagId = 1987,
    rewards = {
        {2160, 1}, -- Crystal Coin
        {2159, 2}--, -- Scarab Coin
      --  {9020, 5}  -- Vampire Token
    }
}
--#
zombie.initEvent = function(self)
    local teleportItem = Game.createItem(self.config.teleportId, 1, self.config.teleportSpawnPosition)
    teleportItem:setActionId(self.config.teleportActionId)
    Teleport(teleportItem.uid):setDestination(Position(
        math.random(self.config.waitingRoom.topLeft.x, self.config.waitingRoom.bottomRight.x),
        math.random(self.config.waitingRoom.topLeft.y, self.config.waitingRoom.bottomRight.y),
        self.config.waitingRoom.topLeft.z
    ))
    Game.broadcastMessage('Zombie event will begin in '.. secondsToReadable(self.config.waitingTime) ..', Hurry up!')
    addEvent(function(z)
        local tpTile = Tile(z.config.teleportSpawnPosition)
        local tpItem = tpTile:getItemById(z.config.teleportId)
        if tpItem then
            tpItem:remove()
        end
        if z:countPlayers() < z.config.minimumPlayers then
            Game.broadcastMessage('Zombie event shutting down... not enough players.', MESSAGE_STATUS_CONSOLE_RED)
            z:kickPlayers()
            return
        end
        z:startEvent()
    end, self.config.waitingTime * 1000, self)
end
--#
zombie.startEvent = function(self)
    Game.setStorageValue(self.config.storageEventStarted, 1)
    Game.broadcastMessage('Zombie event has begun, Good luck!')
    for _, player in pairs(self.players) do
        if player then
            player:teleportTo(Position(
                math.random(self.config.arena.topLeft.x, self.config.arena.bottomRight.x),
                math.random(self.config.arena.topLeft.y, self.config.arena.bottomRight.y),
                self.config.arena.topLeft.z
            ))
        end
    end
    for i = self.config.zombieStartAmount, 1, -1 do
        self:spawnZombie(Position(
            math.random(self.config.arena.topLeft.x, self.config.arena.bottomRight.x),
            math.random(self.config.arena.topLeft.y, self.config.arena.bottomRight.y),
            self.config.arena.topLeft.z
        ))
    end
end
--#
zombie.stopEvent = function(self)
    Game.setStorageValue(self.config.storageEventStarted, -1)
    local winner = self:getWinner()
    if not winner then return end
    local depot = winner:getDepotChest(winner:getTown():getId(), true)
    local bag   = Game.createItem(self.config.rewardBagId, 1)
    local itemId = nil
    local itemCount = nil
    if self.config.randomReward then
        local randomRewardItem = self.config.rewards[math.random(1, #self.config.rewards)]
        itemId = randomRewardItem[1]
        itemCount = randomRewardItem[2]
        bag:addItemEx(Game.createItem(itemId, itemCount), INDEX_WHEREEVER, FLAG_NOLIMIT)
        depot:addItemEx(bag)
        winner:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, '[Zombie] You have received a reward item. Check your depot.')
        return
    end
    for _, reward in pairs(self.config.rewards) do
        itemId = reward[1]
        itemCount = reward[2]
        bag:addItemEx(Game.createItem(itemId, itemCount), INDEX_WHEREEVER, FLAG_NOLIMIT)
    end
    depot:addItemEx(bag)
    winner:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, '[Zombie] You have received reward items. Check your depot.')
    Game.broadcastMessage(winner:getName() .. ' has won zombie event.')
    zombie:kickPlayers()
    zombie:clearZombies()
end
--#
zombie.addPlayer = function(self, p)
    self.players[p:getId()] = p
end
--#
zombie.removePlayer = function(self, player)
    self.players[player:getId()] = nil
    player:teleportTo({x = 1000, y = 1000, z = 7})
    player:addHealth(player:getMaxHealth())
    if self:countPlayers() == 1 then
        self:stopEvent()
    end
end
--#
zombie.countPlayers = function(self)
    local n = 0
    for _, player in pairs(self.players) do
        if player then n = n + 1 end
    end
    return n
end
--#
zombie.kickPlayers = function(self)
    for _, player in pairs(self.players) do
        if player then
            self:removePlayer(player)
        end
    end
    self.players = {}
end
--#
zombie.getWinner = function(self)
    for _, player in pairs(self.players) do
        if player then
            return player
        end
    end
    return nil
end
--#
zombie.clearZombies = function(self)
    for _, zombski in pairs(self.zombies) do
        if zombski then
            zombski:remove()
        end
    end
end
--#
zombie.spawnZombie = function(self, position)
    local zombie = Game.createMonster(self.config.zombieName, position, false, true)
    self.zombies[zombie:getId()] = zombie
    position:sendMagicEffect(CONST_ME_MAGIC_RED)
end
--#
local ge = GlobalEvent('zombieStart')
function ge.onTime(interval)
    local currentDay = os.date("%A"):lower()
    if not zombie.config.startTime.days[currentDay] then
        return true
    end

    local eventStorage = Game.getStorageValue(zombie.config.storageEventStarted)
    local hasStarted = (eventStorage and (eventStorage == 1)) or false
    if hasStarted then
        print('[Error - ZombieEvent:onTime] The event has already started.')
        return true
    end
    local tile = Tile(zombie.config.teleportSpawnPosition)
    if not tile then
        print('[Error - ZombieEvent:onTime] Could not create teleport, tile not found!')
        return true
    end
    zombie:initEvent()
    return true
end
ge:time(zombie.config.startTime.time)
ge:register()
--#
local enterZombie = MoveEvent('enterZombie')
function enterZombie.onStepIn(player, item, position, fromPosition)
    if not item:getId() == zombie.config.teleportId then
        return true
    end
    zombie:addPlayer(player)
    Game.broadcastMessage(player:getName() .. ' has entered zombie event.', MESSAGE_STATUS_CONSOLE_RED)
    if zombie:countPlayers() >= zombie.config.maximumPlayers then
        Game.broadcastMessage('Zombie event will begin in a moment... Get ready!')
        addEvent(function() zombie:startEvent() end, 3 * 1000)
    end
    return true
end
enterZombie:aid(zombie.config.teleportActionId)
enterZombie:register()
--#
local eventCallback = EventCallback
function eventCallback.onTargetCombat(creature, target)
    if not creature or not target then
        return RETURNVALUE_NOERROR
    end
    if (not creature:isMonster())
    or (creature:getName():lower() ~= zombie.config.zombieName:lower())
    or (not target:isPlayer()) then
        return RETURNVALUE_NOERROR
    end
    local deathChance = zombie.config.playerDeathChance
    math.randomseed(os.time())
    if math.random(1, 100) <= deathChance then
        local targetPos = target:getPosition()
        targetPos:sendMagicEffect(CONST_ME_MORTAREA)
        targetPos:sendMagicEffect(CONST_ME_BIGPLANTS)
        target:sendTextMessage(MESSAGE_EVENT_ADVANCE, 'You have been killed by a zombie.')
        zombie:spawnZombie(targetPos)
        zombie:removePlayer(target)
        return RETURNVALUE_NOERROR
    end
    target:say('!survived!', TALKTYPE_MONSTER_SAY)
    target:getPosition():sendMagicEffect(CONST_ME_HOLYAREA)
    return RETURNVALUE_NOERROR
end
eventCallback:register(1)

this is my data/events/scripts/creature.lua ontargetcombat code
Code:
function Creature:onTargetCombat(target)
    local player = Player(self)
    if player and target and CTF_STATUS == 1 then
        if isGreenTeam(player:getName()) and isGreenTeam(target:getName()) then
            return false
        elseif isRedTeam(player:getName()) and isRedTeam(target:getName()) then
            return false
        end
    end

    if hasEventCallback(EVENT_CALLBACK_ONTARGETCOMBAT) then
        return EventCallback(EVENT_CALLBACK_ONTARGETCOMBAT, self, target)
    else
        return RETURNVALUE_NOERROR
    end
end
this is the line 50
Code:
 if hasEventCallback(EVENT_CALLBACK_ONTARGETCOMBAT) then

this is my eventscallback
Code:
local unpack = unpack
local pack = table.pack

local EventCallbackData, callbacks, updateableParameters, autoID = {}, {}, {}, 0
-- This metatable creates an auto-configuration mechanism to create new types of EventCallbacks
local ec = setmetatable({}, { __newindex = function(self, key, value)
    autoID = autoID + 1
    callbacks[key] = autoID
    local info, update = {}, {}
    for k, v in pairs(value) do
        if type(k) == "string" then
            info[k] = v
        else
            update[k] = v
        end
    end
    updateableParameters[autoID] = update
    callbacks[autoID] = info
    EventCallbackData[autoID] = {maxn = 0}
    EVENT_CALLBACK_LAST = autoID
end})

--@ Definitions of valid EventCallback types to hook according to the given field name
--@ The fields within the assigned table, allow to save arbitrary information
-- Creature
ec.onChangeOutfit = {}
ec.onChangeMount = {}
ec.onAreaCombat = {returnValue=true}
ec.onTargetCombat = {returnValue=true}
ec.onHear = {}
ec.onChangeZone = {}
-- Party
ec.onJoin = {}
ec.onLeave = {}
ec.onDisband = {}
ec.onShareExperience = {}
ec.onInvite = {}
ec.onRevokeInvitation = {}
ec.onPassLeadership = {}
-- Player
ec.onBrowseField = {}
ec.onLook = {[5] = 1}
ec.onLookInBattleList = {[4] = 1}
ec.onLookInTrade = {[5] = 1}
ec.onLookInShop = {[4] = 1}
ec.onLookInMarket = {}
ec.onTradeRequest = {}
ec.onTradeAccept = {}
ec.onTradeCompleted = {}
ec.onMoveItem = {returnValue=true}
ec.onItemMoved = {}
ec.onMoveCreature = {}
ec.onReportRuleViolation = {}
ec.onReportBug = {}
ec.onTurn = {}
ec.onRotateItem = {}
ec.onGainExperience = {[3] = 1}
ec.onLoseExperience = {[2] = 1}
ec.onGainSkillTries = {[3] = 1}
ec.onWrapItem = {}
ec.onInventoryUpdate = {}
ec.onUpdateStorage = {}
-- Monster
ec.onDropLoot = {}
ec.onSpawn = {}

EventCallback = {
    register = function(self, triggerIndex)
        if isScriptsInterface() then
            local eventType = rawget(self, 'eventType')
            local callback = rawget(self, 'callback')
            if not eventType or not callback then
                debugPrint("[Warning - EventCallback::register] need to setup a callback before you can register.")
                return
            end

            local eventData = EventCallbackData[eventType]
            eventData.maxn = #eventData + 1
            eventData[eventData.maxn] = {
                callback = callback,
                triggerIndex = tonumber(triggerIndex) or 0
            }
            table.sort(eventData, function(ecl, ecr) return ecl.triggerIndex < ecr.triggerIndex end)
            self.eventType = nil
            self.callback = nil
        end
    end,

    clear = function(self)
        EventCallbackData = {}
        for i = 1, EVENT_CALLBACK_LAST do
            EventCallbackData[i] = {maxn = 0}
        end
    end
}

setmetatable(EventCallback, {
    __newindex = function(self, key, callback)
        if not isScriptsInterface() then
            return
        end

        local eventType = callbacks[key]
        if not eventType then
            debugPrint(string.format("[Warning - EventCallback::%s] is not a valid callback.", key))
            return
        end

        if type(callback) ~= "function" then
            debugPrint(string.format("[Warning - EventCallback::%s] a function is expected.", key))
            return
        end

        rawset(self, 'eventType', eventType)
        rawset(self, 'callback', callback)
    end,

    __index = function(self, key)
        local callback = callbacks[key]
        if not callback then
            if not isScriptsInterface() then
                return
            end

            return rawget(self, key)
        end

        local eventData = EventCallbackData[callback]
        local maxn = eventData.maxn
        if maxn == 0 then
            return
        end

        return function(...)
            local results, args, info = {}, pack(...), callbacks[callback]
            for index = 1, maxn do
                repeat
                    results = {eventData[index].callback(unpack(args))}
                    local output = results[1]
                    -- If the call returns nil then we continue with the next call
                    if output == nil then
                        break
                    end
                    -- If the call returns false then we exit the loop
                    if not output then
                        return false
                    end
                    -- If the call of type returnvalue returns noerror then we continue the loop
                    if info.returnValue then
                        if output == RETURNVALUE_NOERROR then
                            break
                        end
                        return output
                    end
                    -- We left the loop why have we reached the end
                    if index == eventData.maxn then
                        return unpack(results)
                    end
                until true
                -- Update the results for the next call
                for index, value in pairs(updateableParameters[callback]) do
                    args[index] = results[value]
                end
            end
        end
    end
})
 
Solution thanks to @Xikini
had to change creature.lua
Lua:
function Creature:onTargetCombat(target)
    local player = Player(self)
    if player and target and CTF_STATUS == 1 then
        if isGreenTeam(player:getName()) and isGreenTeam(target:getName()) then
            return false
        elseif isRedTeam(player:getName()) and isRedTeam(target:getName()) then
            return false
        end
    end
    
    local onTargetCombat = EventCallback.onTargetCombat
    if onTargetCombat then
        return onTargetCombat(self, target)
    end
    return RETURNVALUE_NOERROR
end

edit item_teleport destionations7.lua
Code:
local itemTeleports = MoveEvent()

function itemTeleports.onAddItem(moveitem, tileitem, position)
    local setting = ItemTeleports[tileitem.actionid]
    if not setting then
        return true
    end

    moveitem:moveTo(setting.destination)
    setting.destination:sendMagicEffect(setting.effect)
    return true
end

itemTeleports:type("additem")
itemTeleports:id(1387, 8058)
itemTeleports:aid(50240, 50241, 56783)
--itemTeleports:uid(56783)
itemTeleports:register()
-- i have added the code 56783

-- register  teleport ids and aid/uid i really don't know pretty well actually :/
add teleport_items_destinations into
core folder this is the missing file
Code:
-- Set to which position items are teleported for each teleport item
-- If no position is set they will not be teleported anywhere
--[[
    [aid] = {destination = Postion(), effect = CONST_ME__}
]]

ItemTeleports = {
    [5630] = {destination = Position(33145, 32863, 7), effect = CONST_ME_MAGIC_GREEN},
    [5631] = {destination = Position(33147, 32864, 7), effect = CONST_ME_MAGIC_GREEN},

    -- Rookgaard level bridge
    [50240] = {destination = Position(32092, 32177, 6), effect = CONST_ME_MAGIC_BLUE},
    -- Rookgaard premium bridge
    [50241] = {destination = Position(32066, 32192, 7), effect = CONST_ME_MAGIC_BLUE}
}
and add the kick coordinates

thanks again @Xikini for your patience and good temper


this issue is solved
 
it saddens see how this community is used to create lazy people on new generations, where they want everything to be solved for them, a year ago everybody cared about learning and solving their own problems was a challenge.

i agree with dakos we are ruining the happiness of fixing every single bug or problem that was presented as challenges in tfs. @Dakos
em ok. didn't said that he made the things he was teaching me what was wrong that's it. you are just making assumptions which are wrong
 
Back
Top