Hello, I'm trying to create a script for a "Tower." The idea is that once you enter the arena, waves of monsters appear for you to defeat. After defeating three waves, a boss appears. Once you kill the boss, you can move on to the next floor. Right now, the script works in such a way that when you enter the arena, the first wave appears, but after killing that wave, the next one doesn't spawn. I suspect the problem is in the death function, because after killing the first wave, the script behaves as though the monsters are still alive. Please give me some advice on how I can solve this problem.
LUA:
-- Configuration
local config = {
debug = true, -- Set to true to enable debugging messages
bossName = "Demon",
bossSpawnPos = Position(32480, 32189, 7), -- gdzie pojawi się boss
arenaFromPos = Position(32471, 32184, 7), -- lewy górny róg obszaru areny
arenaToPos = Position(32489, 32202, 7), -- prawy dolny róg obszaru areny
requiredDamagePercent = 5, -- Minimum percentage of damage required
storages = {
damageEligibility = 3366 -- Storage for damage eligibility
},
-- Definicja fal:
-- Każda fala ma 'delay' (ile sekund po zabiciu poprzedniej się pojawia)
-- oraz listę potworów (name i pos)
waves = {
[1] = {
delay = 0,
monsters = {
{name = "Rat", pos = Position(32474, 32193, 7)},
{name = "Goblin", pos = Position(32486, 32193, 7)}
}
},
[2] = {
delay = 10,
monsters = {
{name = "Minotaur", pos = Position(32474, 32193, 7)},
{name = "Orc", pos = Position(32486, 32193, 7)}
}
},
[3] = {
delay = 10,
monsters = {
{name = "Demon Skeleton", pos = Position(32474, 32193, 7)},
{name = "Ghoul", pos = Position(32486, 32193, 7)}
}
}
},
-- Po ilu sekundach od zabicia trzeciej fali pojawia się boss
bossDelay = 10,
messages = {
bossSpawn = "The boss %s has appeared!",
bossDeath = "Congratulations! You dealt the required damage to pass through the door!",
needKill = "You need to defeat the boss with sufficient damage to pass!",
contribute = "You contributed a total of %d damage to defeat the boss!",
insufficientDamage = "You did not deal enough damage to pass through the door! Required: %d damage.",
topDamager = "You were the top contributor with %d damage!",
bossRemoved = "The boss %s was removed due to the absence of players in the arena!",
bossExists = "The boss is already in the arena! You need to defeat it before summoning another one!",
waveSpawn = "Wave %d has appeared!"
}
}
--------------------------------------------------------------------------------
-- DEBUG FUNCTION
--------------------------------------------------------------------------------
local function debugLog(message, ...)
if config.debug then
print("[DEBUG]", string.format(message, ...))
end
end
--------------------------------------------------------------------------------
-- STAN ARENY
--------------------------------------------------------------------------------
local playersInArena = {} -- [playerId] = true
local damageTracker = {} -- mapowanie obrażeń, ale TYLKO na bossa
local bossInArena = nil -- creatureId bossa (nil = brak)
local waveNumber = 0 -- 0 = fale nie rozpoczęte
local currentWaveMonsters = {} -- potwory bieżącej fali
--------------------------------------------------------------------------------
-- FUNKCJE POMOCNICZE: GRACZE / ARENA
--------------------------------------------------------------------------------
local function addPlayerToArena(player)
local pid = player:getId()
if not playersInArena[pid] then
playersInArena[pid] = true
debugLog("Player added to arena: %s", player:getName())
end
end
local function removePlayerFromArena(player)
local pid = player:getId()
if playersInArena[pid] then
-- sprawdzamy, czy faktycznie wyszedł z obszaru
if not player:getPosition():isInRange(config.arenaFromPos, config.arenaToPos) then
playersInArena[pid] = nil
debugLog("Player removed from arena: %s", player:getName())
end
end
end
local function isAnyoneInArena()
for pid,_ in pairs(playersInArena) do
local p = Player(pid)
if p and p:getPosition():isInRange(config.arenaFromPos, config.arenaToPos) then
return true
end
end
return false
end
-- Usuwa potwory z bieżącej fali
local function removeAllWaveMonsters()
for _, mob in pairs(currentWaveMonsters) do
if mob and mob:isMonster() then
mob:remove()
end
end
currentWaveMonsters = {}
end
--------------------------------------------------------------------------------
-- RESET ARENY (fale, boss, gracze)
--------------------------------------------------------------------------------
local function resetArena()
waveNumber = 0
removeAllWaveMonsters()
currentWaveMonsters = {}
damageTracker = {}
if bossInArena then
local boss = Creature(bossInArena)
if boss then
boss:remove()
end
bossInArena = nil
end
playersInArena = {}
end
--------------------------------------------------------------------------------
-- SPAWNOWANIE FALI
--------------------------------------------------------------------------------
local function spawnWave(waveIndex)
local waveInfo = config.waves[waveIndex]
if not waveInfo then
return
end
debugLog("Spawning wave %d ...", waveIndex)
currentWaveMonsters = {}
for _, mobInfo in ipairs(waveInfo.monsters) do
local monster = Game.createMonster(mobInfo.name, mobInfo.pos)
if monster then
table.insert(currentWaveMonsters, monster)
end
end
-- Komunikat do graczy w arenie
for pid,_ in pairs(playersInArena) do
local p = Player(pid)
if p then
p:sendTextMessage(MESSAGE_INFO_DESCR,
string.format(config.messages.waveSpawn, waveIndex))
end
end
end
--------------------------------------------------------------------------------
-- SPRAWDZANIE, CZY WSZYSTKIE POTWORY FALI ZGINĘŁY
--------------------------------------------------------------------------------
local function areAllWaveMonstersDead()
for _, mob in pairs(currentWaveMonsters) do
if mob and mob:isMonster() and mob:getHealth() > 0 then
return false
end
end
return true
end
--------------------------------------------------------------------------------
-- FUNKCJA WYWOŁYWANA PO ŚMIERCI POTWORÓW FALI
--------------------------------------------------------------------------------
local function checkWaveClear()
if not areAllWaveMonstersDead() then
return
end
debugLog("Wave %d is clear!", waveNumber)
-- Jeśli to nie ostatnia fala, jedziemy z kolejną
if waveNumber < 3 then
waveNumber = waveNumber + 1
local waveInfo = config.waves[waveNumber]
if waveInfo then
addEvent(function()
if isAnyoneInArena() then
spawnWave(waveNumber)
else
resetArena()
end
end, waveInfo.delay * 1000)
end
else
-- Była 3 fala, pora na bossa
addEvent(function()
if isAnyoneInArena() then
spawnBoss()
else
resetArena()
end
end, config.bossDelay * 1000)
end
end
--------------------------------------------------------------------------------
-- FUNKCJA SPAWN BOSSA
--------------------------------------------------------------------------------
local function spawnBoss(playerWhoRequested)
-- jeżeli jest boss, nie stwarzamy nowego
if bossInArena then
if playerWhoRequested then
playerWhoRequested:sendTextMessage(MESSAGE_INFO_DESCR, config.messages.bossExists)
end
return
end
local boss = Game.createMonster(config.bossName, config.bossSpawnPos)
if boss then
bossInArena = boss:getId()
boss:registerEvent("Boss_Waves")
debugLog("Boss spawned: %s", config.bossName)
for pid, _ in pairs(playersInArena) do
local p = Player(pid)
if p then
p:sendTextMessage(MESSAGE_INFO_DESCR,
string.format(config.messages.bossSpawn, config.bossName))
end
end
end
end
--------------------------------------------------------------------------------
-- ROZPOCZĘCIE FAL
--------------------------------------------------------------------------------
local function startWaves()
if waveNumber ~= 0 or bossInArena then
-- Już trwa
return
end
waveNumber = 1
spawnWave(waveNumber)
end
--------------------------------------------------------------------------------
-- CREATUREEVENT: ŚMIERĆ BOSSA (LICZENIE DMG)
--------------------------------------------------------------------------------
local bossArenaEvent = CreatureEvent("Boss_Waves")
function bossArenaEvent.onDeath(creature, corpse, lastHitKiller, mostDamageKiller)
-- sprawdzamy, czy ginie nasz boss
if creature:getName():lower() == config.bossName:lower() then
local topDamage = 0
local topDamagerId = nil
local totalDamage = 0
for attackerId, damageData in pairs(creature:getDamageMap()) do
local totalPlayerDamage = damageData.total or 0
damageTracker[attackerId] = (damageTracker[attackerId] or 0) + totalPlayerDamage
totalDamage = totalDamage + totalPlayerDamage
if damageTracker[attackerId] > topDamage then
topDamage = damageTracker[attackerId]
topDamagerId = attackerId
end
end
debugLog("Total Damage: %d", totalDamage)
local requiredDamage = math.ceil(totalDamage * (config.requiredDamagePercent / 100))
debugLog("Required Damage: %d", requiredDamage)
for attackerId, totalPlayerDamage in pairs(damageTracker) do
local player = Player(attackerId)
if player then
debugLog("Player: %s, Damage: %d", player:getName(), totalPlayerDamage)
player:sendTextMessage(MESSAGE_INFO_DESCR,
string.format(config.messages.contribute, totalPlayerDamage))
if totalPlayerDamage >= requiredDamage then
player:setStorageValue(config.storages.damageEligibility, 1)
player:sendTextMessage(MESSAGE_INFO_DESCR, config.messages.bossDeath)
else
player:sendTextMessage(MESSAGE_INFO_DESCR,
string.format(config.messages.insufficientDamage, requiredDamage))
end
end
end
if topDamagerId then
local topP = Player(topDamagerId)
if topP then
topP:sendTextMessage(MESSAGE_INFO_DESCR,
string.format(config.messages.topDamager, topDamage))
end
end
bossInArena = nil
damageTracker = {}
resetArena()
else
-- to nie boss, może to potwór z fali
addEvent(checkWaveClear, 200)
end
return true
end
bossArenaEvent:register()
--------------------------------------------------------------------------------
-- GLOBAL EVENT: SPRAWDZA, CZY GRACZE SĄ NA ARENIE
--------------------------------------------------------------------------------
local checkArena = GlobalEvent("CheckArenaWithWaves")
function checkArena.onThink(interval)
-- jeśli fale trwają albo jest boss, a gracze wyszli -> reset
if waveNumber > 0 or bossInArena then
if not isAnyoneInArena() then
debugLog("Arena is empty, removing monsters/boss.")
resetArena()
end
end
return true
end
checkArena:interval(10000)
checkArena:register()
--------------------------------------------------------------------------------
-- MOVEEVENT: WEJŚCIE NA ARENĘ (AID=4110)
--------------------------------------------------------------------------------
local entrance = MoveEvent()
function entrance.onStepIn(creature, item, position, fromPosition)
local player = creature:getPlayer()
if not player then
return true
end
addPlayerToArena(player)
-- zamiast spawnBoss, startujemy fale, jeśli waveNumber=0 i bossInArena=nil
if waveNumber == 0 and not bossInArena then
startWaves()
end
return true
end
entrance:type("stepin")
entrance:aid(4110)
entrance:register()
--------------------------------------------------------------------------------
-- MOVEEVENT: WYJŚCIE Z ARENY (AID=4110)
--------------------------------------------------------------------------------
local exit = MoveEvent()
function exit.onStepOut(creature, item, position, fromPosition)
local player = creature:getPlayer()
if not player then
return true
end
if not position:isInRange(config.arenaFromPos, config.arenaToPos) then
removePlayerFromArena(player)
end
return true
end
exit:type("stepout")
exit:aid(4110)
exit:register()
--------------------------------------------------------------------------------
-- BLOKADA DRZWI (AID=4111)
--------------------------------------------------------------------------------
local doorCheck = MoveEvent()
function doorCheck.onStepIn(creature, item, position, fromPosition)
local player = creature:getPlayer()
if not player then
return true
end
-- Sprawdzamy, czy ma storage = 1
if player:getStorageValue(config.storages.damageEligibility) ~= 1 then
player:sendTextMessage(MESSAGE_INFO_DESCR, config.messages.needKill)
player:teleportTo(fromPosition)
player:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
return false
end
return true
end
doorCheck:type("stepin")
doorCheck:aid(4111)
doorCheck:register()