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

[How-to] Doors (TFS 1.4+)

Xikini

I whore myself out for likes
Senator
Premium User
Joined
Nov 17, 2010
Messages
6,756
Solutions
578
Reaction score
5,305
Near the end of TFS 1.3 the way doors function in TFS was changed.

Previously, the doors in TFS were setup in pairs. Closed and Open.
Keys were also setup to open doors. (and if the door was closed, it would 'auto lock')

However, this was inaccurate to how doors work in real Tibia.

In order to update TFS to work like real Tibia, the doors needed to be changed.

So in this thread I'll explain how all the doors work.

Untitled.png

Regular Doors

Locked
-> If no ActionId is present on the door, the door remains in the locked state.
-> If an ActionId is present, the door can be unlocked with a key that has the same ActionId as the door.
-> When looking in the map editor, 1st one found is locked. 2nd one found is closed.

Closed
-> If no ActionId is present on the door, the door can be used, and it will transform into it's open counterpart.
-> If an ActionId is present, the door can be used like normal, however a key that has the same ActionId as the door can be used to transform it back into it's locked counterpart.

Open
-> If no ActionId is present on the door, the door can be used, and it will transform into it's closed counterpart.
-> If an ActionId is present, the door can be used like normal, however a key that has the same ActionId as the door can be used to transform it back into it's locked counterpart.

Note; There are 15-20 doors that do not have a Locked version, as no sprite exists.
In these cases, they all act as a regular door (can be open and closed), and have no other special action.
------------------

Untitled.png

House Doors (Doors with the padlock)

-> Acts like a regular door (can be open and closed)

If placed inside a house
-> Only the owner and people on the guest list with door privileges can use the door.
-> When the door is looked at, will display house information. (House Name / Owner / Price (if unowned))

Note; Currently any closed/open door can be used as a house door, however this may change in a future update to match real Tibia house doors.
------------------

ezgif.com-gif-maker (5).gif

Quest Doors (Doors with blinking knob)

Closed

-> If no ActionId is present the door cannot be used.
-> If an ActionId is present the door can be used by players that have been given a matching storage of any value that is not -1.
-> -> When the door is successfully used, the player is moved inside of the doorway.

Open
-> Should never be placed on the map.
-> When a player walks out of the doorway, this door will transform into it's closed counterpart.
------------------

Untitled.png

Level Doors (Doors that are brown, with yellow stripes)

Closed

-> If no ActionId is present the door cannot be used.
-> If an ActionId is present the door can be used by players with a level that are greater or equal to the ActionId of the door.
-> -> When the door is successfully used, the player is moved inside of the doorway.
-> -> The ActionId must be greater then 1000. (ActionId 1001 would require level 1 to access. ActionId 1234 would require level 234 to access.)

Open

-> Should never be placed on the map.
-> When a player walks out of the doorway, this door will transform into it's closed counterpart.

----------

And here is a overview of how doors currently function as a quick reference guide.

ezgif.com-gif-maker (7).gif
------------------

Cheers!
 
Outstanding How-to! I had no clue about the complexity involved in those doors, thank you for this most useful information.
 
with this code, when you use an unlocked door without using the key its get locked, how do i make to get it locked only when player has used the key?
Lua:
    if doors[itemId] then
        if item.actionid == 0 then
            item:transform(doors[itemId])
        else
            player:sendTextMessage(MESSAGE_INFO_DESCR, "It is locked.")
        end
        return true
    end
    return false
end
 
with this code, when you use an unlocked door without using the key its get locked, how do i make to get it locked only when player has used the key?
Lua:
    if doors[itemId] then
        if item.actionid == 0 then
            item:transform(doors[itemId])
        else
            player:sendTextMessage(MESSAGE_INFO_DESCR, "It is locked.")
        end
        return true
    end
    return false
end
This looks like the old doors.lua code.

The newest code doesn't lock the door when it's closed, unless the key was used to close it.
 
yes. i disabled actions.xml doors and enabled doors from scripts rev folder, now all the doors are closed
and this error appears

Code:
Lua Script Error: [Scripts Interface]
C:\Users\slim\Desktop\TFS-1.5-7.X\data\scripts\actions\others\doors.lua:callback
data/global.lua:44: bad argument #1 to 'pairs' (table expected, got nil)
stack traceback:
        [C]: at 0x7ff77dff1340
        [C]: in function 'pairs'
        data/global.lua:44: in function 'contains'
        ...esktop\TFS-1.5-7.X\data\scripts\actions\others\doors.lua:60: in function <...esktop\TFS-1.5-7.X\data\scripts\actions\others\doors.lua:58>

Lua:
table.contains = function(array, value)
    for _, targetColumn in pairs(array) do
        if targetColumn == value then
            return true
        end
    end
    return false
end

this is the whole global.lua
 
yes. i disabled actions.xml doors and enabled doors from scripts rev folder, now all the doors are closed
and this error appears

Code:
Lua Script Error: [Scripts Interface]
C:\Users\slim\Desktop\TFS-1.5-7.X\data\scripts\actions\others\doors.lua:callback
data/global.lua:44: bad argument #1 to 'pairs' (table expected, got nil)
stack traceback:
        [C]: at 0x7ff77dff1340
        [C]: in function 'pairs'
        data/global.lua:44: in function 'contains'
        ...esktop\TFS-1.5-7.X\data\scripts\actions\others\doors.lua:60: in function <...esktop\TFS-1.5-7.X\data\scripts\actions\others\doors.lua:58>

Lua:
table.contains = function(array, value)
    for _, targetColumn in pairs(array) do
        if targetColumn == value then
            return true
        end
    end
    return false
end

this is the whole global.lua

the table.contains function isn't at fault here.

You're missing a table, or don't have the updated doors / keys tables.

 
the table.contains function isn't at fault here.

You're missing a table, or don't have the updated doors / keys tables.

Yes now i see, thank you.
the doors now work different if there is an item(chest or other) standing in front of door the player is pushed to the contrary side
can you help me changing it, so when players close doors and there is an item in front of it the player gonna be pushed above this item
please?
 
Yes now i see, thank you.
the doors now work different if there is an item(chest or other) standing in front of door the player is pushed to the contrary side
can you help me changing it, so when players close doors and there is an item in front of it the player gonna be pushed above this item
please?
Let's move this conversation into a support/request thread.
This isn't a good spot.

Open one up, and clarify what exactly you want to happen.
 
Im tag you
you are right because at least key door now are working as i wanted, but no the normal one
 
Last edited:
Why sometimes "id" is written in lower case "target.itemid" or "item.actionid" and in others "itemId" and "actionIds.levelDoor"? (I'm not sure if i should ask this here or in support...)
Thank you for the guide! It has been very very helpful to me! :)
 
Last edited:
Why sometimes "id" is written in lower case "target.itemid" or "item.actionid" and in others "itemId" and "actionIds.levelDoor"? (I'm not sure if i should ask this here or in support...)
Thank you for the guide! It has been very very helpful to me! :)
The former is shortcuts from 0.x versions of TFS. (item.itemid, item.actionid/item.aid, item.uniqueid/item.uid)
Generally you should try to not use those anymore.

Instead, grab them using the new functions. (item:getId(), item:getActionId(), item:getUniqueId())
 
Near the end of TFS 1.3 the way doors function in TFS was changed.

Previously, the doors in TFS were setup in pairs. Closed and Open.
Keys were also setup to open doors. (and if the door was closed, it would 'auto lock')

However, this was inaccurate to how doors work in real Tibia.

In order to update TFS to work like real Tibia, the doors needed to be changed.

So in this thread I'll explain how all the doors work.

View attachment 64361

Regular Doors

Locked
-> If no ActionId is present on the door, the door remains in the locked state.
-> If an ActionId is present, the door can be unlocked with a key that has the same ActionId as the door.
-> When looking in the map editor, 1st one found is locked. 2nd one found is closed.

Closed
-> If no ActionId is present on the door, the door can be used, and it will transform into it's open counterpart.
-> If an ActionId is present, the door can be used like normal, however a key that has the same ActionId as the door can be used to transform it back into it's locked counterpart.

Open
-> If no ActionId is present on the door, the door can be used, and it will transform into it's closed counterpart.
-> If an ActionId is present, the door can be used like normal, however a key that has the same ActionId as the door can be used to transform it back into it's locked counterpart.

Note; There are 15-20 doors that do not have a Locked version, as no sprite exists.
In these cases, they all act as a regular door (can be open and closed), and have no other special action.
------------------

View attachment 64362

House Doors (Doors with the padlock)

-> Acts like a regular door (can be open and closed)

If placed inside a house
-> Only the owner and people on the guest list with door privileges can use the door.
-> When the door is looked at, will display house information. (House Name / Owner / Price (if unowned))

Note; Currently any closed/open door can be used as a house door, however this may change in a future update to match real Tibia house doors.
------------------

View attachment 64363

Quest Doors (Doors with blinking knob)

Closed

-> If no ActionId is present the door cannot be used.
-> If an ActionId is present the door can be used by players that have been given a matching storage of any value that is not -1.
-> -> When the door is successfully used, the player is moved inside of the doorway.

Open
-> Should never be placed on the map.
-> When a player walks out of the doorway, this door will transform into it's closed counterpart.
------------------

View attachment 64364

Level Doors (Doors that are brown, with yellow stripes)

Closed

-> If no ActionId is present the door cannot be used.
-> If an ActionId is present the door can be used by players with a level that are greater or equal to the ActionId of the door.
-> -> When the door is successfully used, the player is moved inside of the doorway.
-> -> The ActionId must be greater then 1000. (ActionId 1001 would require level 1 to access. ActionId 1234 would require level 234 to access.)

Open

-> Should never be placed on the map.
-> When a player walks out of the doorway, this door will transform into it's closed counterpart.

----------

And here is a overview of how doors currently function as a quick reference guide.

View attachment 64365
------------------

Cheers!
Isn't it the issue we found a while ago when matching real tibia doors behavior against Othire and TFS? I'm glad you sucessfully improved it :D master
 
Isn't it the issue we found a while ago when matching real tibia doors behavior against Othire and TFS? I'm glad you sucessfully improved it :D master
where is are those script? im facing problem editing doors to work as in 7.72 i have edited script a bit and partially works but it has a bug
in doors 2011 closing to 2010 it display message locked even when it's in the table of closeddoors in global.lua
and if there is an spot to falll down the char fall down when closing door if he is standing in the door fram
Lua:
math.randomseed(os.time())
dofile('data/lib/lib.lua')



ropeSpots = {
    384, 418, 8278, 8592, 13189, 14435, 14436, 14857, 15635, 19518, 24621, 24622, 24623, 24624, 26019
}

keys = {
    2086, 2087, 2088, 2089, 2090, 2091, 2092, 10032
}

openDoors = {
    1211, 1214, 1233, 1236, 1251, 1254, 3546, 3537, 4915, 4918, 5100, 5109, 5118, 5127, 5136, 5139, 5142,
    5145, 5280, 5283, 5734, 5737, 6194, 6197, 6251, 6254, 6893, 6902, 7035, 7044, 8543, 8546, 9167, 9170,
    9269, 9272, 10270, 10273, 10470, 10479, 10777, 10786, 12094, 12101, 12190, 12199, 19842, 19851, 19982,
    19991, 20275, 20284
}
closedDoors = {
    1210, 1213, 1232, 1235, 1250, 1253, 3536, 3545, 4914, 4917, 5099, 5108, 5117, 5126, 5135, 5138, 5141,
    5144, 5279, 5282, 5733, 5736, 6193, 6196, 6250, 6253, 6892, 6901, 7034, 7043, 8542, 8545, 9166, 9169,
    9268, 9271, 10269, 10272, 10766, 10785, 10469, 10478, 12093, 12100, 12189, 12198, 19841, 19850, 19981,
    19990, 20274, 20283
}
lockedDoors = {
    1209, 1212, 1231, 1234, 1249, 1252, 3535, 3544, 4913, 4916, 5098, 5107, 5116, 5125, 5134, 5137, 5140,
    5143, 5278, 5281, 5732, 5735, 6192, 6195, 6249, 6252, 6891, 6900, 7033, 7042, 8541, 8544, 9165, 9168,
    9267, 9270, 10268, 10271, 10468, 10477, 10775, 10784, 12092, 12099, 12188, 12197, 19840, 19849, 19980,
    19989, 20273, 20282
}

openExtraDoors = {
    1540, 1542, 6796, 6798, 6800, 6802, 7055, 7057, 13021, 13023, 17236, 17238, 25159, 25161
}
closedExtraDoors = {
    1539, 1541, 6795, 6797, 6799, 6801, 7054, 7056, 13020, 13022, 17235, 17237, 25158, 25160
}

openHouseDoors = {
    1220, 1222, 1238, 1240, 3539, 3548, 5083, 5085, 5102, 5111, 5120, 5129, 5285, 5287, 5516, 5518, 6199,
    6201, 6256, 6258, 6895, 6904, 7037, 7046, 8548, 8550, 9172, 9174, 9274, 9276, 10275, 10277, 10472, 10481,
    18209, 19844, 19853, 19984, 19993, 20277, 20286
}
closedHouseDoors = {
    1219, 1221, 1237, 1239, 3538, 3547, 5082, 5084, 5101, 5110, 5119, 5128, 5284, 5286, 5515, 5517, 6198,
    6200, 6255, 6257, 6894, 6903, 7036, 7045, 8547, 8549, 9171, 9173, 9273, 9275, 10274, 10276, 10471, 10480,
    18208, 19843, 19852, 19983, 19992, 20276, 20285
}
Code:
local positionOffsets = { --/AGREGADO DOOR PRO TFS1.X
    Position(1, 0, 1), -- east
    Position(0, 1, 0), -- south
    Position(1, 0, 0), -- west
    Position(0, 1, 0) -- north
}

--[[
When closing a door with a creature in it findPushPosition will find the most appropriate
adjacent position following a prioritization order.
The function returns the position of the first tile that fulfills all the checks in a round.
The function loops trough east -> south -> west -> north on each following line in that order.
In round 1 it checks if there's an unhindered walkable tile without any creature.
In round 2 it checks if there's a tile with a creature.
In round 3 it checks if there's a tile blocked by a movable tile-blocking item.
In round 4 it checks if there's a tile blocked by a magic wall or wild growth.
]]
local function findPushPosition(creature, round)
    local pos = creature:getPosition()
    for _, offset in ipairs(positionOffsets) do
        local offsetPosition = pos + offset
        local tile = Tile(offsetPosition)
        if tile then
            local creatureCount = tile:getCreatureCount()
            if round == 1 then
                if tile:queryAdd(creature) == RETURNVALUE_NOERROR and creatureCount == 0 then
                    if not tile:hasFlag(TILESTATE_PROTECTIONZONE) or (tile:hasFlag(TILESTATE_PROTECTIONZONE) and creature:canAccessPz()) then
                        return offsetPosition
                    end
                end
            elseif round == 2 then
                if creatureCount > 0 then
                    if not tile:hasFlag(TILESTATE_PROTECTIONZONE) or (tile:hasFlag(TILESTATE_PROTECTIONZONE) and creature:canAccessPz()) then
                        return offsetPosition
                    end
                end
            elseif round == 3 then
                local topItem = tile:getTopDownItem()
                if topItem then
                    if topItem:getType():isMovable() then
                        return offsetPosition
                    end
                end
            else
                if tile:getItemById(ITEM_MAGICWALL) or tile:getItemById(ITEM_WILDGROWTH) then
                    return offsetPosition
                end
            end
        end
    end
    if round < 4 then
        return findPushPosition(creature, round + 1)
    end
end

local door = Action()

function door.onUse(player, item, fromPosition, target, toPosition, isHotkey)
    local itemId = item:getId()
    if table.contains(closedQuestDoors, itemId) then
        if player:getStorageValue(item.actionid) ~= -1 or player:getGroup():getAccess() then
            item:transform(itemId + 1)
            player:teleportTo(toPosition, true)
        else
            player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The door seems to be sealed against unwanted intruders.")
        end
        return true
    elseif table.contains(closedLevelDoors, itemId) then
        if item.actionid > 0 and player:getLevel() >= item.actionid - actionIds.levelDoor or player:getGroup():getAccess() then
            item:transform(itemId + 1)
            player:teleportTo(toPosition, true)
        else
            player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Only the worthy may pass.")
        end
        return true
    elseif table.contains(keys, itemId) then
        local tile = Tile(toPosition)
        if not tile then
            return false
        end
        target = tile:getTopVisibleThing()
        if target.actionid == 0 then
            return false
        end
        if table.contains(keys, target.itemid) then
            return false
        end
        if not table.contains(openDoors, target.itemid) and not table.contains(closedDoors, target.itemid) and not table.contains(lockedDoors, target.itemid) then
            return false
        end
        if item.actionid ~= target.actionid then
            player:sendTextMessage(MESSAGE_STATUS_SMALL, "The key does not match.")
            return true
        end
        local transformTo = target.itemid + 2
        if table.contains(openDoors, target.itemid) then
            transformTo = target.itemid - 2
        elseif table.contains(closedDoors, target.itemid) then
            transformTo = target.itemid - 1
        end
        target:transform(transformTo)
        return true
    elseif table.contains(lockedDoors, itemId) then
        player:sendTextMessage(MESSAGE_INFO_DESCR, "It is locked.")
        return true   
    elseif table.contains(openDoors, itemId) or table.contains(openExtraDoors, itemId) or table.contains(openHouseDoors, itemId) then
        local creaturePositionTable = {}
        local doorCreatures = Tile(toPosition):getCreatures()
        if doorCreatures and #doorCreatures > 0 then
            for _, doorCreature in pairs(doorCreatures) do
                local pushPosition = findPushPosition(doorCreature, 1)
                if not pushPosition then
                    player:sendCancelMessage(RETURNVALUE_NOTENOUGHROOM)
                    return true
                end
                table.insert(creaturePositionTable, {creature = doorCreature, position = pushPosition})
            end
            for _, tableCreature in ipairs(creaturePositionTable) do
                tableCreature.creature:teleportTo(tableCreature.position, true)
            end
        end
    
        item:transform(itemId - 1)
        return true   
    elseif table.contains(closedDoors, itemId) or table.contains(closedExtraDoors, itemId) or table.contains(closedHouseDoors, itemId) then
        item:transform(itemId + 1)
        return true
    end
    return false
end

local doorTables = {keys, openDoors, closedDoors, lockedDoors, openExtraDoors, closedExtraDoors, openHouseDoors, closedHouseDoors, closedQuestDoors, closedLevelDoors}
for _, doors in pairs(doorTables) do
    for _, doorId in pairs(doors) do
        door:id(doorId)
    end
end
door:register()
 
There is no way to prevent a player from using closedQuestDoors if they have a specific storageID?
-> If an ActionId is present the door it cannot be used by players that have been given a matching storage of any value that is not -1.

In Annihilator Quest there is a door preventing the player from entering the room if they have Storage.AnnhilatorDone
1651178412786.png
 
There is no way to prevent a player from using closedQuestDoors if they have a specific storageID?
-> If an ActionId is present the door it cannot be used by players that have been given a matching storage of any value that is not -1.

In Annihilator Quest there is a door preventing the player from entering the room if they have Storage.AnnhilatorDone
View attachment 67406
By default, no.

Quest doors are meant to provide access to an area, not restrict.

You'd have to create a custom script for that door and/or create an exception inside the regular doors script.
 
How would I edit doors.lua to make that happen?

if player does not have storage value x then let them pass
 
There is no way to prevent a player from using closedQuestDoors if they have a specific storageID?
-> If an ActionId is present the door it cannot be used by players that have been given a matching storage of any value that is not -1.

In Annihilator Quest there is a door preventing the player from entering the room if they have Storage.AnnhilatorDone
View attachment 67406
The storage door will let you pass if it have a 1 as a value in the quest storage.
Add a movement event for the tile between the exp door and the quest door and force the tile to check if the player have the annihilation storage in 1, if that's the case, then set the door storage to a number different than 1 (2,3,4 or whatever). That way, the player that haven't done the annihilation will pass as long it doesn't take the item, as soon it takes the item the storage will be set and as soon it steps in front of the quest door, it gonna get a different storage value and won't be able to change it ever again, neither able to open the door anymore.
 
Edit : NVM my question (Im just stupid) ^^

Well this tutorial was freaking amazing! Thanks a lot @Xikini
 
Last edited:
Back
Top