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

Psychonaut's Free Scripting Service [TFS 1.x]

psychonaut

Well-Known Member
Joined
Dec 1, 2017
Messages
75
Solutions
6
Reaction score
56
Like Xikini i'm only gonna take quick scripts and i don't wanna do spells, weapons, raids or monster scripts, websites edits and database queries. Source edits need to be a really small thing(would be very boring to keep cloning TFS repo to test each one). Post in the topic too, no dm.

I wanna reserve this for interesting scripts too and try to detail as much as possible how the script need to work.

Diablo III Rifts idea by Aeronx
Sokoban game, idea by Xikini

ouzZjhG.gif
 
Last edited:
Got pictures

N2hSvge.gif



1K08EIX.gif


Sokoban_ani.gif


I'd love to see a recreation of a sokoban puzzle.

I found a gif to make the explanation of the game mechanics more simple.

The player is in an enclosed space, with 'switches' on the floor, that can only be activated by pushing a box on top of them.
When all switches have been pressed, the game is over and you win.

For tibia's implementation,

Players starting location is important.
Players must push a box, by walking into it. (no diagonals allowed.)
Players cannot stand on a box.
The boxes cannot go on top of other boxes, and can't be pushed into the walls.

A more simple option would be to have the players use the box, and drag the player in the direction the box is moving.

Ways in and out of the puzzle.
- A simple teleport to get inside, with a 10 minute timer before the player is kicked from the area, and the puzzle is reset.
- - (to stop the player from destroying the puzzle by accident while teleporting, restrict player movement for 2 seconds.)
- - (countdown timer per 30 seconds visible to player. Last 30 seconds counts down per 5 seconds, and last 5 seconds counts down per 1 second.)
- - (10:00, 9:30, ... 1:00, 0:30, 0:25, ... 0:10, 0:05, 0:04, ... 0:01)
- If the puzzle is solved, teleport player to a reward area, and of course reset the puzzle.

- To leave the area prior to the timer counting to zero, have the player 'double/triple click' any exterior wall.

Anti-cheating safeguards.
- 1 person limit.
- make sure players cannot logout inside the area.
- if server crash, have a safeguard to remove them from the puzzle area.
- Players cannot move the box with their mouse.
- Basically, make the box an immoveable, unpickupable, undraggable, unwalkable, unopenable, untradeable object.. like a wall, that you can only interact with by smashing your body into the side of it. xD
- Players cannot place any object onto the playing surface. (do not destroy the players griefing item attempts. Simply deny them the opportunity to place the items down from their inventory.)

(I can't think of anything else for player griefing, but you might find something while testing that I'm simply forgetting.)

Anyways, that's my idea.

Cheers,

Xikini

vBqBYbG.png


Exactly like the gif i guess, not with code yet. Missed one spot >
 
Last edited by a moderator:
Script working perfect. I had to add some more functions like spawning diferent monsters, and if your life gets to zero you get teleported to starting zone and your time gets shorter. Also added a boss spawn after all monsters are killed, instead of finishing the event.

Now im battling to save the player's best score (higher level with best time) somewhere to be shown in a talkaction and website >.<

Thank you sir!

EDIT: Making the time shorter isnt working. The only thing that was doing is changing the time it says you accomplish the rift, but the timelimit event happens after 15 minutes anyway. Is there a way to change the time an event will happen that is already in motion?

EDIT2: Thinking on changing the timelimit event and setting a separated event on global to check if getplayerStorageValue(player, os.time) > 15*60 then
so i can add +30 seconds to playerstorage os.time on death.
 
Last edited:
Script working perfect. I had to add some more functions like spawning diferent monsters, and if your life gets to zero you get teleported to starting zone and your time gets shorter. Also added a boss spawn after all monsters are killed, instead of finishing the event.

Now im battling to save the player's best score (higher level with best time) somewhere to be shown in a talkaction and website >.<

Thank you sir!

You can do a select first and then if it's a higher level with a better time you update, so each player would get one row. When life becomes 0/die and these changes i guess that everyone can do as they want, could just set the storages back too.

EDIT: Making the time shorter isnt working. The only thing that was doing is changing the time it says you accomplish the rift, but the timelimit event happens after 15 minutes anyway. Is there a way to change the time an event will happen that is already in motion?

EDIT2: Thinking on changing the timelimit event and setting a separated event on global to check if getplayerStorageValue(player, os.time) > 15*60 then
so i can add +30 seconds to playerstorage os.time on death.

You can try to stop the event and call another with less time.
 
Last edited:
followed your advice and did this:

Code:
stopEvent(rift.timelimit)
local time = os.date("*t", os.time() - player:getStorageValue(rift.storages.playerTime))
local timerless = ((15 * 60) - time) - 30
addEvent(rift.timelimit, timerless , player.uid)

EDIT: Code not working since time is a table.
Tried to use it like this:

Code:
local time = os.date("*t", os.time() - player:getStorageValue(rift.storages.playerTime))
if time.min > 0 then
            time = time.min .. " minutes and " .. time.sec .. " seconds"
        else
            time = time.sec .. " seconds"
        end

But i cannot withdraw 30 seconds from this time, or at least i dont know how. Any ideas? Thanks.

EDIT2:

I did figure out how to do the time thing. But when i try to addEvent, it says that cant load rift.timelimit event. Getting error on last line pasted here.

Code:
stopEvent(rift.timelimit)
local time = os.date("*t", os.time() - player:getStorageValue(rift.storages.playerTime))
if time.min > 0 then
            time = ( time.min * 60 ) + time.sec
        else
            time = time.sec
        end
    --    print(time)
    --    print(time + 30)
local timerless = ((15 * 60) - time) + 30
addEvent(rift.timelimit, timerless, player.uid)
 
Last edited:
You can use os.difftime to do it, but maybe subtract from os.time

player:getStorageValue(rift.storages.playerTime) will return the os.time from when the player started it
 
Code:
function onPrepareDeath(player, corpse, killer, mostDamageKiller, unjustified, mostDamageUnjustified)
if not player then
return true
end
player:setDropLoot(false)
player:teleportTo(Position(908,859,7))
player:addHealth(player:getMaxHealth())
player:sendTextMessage(MESSAGE_INFO_DESCR, 'You died! 30 seconds have been rested to your timer.')
stopEvent(rift.timelimit)
local time = os.date("*t", os.time() - player:getStorageValue(rift.storages.playerTime))
if time.min > 0 then
            time = ( time.min * 60 ) + time.sec
        else
            time = time.sec
        end
        print(time)
        print(time + 30)
local timerless = ((15 * 60) - (time + 30)
addEvent(timeLimit, timerless, player.uid)
return true
end

Fixed.
 
JqCWMz5.png


Using something sparkling on crates, so can't move, put in backpack or anything else. Just walk in and trigger the movement.

WIP

Au7vMdT.gif
 
Last edited by a moderator:
Hard part done. xP

Your missing 1 pressure point bottom left middle btw. xD

Yea, however that's the way i found to avoid grief:

gyrffDx.gif


It removes anything that is not a crate, the spark or that + spot. I don't think that someone would throw a rare item to grief, but it's possible to move them to a room or give back to the player.
Crates , + spot and spark are spawned from a lua table so i can reset it to the original position when the player leave the room.
 
Yea, however that's the way i found to avoid grief:

gyrffDx.gif


It removes anything that is not a crate, the spark or that + spot. I don't think that someone would throw a rare item to grief, but it's possible to move them to a room or give back to the player.
Crates , + spot and spark are spawned from a lua table so i can reset it to the original position when the player leave the room.
You could just 'return false' when they throw the item, no?

Is there no 'onThrow' in 1.x servers?

Oh well. xP

Looks good.
I hope it all goes well from here.
 
You could just 'return false' when they throw the item, no?

Is there no 'onThrow' in 1.x servers?

Oh well. xP

Looks good.
I hope it all goes well from here.

Thanks, return false keeps the item on the ground, i'm using onAddItem.
 
Thanks, return false keeps the item on the ground, i'm using onAddItem.

You could use the event Player: onMoveItem(...) for that.
Lua:
function Player:onMoveItem(item, count, fromPosition, toPosition, fromCylinder, toCylinder)
  if self:isInSokobanGame() then
    return false
  end
  return true
end

Something like that.
Note: You need to activate this function on events.xml also.
 
events/player.lua Player:eek:nMoveItem
easier to do it there and return false than having to register action ids and whatnot

You could use the event Player: onMoveItem(...) for that.
Lua:
function Player:onMoveItem(item, count, fromPosition, toPosition, fromCylinder, toCylinder)
  if self:isInSokobanGame() then
    return false
  end
  return true
end

Something like that.
Note: You need to activate this function on events.xml also.

But every time that the player moves something it would check for the actionid
 
But every time that the player moves something it would check for the actionid
that's already done inside the sources whenever you use onAddItem anyways, every time you move an item it gets the registered event for that tile and executes the move event based on whether you're adding the item or removing it
either way it's not like it'll slow the code down at all
the difference is that it's easier to use onMoveItem than having to register action ids inside the map and movements for multiple games
plus you don't have to check for action ids inside of onMoveItem, like river said you can check if they're inside a game or not (storage value)
 
that's already done inside the sources whenever you use onAddItem anyways, every time you move an item it gets the registered event for that tile and executes the move event based on whether you're adding the item or removing it
either way it's not like it'll slow the code down at all
the difference is that it's easier to use onMoveItem than having to register action ids inside the map and movements for multiple games
plus you don't have to check for action ids inside of onMoveItem, like river said you can check if they're inside a game or not (storage value)

Yea with storage it sounds nice, just did it here, btw wrong button lol
 
I'd like to request a script that would work like this. Whenever a player click on item id, it dissapears and give player storage value.
 
I'd like to request a script that would work like this. Whenever a player click on item id, it dissapears and give player storage value.

Lua:
function onUse(player, item, fromPosition, target, toPosition, isHotkey)
    item:remove()
    player:setStorageValue(storage, value)
    return true
end

bNUfg6q.gif



Code:
    <movevent event="StepIn" fromaid="2412" toaid = "2413" script="tpSokoban.lua" />
    <movevent event="StepIn" itemid="10846" script="tpSokoban.lua" />
Lua:
local crates = {{x = 996, y = 968, z = 7}, {x = 997, y = 969, z = 7}, {x = 997, y = 970, z = 7}, {x = 994, y = 972, z = 7}, {x = 996, y = 972, z = 7}
, {x = 997, y = 972, z = 7}, {x = 998, y = 972, z = 7}}
sokoban = {spot = 11993, crateId = 12284, gameStorage = 3153, storage = 3152, specialAid = 2413, thingsUid = {}}

local levels = {[2412] = true}

local groundId = 10846
local maxSpots = 7
local tpBack = {x = 1022, y = 1015, z = 7}

function onStepIn(creature, item, position, fromPosition)
    local player = creature:getPlayer()
    if not player then
       return false
    end
    if item.actionid ~= sokoban.specialAid and item.itemid ~= groundId and not levels[item.actionid] then
        return false
    end
     
    sokobanSetLevel(player, item.actionid, 2412, crates, 7)
 
    if runSokoban(player, item, fromPosition, position) == 0 then
        sokobanStop(player)
        doTeleportThing(player.uid, tpBack)
        player:say("Congratulations, you won!", TALKTYPE_MONSTER_SAY)
    end
 
   return true
end

Go to events and change onMoveItem in the xml to enabled="1"
Lua:
function Player:onMoveItem(item, count, fromPosition, toPosition, fromCylinder, toCylinder)
    if self:getStorageValue(sokoban.gameStorage) == 1 then
        return false
    end
    return true
end

lib.lua
Code:
dofile('data/lib/sokobanGame.lua')

Lua:
function runSokoban(player, item, fromPosition, position, specialAid)
    if player:getStorageValue(sokoban.gameStorage) ~= 1 then
        return
    end
    if (fromPosition.x == position.x+1 and fromPosition.y == position.y+1) then
        doTeleportThing(player.uid, fromPosition, true)
        return
    elseif (fromPosition.x == position.x-1 and fromPosition.y == position.y+1) then
        doTeleportThing(player.uid, fromPosition, true)
        return
    elseif (fromPosition.x == position.x+1 and fromPosition.y == position.y-1) then
        doTeleportThing(player.uid, fromPosition, true)
        return
    elseif (fromPosition.x == position.x-1 and fromPosition.y == position.y-1) then
        doTeleportThing(player.uid, fromPosition, true)
        return
    end
 
    if (item.actionid ~= sokoban.specialAid) then
        return
    end

    local nextPos
    if (fromPosition.x < position.x) then
        nextPos = {x = (position.x+1), y = position.y, z = position.z}
    elseif (fromPosition.x > position.x) then
        nextPos = {x = (position.x-1), y = position.y, z = position.z}
    elseif (fromPosition.y < position.y) then
        nextPos = {x = (position.x), y = position.y+1, z = position.z}
    elseif (fromPosition.y > position.y) then
        nextPos = {x = (position.x), y = position.y-1, z = position.z}
    else
        return
    end
 
    if getTileThingByTopOrder(nextPos, 1).itemid ~= 0 and getTileThingByTopOrder(nextPos, 1).itemid ~= sokoban.spot or getTileItemById(nextPos, sokoban.crateId).itemid == sokoban.crateId or getTileThingByTopOrder(nextPos, 2).itemid ~= 0  or getTileThingByTopOrder(nextPos, 3).itemid ~= 0 then
        doTeleportThing(player.uid, fromPosition, true)
        return
    end
 
    if getTileThingByTopOrder(nextPos, 1).itemid == sokoban.spot then
        player:setStorageValue(sokoban.storage, player:getStorageValue(sokoban.storage) - 1)
    elseif getTileThingByTopOrder(position, 1).itemid == sokoban.spot and getTileThingByTopOrder(nextPos, 1).itemid ~= sokoban.spot then
        player:setStorageValue(sokoban.storage, player:getStorageValue(sokoban.storage) + 1)
    end
 
    doTeleportThing(item.uid, nextPos, true)
    doTeleportThing(getTileItemById(position, 8047).uid, nextPos, true)
    return player:getStorageValue(sokoban.storage)
end

function sokobanStop(player) 
    for _, someThing in ipairs(sokoban.thingsUid) do
        doRemoveItem(someThing.uid)
        sokoban.thingsUid[_] = nil
    end
    player:setStorageValue(sokoban.storage, 0)
    player:setStorageValue(sokoban.gameStorage, 0)
end

function sokobanBuildCrates(player, crates, specialAid)
    for _, crate in ipairs(crates) do
        if getTileItemById(crate, sokoban.spot).itemid == sokoban.spot then
            player:setStorageValue(sokoban.storage, player:getStorageValue(sokoban.storage) - 1)
        end
        local c = doCreateItem(sokoban.crateId, 9500, crate)
        local spark = doCreateItem(8047, 0, crate)
        Item(c):setActionId(specialAid)
        sokoban.thingsUid[#sokoban.thingsUid + 1] = Item(c)
        sokoban.thingsUid[#sokoban.thingsUid + 1] = Item(spark)
    end
end

function sokobanSetLevel(player, itemActionid, tpActionid, crates, spots)
    if itemActionid ~= tpActionid then
        return
    end
    player:setStorageValue(sokoban.storage, spots)
    sokobanBuildCrates(player, crates, sokoban.specialAid)
    player:setStorageValue(sokoban.gameStorage, 1)     
    return true
end

Put restrictions as you want, you can do an addEvent to remove the player with time limit, or remove the TP, do a command or npc to talk if the player failed, just call sokobanStop(player) for that, it will remove everything and set storages back.

2412 as set in movements.xml is the actionid for the tp. 2413 need to be the same as specialAid in the script, it's the crate, 10846 is the ground id(don't need to set the actionId for every tile, just put the id there).
 
Last edited by a moderator:
Back
Top