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

Lua Teleport player to random place in area

clouf

Member
Joined
Jul 5, 2012
Messages
162
Reaction score
23
Hello, i have problem in making script. What i want is to teleport player to random place in area, but it can't be protection zone and must be walkable because my script is teleport players into water or walls...

Lua:
function onSay(player, words, param)

 if not player:getTile():hasFlag(TILESTATE_PROTECTIONZONE) then
        player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can only use this command inside Protection Zone!")
        player:getPosition():sendMagicEffect(CONST_ME_POFF)
        return false
    end

local from = {x=1075,y=1061}
local to = {x=1221,y=1186}
local setZ = 7
local setX = math.random(from.x,to.x)
local setY = math.random(from.y,to.y)
local position = {x = setX,y = setY,z = setZ}

player:teleportTo(position)
player:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
    return true
end
 
Hello, i have problem in making script. What i want is to teleport player to random place in area, but it can't be protection zone and must be walkable because my script is teleport players into water or walls...

Lua:
function onSay(player, words, param)

 if not player:getTile():hasFlag(TILESTATE_PROTECTIONZONE) then
        player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can only use this command inside Protection Zone!")
        player:getPosition():sendMagicEffect(CONST_ME_POFF)
        return false
    end

local from = {x=1075,y=1061}
local to = {x=1221,y=1186}
local setZ = 7
local setX = math.random(from.x,to.x)
local setY = math.random(from.y,to.y)
local position = {x = setX,y = setY,z = setZ}

player:teleportTo(position)
player:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
    return true
end
Untested, can be written cleaner, but should work
Lua:
local config = {
    from = {x = 1075, y = 1061, z = 7},
    to = {x = 1221, y = 1186, z = 7}
}

function onSay(player, words, param)

    if not player:getTile():hasFlag(TILESTATE_PROTECTIONZONE) then
        player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can only use this command from inside a protection zone!")
        player:getPosition():sendMagicEffect(CONST_ME_POFF)
        return false
    end
   
    local position, attempts = nil, 0  
    repeat  
        local _position = Position(
            math.random(config.from.x, config.to.x),
            math.random(config.from.y, config.to.y),
            math.random(config.from.z, config.to.z)
        )  
       
        local tile = Tile(_position)      
        if not tile:hasFlag(TILESTATE_BLOCKSOLID) then
            position = _position
        end
       
        attempts = attempts + 1
       
    until position ~= nil or attempts == 15
   
    if not position then
        player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Failed to teleport, please try again!")
        player:getPosition():sendMagicEffect(CONST_ME_POFF)
        return false
    end

    player:teleportTo(position)
    player:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
    return true
   
end
 
Untested, can be written cleaner, but should work
Lua:
local config = {
    from = {x = 1075, y = 1061, z = 7},
    to = {x = 1221, y = 1186, z = 7}
}

function onSay(player, words, param)

    if not player:getTile():hasFlag(TILESTATE_PROTECTIONZONE) then
        player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can only use this command from inside a protection zone!")
        player:getPosition():sendMagicEffect(CONST_ME_POFF)
        return false
    end
  
    local position, attempts = nil, 0 
    repeat 
        local _position = Position(
            math.random(config.from.x, config.to.x),
            math.random(config.from.y, config.to.y),
            math.random(config.from.z, config.to.z)
        ) 
      
        local tile = Tile(_position)     
        if not tile:hasFlag(TILESTATE_BLOCKSOLID) then
            position = _position
        end
      
        attempts = attempts + 1
      
    until position ~= nil or attempts == 15
  
    if not suitableTile then
        player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Failed to teleport, please try again!")
        player:getPosition():sendMagicEffect(CONST_ME_POFF)
        return false
    end

    player:teleportTo(position)
    player:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
    return true
  
end
Thanks, what about check if there is no other players around that tile?
 
Thanks, what about check if there is no other players around that tile?
Once again, there is definitely a more logical way to do it, but it will solve your issue for now.

Having to loop through neighbours makes the loop more expensive, and you will now get more failed attempts, so its probably either better to hardcode co-ordinates in (to prevent the need for a loop), or raise the config.maxAttempts higher if its failing alot....

(untested again)
Lua:
local config = {
    from = {x = 1075, y = 1061, z = 7},
    to = {x = 1221, y = 1186, z = 7},
    maxAttempts = 25
}

local neighbours = {
    {x = 0, y = -1},
    {x = 1, y = -1},
    {x = 1, y = 0},
    {x = 1, y = 1},
    {x = 0, y = 1},
    {x = -1, y = 1},
    {x = -1, y = 0},
    {x = -1, y = -1},
}

function onSay(player, words, param)

    if not player:getTile():hasFlag(TILESTATE_PROTECTIONZONE) then
        player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can only use this command from inside a protection zone!")
        player:getPosition():sendMagicEffect(CONST_ME_POFF)
        return false
    end
 
    local position, attempts = nil, 0
    repeat
        local _position = Position(
            math.random(config.from.x, config.to.x),
            math.random(config.from.y, config.to.y),
            math.random(config.from.z, config.to.z)
        )
     
        local tile = Tile(_position)    
        if tile and not tile:hasFlag(TILESTATE_BLOCKSOLID) then
     
            local creatureFound = false
            for _, offset in ipairs(neighbours) do
                local neighbourPosition = Position(
                    _position.x + offset.x,
                    _position.y + offset.y,
                    _position.z
                )
                local neighbourTile = Tile(neighbourPosition)
                if neighbourTile and neighbourTile:getTopCreature() then
                    creatureFound = true
                    break
                end
            end
         
            if not creatureFound then
                position = _position
            end
        end
     
        attempts = attempts + 1
     
    until position ~= nil or attempts == config.maxAttempts
 
    if not suitableTile then
        player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Failed to teleport, please try again!")
        player:getPosition():sendMagicEffect(CONST_ME_POFF)
        return false
    end

    player:teleportTo(position)
    player:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
    return true
 
end
 
Once again, there is definitely a more logical way to do it, but it will solve your issue for now.

Having to loop through neighbours makes the loop more expensive, and you will now get more failed attempts, so its probably either better to hardcode co-ordinates in (to prevent the need for a loop), or raise the config.maxAttempts higher if its failing alot....

(untested again)
Lua:
local config = {
    from = {x = 1075, y = 1061, z = 7},
    to = {x = 1221, y = 1186, z = 7},
    maxAttempts = 25
}

local neighbours = {
    {x = 0, y = -1},
    {x = 1, y = -1},
    {x = 1, y = 0},
    {x = 1, y = 1},
    {x = 0, y = 1},
    {x = -1, y = 1},
    {x = -1, y = 0},
    {x = -1, y = -1},
}

function onSay(player, words, param)

    if not player:getTile():hasFlag(TILESTATE_PROTECTIONZONE) then
        player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can only use this command from inside a protection zone!")
        player:getPosition():sendMagicEffect(CONST_ME_POFF)
        return false
    end
 
    local position, attempts = nil, 0
    repeat
        local _position = Position(
            math.random(config.from.x, config.to.x),
            math.random(config.from.y, config.to.y),
            math.random(config.from.z, config.to.z)
        )
    
        local tile = Tile(_position)   
        if tile and not tile:hasFlag(TILESTATE_BLOCKSOLID) then
    
            local creatureFound = false
            for _, offset in ipairs(neighbours) do
                local neighbourPosition = Position(
                    _position.x + offset.x,
                    _position.y + offset.y,
                    _position.z
                )
                local neighbourTile = Tile(neighbourPosition)
                if neighbourTile and neighbourTile:getTopCreature() then
                    creatureFound = true
                    break
                end
            end
        
            if not creatureFound then
                position = _position
            end
        end
    
        attempts = attempts + 1
    
    until position ~= nil or attempts == config.maxAttempts
 
    if not suitableTile then
        player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Failed to teleport, please try again!")
        player:getPosition():sendMagicEffect(CONST_ME_POFF)
        return false
    end

    player:teleportTo(position)
    player:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
    return true
 
end
I was thinking about use getSpectators function instead.
 
I was thinking about use getSpectators function instead.
Sure but if you do it that way, you still need the main loop to find a correct tile.

Make sure you only call the getSpectators once and store the results, before you enter the loop. Otherwise you will unnecessarily call getSpectators multiple times.

And there becomes a point where checking 8 neighbour tiles may be better than checking against 50 player positions... all depends on how many people can be in the arena, and how big the getSpectators radiuses are.

p.s also make sure they haven't already been teleported there...otherwise they can spam the whole talkaction.
 
Sure but if you do it that way, you still need the main loop to find a correct tile.

Make sure you only call the getSpectators once and store the results, before you enter the loop. Otherwise you will unnecessarily call getSpectators multiple times.

And there becomes a point where checking 8 neighbour tiles may be better than checking against 50 player positions... all depends on how many people can be in the arena, and how big the getSpectators radiuses are.

p.s also make sure they haven't already been teleported there...otherwise they can spam the whole talkaction.
Understand, as i said i need talkaction to kick(teleport) out from temple on war server. I want to do it so other players not gonna feel like some1 teleported next to them, or avoid come back straight after player dead. I was thinking about make possibility to teleport for guilds/partys also but no have idea how to make it this way.
 
Understand, as i said i need talkaction to kick(teleport) out from temple on war server. I want to do it so other players not gonna feel like some1 teleported next to them, or avoid come back straight after player dead. I was thinking about make possibility to teleport for guilds/partys also but no have idea how to make it this way.
I can only help you so much with the spare time i have, good luck!!

Hopefully someone else can continue to help improve your script further :)
 
Last edited:
not tested.

Lua:
local config = {
    from = {x = 1075, y = 1061, z = 7},
    to = {x = 1221, y = 1186, z = 7},
    maxAttempts = 25,
    teleportRadius = 2 -- Sets the scan distance around the teleport point
}

local neighbours = {
    {x = 0, y = -1}, {x = 1, y = -1}, {x = 1, y = 0}, {x = 1, y = 1},
    {x = 0, y = 1}, {x = -1, y = 1}, {x = -1, y = 0}, {x = -1, y = -1}
}

function isPositionSafe(position, player)
    local tile = Tile(position)
    if not tile or tile:hasFlag(TILESTATE_PROTECTIONZONE) or tile:hasFlag(TILESTATE_BLOCKSOLID) then
        return false
    end

    local spectators = Game.getSpectators(position, false, true, config.teleportRadius, config.teleportRadius, config.teleportRadius, config.teleportRadius)
    for i = 1, #spectators do
        local spectator = spectators[i]
        if spectator:isPlayer() then
            if spectator:getId() ~= player:getId() then
                -- Checks if players are from different guilds or not in the same party
                local sameGuild = player:getGuild() and player:getGuild() == spectator:getGuild()
                local sameParty = player:getParty() and spectator:getParty() and player:getParty() == spectator:getParty()
                if not sameGuild and not sameParty then
                    return false
                end
            end
        end
    end

    return true
end

function getRandomSafePosition(player)
    local attempts = 0
    repeat
        local randomPosition = Position(
            math.random(config.from.x, config.to.x),
            math.random(config.from.y, config.to.y),
            config.from.z -- The height (z) is constant in this case
        )

        if isPositionSafe(randomPosition, player) then
            return randomPosition
        end

        attempts = attempts + 1
    until attempts >= config.maxAttempts

    return nil
end

function onSay(player, words, param)
    if not player:getTile():hasFlag(TILESTATE_PROTECTIONZONE) then
        player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can only use this command from inside a protection zone!")
        player:getPosition():sendMagicEffect(CONST_ME_POFF)
        return false
    end

    local position = getRandomSafePosition(player)
    if not position then
        player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Failed to teleport, please try again!")
        player:getPosition():sendMagicEffect(CONST_ME_POFF)
        return false
    end

    -- Check if the position is not occupied by another player
    local tile = Tile(position)
    if tile and tile:getTopCreature() and tile:getTopCreature():isPlayer() then
        player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The chosen location is not safe, please try again!")
        player:getPosition():sendMagicEffect(CONST_ME_POFF)
        return false
    end

    player:teleportTo(position)
    position:sendMagicEffect(CONST_ME_TELEPORT)
    return true
end
 
not tested.

Lua:
local config = {
    from = {x = 1075, y = 1061, z = 7},
    to = {x = 1221, y = 1186, z = 7},
    maxAttempts = 25,
    teleportRadius = 2 -- Sets the scan distance around the teleport point
}

local neighbours = {
    {x = 0, y = -1}, {x = 1, y = -1}, {x = 1, y = 0}, {x = 1, y = 1},
    {x = 0, y = 1}, {x = -1, y = 1}, {x = -1, y = 0}, {x = -1, y = -1}
}

function isPositionSafe(position, player)
    local tile = Tile(position)
    if not tile or tile:hasFlag(TILESTATE_PROTECTIONZONE) or tile:hasFlag(TILESTATE_BLOCKSOLID) then
        return false
    end

    local spectators = Game.getSpectators(position, false, true, config.teleportRadius, config.teleportRadius, config.teleportRadius, config.teleportRadius)
    for i = 1, #spectators do
        local spectator = spectators[i]
        if spectator:isPlayer() then
            if spectator:getId() ~= player:getId() then
                -- Checks if players are from different guilds or not in the same party
                local sameGuild = player:getGuild() and player:getGuild() == spectator:getGuild()
                local sameParty = player:getParty() and spectator:getParty() and player:getParty() == spectator:getParty()
                if not sameGuild and not sameParty then
                    return false
                end
            end
        end
    end

    return true
end

function getRandomSafePosition(player)
    local attempts = 0
    repeat
        local randomPosition = Position(
            math.random(config.from.x, config.to.x),
            math.random(config.from.y, config.to.y),
            config.from.z -- The height (z) is constant in this case
        )

        if isPositionSafe(randomPosition, player) then
            return randomPosition
        end

        attempts = attempts + 1
    until attempts >= config.maxAttempts

    return nil
end

function onSay(player, words, param)
    if not player:getTile():hasFlag(TILESTATE_PROTECTIONZONE) then
        player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can only use this command from inside a protection zone!")
        player:getPosition():sendMagicEffect(CONST_ME_POFF)
        return false
    end

    local position = getRandomSafePosition(player)
    if not position then
        player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Failed to teleport, please try again!")
        player:getPosition():sendMagicEffect(CONST_ME_POFF)
        return false
    end

    -- Check if the position is not occupied by another player
    local tile = Tile(position)
    if tile and tile:getTopCreature() and tile:getTopCreature():isPlayer() then
        player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The chosen location is not safe, please try again!")
        player:getPosition():sendMagicEffect(CONST_ME_POFF)
        return false
    end

    player:teleportTo(position)
    position:sendMagicEffect(CONST_ME_TELEPORT)
    return true
end
This is exactly what i said to try and avoid, repeated calls of getSpectators, but it will definitely work!
 
Back
Top