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

TFS 1.X+ Simple boss system by me

Sprrw

Well-Known Member
Joined
Jun 22, 2021
Messages
100
Reaction score
55
Location
Sweden
Hey guys, I made a simple boss system (as said in title).... xd
Well with the help of you guys ofc!

Well after a couple of hours of trail and error I came up with this.
CreatureScript: (This script was inspired like 100% by someone, I cant remember who. If you recognise this script. Please hmu and tell me who it is so I can cred him/her).
Lua:
bossConfig = {
    ["Burr the Mighty"] = {
        bossMessage = "WHY?! HOW?!! THIS CAN'T BE TRUE!!!! A MAN CANNOT KILL A GOD!.....",
        playerMessage = "You can feel the evil presence of Burr's spirit leaving the island.",
        obstacle = 1449,
        obstaclePos = {
            x = 1456,
            y = 1105,
            z = 4
        },
        rewardRoomPos = {
            x = 1438,
            y = 1087,
            z = 4
        }
    }
}

function onKill(player, target)
    if player:isPlayer() and target:isMonster() then
        local playerName = player
        local bossName = bossConfig[Creature(target):getName()]
        local bossObstacle = Tile(bossName.obstaclePos):getItemById(bossName.obstacle)
        if bossName then
            for uid, _ in pairs(target:getDamageMap()) do
                local attacker = Player(uid)
                if attacker then
                    attacker:sendTextMessage(18, bossName.bossMessage)
                    attacker:sendTextMessage(22, bossName.playerMessage)
                    attacker:teleportTo(bossName.rewardRoomPos)
                    bossObstacle:remove()
                end
            end
        end
    end
    return true
end

Movements(Cant find the exact post but I remember that @Xikini really inspired this script! Thanks a lot pal! <3 :
Lua:
local fromPos = {
    x = 1431,
    y = 1101,
    z = 4
}
local toPos = {
    x = 1470,
    y = 1126,
    z = 4
}
local entrance = {
    x = 1456,
    y = 1105,
    z = 4
}
local obstacle = 1449

function onStepIn(creature, item, position, fromPosition)
    local player = creature:getPlayer()
    if not player then
        return false
    end
    if not Tile(entrance):getItemById(obstacle) then
        doCreateItem(obstacle, 1, entrance)
    end
    local search = false
    player:teleportTo(fromPosition, true)
    for x = fromPos.x, toPos.x do
        for y = fromPos.y, toPos.y do
            for z = fromPos.z, toPos.z do
                local bossRoomArea = {
                    x = x,
                    y = y,
                    z = z
                }
                local searchCreature = getTopCreature(bossRoomArea).uid
                local bossName = "Burr the Mighty"
                if isMonster(searchCreature) and getCreatureName(searchCreature) == bossName then
                    search = true
                end
            end
        end
    end
    if search == true then
        player:sendTextMessage(18, "There is an invisible force blocking the way.")
    else
        player:sendTextMessage(18, "YOU HAVE AWOKE THE CURSE OF BURR THE MIGHTY!")
        doSummonCreature("Burr the Mighty", position)
    end
end
So as you might have noticed, I posted this in the support thread. This is not by accident. I was actually wondering how I would make the movement script a little more dynamic, more like the creatuescript. In the creaturescript, I can just easily add another monster/boss to the config and it would work, but for the movement I would have to create an entirely new script for every monster/boss. So yea, is it possible to do something similar to what I did for creaturescript for the movement?
 
Instead of searching tile by tile for the boss creature, I'd suggest upgrading to using the getSpectators function, which is highly optimized for finding creatures/players inside of an area.

--
If there is different boss area's, you could separate them by position of the tile being stepped on?
So if it's within the from/to distance, then you know what part of the table you're looking at.

Or you could separate it by actionId's.

as an example, using actionId's.
Lua:
local bossConfig = {
    [45000] = { -- tileActionId
        fromPos  = {x = 1431, y = 1101, z = 4},
        toPos    = {x = 1470, y = 1126, z = 4},
        entrance = {x = 1456, y = 1105, z = 4},
        obstacle = 1449,
        boss = "Burr the Mighty"
    },
    [45001] = {
        fromPos  = {x = 1431, y = 1101, z = 4},
        toPos    = {x = 1470, y = 1126, z = 4},
        entrance = {x = 1456, y = 1105, z = 4},
        obstacle = 1449,
        boss = "rat"
    },
    [45002] = {
        fromPos  = {x = 1431, y = 1101, z = 4},
        toPos    = {x = 1470, y = 1126, z = 4},
        entrance = {x = 1456, y = 1105, z = 4},
        obstacle = 1449,
        boss = "cave rat"
    }
}

--
 
Instead of searching tile by tile for the boss creature, I'd suggest upgrading to using the getSpectators function, which is highly optimized for finding creatures/players inside of an area.

--
If there is different boss area's, you could separate them by position of the tile being stepped on?
So if it's within the from/to distance, then you know what part of the table you're looking at.

Or you could separate it by actionId's.

as an example, using actionId's.
Lua:
local bossConfig = {
    [45000] = { -- tileActionId
        fromPos  = {x = 1431, y = 1101, z = 4},
        toPos    = {x = 1470, y = 1126, z = 4},
        entrance = {x = 1456, y = 1105, z = 4},
        obstacle = 1449,
        boss = "Burr the Mighty"
    },
    [45001] = {
        fromPos  = {x = 1431, y = 1101, z = 4},
        toPos    = {x = 1470, y = 1126, z = 4},
        entrance = {x = 1456, y = 1105, z = 4},
        obstacle = 1449,
        boss = "rat"
    },
    [45002] = {
        fromPos  = {x = 1431, y = 1101, z = 4},
        toPos    = {x = 1470, y = 1126, z = 4},
        entrance = {x = 1456, y = 1105, z = 4},
        obstacle = 1449,
        boss = "cave rat"
    }
}

--
Eyy how didnt I think about that. Thats really clever! I love it! Thanks a lot bro! Hmm Im not sure how the spectator function works. like GetSpectators(posX,posY)?
Post automatically merged:

Someone can add here remove boss if someone die or leave tp?
Thanks for your reply! I will keep you updated!
Post automatically merged:

Someone can add here remove boss if someone die or leave tp?
I think you would have to do something like "onKill" but for the boss. And then check if the area is empty or if there is any players left. I think you could just check the exit tile if someone is leaving, something if "boss is spawned and you step on the tile + the room is empty." Then remove the creature. Give me a couple of hours to get home and Ima fix it!
 
Eyy how didnt I think about that. Thats really clever! I love it! Thanks a lot bro! Hmm Im not sure how the spectator function works. like GetSpectators(posX,posY)?
Post automatically merged:


Thanks for your reply! I will keep you updated!
Post automatically merged:


I think you would have to do something like "onKill" but for the boss. And then check if the area is empty or if there is any players left. I think you could just check the exit tile if someone is leaving, something if "boss is spawned and you step on the tile + the room is empty." Then remove the creature. Give me a couple of hours to get home and Ima fix it!
// Game.getSpectators(position[, multifloor = false[, onlyPlayer = false[, minRangeX = 0[, maxRangeX = 0[, minRangeY = 0[, maxRangeY = 0]]]]]])

Specify a position. If you're looking for the boss creature, and/or any other creatures keep onlyPlayer as false, then you set a range, so its so many sqms around that position you specify. For example, if you want to get 6 sqm's around a position, you do:
Lua:
local spectators = Game.getSpectators(pos, false, false, 6, 6, 6, 6)
Make sure when looping through, that you are checking is the spectator is a monster.

In order to remove the boss if someone dies, there are multiple ways you can do this.

If you use a lever etc to enter the boss room. You can simply call getSpectators in the action script to check if any players exist. If not, then when looping through the spectators, simply remove them.

The other way, is to register an event to any players entering the boss room, and then track their progress either onDeath or onStepIn(exit tp). Keep a running count of how many players are in the boss arena, and decrement this count if one of the following events occur.
 
// Game.getSpectators(position[, multifloor = false[, onlyPlayer = false[, minRangeX = 0[, maxRangeX = 0[, minRangeY = 0[, maxRangeY = 0]]]]]])

Specify a position. If you're looking for the boss creature, and/or any other creatures keep onlyPlayer as false, then you set a range, so its so many sqms around that position you specify. For example, if you want to get 6 sqm's around a position, you do:
Lua:
local spectators = Game.getSpectators(pos, false, false, 6, 6, 6, 6)
Make sure when looping through, that you are checking is the spectator is a monster.

In order to remove the boss if someone dies, there are multiple ways you can do this.

If you use a lever etc to enter the boss room. You can simply call getSpectators in the action script to check if any players exist. If not, then when looping through the spectators, simply remove them.

The other way, is to register an event to any players entering the boss room, and then track their progress either onDeath or onStepIn(exit tp). Keep a running count of how many players are in the boss arena, and decrement this count if one of the following events occur.
Hmm thanks for your reply. Well how'd that work. So I would have a set position like 1000,1000,7 and then min/max x/y = how much I want it to cover? Sorry that Im a lil slow....
 
Hmm thanks for your reply. Well how'd that work. So I would have a set position like 1000,1000,7 and then min/max x/y = how much I want it to cover? Sorry that Im a lil slow....
Exactly, in the example I used, it would get the whole area around the position, within a range of 6 sqm, so it will get all the spectators from an area of 13x13 tiles
 
Instead of searching tile by tile for the boss creature, I'd suggest upgrading to using the getSpectators function, which is highly optimized for finding creatures/players inside of an area.

--
If there is different boss area's, you could separate them by position of the tile being stepped on?
So if it's within the from/to distance, then you know what part of the table you're looking at.

Or you could separate it by actionId's.

as an example, using actionId's.
Lua:
local bossConfig = {
    [45000] = { -- tileActionId
        fromPos  = {x = 1431, y = 1101, z = 4},
        toPos    = {x = 1470, y = 1126, z = 4},
        entrance = {x = 1456, y = 1105, z = 4},
        obstacle = 1449,
        boss = "Burr the Mighty"
    },
    [45001] = {
        fromPos  = {x = 1431, y = 1101, z = 4},
        toPos    = {x = 1470, y = 1126, z = 4},
        entrance = {x = 1456, y = 1105, z = 4},
        obstacle = 1449,
        boss = "rat"
    },
    [45002] = {
        fromPos  = {x = 1431, y = 1101, z = 4},
        toPos    = {x = 1470, y = 1126, z = 4},
        entrance = {x = 1456, y = 1105, z = 4},
        obstacle = 1449,
        boss = "cave rat"
    }
}

--
Hola amigo! Just a quick question. How would I add this in the movements.xml. I mean since now I have it like this "
<movevent event="StepIn" actionid="45002" script="bossScript.lua"/>", how would I put the actionIds with this solution?
 
XML:
<movevent event="StepIn" actionid="45000" script="bossScript.lua"/>
<movevent event="StepIn" actionid="45001" script="bossScript.lua"/>
<movevent event="StepIn" actionid="45002" script="bossScript.lua"/>
<movevent event="StepIn" actionid="45003" script="bossScript.lua"/>
This should work as well.
Lua:
<movevent event="StepIn" actionid="45000-45003" script="bossScript.lua"/>
If you changed your script to be using revscripts, you could automatically grab all the actionid's from the table
Lua:
for actionId, _ in pairs(bossConfig) do
    movementScriptName:aid(actionId)
end
 
XML:
<movevent event="StepIn" actionid="45000" script="bossScript.lua"/>
<movevent event="StepIn" actionid="45001" script="bossScript.lua"/>
<movevent event="StepIn" actionid="45002" script="bossScript.lua"/>
<movevent event="StepIn" actionid="45003" script="bossScript.lua"/>
This should work as well.
Lua:
<movevent event="StepIn" actionid="45000-45003" script="bossScript.lua"/>
If you changed your script to be using revscripts, you could automatically grab all the actionid's from the table
Lua:
for actionId, _ in pairs(bossConfig) do
    movementScriptName:aid(actionId)
end
Ohh, interesting. How would I tackle revscript? That seems like a more modern way of doing it. Thanks a lot for all your answers btw! They really mean a lot! <3
 
Ohh, interesting. How would I tackle revscript? That seems like a more modern way of doing it. Thanks a lot for all your answers btw! They really mean a lot! <3
 
Edit, I kinda figured that error out.
Post automatically merged:

data/scripts instead of data/movements/scripts
Yea just realised when I reread the github documentation. Thanks a lot!
Post automatically merged:

XML:
<movevent event="StepIn" actionid="45000" script="bossScript.lua"/>
<movevent event="StepIn" actionid="45001" script="bossScript.lua"/>
<movevent event="StepIn" actionid="45002" script="bossScript.lua"/>
<movevent event="StepIn" actionid="45003" script="bossScript.lua"/>
This should work as well.
Lua:
<movevent event="StepIn" actionid="45000-45003" script="bossScript.lua"/>
If you changed your script to be using revscripts, you could automatically grab all the actionid's from the table
Lua:
for actionId, _ in pairs(bossConfig) do
    movementScriptName:aid(actionId)
end
Oh and btw, what do I need that loop for? Like I get what it does but is that even needed if I register tha actionsIds like this :
Lua:
    local creaturePos = Tile(creature:getPosition()):getGround():getActionId()
    local bossSelector = bossConfig[creaturePos]
Post automatically merged:

Nm, its obv needed for it to work... I dont understand why I type before I properly test XD
Post automatically merged:

Okay I've tested everything this time (well obv not the correct thing)....
But I just cant convert my onKill script to revscript. I even tried something simple like :

Lua:
local bossDeathScript = CreatureEvent("bossDeathScript")
function bossDeathScript.onKill(creature, target)
    local player = creature:getName()
    print(player)
end

bossDeathScript:register()
And Im conviced onKill just doesnt work with revscript XD
Post automatically merged:

UPDATE:
Changelog:
[I with much help from @Xikini updated the script to use Revscript + we made the moveevent dynamic.]

New creaturescript: (remember that this should be located at data/scripts/creaturescript, not data/creaturescripts/scripts.)
Lua:
local bossDeathScript = CreatureEvent("bossDeathScript")

bossDeathScript:register()

local bossConfig = {
    ["Burr the Mighty"] = {
        bossMessage = "WHY?! HOW?!! THIS CAN'T BE TRUE!!!! A MAN CANNOT KILL A GOD!.....",
        playerMessage = "You can feel the evil presence of Burr's spirit leaving the island.",
        obstacle = 1449,
        obstaclePos = {
            x = 1456,
            y = 1105,
            z = 4
        },
        rewardRoomPos = {
            x = 1438,
            y = 1087,
            z = 4
        }
    },
    ["Rat"] = {
        bossMessage = "WHY?! HOW?!! THIS CAN'T BE TRUE!!!! A MAN CANNOT KILL A GOD!.....",
        playerMessage = "You can feel the evil presence of Burr's spirit leaving the island.",
        obstacle = 1449,
        obstaclePos = {
            x = 1456,
            y = 1105,
            z = 4
        },
        rewardRoomPos = {
            x = 1438,
            y = 1087,
            z = 4
        }
    }
}

function bossDeathScript.onDeath(creature, corpse, killer, mostDamageKiller, lastHitUnjustified, mostDamageUnjustified)
    if killer:isPlayer() and creature:isMonster() then
        local player = killer:getName()
        local bossName = creature:getName()
        local bossSelector = bossConfig[bossName]
        local obstacle = Tile(bossSelector.obstaclePos):getItemById(bossSelector.obstacle)
        local obstaclePos = bossSelector.obstaclePos
        local rewardRoomPos = bossSelector.rewardRoomPos
        local bossMessage = bossSelector.bossMessage
        local playerMessage = bossSelector.playerMessage

        if bossSelector then
            for uid, _ in pairs(creature:getDamageMap()) do
                local attacker = Player(uid)
                if attacker then
                    attacker:sendTextMessage(18, bossMessage)
                    attacker:sendTextMessage(22, playerMessage)
                    attacker:teleportTo(rewardRoomPos)
                    obstacle:remove()
                end
            end
        end
    end
    return true
end

bossDeathScript:register()

New movement: (remember that this should be located at data/scripts/movements, not data/movements/scripts.)

Lua:
local bossTileScript = MoveEvent("bossTileScript")

local bossConfig = {
    [45002] = { -- tileActionId
        fromPos = {
            x = 1431,
            y = 1101,
            z = 4
        },
        toPos = {
            x = 1470,
            y = 1126,
            z = 4
        },
        entrance = {
            x = 1456,
            y = 1105,
            z = 4
        },
        obstacle = 1449,
        boss = "Burr the Mighty", -- Boss name
        msgBlocking = "There is an invisible force blocking the way.", -- Unwalkable tile msg (if you dont want this just keep it empty like: " ")
        msgSummoned = "YOU HAVE AWOKE THE CURSE OF BURR THE MIGHTY!" -- When the boss is summoned (if you dont want this just keep it empty like: " "
    },
    [45003] = { -- tileActionId
        fromPos = {
            x = 1431,
            y = 1101,
            z = 4
        },
        toPos = {
            x = 1470,
            y = 1126,
            z = 4
        },
        entrance = {
            x = 1456,
            y = 1105,
            z = 4
        },
        obstacle = 1449,
        boss = "Rat",
        msgBlocking = "There is an invisible force blocking the way.",
        msgSummoned = "YOU HAVE AWOKE THE CURSE OF BURR THE MIGHTY!"
    }
}

for actionId, _ in pairs(bossConfig) do
    bossTileScript:aid(actionId)
end

function bossTileScript.onStepIn(creature, item, position, fromPosition)
    local creaturePos = Tile(creature:getPosition()):getGround():getActionId()
    local bossSelector = bossConfig[creaturePos]
    local player = creature:getPlayer()
    if not player then
        return false
    end
    if not Tile(bossSelector.entrance):getItemById(bossSelector.obstacle) then
        doCreateItem(bossSelector.obstacle, 1, bossSelector.entrance)
    end
    local search = false
    player:teleportTo(fromPosition, true)
    for x = bossSelector.fromPos.x, bossSelector.toPos.x do
        for y = bossSelector.fromPos.y, bossSelector.toPos.y do
            for z = bossSelector.fromPos.z, bossSelector.toPos.z do
                local bossRoomArea = {
                    x = x,
                    y = y,
                    z = z
                }
                local searchCreature = getTopCreature(bossRoomArea).uid
                local bossName = bossSelector.boss
                if isMonster(searchCreature) and getCreatureName(searchCreature) == bossName then
                    search = true
                end
            end
        end
    end
    if search == true then
        player:sendTextMessage(18, bossSelector.msgBlocking)
    else
        player:sendTextMessage(18, bossSelector.msgSummoned)
        doSummonCreature(bossSelector.boss, position)
    end
end

bossTileScript:register()
 
Last edited:
Back
Top