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

GlobalEvent TFS 1.3 Monster Wave Event

Itutorial

Excellent OT User
Joined
Dec 23, 2014
Messages
2,307
Solutions
68
Reaction score
982
This is a monster wave event. Any amount of players can enter. Here is a list of what it does.

1) Checks to see if player can participate (only has level check)
2) Teleports players to the event area
3) Spawns waves
4) Gives rewards at the end of the wave
5) Event will handle everything itself and will start again by itself. It will also retry the event if there is not enough players
6) Talkaction for gm's to stop the event at any point

Rewards are: items, exp, outfits, mounts

lib
Lua:
---- DONT TOUCH ---
MW_STATUS = 0
MW_PLAYERS = {}
MW_MONSTERS = {}
local MW_WAVE = 1
-------------------

--- CONFIG ----
MW_minLevel = 50 -- Player must be this level to enter event --
MW_positionEnter = Position(1000, 1000, 7) -- Wait area for event to start position --
MW_positionLeave = Position(1000, 1000, 7) -- Exit waiting area position --
local MW_minPlayersToStart = 5 -- How many people are required for event to start --
local MW_firstWaveStart = 30 -- 30 seconds for the first wave to start --
local MW_reTryEvent = 60 -- Time in minutes to retry the event if there are not enough players --
local MW_startEventAgain = 60 -- Start the event again after 60 minutes --
---------------

local MW_PLAYER_TELEPORT_TO = { -- Teleport players to this area for event --
    min = Position(1000, 1000, 7), -- Top left square of area --
    max = Position(1000, 1000, 7) -- bottom right square of area --
}

local MONSTER_WAVES = {
    [1] = {exp = 20000, waitTimeBoss = 60,
        monsters = {
            {"Cyclops", Position(1000, 1000, 7)},
            {"Cyclops", Position(1000, 1000, 7)},
            {"Cyclops", Position(1000, 1000, 7)},
            {"Cyclops Drone", Position(1000, 1000, 7)},
            {"Cyclops Drone", Position(1000, 1000, 7)}
        },
        boss = { -- Boss is spawned after waitTimeBoss seconds after the wave is stated --
            {"Cyclops Smith", Position(1000, 1000, 7)}
        }
        itemRewards = { -- Items rewarded for completing the wave. --
            {2152, 50},
            {2160, 1}
        },
        outfitReward = {male = 260, female = 261, addons = 0, name = "Citizen"}, -- outfit rewards for completing the wave --
        mountReward = {126, "Horse"}
    } -- You can delete any of the values to remove it from the wave. exp, itemreward, boss, outfitreward, mountreward (DO NOT DELETE THE monsters TABLE) --
  
    -- [2]
  
}

function MW_sendMessage(msgType, msg)
    for i = 1, #MW_PLAYERS do
        local player = Player(MW_PLAYERS[i])
        if player then
            player:sendTextMessage(msgType, msg)
        end
    end
end

function MW_teleportPlayers()
    for i = 1, #MW_PLAYERS do
        local player = Player(MW_PLAYERS[i])
        if player then
            local position = Position(math.random(MW_PLAYER_TELEPORT_TO.min.x, MW_PLAYER_TELEPORT_TO.max.x),math.random(MW_PLAYER_TELEPORT_TO.min.y, MW_PLAYER_TELEPORT_TO.max.y), MW_PLAYER_TELEPORT_TO.min.z)
            player:teleportTo(position)
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_RED, "Welcome to monster wave event. It will begin in "..MW_firstWaveStart.." seconds.")
        end
    end
end

function MW_tryEvent()
    if #MW_PLAYERS < MW_minPlayersToStart then
        MW_sendMessage(MESSAGE_STATUS_CONSOLE_RED, "There are not enough players for the event to start. The event will try again after "..MW_reTryEvent.." minutes.")
        addEvent(MW_tryEvent, MW_reTryEvent * 60 * 1000)
        return true
    end
  
    MW_teleportPlayers()
    MW_STATUS = 2
    addEvent(MW_startWave, MW_firstWaveStart * 1000)
end

function MW_checkMonsters()
    for i = 1, #MW_MONSTERS do
        local monster = Monster(MW_MONSTERS[i])
        if monster then
            return true
        end
    end
    return false
end

function MW_removeMonsters()
    for i = 1, #MW_MONSTERS do
        local monster = Monster(MW_MONSTERS[i])
        if monster then
            monster:remove()
        end
        MW_MONSTERS[i] = nil
    end
end

function MW_startWave()
    local WAVE = MONSTER_WAVES[MW_WAVE]
  
    for i = 1, #WAVE.monsters do
        local MONS = Game.createMonster(WAVE.monsters[i][1], WAVE.monsters[i][2])
        if not MW_MONSTERS[1] then
            MW_MONSTERS[1] = MONS:getId()
        else
            MW_MONSTERS[#MW_MONSTERS + 1] = MONS:getId()
        end
    end
  
    MW_sendMessage(MESSAGE_STATUS_CONSOLE_BLUE, "The wave has spawned. Kill all monsters for the next wave to begin.")
  
    if WAVE.boss then
        addEvent(spawnBosses, WAVE.waitTimeBoss * 1000, WAVE)
        MW_sendMessage(MESSAGE_STATUS_CONSOLE_BLUE, "This waves bosses will spawn in "..WAVE.waitTimeBoss.." seconds.")
    end
  
    addEvent(MW_proccessWave, 60 * 1000)
end

function MW_spawnBosses(waveTable)
    for i = 1, #waveTable.boss do
        local MONS = Game.createMonster(waveTable[i].boss[1], waveTable[i].boss[2])
        MW_MONSTERS[#MW_MONSTERS + 1] = MONS:getId()
    end
end

function MW_addOutfitReward(WAVE, player)
    if player:getSex() == 0 then
        player:addOutfit(WAVE.outfitReward.female)
        if addons > 0 then
            for x = 1, addons do
                player:addOutfitAddons(WAVE.outfitReward.female, x)
            end
        end
    else
        player:addOutfit(WAVE.outfitReward.male)
        if addons > 0 then
            for x = 1, addons do
                player:addOutfitAddons(WAVE.outfitReward.male, x)
            end
        end
    end
end

function MW_proccessWave()
    if MW_checkMonsters() then
        MW_sendMessage(MESSAGE_STATUS_CONSOLE_RED, "There are still monsters left to kill. Kill them to start the next wave.")
        addEvent(MW_proccessWave, 60 * 1000)
        return true
    end
  
    local WAVE = MONSTER_WAVES[MW_WAVE]
  
    local text = "[WAVE REWARD]:"
  
    if WAVE.exp then
        for i = 1, #MW_PLAYERS do
            local player = Player(MW_PLAYERS[i])
            if player then
                if i == 1 then
                    text = text.." "..WAVE.exp.." experience."
                end
                player:addExperience(WAVE.exp)
            end
        end
    end
  
    if WAVE.outfitReward then
        for i = 1, #MW_PLAYERS do
            local player = Player(MW_PLAYERS[i])
            if player then
                if i == 1 then
                    text = text.." "..WAVE.outfitReward.name.." outfit."
                end
                MW_addOutfitReward(WAVE, player)  
            end
        end
    end
  
    if WAVE.mountReward then
        for i = 1, #MW_PLAYERS do
            local player = Player(MW_PLAYERS[i])
            if player then
                if i == 1 then
                    text = text.." "..WAVE.mountReward[2].." mount."
                end
                player:addMount(WAVE.mountReward[1])
            end
        end
    end
  
    MW_sendMessage(MESSAGE_STATUS_CONSOLE_RED, text)
    MW_WAVE = MW_WAVE + 1
    MW_removeMonsters()
  
    if not MONSTER_WAVES[MW_WAVE] then
        MW_sendMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Well done. You completed all waves. The event will end in 30 seconds!")
        MW_STATUS = 3
        addEvent(MW_endEvent, 30 * 1000)
        return true
    end
  
    MW_sendMessage(MESSAGE_STATUS_CONSOLE_BLUE, "The next wave will spawn in 1 minute.")
    addEvent(MW_startWave, 60 * 1000)
    addEvent(MW_CleanFields, 5 * 1000)
end

function MW_endEvent()
    for i = 1, #MW_PLAYERS do
        local player = Player(MW_PLAYERS[i])
        if player then
            player:teleportTo(MW_positionLeave)
        end
        MW_PLAYERS[i] = nil
    end
  
    MW_removeMonsters()
    addEvent(MW_restartEvent, MW_startEventAgain * 60 * 1000)
end

function MW_restartEvent()
    MW_STATUS = 0
end

function MW_CleanFields()
local area = {
      fromPos = {x = 32470, y = 32315, z = 9},
      toPos = {x = 32479, y = 32322, z = 9}
  }

local tileCache = {}

local itemIds = {1487, 1486,1485,1490,1491,1497}
    -- initialize tiles we need to clean, store all tiles in a cache to avoid reconstructing them in the future
    if #tileCache == 0 then
        for x = area.fromPos.x, area.toPos.x do
            for y = area.fromPos.y, area.toPos.y do
                for z = area.fromPos.z, area.toPos.z do
                    tileCache[#tileCache+1] = Tile(x, y, z)
                end
            end
        end
    end
    -- clean based off of cache
    for i = 1, #tileCache do
        local items = tileCache[i]:getItems()
        if items and next(items) then
            for index = 1, #items do
                local item = items[index]
                if table.contains(itemIds, item:getId()) then
                    item:remove()
                end
            end
        end
    end
    return true
end

Movement
Lua:
local aidEnter = 1510 -- ActionID of teleport/tile to enter the event waiting area
local aidLeave = 1511 -- ActionId of teleport/tile to leave the event waiting area

function onStepIn(creature, item, position, fromPosition)
    local player = Player(creature)

    if not player then creature:teleportTo(fromPosition) return true end

    if item.actionid == aidEnter then
        if MW_STATUS ~= 1 then
            player:teleportTo(fromPosition)
            player:sendCancelMessage("Monster wave event is not open.")
            return true
        end
    
        if player:getLevel() < MW_minLevel then
            player:teleportTo(fromPosition)
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_RED, "You must be level "..MW_minLevel.." to enter this event.")
        else
            player:teleportTo(MW_positionEnter)
            if not MW_PLAYERS[1] then
                MW_PLAYERS[1] = player:getName()
            else
                MW_PLAYERS[#MW_PLAYERS + 1] = player:getName()
            end
        end
    else
        for i = 1, #MW_PLAYERS do
            if MW_PLAYERS[i] == player:getName() then
                MW_PLAYERS[i] = nil
            end
        end
    
        player:teleportTo(MW_positionLeave)
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_RED, "You will no longer paticipate in the event.")
    end
    return true
end

GlobalEvent
Lua:
local timeWait = 5 -- Time in minutes to wait until trying to start the event. --

function onThink(interval)
    if MW_STATUS == 0 then
        Game.broadcastMessage("Monster wave event is now open. Enter the event portal to particiapte.", 1)
        MW_STATUS = 1
        addEvent(MW_tryEvent, timeWait * 60 * 1000)
    end
    return true
end

talkaction
Lua:
function onSay(player, words, param, channel)
    if not player:getGroup():getAccess() then
        return true
    end

    if MW_STATUS == 1 or MW_STATUS == 2 then
        MW_endEvent()
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "You have ended the event. It will start again after the defined time in the lib file.")
    else
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "The event is not running.")
    end
    return true
end
 
Last edited:
Lua:
-- Count players inside event area (waiting to enter)
function countMWEventArea()
    local count = 0
    for x = MW_PLAYER_TELEPORT_AREA.min.x, MW_PLAYER_TELEPORT_AREA.max.x do
        for y = MW_PLAYER_TELEPORT_AREA.min.y, MW_PLAYER_TELEPORT_AREA.max.y do
            local position = Position(x, y, MW_PLAYER_TELEPORT_AREA.min.z)
       
            if Tile(position) and Tile(position):getTopCreature() and Tile(position):getTopCreature():isPlayer() then
                if Player(Tile(position):getTopCreature()):getLevel() < minLevel then
                    Player(Tile(position):getTopCreature()):teleportTo(teleportPlayerOut)
                else
                    count = count + 1
                end
            end
        end
    end
    return count
end

Why using nested for loop instead of simple getSpectators?
Checking only topCreature? What if there are multiple players on stack?
Why do you wrote unreadable(and unefficient(Map::getTile is one of functions that bottlenecks TFS the most)) code by using "Tile(position)" multiple times instead of once?

It is only one function and I can see you're not sticking to your correct way of coding:
There is a correct way to write code. That would be the most efficient, and readable is the correct way. Just because it works doesn't mean its good.
In the end you didn't write correct code even by my criteria because it can be bugged by having multiple players on stack.
 
whats the point of hating eachother guys

its useless
I'm not hating anyone xD
I'm only wanted to inform him that his code might have unwanted behavior and that he's contradicting himself. If that is his reaction for mentioning him that his code might have bug then probably he has some problems.

In the other thread he called me disgusting and hostility when anything I tried to do was protecting some user that might not even have an account on this forum from bad people that wanted to laugh at him for no reason, so I'm guess yeah I'm full of hate and hostility.
 
Did I say that is the most efficient code you petty kid? Your standards are 200 lines of elseifs so don't talk to me about standards.
Well he is right, that code snippet is garbage. It contains a rather obvious bug, is inefficient and slow.
There are more instances in the code where you're iterating over a map area with nested for loops, which is bad in itself, but then also calling the Tile constructor multiple times instead of just once and reusing that object. You should be using getSpectators for this task at least, it's heavily optimized for this exact task, and you will run into performance issues sooner rather than later, if you stick to your current method and run that over a larger area.
 
What they all meant is reusing already known variables because getting them each time you check something is just slow. It may be 2 ms difference for a single loop step, but if you iterate over let's say 50x50 area, it's 2.5 second difference. 2ms is a little exaggerated but if you want to use many arenas with same code, calling Tile(pos) like 5 times for a single step in every arena may lag your server.

Here is what they suggested:
Code:
local tile = Tile(position)

if tile then
    local topCreature = tile:getTopCreature()

    if topCreature then
        if topCreature:isPlayer() and topCreature:getLevel() < minLevel then
            topCreature:teleportTo(teleportPlayerOut)
        else
            count = count + 1
        end
    end
end
 
Last edited:
There is no point in saying anything too much, the author of the topic will send everyone who is right.. to programming school. xDD

So.. kekw again.
 
I'm not hating anyone xD
I'm only wanted to inform him that his code might have unwanted behavior and that he's contradicting himself. If that is his reaction for mentioning him that his code might have bug then probably he has some problems.

In the other thread he called me disgusting and hostility when anything I tried to do was protecting some user that might not even have an account on this forum from bad people that wanted to laugh at him for no reason, so I'm guess yeah I'm full of hate and hostility.
That is because you literally said "If people code incorrectly its their problem" which is something that shouldn't be on this forum.


Well he is right, that code snippet is garbage. It contains a rather obvious bug, is inefficient and slow.
There are more instances in the code where you're iterating over a map area with nested for loops, which is bad in itself, but then also calling the Tile constructor multiple times instead of just once and reusing that object. You should be using getSpectators for this task at least, it's heavily optimized for this exact task, and you will run into performance issues sooner rather than later, if you stick to your current method and run that over a larger area.
Its true I could use one
Code:
local tile = Tile(position)
local player = Player(tile:getTopCreature())

The "bug" is only a bug if there are multiple players standing on a tile. Which is something I should of went over in the original post true. I didn't because it was late at night and I had just put the code together. Now, im not saying I like to release code that I haven't finished (as in go over and fix things after getting the initial system working) but it is what I did here.

Which for people that CANT make the code its better than not having it at all. Again, I still think its best to give people the best code seems this forum was built for sharing information. I never said this is the best way to do this system or anything like that. If it is garbage then why don't you post the better way to do it? Let me guess, you can't be bothered? That's worse than me posting that code.
There is no point in saying anything too much, the author of the topic will send everyone who is right.. to programming school. xDD

So.. kekw again.
You should get off your butt-buddies, well you know what.
 
You can all go back to school! xD why would I call getSpectators when I can simple throw the monster ids into a table and iterate over that table and confirm that the monsters still exist (most efficient way) same with players :'D
You always so smart AWW
 
You can all go back to school! xD why would I call getSpectators when I can simple throw the monster ids into a table and iterate over that table and confirm that the monsters still exist (most efficient way) same with players :'D
the problem we were discussing was getting players from one room to another, nothing with monsters but ok
 
You can all go back to school! xD why would I call getSpectators when I can simple throw the monster ids into a table and iterate over that table and confirm that the monsters still exist (most efficient way) same with players :'D
I actually thought about doing that but... In my server, and there may be more people that have the same set up. Some monsters summons do not go away once they die.
Post automatically merged:

Here is the system a bit more optimized. Again, NOT PERFECT seems I have two kids following me around now.

lib
Lua:
---- DONT TOUCH ---
MW_STATUS = 0
MW_PLAYERS = {}
MW_MONSTERS = {}
local MW_WAVE = 1
-------------------

--- CONFIG ----
MW_minLevel = 50 -- Player must be this level to enter event --
MW_positionEnter = Position(1000, 1000, 7) -- Wait area for event to start position --
MW_positionLeave = Position(1000, 1000, 7) -- Exit waiting area position --
local MW_minPlayersToStart = 5 -- How many people are required for event to start --
local MW_firstWaveStart = 30 -- 30 seconds for the first wave to start --
local MW_reTryEvent = 60 -- Time in minutes to retry the event if there are not enough players --
local MW_startEventAgain = 60 -- Start the event again after 60 minutes --
---------------

local MW_PLAYER_TELEPORT_TO = { -- Teleport players to this area for event --
    min = Position(1000, 1000, 7), -- Top left square of area --
    max = Position(1000, 1000, 7) -- bottom right square of area --
}

local MONSTER_WAVES = {
    [1] = {exp = 20000, waitTimeBoss = 60,
        monsters = {
            {"Cyclops", Position(1000, 1000, 7)},
            {"Cyclops", Position(1000, 1000, 7)},
            {"Cyclops", Position(1000, 1000, 7)},
            {"Cyclops Drone", Position(1000, 1000, 7)},
            {"Cyclops Drone", Position(1000, 1000, 7)}
        },
        boss = { -- Boss is spawned after waitTimeBoss seconds after the wave is stated --
            {"Cyclops Smith", Position(1000, 1000, 7)}
        }
        itemRewards = { -- Items rewarded for completing the wave. --
            {2152, 50},
            {2160, 1}
        },
        outfitReward = {male = 260, female = 261, addons = 0, name = "Citizen"}, -- outfit rewards for completing the wave --
        mountReward = {126, "Horse"}
    } -- You can delete any of the values to remove it from the wave. exp, itemreward, boss, outfitreward, mountreward (DO NOT DELETE THE monsters TABLE) --
   
    -- [2]
   
}

function MW_sendMessage(msgType, msg)
    for i = 1, #MW_PLAYERS do
        local player = Player(MW_PLAYERS[i])
        if player then
            player:sendTextMessage(msgType, "")
        end
    end
end

function MW_teleportPlayers()
    for i = 1, #MW_PLAYERS do
        local player = Player(MW_PLAYERS[i])
        if player then
            local position = Position(math.random(MW_PLAYER_TELEPORT_TO.min.x, MW_PLAYER_TELEPORT_TO.max.x),math.random(MW_PLAYER_TELEPORT_TO.min.y, MW_PLAYER_TELEPORT_TO.max.y), MW_PLAYER_TELEPORT_TO.min.z)
            player:teleportTo(position)
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "Welcome to monster wave event. It will begin in "..MW_firstWaveStart.." seconds.")
        end
    end
end

function MW_tryEvent()
    if #MW_PLAYERS < MW_minPlayersToStart then
        MW_sendMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "There are not enough players for the event to start. The event will try again after "..MW_reTryEvent.." minutes.")
        addEvent(MW_tryEvent, MW_reTryEvent * 60 * 1000)
        return true
    end
   
    MW_teleportPlayers()
    MW_STATUS = 2
    addEvent(startWave, 2 * 60 * 1000)
end

function MW_checkMonsters()
    for i = 1, #MW_MONSTERS do
        local monster = Monster(MW_MONSTERS[i])
        if monster then
            return true
        end
    end
    return false
end

function MW_removeMonsters()
    for i = 1, #MW_MONSTERS do
        local monster = Monster(MW_MONSTERS[i])
        if monster then
            monster:remove()
        end
        MW_MONSTERS[i] = nil
    end
end

function MW_startWave()
    local WAVE = MOSNTER_WAVES[MW_WAVE]
   
    for i = 1, #MONSTER_WAVES.monsters do
        local MONS = Game.createMonster(MONSTER_WAVES.monsters[i][1], MONSTER_WAVES.monsters[i][2])
        if not MW_MONSTERS[1] then
            MW_MONSTERS[1] = MONS:getId()
        else
            MW_MONSTERS[#MW_MONSTERS + 1] = MONS:getId()
        end
    end
   
    MW_sendMessage(MESSAGE_STATUS_CONSOLE_BLUE, "The wave has spawned. Kill all monsters for the next wave to begin.")
   
    if WAVE.boss then
        addEvent(spawnBosses, WAVE.waitTimeBoss * 1000)
        MW_sendMessage(MESSAGE_STATUS_CONSOLE_BLUE, "This waves bosses will spawn in "..WAVE.waitTimeBoss.." seconds.")
    end
   
    addEvent(MW_proccessWave, 60 * 1000)
end

function MW_addOutfitReward(WAVE, player)
    if player:getSex() == 0 then
        player:addOutfit(WAVE.outfitReward.female)
        if addons > 0 then
            for x = 1, addons do
                player:addOutfitAddons(WAVE.outfitReward.female, x)
            end
        end
    else player:getSex() then
        player:addOutfit(WAVE.outfitReward.male)
        if addons > 0 then
            for x = 1, addons do
                player:addOutfitAddons(WAVE.outfitReward.male, x)
            end
        end
    end
end

function MW_proccessWave()
    if MW_checkMonsters() then
        MW_sendMessage(MESSAGE_STATUS_CONSOLE_RED, "There are still monsters left to kill. Kill them to start the next wave.")
        addEvent(proccessWave, 60 * 1000)
        return true
    end
   
    local WAVE = MOSNTER_WAVES[MW_WAVE]
   
    local text = "[WAVE REWARD]:"
   
    if WAVE.exp then
        for i = 1, #MW_PLAYERS do
            local player = Player(MW_PLAYERS[i])
            if player then
                if i == 1 then
                    text = text.." "..WAVE.exp.." experience."
                end
                player:addExperience(WAVE.exp)
            end
        end
    end
   
    if WAVE.outfitReward then
        for i = 1, #MW_PLAYERS do
            local player = Player(MW_PLAYERS[i])
            if player then
                if i == 1 then
                    text = text.." "..WAVE.outfitReward.name.." outfit."
                end
                MW_addOutfitReward(WAVE, player)   
            end
        end
    end
   
    if WAVE.mountReward then
        for i = 1, #MW_PLAYERS do
            local player = Player(MW_PLAYERS[i])
            if player then
                if i == 1 then
                    text = text.." "..WAVE.mountReward[2].." mount."
                end
                player:addMount(WAVE.mountReward[1])
            end
        end
    end
   
    MW_sendMessage(MESSAGE_STATUS_CONSOLE_ORANGE, text)
    MW_WAVE = MW_WAVE + 1
    MW_removeMonsters()
   
    if not MOSNTER_WAVES[MW_WAVE] then
        MW_sendMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Well done. You completed all waves. The event will end in 30 seconds!")
        MW_STATUS = 3
        addEvent(MW_endEvent, 30 * 1000)
        return true
    end
   
    MW_sendMessage(MESSAGE_STATUS_CONSOLE_BLUE, "The next wave will spawn in 1 minute.")
    addEvent(MW_startWave, 60 * 1000)
end

function MW_endEvent()
    for i = 1, #MW_PLAYERS do
        local player = Player(MW_PLAYERS[i])
        if player then
            player:teleportTo(MW_positionLeave)
        end
        MW_PLAYERS[i] = nil
    end
   
    MW_removeMonsters()
    addEvent(MW_restartEvent, MW_startEventAgain * 60 * 1000)
end

function MW_restartEvent()
    MW_STATUS = 0
end

Talkaction for GM's to end the event
Lua:
function onSay(player, words, param, channel)
    if not player:getGroup():getAccess() then
        return true
    end
  
    if MW_STATUS == 1 or MW_STATUS == 2 then
        MW_endEvent()
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "You have ended the event. It will start again after the defined time in the lib file.")
    else
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "The event is not running.")
    end
    return true
end

MoveEvent for players to enter/leave event waiting area
Lua:
local aidEnter = 1510 -- ActionID of teleport/tile to enter the event waiting area
local aidLeave = 1511 -- ActionId of teleport/tile to leave the event waiting area

function onStepIn(creature, item, position, fromPosition)
    local player = Player(creature)
  
    if not player then creature:teleportTo(fromPosition) return true end
  
    if item.actionid == aidEnter then
        if MW_STATUS ~= 1 then
            player:teleportTo(fromPosition)
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "Monster wave event is not open.")
            return true
        end
      
        if player:getLevel() < MW_minLevel then
            player:teleportTo(fromPosition)
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "You must be level "..MW_minLevel.." to enter this event.")
        else
            player:teleportTo(MW_positionEnter)
            if not MW_PLAYERS[1] then
                MW_PLAYERS[1] = player:getName()
            else
                MW_PLAYERS[#MW_PLAYERS + 1] = player:getName()
            end
        end
    else
        for i = 1, #MW_PLAYERS do
            if MW_PLAYERS[i] == player:getName() then
                MW_PLAYERS[i] = nil
            end
        end
      
        player:teleportTo(MW_positionLeave)
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "You will no longer paticipate in the event.")
    end
    return true
end

GlobalEvent so the system can run by itself.
Lua:
local timeWait = 5 -- Time in minutes to wait until trying to start the event. --

function onThink(interval)
    if MW_STATUS == 0 then
        Game.broadcastMessage("Monster wave event is now open. Enter the event portal to particiapte.", 1)
        MW_STATUS = 1
        addEvent(MW_tryEvent, timeWait * 60 * 1000)
    end
    return true
end

Hope you enjoy. If you want to see something added please let me know.
 
Last edited:
I actually thought about doing that but... In my server, and there may be more people that have the same set up. Some monsters summons do not go away once they die.
Post automatically merged:

Here is the system a bit more optimized. Again, NOT PERFECT seems I have two kids following me around now.

lib
Lua:
---- DONT TOUCH ---
MW_STATUS = 0
MW_PLAYERS = {}
MW_MONSTERS = {}
local MW_WAVE = 1
-------------------

--- CONFIG ----
MW_minLevel = 50 -- Player must be this level to enter event --
MW_positionEnter = Position(1000, 1000, 7) -- Wait area for event to start position --
MW_positionLeave = Position(1000, 1000, 7) -- Exit waiting area position --
local MW_minPlayersToStart = 5 -- How many people are required for event to start --
local MW_firstWaveStart = 30 -- 30 seconds for the first wave to start --
local MW_reTryEvent = 60 -- Time in minutes to retry the event if there are not enough players --
local MW_startEventAgain = 60 -- Start the event again after 60 minutes --
---------------

local MW_PLAYER_TELEPORT_TO = { -- Teleport players to this area for event --
    min = Position(1000, 1000, 7), -- Top left square of area --
    max = Position(1000, 1000, 7) -- bottom right square of area --
}

local MONSTER_WAVES = {
    [1] = {exp = 20000, waitTimeBoss = 60,
        monsters = {
            {"Cyclops", Position(1000, 1000, 7)},
            {"Cyclops", Position(1000, 1000, 7)},
            {"Cyclops", Position(1000, 1000, 7)},
            {"Cyclops Drone", Position(1000, 1000, 7)},
            {"Cyclops Drone", Position(1000, 1000, 7)}
        },
        boss = { -- Boss is spawned after waitTimeBoss seconds after the wave is stated --
            {"Cyclops Smith", Position(1000, 1000, 7)}
        }
        itemRewards = { -- Items rewarded for completing the wave. --
            {2152, 50},
            {2160, 1}
        },
        outfitReward = {male = 260, female = 261, addons = 0, name = "Citizen"}, -- outfit rewards for completing the wave --
        mountReward = {126, "Horse"}
    } -- You can delete any of the values to remove it from the wave. exp, itemreward, boss, outfitreward, mountreward (DO NOT DELETE THE monsters TABLE) --
 
    -- [2]
 
}

function MW_sendMessage(msgType, msg)
    for i = 1, #MW_PLAYERS do
        local player = Player(MW_PLAYERS[i])
        if player then
            player:sendTextMessage(msgType, "")
        end
    end
end

function MW_teleportPlayers()
    for i = 1, #MW_PLAYERS do
        local player = Player(MW_PLAYERS[i])
        if player then
            local position = Position(math.random(MW_PLAYER_TELEPORT_TO.min.x, MW_PLAYER_TELEPORT_TO.max.x),math.random(MW_PLAYER_TELEPORT_TO.min.y, MW_PLAYER_TELEPORT_TO.max.y), MW_PLAYER_TELEPORT_TO.min.z)
            player:teleportTo(position)
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "Welcome to monster wave event. It will begin in "..MW_firstWaveStart.." seconds.")
        end
    end
end

function MW_tryEvent()
    if #MW_PLAYERS < MW_minPlayersToStart then
        MW_sendMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "There are not enough players for the event to start. The event will try again after "..MW_reTryEvent.." minutes.")
        addEvent(MW_tryEvent, MW_reTryEvent * 60 * 1000)
        return true
    end
 
    MW_teleportPlayers()
    MW_STATUS = 2
    addEvent(startWave, 2 * 60 * 1000)
end

function MW_checkMonsters()
    for i = 1, #MW_MONSTERS do
        local monster = Monster(MW_MONSTERS[i])
        if monster then
            return true
        end
    end
    return false
end

function MW_removeMonsters()
    for i = 1, #MW_MONSTERS do
        local monster = Monster(MW_MONSTERS[i])
        if monster then
            monster:remove()
        end
        MW_MONSTERS[i] = nil
    end
end

function MW_startWave()
    local WAVE = MOSNTER_WAVES[MW_WAVE]
 
    for i = 1, #MONSTER_WAVES.monsters do
        local MONS = Game.createMonster(MONSTER_WAVES.monsters[i][1], MONSTER_WAVES.monsters[i][2])
        if not MW_MONSTERS[1] then
            MW_MONSTERS[1] = MONS:getId()
        else
            MW_MONSTERS[#MW_MONSTERS + 1] = MONS:getId()
        end
    end
 
    MW_sendMessage(MESSAGE_STATUS_CONSOLE_BLUE, "The wave has spawned. Kill all monsters for the next wave to begin.")
 
    if WAVE.boss then
        addEvent(spawnBosses, WAVE.waitTimeBoss * 1000)
        MW_sendMessage(MESSAGE_STATUS_CONSOLE_BLUE, "This waves bosses will spawn in "..WAVE.waitTimeBoss.." seconds.")
    end
 
    addEvent(MW_proccessWave, 60 * 1000)
end

function MW_addOutfitReward(WAVE, player)
    if player:getSex() == 0 then
        player:addOutfit(WAVE.outfitReward.female)
        if addons > 0 then
            for x = 1, addons do
                player:addOutfitAddons(WAVE.outfitReward.female, x)
            end
        end
    else player:getSex() then
        player:addOutfit(WAVE.outfitReward.male)
        if addons > 0 then
            for x = 1, addons do
                player:addOutfitAddons(WAVE.outfitReward.male, x)
            end
        end
    end
end

function MW_proccessWave()
    if MW_checkMonsters() then
        MW_sendMessage(MESSAGE_STATUS_CONSOLE_RED, "There are still monsters left to kill. Kill them to start the next wave.")
        addEvent(proccessWave, 60 * 1000)
        return true
    end
 
    local WAVE = MOSNTER_WAVES[MW_WAVE]
 
    local text = "[WAVE REWARD]:"
 
    if WAVE.exp then
        for i = 1, #MW_PLAYERS do
            local player = Player(MW_PLAYERS[i])
            if player then
                if i == 1 then
                    text = text.." "..WAVE.exp.." experience."
                end
                player:addExperience(WAVE.exp)
            end
        end
    end
 
    if WAVE.outfitReward then
        for i = 1, #MW_PLAYERS do
            local player = Player(MW_PLAYERS[i])
            if player then
                if i == 1 then
                    text = text.." "..WAVE.outfitReward.name.." outfit."
                end
                MW_addOutfitReward(WAVE, player) 
            end
        end
    end
 
    if WAVE.mountReward then
        for i = 1, #MW_PLAYERS do
            local player = Player(MW_PLAYERS[i])
            if player then
                if i == 1 then
                    text = text.." "..WAVE.mountReward[2].." mount."
                end
                player:addMount(WAVE.mountReward[1])
            end
        end
    end
 
    MW_sendMessage(MESSAGE_STATUS_CONSOLE_ORANGE, text)
    MW_WAVE = MW_WAVE + 1
    MW_removeMonsters()
 
    if not MOSNTER_WAVES[MW_WAVE] then
        MW_sendMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Well done. You completed all waves. The event will end in 30 seconds!")
        MW_STATUS = 3
        addEvent(MW_endEvent, 30 * 1000)
        return true
    end
 
    MW_sendMessage(MESSAGE_STATUS_CONSOLE_BLUE, "The next wave will spawn in 1 minute.")
    addEvent(MW_startWave, 60 * 1000)
end

function MW_endEvent()
    for i = 1, #MW_PLAYERS do
        local player = Player(MW_PLAYERS[i])
        if player then
            player:teleportTo(MW_positionLeave)
        end
        MW_PLAYERS[i] = nil
    end
 
    MW_removeMonsters()
    addEvent(MW_restartEvent, MW_startEventAgain * 60 * 1000)
end

function MW_restartEvent()
    MW_STATUS = 0
end

Talkaction for GM's to end the event
Lua:
function onSay(player, words, param, channel)
    if not player:getGroup():getAccess() then
        return true
    end

    if MW_STATUS == 1 or MW_STATUS == 2 then
        MW_endEvent()
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "You have ended the event. It will start again after the defined time in the lib file.")
    else
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "The event is not running.")
    end
    return true
end

MoveEvent for players to enter/leave event waiting area
Lua:
local aidEnter = 1510 -- ActionID of teleport/tile to enter the event waiting area
local aidLeave = 1511 -- ActionId of teleport/tile to leave the event waiting area

function onStepIn(creature, item, position, fromPosition)
    local player = Player(creature)

    if not player then creature:teleportTo(fromPosition) return true end

    if item.actionid == aidEnter then
        if MW_STATUS ~= 1 then
            player:teleportTo(fromPosition)
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "Monster wave event is not open.")
            return true
        end
    
        if player:getLevel() < MW_minLevel then
            player:teleportTo(fromPosition)
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "You must be level "..MW_minLevel.." to enter this event.")
        else
            player:teleportTo(MW_positionEnter)
            if not MW_PLAYERS[1] then
                MW_PLAYERS[1] = player:getName()
            else
                MW_PLAYERS[#MW_PLAYERS + 1] = player:getName()
            end
        end
    else
        for i = 1, #MW_PLAYERS do
            if MW_PLAYERS[i] == player:getName() then
                MW_PLAYERS[i] = nil
            end
        end
    
        player:teleportTo(MW_positionLeave)
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "You will no longer paticipate in the event.")
    end
    return true
end

GlobalEvent so the system can run by itself.
Lua:
local timeWait = 5 -- Time in minutes to wait until trying to start the event. --

function onThink(interval)
    if MW_STATUS == 0 then
        Game.broadcastMessage("Monster wave event is now open. Enter the event portal to particiapte.", 1)
        MW_STATUS = 1
        addEvent(MW_tryEvent, timeWait * 60 * 1000)
    end
    return true
end

Hope you enjoy. If you want to see something added please let me know.
Hey thanks for the release, I just tested it (without the talkaction) and I had to replace some stuff.

Lib
Line 13 (MW_firstWaveStart wasn't being used)
Lua:
local MW_firstWaveStart = 30 -- 30 seconds for the first wave to start --
Lua:
local MW_firstWaveStart = 30 -- 30 seconds for each wave to start --
Line 76 (added MW_firstWaveStart)
Lua:
    addEvent(startWave, 2 * 60 * 1000)
Lua:
    addEvent(MW_startWave, MW_firstWaveStart * 1000)
Line 100 (typo in MONSTER)
Lua:
    local WAVE = MOSNTER_WAVES[MW_WAVE]
Lua:
    local WAVE = MONSTER_WAVES[MW_WAVE]
Line 129 (was getting errors)
Lua:
    else player:getSex() then
Lua:
    elseif player:getSex() then
Line 142 (added MW_firstWaveStart)
Lua:
        addEvent(proccessWave, 60 * 1000)
Lua:
        addEvent(proccessWave, MW_firstWaveStart * 1000)
Line 146 (typo in MONSTER)
Lua:
    local WAVE = MOSNTER_WAVES[MW_WAVE]
Lua:
    local WAVE = MONSTER_WAVES[MW_WAVE]
Line 190 (typo in MONSTER)
Lua:
    if not MOSNTER_WAVES[MW_WAVE] then
Lua:
    if not MONSTER_WAVES[MW_WAVE] then

MoveEvent
Line 12 (the message wasn't working)
Lua:
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "Monster wave event is not open.")
Lua:
            player:say('Monster wave event is not open.', TALKTYPE_MONSTER_SAY)
Line 18 (the message wasn't working)
Lua:
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "You must be level "..MW_minLevel.." to enter this event.")
Lua:
            player:say('You must be level '..MW_minLevel..' to enter this event.', TALKTYPE_MONSTER_SAY)
Line 35 (typo in participate & the message wasn't working)
Lua:
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "You will no longer paticipate in the event.")
Lua:
        player:say('You will no longer participate in the event.', TALKTYPE_MONSTER_SAY)

GlobalEvent
Line 6 (typo in participate and the ,1 wasn't working)
Lua:
        Game.broadcastMessage("Monster wave event is now open. Enter the event portal to particiapte.", 1)
Lua:
        Game.broadcastMessage("Monster wave event is now open. Enter the event portal to participate.")

It's still giving the following error that I don't know how to solve:
1609543756827.png
 
replace

function MW_startWave

with

Lua:
function MW_startWave()
    local WAVE = MONSTER_WAVES[MW_WAVE]
    
    for i = 1, #WAVE.monsters do
        local MONS = Game.createMonster(WAVE.monsters[i][1], WAVE.monsters[i][2])
        if not MW_MONSTERS[1] then
            MW_MONSTERS[1] = MONS:getId()
        else
            MW_MONSTERS[#MW_MONSTERS + 1] = MONS:getId()
        end
    end
    
    MW_sendMessage(MESSAGE_STATUS_CONSOLE_BLUE, "The wave has spawned. Kill all monsters for the next wave to begin.")
    
    if WAVE.boss then
        addEvent(spawnBosses, WAVE.waitTimeBoss * 1000)
        MW_sendMessage(MESSAGE_STATUS_CONSOLE_BLUE, "This waves bosses will spawn in "..WAVE.waitTimeBoss.." seconds.")
    end
    
    addEvent(MW_proccessWave, 60 * 1000)
end
 
replace

function MW_startWave

with

Lua:
function MW_startWave()
    local WAVE = MONSTER_WAVES[MW_WAVE]
   
    for i = 1, #WAVE.monsters do
        local MONS = Game.createMonster(WAVE.monsters[i][1], WAVE.monsters[i][2])
        if not MW_MONSTERS[1] then
            MW_MONSTERS[1] = MONS:getId()
        else
            MW_MONSTERS[#MW_MONSTERS + 1] = MONS:getId()
        end
    end
   
    MW_sendMessage(MESSAGE_STATUS_CONSOLE_BLUE, "The wave has spawned. Kill all monsters for the next wave to begin.")
   
    if WAVE.boss then
        addEvent(spawnBosses, WAVE.waitTimeBoss * 1000)
        MW_sendMessage(MESSAGE_STATUS_CONSOLE_BLUE, "This waves bosses will spawn in "..WAVE.waitTimeBoss.." seconds.")
    end
   
    addEvent(MW_proccessWave, 60 * 1000)
end
Getting these errors now (monsters are spawning now but the boss doesn't).
1609552923257.png
Not sure how to fix the first error, I think it's because spawnBosses function doesn't exist (line 114)

The second error can be fixed by replacing line 142
Lua:
        addEvent(proccessWave, MW_firstWaveStart * 1000)
Lua:
        addEvent(MW_proccessWave, MW_firstWaveStart * 1000)
 
Back
Top