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

[1.X] Boss system (with checker, auto-respawn and day configuration)

ralke

(҂ ͠❛ ෴ ͡❛)ᕤ
Joined
Dec 17, 2011
Messages
1,513
Solutions
27
Reaction score
866
Location
Santiago - Chile
GitHub
ralke23
Twitch
ralke23
Hi! This system it's the outcome of a request thread Lua - [1.X] Boss checker (https://otland.net/threads/1-x-boss-checker.281905/#post-2701825), it has been made by @Xikini and it's been correctly tested on TFS 1.5 downgrades.

It has some samples for daily bosses (in this case Orshabaal, Ferumbras, Ghazbaran), and added Pits of Inferno bosses, just change the coordinates and everything will work. If you set the day to Any it will show the current day as spawn day.


To install, go to data/scripts and create a new file with:
Lua:
local raidBossInformation = {
    {
        dayToSpawn = "Sunday",
        position = Position(1008, 1147, 15),
        monsterName = "Orshabaal",
        respawnTime = 4, -- hours
        messageOnSpawn = "Orshabaal has been summoned from hell to plague the lands of mortals again.",
        messageOnDeath = "Orshabaal has been defeated, the demon master will reveal himself in Hrodimir in 4 hours.",
        creatureId = 0, -- don't touch
        lastKilledTime = 0 -- don't touch
    },
    {
        dayToSpawn = "Monday",
        position = Position(1008, 1147, 15),
        monsterName = "Orshabaal",
        respawnTime = 4, -- hours
        messageOnSpawn = "Orshabaal has been summoned from hell to plague the lands of mortals again.",
        messageOnDeath = "Orshabaal has been defeated, the demon master will reveal himself in Hrodimir in 4 hours.",
        creatureId = 0, -- don't touch
        lastKilledTime = 0 -- don't touch
    },
    {
        dayToSpawn = "Friday",
        position = Position(861, 890, 12),
        monsterName = "Ghazbaran",
        respawnTime = 4, -- hours
        messageOnSpawn = "Ghazbaran is awakening... demonic entities are entering the mortal realm in the Hrodmir mines.",
        messageOnDeath = "Ghazbaran has been defeated, the demon master will reveal himself in Hrodimir in 4 hours.",
        creatureId = 0, -- don't touch
        lastKilledTime = 0 -- don't touch
    },
    {
        dayToSpawn = "Saturday",
        position = Position(861, 890, 12),
        monsterName = "Ghazbaran",
        respawnTime = 4, -- hours
        messageOnSpawn = "Ghazbaran is awakening... demonic entities are entering the mortal realm in the Hrodmir mines.",
        messageOnDeath = "Ghazbaran has been defeated, the demon master will reveal himself in Hrodimir in 4 hours.",
        creatureId = 0, -- don't touch
        lastKilledTime = 0 -- don't touch
    },
    {
        dayToSpawn = "Thursday",
        position = Position(1418, 1016, 4),
        monsterName = "Ferumbras",
        respawnTime = 4, -- hours
        messageOnSpawn = "Ferumbras return is at hand. The Mi'hen Academy calls for Heroes to fight that evil..",
        messageOnDeath = "Ferumbras has been defeated, the wizard master will reveal himself in Citadel in 4 hours.",
        creatureId = 0, -- don't touch
        lastKilledTime = 0 -- don't touch
    },
    {
        dayToSpawn = "Wednesday",
        position = Position(1418, 1016, 4),
        monsterName = "Ferumbras",
        respawnTime = 4, -- hours
        messageOnSpawn = "Ghazbaran is awakening... demonic entities are entering the mortal realm in the Hrodmir mines.",
        messageOnDeath = "Ghazbaran has been defeated, the demon master will reveal himself in Hrodimir in 4 hours.",
        creatureId = 0, -- don't touch
        lastKilledTime = 0 -- don't touch
    },
    ------ Pits of Inferno
    {
        dayToSpawn = "Any",
        position = Position(1358, 802, 15),
        monsterName = "The Plasmother",
        respawnTime = 4, -- hours
        messageOnSpawn = "The Plasmother is awakening... demonic entities are spawning in the Verminor's throneroom.",
        messageOnDeath = "The Plasmother has been defeated, this demonic spawn will reveal himself in Verminor's throneroom in 1 hour.",
        creatureId = 0, -- don't touch
        lastKilledTime = 0 -- don't touch
    },
    {
        dayToSpawn = "Any",
        position = Position(1299, 834, 15),
        monsterName = "Countess Sorrow",
        respawnTime = 1, -- hour
        messageOnSpawn = "Countess Sorrow is awakening... demonic entities are spawning in the Bazir's throneroom.",
        messageOnDeath = "Countess Sorrow has been defeated, this demonic spawn will reveal himself in Bazir's throneroom in 1 hour.",
        creatureId = 0, -- don't touch
        lastKilledTime = 0 -- don't touch
    },
    {
        dayToSpawn = "Any",
        position = Position(1378, 740, 15),
        monsterName = "Massacre",
        respawnTime = 1, -- hour
        messageOnSpawn = "Massacre is awakening... demonic entities are spawning in the Bazir's throneroom.",
        messageOnDeath = "Massacre has been defeated, this demonic spawn will reveal himself in Apocalypse's throneroom in 1 hour.",
        creatureId = 0, -- don't touch
        lastKilledTime = 0 -- don't touch
    },
    {
        dayToSpawn = "Any",
        position = Position(1341, 783, 15),
        monsterName = "Dracola",
        respawnTime = 1, -- hour
        messageOnSpawn = "Dracola is awakening... demonic entities are spawning in the Ashfalor's throneroom.",
        messageOnDeath = "Dracola has been defeated, this demonic spawn will reveal himself in Ashfalor's throneroom in 1 hour.",
        creatureId = 0, -- don't touch
        lastKilledTime = 0 -- don't touch
    },
    {
        dayToSpawn = "Any",
        position = Position(1409, 691, 15),
        monsterName = "The Imperor",
        respawnTime = 1, -- hour
        messageOnSpawn = "The Imperor is awakening... demonic entities are spawning in the Ashfalor's throneroom.",
        messageOnDeath = "The Imperor has been defeated, this demonic spawn will reveal himself in Ashfalor's throneroom in 1 hour.",
        creatureId = 0, -- don't touch
        lastKilledTime = 0 -- don't touch
    },
    {
        dayToSpawn = "Any",
        position = Position(1296, 756, 15),
        monsterName = "The Handmaiden",
        respawnTime = 1, -- hour
        messageOnSpawn = "The Handmaiden is awakening... demonic entities are spawning in the Pumin's throneroom.",
        messageOnDeath = "The Handmaiden has been defeated, this demonic spawn will reveal himself in Pumin's throneroom in 1 hour.",
        creatureId = 0, -- don't touch
        lastKilledTime = 0 -- don't touch
    },
    {
        dayToSpawn = "Any",
        position = Position(1269, 718, 15),
        monsterName = "Mr. Punish",
        respawnTime = 1, -- hour
        messageOnSpawn = "Mr. Punish is awakening... demonic entities are spawning in the Tafariel's throneroom.",
        messageOnDeath = "Mr. Punish has been defeated, this demonic spawn will reveal himself in Tafariel's throneroom in 1 hour.",
        creatureId = 0, -- don't touch
        lastKilledTime = 0 -- don't touch
    },
}

local day -- don't touch

local function respawnBoss(index)
    local spawn = raidBossInformation[index]
    local monster = Game.createMonster(spawn.monsterName, spawn.position, false, true)
    if monster then
        spawn.creatureId = monster:getId()
        monster:setMasterPosition(spawn.position)
        monster:registerEvent("raidBossDeath")
        Game.broadcastMessage(spawn.messageOnSpawn, MESSAGE_EVENT_ADVANCE)
    else
        print("Failed to respawn index: " .. index .. " -> " .. spawn.monsterName .. "")
    end
end

local globalevent = GlobalEvent("raidBosses")

function globalevent.onStartup()
    day = os.date('%A')
    for index, spawn in pairs(raidBossInformation) do
        if spawn.dayToSpawn == day or spawn.dayToSpawn == "Any" then
            local monster = Game.createMonster(spawn.monsterName, spawn.position, false, true)
            if monster then
                spawn.creatureId = monster:getId()
                monster:setMasterPosition(spawn.position)
                monster:registerEvent("raidBossDeath")
            else
                print("Failed to spawn index: " .. index .. " -> " .. spawn.monsterName .. "")
            end
        end
    end
    return true
end

globalevent:register()


local creatureevent = CreatureEvent("raidBossDeath")

function creatureevent.onDeath(creature, corpse, killer, mostDamageKiller, lastHitUnjustified, mostDamageUnjustified)
    local bossId = creature:getId()
    for i = 1, #raidBossInformation do
        if raidBossInformation[i].creatureId == bossId then
            raidBossInformation[i].creatureId = 0
            raidBossInformation[i].lastKilledTime = os.time()
            Game.broadcastMessage(raidBossInformation[i].messageOnDeath, MESSAGE_EVENT_ADVANCE)
            addEvent(respawnBoss, 1000 * 60 * 60 * raidBossInformation[i].respawnTime, i)
            return true
        end
    end
    print("Something went wrong in raidBossDeath script.")
    return true
end

creatureevent:register()


local talk = TalkAction("/bosscheck", "!bosscheck")

function talk.onSay(player, words, param)
    local text = ""
    for i = 1, #raidBossInformation do
        if text ~= "" then
            text = text .. "\n"
        end
        text = text .. raidBossInformation[i].monsterName
        text = text .. " " .. (raidBossInformation[i].dayToSpawn == "Any" and " [" .. day .. "]" or " [" .. raidBossInformation[i].dayToSpawn .. "]") .. "\n    "
        if raidBossInformation[i].creatureId == 0 then
            if raidBossInformation[i].lastKilledTime == 0 then
                text = text .. "Unavailable"
            else
                text = text .. "Dead -> respawning in " .. os.date("!%Hh %Mm %Ss", (raidBossInformation[i].lastKilledTime + (60 * 60 * raidBossInformation[i].respawnTime)) - os.time())
            end
        else
            text = text .. "Alive"
        end
    end
    player:showTextDialog(2239, text, false)
    return false
end

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

boss_check.png

There's also source modifications needed. We have to add setMasterPos function, so here's the steps:

Go to luascript.h
under
Code:
static int luaMonsterRename(lua_State* L);

add
C++:
static int luaMonsterSetMasterPosition(lua_State* L);

luascript.cpp

under
Code:
registerMethod("Monster", "rename", LuaScriptInterface::luaMonsterRename);

add
C++:
registerMethod("Monster", "setMasterPosition", LuaScriptInterface::luaMonsterSetMasterPosition);

find
Code:
int LuaScriptInterface::luaMonsterRename(lua_State* L)

under that whole method add
Lua:
int LuaScriptInterface::luaMonsterSetMasterPosition(lua_State* L)
{
    // monster:setMasterPosition(pos)
    Monster* monster = getUserdata<Monster>(L, 1);
    if (!monster) {
        lua_pushnil(L);
        return 1;
    }

    Position position;
    if (isTable(L, 2)) {
        position = getPosition(L, 2);
    }
    else {
        position.x = getNumber<uint16_t>(L, 2);
        position.y = getNumber<uint16_t>(L, 3);
        position.z = getNumber<uint16_t>(L, 4);
    }

    Tile* tile = g_game.map.getTile(position);
    if (!tile) {
        lua_pushnil(L);
        return 1;
    }

    monster->setMasterPos(position);
    pushBoolean(L, true);
    return 1;
}

Regards!
 
Last edited:
Really cool

error for me

Lua:
attempt to call method 'setMasterPosition' (a nil value)

It seems like setMasterPosition doesnt exists by default?
 
Last edited:
@Levi999x thanks for the report! I forgot to say that function setMasterPos is requiered to make this script work. You can find it here:

Also uploaded the thread with the source configuration directly.
Regards!
 
Hi! This system it's the outcome of a request thread Lua - [1.X] Boss checker (https://otland.net/threads/1-x-boss-checker.281905/#post-2701825), it has been made by @Xikini and it's been correctly tested on TFS 1.5 downgrades.

It has some samples for daily bosses (in this case Orshabaal, Ferumbras, Ghazbaran), and added Pits of Inferno bosses, just change the coordinates and everything will work. If you set the day to Any it will show the current day as spawn day.


To install, go to data/scripts and create a new file with:
Lua:
local raidBossInformation = {
    {
        dayToSpawn = "Sunday",
        position = Position(1008, 1147, 15),
        monsterName = "Orshabaal",
        respawnTime = 4, -- hours
        messageOnSpawn = "Orshabaal has been summoned from hell to plague the lands of mortals again.",
        messageOnDeath = "Orshabaal has been defeated, the demon master will reveal himself in Hrodimir in 4 hours.",
        creatureId = 0, -- don't touch
        lastKilledTime = 0 -- don't touch
    },
    {
        dayToSpawn = "Monday",
        position = Position(1008, 1147, 15),
        monsterName = "Orshabaal",
        respawnTime = 4, -- hours
        messageOnSpawn = "Orshabaal has been summoned from hell to plague the lands of mortals again.",
        messageOnDeath = "Orshabaal has been defeated, the demon master will reveal himself in Hrodimir in 4 hours.",
        creatureId = 0, -- don't touch
        lastKilledTime = 0 -- don't touch
    },
    {
        dayToSpawn = "Friday",
        position = Position(861, 890, 12),
        monsterName = "Ghazbaran",
        respawnTime = 4, -- hours
        messageOnSpawn = "Ghazbaran is awakening... demonic entities are entering the mortal realm in the Hrodmir mines.",
        messageOnDeath = "Ghazbaran has been defeated, the demon master will reveal himself in Hrodimir in 4 hours.",
        creatureId = 0, -- don't touch
        lastKilledTime = 0 -- don't touch
    },
    {
        dayToSpawn = "Saturday",
        position = Position(861, 890, 12),
        monsterName = "Ghazbaran",
        respawnTime = 4, -- hours
        messageOnSpawn = "Ghazbaran is awakening... demonic entities are entering the mortal realm in the Hrodmir mines.",
        messageOnDeath = "Ghazbaran has been defeated, the demon master will reveal himself in Hrodimir in 4 hours.",
        creatureId = 0, -- don't touch
        lastKilledTime = 0 -- don't touch
    },
    {
        dayToSpawn = "Thursday",
        position = Position(1418, 1016, 4),
        monsterName = "Ferumbras",
        respawnTime = 4, -- hours
        messageOnSpawn = "Ferumbras return is at hand. The Mi'hen Academy calls for Heroes to fight that evil..",
        messageOnDeath = "Ferumbras has been defeated, the wizard master will reveal himself in Citadel in 4 hours.",
        creatureId = 0, -- don't touch
        lastKilledTime = 0 -- don't touch
    },
    {
        dayToSpawn = "Wednesday",
        position = Position(1418, 1016, 4),
        monsterName = "Ferumbras",
        respawnTime = 4, -- hours
        messageOnSpawn = "Ghazbaran is awakening... demonic entities are entering the mortal realm in the Hrodmir mines.",
        messageOnDeath = "Ghazbaran has been defeated, the demon master will reveal himself in Hrodimir in 4 hours.",
        creatureId = 0, -- don't touch
        lastKilledTime = 0 -- don't touch
    },
    ------ Pits of Inferno
    {
        dayToSpawn = "Any",
        position = Position(1358, 802, 15),
        monsterName = "The Plasmother",
        respawnTime = 4, -- hours
        messageOnSpawn = "The Plasmother is awakening... demonic entities are spawning in the Verminor's throneroom.",
        messageOnDeath = "The Plasmother has been defeated, this demonic spawn will reveal himself in Verminor's throneroom in 1 hour.",
        creatureId = 0, -- don't touch
        lastKilledTime = 0 -- don't touch
    },
    {
        dayToSpawn = "Any",
        position = Position(1299, 834, 15),
        monsterName = "Countess Sorrow",
        respawnTime = 1, -- hour
        messageOnSpawn = "Countess Sorrow is awakening... demonic entities are spawning in the Bazir's throneroom.",
        messageOnDeath = "Countess Sorrow has been defeated, this demonic spawn will reveal himself in Bazir's throneroom in 1 hour.",
        creatureId = 0, -- don't touch
        lastKilledTime = 0 -- don't touch
    },
    {
        dayToSpawn = "Any",
        position = Position(1378, 740, 15),
        monsterName = "Massacre",
        respawnTime = 1, -- hour
        messageOnSpawn = "Massacre is awakening... demonic entities are spawning in the Bazir's throneroom.",
        messageOnDeath = "Massacre has been defeated, this demonic spawn will reveal himself in Apocalypse's throneroom in 1 hour.",
        creatureId = 0, -- don't touch
        lastKilledTime = 0 -- don't touch
    },
    {
        dayToSpawn = "Any",
        position = Position(1341, 783, 15),
        monsterName = "Dracola",
        respawnTime = 1, -- hour
        messageOnSpawn = "Dracola is awakening... demonic entities are spawning in the Ashfalor's throneroom.",
        messageOnDeath = "Dracola has been defeated, this demonic spawn will reveal himself in Ashfalor's throneroom in 1 hour.",
        creatureId = 0, -- don't touch
        lastKilledTime = 0 -- don't touch
    },
    {
        dayToSpawn = "Any",
        position = Position(1409, 691, 15),
        monsterName = "The Imperor",
        respawnTime = 1, -- hour
        messageOnSpawn = "The Imperor is awakening... demonic entities are spawning in the Ashfalor's throneroom.",
        messageOnDeath = "The Imperor has been defeated, this demonic spawn will reveal himself in Ashfalor's throneroom in 1 hour.",
        creatureId = 0, -- don't touch
        lastKilledTime = 0 -- don't touch
    },
    {
        dayToSpawn = "Any",
        position = Position(1296, 756, 15),
        monsterName = "The Handmaiden",
        respawnTime = 1, -- hour
        messageOnSpawn = "The Handmaiden is awakening... demonic entities are spawning in the Pumin's throneroom.",
        messageOnDeath = "The Handmaiden has been defeated, this demonic spawn will reveal himself in Pumin's throneroom in 1 hour.",
        creatureId = 0, -- don't touch
        lastKilledTime = 0 -- don't touch
    },
    {
        dayToSpawn = "Any",
        position = Position(1269, 718, 15),
        monsterName = "Mr. Punish",
        respawnTime = 1, -- hour
        messageOnSpawn = "Mr. Punish is awakening... demonic entities are spawning in the Tafariel's throneroom.",
        messageOnDeath = "Mr. Punish has been defeated, this demonic spawn will reveal himself in Tafariel's throneroom in 1 hour.",
        creatureId = 0, -- don't touch
        lastKilledTime = 0 -- don't touch
    },
}

local day -- don't touch

local function respawnBoss(index)
    local spawn = raidBossInformation[index]
    local monster = Game.createMonster(spawn.monsterName, spawn.position, false, true)
    if monster then
        spawn.creatureId = monster:getId()
        monster:setMasterPosition(spawn.position)
        monster:registerEvent("raidBossDeath")
        Game.broadcastMessage(spawn.messageOnSpawn, MESSAGE_EVENT_ADVANCE)
    else
        print("Failed to respawn index: " .. index .. " -> " .. spawn.monsterName .. "")
    end
end

local globalevent = GlobalEvent("raidBosses")

function globalevent.onStartup()
    day = os.date('%A')
    for index, spawn in pairs(raidBossInformation) do
        if spawn.dayToSpawn == day or spawn.dayToSpawn == "Any" then
            local monster = Game.createMonster(spawn.monsterName, spawn.position, false, true)
            if monster then
                spawn.creatureId = monster:getId()
                monster:setMasterPosition(spawn.position)
                monster:registerEvent("raidBossDeath")
            else
                print("Failed to spawn index: " .. index .. " -> " .. spawn.monsterName .. "")
            end
        end
    end
    return true
end

globalevent:register()


local creatureevent = CreatureEvent("raidBossDeath")

function creatureevent.onDeath(creature, corpse, killer, mostDamageKiller, lastHitUnjustified, mostDamageUnjustified)
    local bossId = creature:getId()
    for i = 1, #raidBossInformation do
        if raidBossInformation[i].creatureId == bossId then
            raidBossInformation[i].creatureId = 0
            raidBossInformation[i].lastKilledTime = os.time()
            Game.broadcastMessage(raidBossInformation[i].messageOnDeath, MESSAGE_EVENT_ADVANCE)
            addEvent(respawnBoss, 1000 * 60 * 60 * raidBossInformation[i].respawnTime, i)
            return true
        end
    end
    print("Something went wrong in raidBossDeath script.")
    return true
end

creatureevent:register()


local talk = TalkAction("/bosscheck", "!bosscheck")

function talk.onSay(player, words, param)
    local text = ""
    for i = 1, #raidBossInformation do
        if text ~= "" then
            text = text .. "\n"
        end
        text = text .. raidBossInformation[i].monsterName
        text = text .. " " .. (raidBossInformation[i].dayToSpawn == "Any" and " [" .. day .. "]" or " [" .. raidBossInformation[i].dayToSpawn .. "]") .. "\n    "
        if raidBossInformation[i].creatureId == 0 then
            if raidBossInformation[i].lastKilledTime == 0 then
                text = text .. "Unavailable"
            else
                text = text .. "Dead -> respawning in " .. os.date("!%Hh %Mm %Ss", (raidBossInformation[i].lastKilledTime + (60 * 60 * raidBossInformation[i].respawnTime)) - os.time())
            end
        else
            text = text .. "Alive"
        end
    end
    player:showTextDialog(2239, text, false)
    return false
end

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

View attachment 69742

There's also source modifications needed. We have to add setMasterPos function, so here's the steps:

Go to luascript.h
under
Code:
static int luaMonsterRename(lua_State* L);

add
C++:
static int luaMonsterSetMasterPosition(lua_State* L);

luascript.cpp

under
Code:
registerMethod("Monster", "rename", LuaScriptInterface::luaMonsterRename);

add
C++:
registerMethod("Monster", "setMasterPosition", LuaScriptInterface::luaMonsterSetMasterPosition);

find
Code:
int LuaScriptInterface::luaMonsterRename(lua_State* L)

under that whole method add
Lua:
int LuaScriptInterface::luaMonsterSetMasterPosition(lua_State* L)
{
    // monster:setMasterPosition(pos)
    Monster* monster = getUserdata<Monster>(L, 1);
    if (!monster) {
        lua_pushnil(L);
        return 1;
    }

    Position position;
    if (isTable(L, 2)) {
        position = getPosition(L, 2);
    }
    else {
        position.x = getNumber<uint16_t>(L, 2);
        position.y = getNumber<uint16_t>(L, 3);
        position.z = getNumber<uint16_t>(L, 4);
    }

    Tile* tile = g_game.map.getTile(position);
    if (!tile) {
        lua_pushnil(L);
        return 1;
    }

    monster->setMasterPos(position);
    pushBoolean(L, true);
    return 1;
}

Regards!

there way to add this for TFS 0.x?
 
there way to add this for TFS 0.x?

It can only be added to a server that support revscripsys. What you could try actually is to register globalevent, talkaction and creaturescript separately, but you gonna still miss the .lua syntaxis that 1.X provides, the only way would be to replace every function, for the old syntax (example: doMonsterAsdasdasd instead of monster:Asdasdasd)

In resume, the best thing you can do is to switch to TFS 1.X.
 
Did you make /reload? Thats most likely the reason
Value to day is being set on starting server, if you make /reload day becomes nil again

Solution: use local day = os.date('%A') inside talkaction code
This will 'fix' the issue, but won't give correct information.
If the server save is at 6am, for example, 12:00am-5:59am the talkaction will tell the wrong information.
 
Technically it will display correct information since "any" its suppose to show current day but I got you, it may confuse players due respawing time

So better to do this edit instead, just in case owner makes /reload
Lua:
" .. (day or os.date('%A')) .. "
 
Technically it will display correct information since "any" its suppose to show current day but I got you, it may confuse players due respawing time

So better to do this edit instead, just in case owner makes /reload
Lua:
" .. (day or os.date('%A')) .. "

Great! This was actually a bug. Here's the log
day_error.png

Thanks for the solution,
Regards!
 
Back
Top