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

Mapeditor Triggers

Sizaro

Advanced OT User
Joined
Aug 20, 2007
Messages
5,158
Solutions
5
Reaction score
221
Location
Sweden
GitHub
coldensjo
I have created (with some help from a few friends) a system where you can trigger specific things to happen, straight from the mapeditor. It is made to be as simple as possible.
The idea is that all simple scripts should be possible to do in the mapeditor, without any creation of basic scripts in the server files. One script to handle it all.

Why am I making this thread?
To spark some imagination on what I believe the OT community should focus on.

Here is all the possibilities (currently).

[spawn] Spawns a monster
Example: spawn("rat", 5, (Position(100, 100, 7))

[text] Send text to player
Example: text("Hello World!")

[remove] Remove item on map
Example: remove(1234, Position(100, 100, 7))
If actionId == 2101 then assume its a lever and transform the lever

[create] Create item on map
Example: build(1234, Position(100, 100, 7))

[effect] Send effect to position
Example: effect(CONST_ME_MAGIC_RED, Position(100, 100, 7))

[switch] Transform an item from one to another
Example: switch(Position(100, 100, 7), 1234, 1235)

[raid] Start a raid
Example: raid("worldraid")

[teleport] Teleport player to position
Example: teleport(Position(100, 100, 7))

[access] Recieve Access
Example: access(12345)
And then put the same number on the door you want the player to have access to.

[learn] Learn Spells
Example: learn("Light Healing")



Below I will post some examples of what it can do.

1719701497260.pngironcore_dx_PqAJvEQQMi.gifironcore_dx_tDOPpz9iVP.gifEditor_x64_TjGD1CYvTW.pngironcore_dx_oikHjOebnh.gif
 
spawn("rat", 5, (Position(100, 100, 7))
Great example.
What about same player spawning them 10x per second by walking in-out some position or using same item again?
Shouldn't it be allowed to? OK, we need some 'player storage' or 'global storage' and Lua script to handle it, and limit to 1 time per player or 1 time per XXX seconds.

Not even going to 'open source projects' like canary which removed all actionID/uniqueID from map .otbm and moved them to Lua, to keep track of .otbm changes (.otbm "hacks" - hidden valuable items inside ex. "chests").
.otbm = Open Tibia Binary Map, 'binary' part is important and makes most of data unreadable by any other tool except OTS/RME.
Even, if any tool can read changes, it's hard to track them without 'human-readable' code ex. Lua.
Ex. 'git' tracks changes in text files, it can easily detect changes in unique_items.lua, but it can't track canary.otbm.

Splitting logic between .otbm file (items + AID/UID IDs) and .lua is not a good idea.
Keeping .otbm with no AID and UID and add them all from .lua is pretty good idea.
 
Great example.
What about same player spawning them 10x per second by walking in-out some position or using same item again?
Shouldn't it be allowed to? OK, we need some 'player storage' or 'global storage' and Lua script to handle it, and limit to 1 time per player or 1 time per XXX seconds.

Not even going to 'open source projects' like canary which removed all actionID/uniqueID from map .otbm and moved them to Lua, to keep track of .otbm changes (.otbm "hacks" - hidden valuable items inside ex. "chests").
.otbm = Open Tibia Binary Map, 'binary' part is important and makes most of data unreadable by any other tool except OTS/RME.
Even, if any tool can read changes, it's hard to track them without 'human-readable' code ex. Lua.
Ex. 'git' tracks changes in text files, it can easily detect changes in unique_items.lua, but it can't track canary.otbm.

Splitting logic between .otbm file (items + AID/UID IDs) and .lua is not a good idea.
Keeping .otbm with no AID and UID and add them all from .lua is pretty good idea.
Remember a system like this needs to be 100% oversimplified.
spawn is currently designed to be a one time use. So the trigger item is removed upon activation. I added this, for exceptions;

Lua:
if not item:getActionId() == 2100 then
    item:remove()
end

I have never used canary so I don't understand your examples.

If anyone want to attempt to improve the code, remember that its in infant stage and not 100% ready yet. Some things arent working as intended. It uses some basic functions from Nostalrius like Game.transformItemOnMap() and Game.isItemThere(), these can easily be ported to any version and put into library file.
It is also very tailored to my own server currently. But the code is very readable and can easible be fixed to work better for your server.
Lua:
function onUse(player, item, fromPosition, target, toPosition, isHotkey)
    local functions = item:getAttribute(ITEM_ATTRIBUTE_TEXT)

    for luaCode in functions:gmatch("[^\n]+") do
        local func, err = load("return " .. luaCode,
            ("pos(%d,%d,%d)"):format(fromPosition.x, fromPosition.y, fromPosition.z))
        if func then
            local execute = func()
            if not item:getActionId() == 2100 then
                item:remove()
            end

            -- [text] Send text to player
            -- Example: text("Hello World!")
            if execute.func == "text" then
                local text = execute.message
                player:sendTextMessage(MESSAGE_EVENT_ADVANCE, text)
            end

            -- [remove] Remove item on map
            -- Example: remove(1234, Position(100, 100, 7))
            -- If actionId == 2101 then assume its a lever and transform the lever
            if execute.func == "remove" then
                if item:getActionId() == 2101 then
                    if Game.isItemThere(fromPosition, 1945) then
                        Game.transformItemOnMap(fromPosition, 1945, 1946)
                    else
                        player:sendTextMessage(MESSAGE_STATUS_SMALL, "It's stuck.")
                        return true
                    end
                end
                local item = execute.message.item
                local pos = execute.message.pos
                if Game.isItemThere(pos, item) then
                    Game.removeItemOnMap(pos, item)
                    pos:sendMagicEffect(CONST_ME_POFF)
                end
            end

            -- [create] Create item on map
            -- Example: build(1234, Position(100, 100, 7))
            if execute.func == "create" then
                local item = execute.message.item
                local pos = execute.message.pos
                Game.createItem(item, 1, pos)
            end

            -- [effect] Send effect to position
            -- Example: effect(CONST_ME_MAGIC_RED, Position(100, 100, 7))
            if execute.func == "effect" then
                local effect = execute.message.effect
                local pos = execute.message.pos
                pos:sendMagicEffect(effect)
            end

            -- [switch] Transform an item from one to another
            -- Example: switch(Position(100, 100, 7), 1234, 1235)
            if execute.func == "switch" then
                local pos = execute.message.pos
                local old = execute.message.old
                local new = execute.message.new
                if Game.isItemThere(pos, old) then
                    Game.transformItemOnMap(pos, old, new)
                    pos:sendMagicEffect(CONST_ME_POFF)
                elseif Game.isItemThere(pos, new) then
                    Game.transformItemOnMap(pos, new, old)
                    pos:sendMagicEffect(CONST_ME_POFF)
                end
            end

            -- [raid] Start a raid
            -- Example: raid("worldraid")
            if execute.func == "raid" then
                local raid = execute.message
                Game.startRaid(raid)
            end

            -- [teleport] Teleport player to position
            -- Example: teleport(Position(100, 100, 7))
            if execute.func == "teleport" then
                local pos = execute.message
                player:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
                player:teleportTo(pos)
                player:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
            end

            -- [access] Recieve Access
            -- Example: access(12345)
            if execute.func == "access" then
                if execute.message > 65535 then
                    return
                end
                if player:getStorageValue(execute.message) > 0 then
                    -- do nothing
                    player:sendTextMessage(MESSAGE_EVENT_ADVANCE, 'Nothing happens.')
                    return true
                end
                player:sendTextMessage(MESSAGE_EVENT_ADVANCE, 'You have unlocked access to something.')
                fromPosition:sendMagicEffect(CONST_ME_MAGIC_GREEN)
                player:setStorageValue(execute.message, 1)
            end

            -- [learn] Learn Spells
            -- Example: learn("exura")
            if execute.func == "learn" then
                if not player:canLearnSpell(execute.message) then
                    player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The magic is exhausted.")
                    player:getPosition():sendMagicEffect(CONST_ME_POFF)
                    return true
                end
                if player:hasLearnedSpell(execute.message) then
                    player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The magic is exhausted.")
                    player:getPosition():sendMagicEffect(CONST_ME_POFF)
                    return true
                end
                player:learnSpell(execute.message)
                player:sendTextMessage(MESSAGE_EVENT_ADVANCE,
                    "You've absorbed the magic and learned the spell " .. execute.message .. ".")
                player:getPosition():sendMagicEffect(CONST_ME_MAGIC_RED)
            end

            -- [spawn] Spawns a monster
            -- Example: spawn("rat", 5, (Position(100, 100, 7))
            if execute.func == "spawn" then
                local name = execute.message.name
                local count = execute.message.count
                local position = execute.message.position
                if not name or not count or not position then
                    print("Invalid spawn function")
                    return
                end
                for _ = 1, count do
                    Game.createMonster(name, position or player:getPosition())
                end
            end
            return true
        else
            print(err)
        end
    end
end

To make it work you need a new item on your server that has these flags and properties.1719710736425.png
 
Last edited:
It makes a lot more sense with the code. I think you achieved your goal, I know most mappers do not have any idea how to code. Your intention is to make it possible to implement oversimplified code into the map. For your project it works, gesior taking the broad approach here saying for something like tfs-OTA global, it would not be logical. I think overall the idea is cool, I would rather just implement a lua script for more variability, specificity, and tracking changes is much easier, clearer, and I’m not updating a 70 mb file every time I make a commit.

Overall, I still give you 10/10 for the unique idea, I haven’t seen anyone else take this approach.
 
This looks like the moveuse approach of cip, I personally like it.
 
Cool stuff! All of the mappers which dislike programming would like simple mechanics like this in their RME :)
Also: It is indeed no git friendly solution as Gesior said but people like to use no code solutions cause its simple. Also a list of special function-positions would make easy to track all of the triggers available and perhaps find the vulnerable ones in case of something.
 
Last edited:
Cool stuff! All of the mappers which dislike programming would like simple mechanics like this in their RME :)
Also: It is indeed no git friendly solution as Gesior said but people like to use no code solutions cause its simple. Also a list of special function-positions would make easy to track all of the triggers available and perhaps find the vulnerable ones in case of something.
Personally I would be more of a fan of creating notepad inside map editor and having the per say "action id="9999" stuff generate automatically when you connect it to .lua script that it would create in your ots folder but OTARME will probably never get this far.
 
This is the kind of stuff we need to see more of, it will make the prototyping much faster!

Great job, way to think past the normal constraints! Thank you for sharing!
 
Back
Top