• 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 Xikini's Free Scripting Service TFS 1.4.2

Xikini

I whore myself out for likes
Senator
Joined
Nov 17, 2010
Messages
6,814
Solutions
585
Reaction score
5,383
Request whatever you want, within reason.

Please do not request for things that require source editing or database queries.

------------------------------------
If I've reacted to your post, it means I've read it. ๐Ÿ‘๐Ÿ˜๐Ÿคฃ๐Ÿ˜ฒ๐Ÿ™๐Ÿ˜ก๐Ÿค”๐Ÿ˜‰

------------------------------------
Completed Requests

A boss that randomly creates effects on tiles, that when stepped on, gives the player increased damage/mitigation to that boss.
Kill monster and gain X% extra loot chance or Y% extra experience for Z minutes
A pack of 6 useful simple scripts.
-> (1) Simple quest npc
-> (2,3,4,5) use levers in specific order / stand with 4 players / use 4 objects / place items -> to open wall
-> (6) use lever to add/remove/replace objects
Wave type spell, with multiple effects
-> Same spell, but with default combat system
House market system. (owner places price on blackboard and object on the ground, other players buy with a lever)
Respawn System (closest anchor (like darks souls bonfire) / tavern / temple)
Vocation Death Protection (protect the noobs!)
Spell that shows area it will damage, before it strikes.
RAID SYSTEM - rebuilt in Lua, with some more features.
Modal Window - Teleport Item with saving/deleting of positions
Show top 3 Online Players (outfit, name, level) (as monsters, in set positions)
onLook function showing kill count of player.
Modal Window - Use Item -> choose reward.
Talkaction - !backpacks - to buy backpacks (or single items) anywhere. uses bank & current money on players
Quest/Event? Turn a bunch of objects with a monster, spawn portal.
Spawn Monsters, after they've all been killed, give global increased experience/loot in specific area's
Evolving Weapons - Kill X amount of specific creatures, evolve weapon and gain extra damage to those creatures.
Random Portals spawn at XX:XX time(s). (can exit 'off' portals, if you have the storage.)
Monster that adjusts speed based on target
Monster that increases damage output based on # of players nearby
Experience recovery Item. Die -> Use Item -> gain % of lost experience
Character Snapshot - Keeps track of all skills & level, for resetting later.
Fire Dagger - Physical Damage Melee Weapon, with % added damage
Players in specific level ranges don't lose skills/loot/experience, when they die.
Multiclient Limit Check - with admin account not being counted towards limit
Capacity Increasing Items
Upgradeable Protection Amulet - 10% to all elements
-> upgrade amulet, but for all items.
onKill - give reward to all players who dealt damage
-> example: give reward based on damage contribution
Quest Book - Record your quest progress into a book.
Stat System, using modal windows
Holy Tible (POI) - Require Item to use teleport tiles
Item Upgrade System
Skill Stages
-> individual stages for each skill type
-> talkaction to check rates (by @Extrodus)
Random Reward Item - gives different rewards based on vocation
Bounty Hunter System
NPC & Player Walk System (Follow Nodes to destination)
Health/Mana gain permanent - limited use items

------------------------------------
Support

If you have an issue with one of my scripts, I will attempt to help you, but not in this thread.
Make a thread in the support board.
Ensure to follow all rules of the support board.
Without all necessary information it's impossible to help you.

------------------------------------
I will only be scripting for TFS 1.4.2

Not TFS 1.1 / 1.2
Not OTServBR / OTX
and certainly not TFS 0.4

When requesting a script, don't ask for "this script I saw on super popular OT".

I don't care where the idea came from.
I don't want to see a video of the script in action.

Just describe what the script is supposed to do, and I'll try to make it.

Any script that I make in response to a request from this thread will be shared publicly, here, in this thread.

I'm not going to make anything in private, so post your request in this thread only.
Please, for the love of god, don't pm me asking to make a script.
I will actually add you to my ignore list if you do that.
--------------

Anyways!

Thanks for coming by and checking the thread out.
If you think there is a better way to script something, feel free to let me know.
I'm here to learn.

Cheers,

Xikini
---------

P.S.
I've been doing free scripting service's on/off for awhile.
And if you want to see the previous threads, go check them here.

 
Last edited by a moderator:
hi i am building an ot 1.4.2, futuristic and i would like a script for gift boxes. so when you open a gift box you got modalwindow with choises like.

1. boost exp.
2. xxx item,
3. xxx money.

thanks!
We basically built this exact same thing a few days ago.


For the experience boost, just make it an item that gives the experience boost.
Post automatically merged:

I just had some time to check this. Man such a nice script, I just finished reading it and it is just perfect.
A little question, if I set forceAfterAttempts = -1 and chance = 10000, this means it will have a 100% of chance to occur at specificTimes?

ps. I haven't tested it too much but I will definitely post back the feedback asap. ^^

@Xikini I updated this post with a second request if it is possible. It is a really simple one based on this post:
I will just quote myself:

bandicam2024-02-0509-36-42-890-ezgif.com-video-to-gif-converter.gif
Untitled.png
Finished it offline.
Kinda of hard to make a video for it xD

Made it take money from player and bank.

!backpack OR !backpack name
!backpack confirm -> confirms the purchase details
!backpack list

checks for money and inventory space / capacity

data/scripts/talkaction_backpacks.lua
Lua:
local backpacks = {
    ["brown"] = {itemId = 1988, price = 20},
    ["crown"] = {itemId = 10522, price = 10000}
}

-- END OF CONFIG

local playerConfirmation = {}

local function getItemNameString(item)
    return item:getNameDescription(item:getSubType(), true)
end

local talkaction = TalkAction("!backpack")

function talkaction.onSay(player, words, param, type)
   
    if param == "" then
        param = "brown" -- aka: default backpack
    end
   
    param = param:lower()
    local playerId = player:getId()
    local index = playerConfirmation[playerId]
    playerConfirmation[playerId] = nil   
   
    if param == "confirm" and index then
        if player:getTotalMoney() < backpacks[index].price then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "You do not have sufficient funds to purchase this item.")
            return false
        end
        local backpack = Game.createItem(backpacks[index].itemId, 1)
        if player:addItemEx(backpack, false) ~= RETURNVALUE_NOERROR then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "You do not have sufficient room or capacity to receive this item.")
            return false
        end
        player:removeTotalMoney(backpacks[index].price)
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "You have purchased " .. getItemNameString(backpack) .. " for " .. backpacks[index].price .. " gold.")
        return false
    elseif param == "list" then
        local text = ""
        for k, v in pairs(backpacks) do
            if text ~= "" then
                text = text .. "\n"
            end
            text = text .. k .. " backpack - " .. v.price .. " gold"
        end
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Here is the list of backpacks.")
        player:showTextDialog(1988, "List of backpacks:\n\n" .. text)
        return false
    end
   
    index = backpacks[param]
   
    if not index then
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "There is no backpack named " .. param .. ".")
        return false
    end
   
    playerConfirmation[playerId] = param
    player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Would you like to purchase a " .. param .. " backpack for " .. index.price .. " gold?")
    player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "To finalise the purchase use !backpack confirm")
    return false
end

talkaction:separator(" ")
talkaction:register()
Post automatically merged:

Can you make a script where 1 monster to rotate the Dangerous Apparatus onStep on tiles next to it, to face next direction till we make it reposition correct, until it all face the Strange Machine. You have to reposition all the Dangerous Apparatus in 15 minutes or you get kicked of the room. After repositioning all the Dangerous Apparatus then a tp open for 2 min to next mission.After 15 minutes all Dangerous Apparatus shuffle directions again when all players kicked or failed.
thank you In advance

latest

bandicam2024-02-0513-00-35-262-ezgif.com-video-to-gif-converter.gif

Untitled.png

data/scripts/Madusa_Request.lua
Lua:
local config = {
    actionId = 45036,
    rotateMonster = "cat",
    rotateItems = {7163, 7166, 7165, 7164}, -- needs to have 4 items (south, west, north, east)
    teleport = {
        duration = 120, -- seconds
        spawnPosition = Position(965, 1019, 7),
        destination = Position(1000, 1000, 7) -- reward area?
    },
    requiredRotatedItems = {
        {itemId = 7166, position = Position(973, 1008, 7)},
        {itemId = 7163, position = Position(967, 1008, 7)},
        {itemId = 7164, position = Position(967, 1014, 7)},
        {itemId = 7163, position = Position(972, 1014, 7)}, -- if all of these are correct items, then open portal
        {itemId = 7166, position = Position(972, 1018, 7)},
        {itemId = 7165, position = Position(962, 1018, 7)},
        {itemId = 7165, position = Position(962, 1009, 7)},
        {itemId = 7166, position = Position(962, 1004, 7)}
    },
    area = {
        from = Position(957, 1004, 7), -- the event area
        to = Position(973, 1019, 7)
    },
    lever = {
        left = 1945,
        right = 1946,
        playerArea = {
            from = Position(982, 1013, 7), -- area around lever
            to = Position(988, 1019, 7)
        },
        position = Position(985, 1016, 7),
        destination = Position(957, 1019, 7)
    },
    kickTimer = 900, -- seconds
    kickPosition = Position(1000, 1000, 7)
}

-- END OF CONFIG

local event_players = {}
local event_kickPlayers = -1

local function rotateItem(item, rotateRandomDirection)
    local itemId = item:getId()
    if rotateRandomDirection then
        itemId = config.rotateItems[math.random(#config.rotateItems)]
    else
        for i = 1, #config.rotateItems do
            if config.rotateItems[i] == itemId then
                if i == 4 then
                    i = 0
                end
                itemId = config.rotateItems[i + 1]
                break
            end
        end
    end
    item:transform(itemId)
end

local function randomRotateAllItemsInArea(from, to)
    for x = from.x, to.x do
        for y = from.y, to.y do
            for z = from.z, to.z do
                local position = Position(x, y, z)
                local tile = Tile(position)
                if tile then
                    for _, itemId in pairs(config.rotateItems) do
                        local rotatableItem = tile:getItemById(itemId)
                        if rotatableItem then
                            rotateItem(rotatableItem, true)
                            break
                        end
                    end
                end
            end
        end
    end
end

local function isAllObjectsRotatedCorrectly()
    for k, v in pairs(config.requiredRotatedItems) do
        if not Tile(v.position):getItemById(v.itemId) then
            return false
        end
    end
    return true
end

local function createTimedPortal(position, destination, timer, portalSpawned)
    if not portalSpawned then
        local teleport = Game.createItem(1387, 1, position)
        teleport:setDestination(destination)
        teleport:setActionId(config.actionId)
        portalSpawned = true
    end
    local portal = Tile(position):getItemById(1387)
    if portal then
        if timer % 5 == 0 and timer ~= 0 then
            for _, creature in pairs(Game.getPlayers()) do
                creature:say(timer, TALKTYPE_MONSTER_SAY, false, nil, position)
                break
            end
        end
        if timer > 0 then
            addEvent(createTimedPortal, 1000, position, destination, timer - 1, portalSpawned)
            return
        end
        portal:remove()
    end
end

local function kickEventPlayers()
    for k, v in pairs(event_players) do
        local player = Player(k)
        if player then
            player:teleportTo(config.kickPosition)
        end
    end
end

local function resetEvent(forceStop)
    if forceStop then
        kickEventPlayers()
        event_players = {}
    end
    
    if next(event_players) == nil then
        if event_kickPlayers ~= -1 then
            stopEvent(event_kickPlayers)
        end
        event_kickPlayers = -1
    end    
    
    local portal = Tile(config.teleport.spawnPosition):getItemById(1387)
    if portal then
        portal:remove()
    end
    
    randomRotateAllItemsInArea(config.area.from, config.area.to)
    
    local lever = Tile(config.lever.position):getItemById(config.lever.right)
    if lever then
        lever:transform(config.lever.left)
    end
end

local function sendMessageToEventPlayers(message, messageType)   
    for _player, _ in pairs(event_players) do
        local player = Player(_player)
        if player then
            player:sendTextMessage(messageType, message)
        end
    end
end


local action = Action()

function action.onUse(player, item, fromPosition, target, toPosition, isHotkey)
    if item:getId() == config.lever.right then
       
        return
    end
    item:transform(config.lever.right)
   
    -- get all players, teleport, and register
    local spectators = Game.getSpectators(item:getPosition(), false, true)
    for i = 1, #spectators do
        local spectator = spectators[i]
        if spectator:getPosition():isInRange(config.lever.playerArea.from, config.lever.playerArea.to) then
            spectator:teleportTo(config.lever.destination)
            event_players[spectator:getId()] = 0
        end
    end
   
    event_kickPlayers = addEvent(resetEvent, config.kickTimer * 1000, true)
    return true
end

action:aid(config.actionId)
action:register()


local moveevent = MoveEvent()

function moveevent.onStepIn(creature, item, position, fromPosition)
    local itemId = item:getId()
    if itemId == config.lever.left or itemId == config.lever.right then
        return true
    end       

    if itemId == 1387 then
        if not creature:isPlayer() then
            return true
        end
        event_players[creature:getId()] = nil
        if next(event_players) == nil then
            resetEvent()
        end
        return true
    end

    if not creature:isMonster() then
        return true
    end
   
    local monsterName = creature:getName():lower()
    if config.rotateMonster:lower() ~= monsterName then
        return true
    end
   
    if Tile(config.teleport.spawnPosition):getItemById(1387) then
        return true
    end

    for direction = 0, 3 do
        local checkPosition = Position(position)
        checkPosition:getNextPosition(direction)
        local tile = Tile(checkPosition)
        if tile then
            for _, itemId in pairs(config.rotateItems) do
                local rotatableItem = tile:getItemById(itemId)
                if rotatableItem then
                    rotateItem(rotatableItem)
                    break
                end
            end
        end
    end
   
    if not isAllObjectsRotatedCorrectly() then
        return true
    end
   

    createTimedPortal(config.teleport.spawnPosition, config.teleport.destination, config.teleport.duration)
    addEvent(randomRotateAllItemsInArea, config.teleport.duration * 1000, config.area.from, config.area.to)
    sendMessageToEventPlayers("The portal has been activated!\nGo through it quickly!", MESSAGE_INFO_DESCR)
    return true
end

moveevent:aid(config.actionId)
moveevent:register()


local creatureevent = CreatureEvent("onLogin_MadusaRequest")

function creatureevent.onLogin(player)
    if player:getPosition():isInRange(config.area.from, config.area.to) then
        player:teleportTo(config.kickPosition)
    end
    return true
end

creatureevent:register()



local globalevent = GlobalEvent("onStartUp_MadusaRequest")

function globalevent.onStartup()
    randomRotateAllItemsInArea(config.area.from, config.area.to)
    return true
end

globalevent:register()
 
Last edited:
Hi @Xikini

2156.png
Secret Red Gem adds extra +50 health points
2158.png
Secret Blue Gem adds extra +50 mana points

Players without VIP Access can use a maximum of 30 secret gems, while VIP Access players can use 50 secret gems.

Here is the table of available usages of secret gems (with VIP Access):

Available usages of secret gems:
Vocation:Max usages of Secret Red Gems:Max usages of Secret Blue Gems:
Sorcerer20 (+1000 health points)50 (+2500 mana points)
Druid20 (+1000 health points)50 (+2500 mana points)
Paladin25 (+1250 health points)35 (+1750 mana points)
Knight35 (+1750 health points)40 (+2000 mana points)

This is your decision which gems you will use to upgrade your character.

For example: Your character with vocation Sorcerer can have +2400 extra mana points, but you used 48 of 50 total available gems. It means you can add only +100 extra mana or health points depends on your choice.

You can check informations about used or total available gems by command !gems.
 
Alright well, I spent a good chunk of time on this thread, so will be stopping to work on my game again.

I got to do the couple of things I wanted to do (learn modal windows, npc path finding).

It's been fun.

Sorry to the rest of the requests, but I don't have the interest to keep going.

Thread closed!
Good luck out there.
 
A script for a boss that randomly creates two different effects on tiles. When a player steps into these tiles/effects, one effect gives extra damage for a duration, and the other effect gives extra protection for a duration, both against that boss only.Then effect disappear so only 1 player can step into it.
effect example:

boss d.png
effect shows with a chance
 
I'm glad you're back. I'd like to ask a favor: create a script that, when the player uses the ring and dies, it removes the ring and is revived after about 5 seconds. Of course, upon dying and reviving, they would lose levels and skills normally, as it happens when dying in the usual way. If you could create a script for me, I would appreciate it. I'm using TFS 1.4.2, and it can be in revscripts. ๐Ÿ˜
 
I'm glad you're back. I'd like to ask a favor: create a script that, when the player uses the ring and dies, it removes the ring and is revived after about 5 seconds. Of course, upon dying and reviving, they would lose levels and skills normally, as it happens when dying in the usual way. If you could create a script for me, I would appreciate it. I'm using TFS 1.4.2, and it can be in revscripts. ๐Ÿ˜

If you really want to kill the player first then u prob need source edit or maybe it'll work with onpreparedeath

edit: nvm should work
 
Last edited:
As an old school mapper who knew little about scripting i always felt basic confugrable "quest scripts" were a thing that were hard to come by. You could extract them out of other quests and scripts but a nice modern up to date generic set of quest scripts would be a cool thng to see & would go along way with newer people imo. Either you do one or a small "pack" either is fine.

  • Place X items on X Squares & Remove X walls/Objects
  • Flick Levers Remove Walls/Objects
  • Use X items on X objects to open a door
  • X Players Click X objects within X time to remove X walls/objects
  • X Players stand in X position to remove X walls/objects
  • Activae X levers in X seqence to remove walls/objects
  • Give NPC X Item and get X storage
 
Wave spell that is based on three animations so starting/middle/ending for example what i mean is firstly it would execute starting effect once that effect ends it starts middle effect, after middle ends it starts ending effect so its like seamless one spell effect just combined from three sprites
 
A script for a boss that randomly creates two different effects on tiles. When a player steps into these tiles/effects, one effect gives extra damage for a duration, and the other effect gives extra protection for a duration, both against that boss only.Then effect disappear so only 1 player can step into it.
effect example:

View attachment 81593
effect shows with a chance

Hey, hope you enjoy it. :)
This ended up being more complex then I originally expected. xD

bossTileEffectBuffs.gif

data/events/events.xml -- make sure onSpawn is enabled
XML:
<event class="Monster" method="onSpawn" enabled="1" />
data/lib/core/core.lua
Lua:
dofile('data/lib/core/xikiniCustomFunctions.lua')
data/lib/core/xikiniCustomFunctions.lua
Lua:
--[[ quick reference of events

    CREATURE_EVENT_NONE
    CREATURE_EVENT_LOGIN
    CREATURE_EVENT_LOGOUT
    CREATURE_EVENT_THINK
    CREATURE_EVENT_PREPAREDEATH
    CREATURE_EVENT_DEATH
    CREATURE_EVENT_KILL
    CREATURE_EVENT_ADVANCE
    CREATURE_EVENT_MODALWINDOW
    CREATURE_EVENT_TEXTEDIT
    CREATURE_EVENT_HEALTHCHANGE
    CREATURE_EVENT_MANACHANGE
    CREATURE_EVENT_EXTENDED_OPCODE
]]--

function Player:hasEvent(type, name)
    for k, v in pairs(self:getEvents(type)) do
        if v == name then
            return true
        end
    end
    return false
end
data/scripts/bossTileEffectBuffs.lua
Lua:
local buffs = {
--    [actionId]
    [45007] = {
        tileSpawnShootEffect = CONST_ANI_FIRE,
        tileEffects = {
            CONST_ME_TUTORIALARROW, 
            CONST_ME_TUTORIALSQUARE,
            CONST_ME_HEARTS
        },
        buff = {
            tileDuration = 10, -- seconds (how long the tile remains 'active' before disappearing)
            effect = "damage mitigation",
            duration = 30, -- seconds
            percent = 80   -- 1-100%
        }
    },
    [45008] = {
        tileSpawnShootEffect = CONST_ANI_SUDDENDEATH,
        tileEffects = {
            CONST_ME_TUTORIALARROW, 
            CONST_ME_TUTORIALSQUARE,
            CONST_ME_MORTAREA
        },
        buff = {
            tileDuration = 10,
            effect = "damage increase",
            duration = 30,
            percent = 300  -- 100 = double damage
        }
    }
}

local bosses = {
    ["rat"] = { -- keep names all lowercase
        {
            actionIds = {45007, 45008}, -- actionIds from buffs table
            interval = 3, -- how often it attempts to spawn a buffTile (seconds)
            chance = 100,
            spawnDistance = 7, -- how far away from boss they can spawn
            requireLineOfSight = true, -- basically, can a buff spawn behind a wall?
            location = {
                from = Position(995, 995, 7), -- top left corner -- only registers event to bosses that spawn in this area
                to =   Position(1005, 1005, 7)    -- bottom right corner
            }
        },
--        { -- 2nd boss location with same name
--            actionIds = {11111, 22222},
--            interval = 5,
--            chance = 30,
--            spawnDistance = 7,
--            requireLineOfSight = true,
--            location = {
--                from = Position(1000, 1000, 7),
--                to =   Position(1001, 1001, 7)
--            }
--        }
    },
--    ["some other boss"] = {
--        {
--            actionIds = {11111, 22222},
--            interval = 5,
--            chance = 30,
--            spawnDistance = 7,
--            requireLineOfSight = true,
--            location = {
--                from = Position(1000, 1000, 7),
--                to =   Position(1001, 1001, 7)
--            }
--        }
--    }
}

-- END OF CONFIG --
-- Don't modify below --

local bossTracker = {
--    [bossId] = {index = i, thinkTimer = 0}
}

local activeBossBuffs = {
--    [bossId] = {
--        [playerId] = {
--            {receivedAt = os.mtime(), actionId = 11111}
--        }
--    }
}

local buffedTiles = {
--    [Position(1000, 1000, 7)] = {bossId = 1111, receivedAt = os.mtime()}
}

local function getRandomCircularOffset(maxDistance)
    local angle = math.random() * 2 * math.pi
    local radius = math.random() * maxDistance
    local offsetX = math.floor(radius * math.cos(angle) + 0.5)
    local offsetY = math.floor(radius * math.sin(angle) + 0.5)
    return offsetX, offsetY
end

local function resetTileActionId(position)
    local tile = Tile(position)
    if not tile then
        return
    end
    local ground = tile:getGround()
    if not ground then
        return
    end
    ground:removeAttribute(ITEM_ATTRIBUTE_ACTIONID)
end

local function tileChecker(position, actionId)
    local tileIndex = buffedTiles[position]
    if not tileIndex then
        resetTileActionId(position)
        return
    end
    
    local boss = Creature(tileIndex.bossId)
    if not boss then
        resetTileActionId(position)
        buffedTiles[position] = nil
        return
    end
    
    local index = buffs[actionId]
    if os.mtime() > tileIndex.receivedAt + (1000 * index.buff.tileDuration) then
        resetTileActionId(position)
        buffedTiles[position] = nil
        return true
    end
    
    for i = 1, #index.tileEffects do
        position:sendMagicEffect(index.tileEffects[i])
    end
    
    addEvent(tileChecker, 1000, position, actionId)
end

local function createBuffTile(bossId, bossIndex, actionId)
    local boss = Creature(bossId)
    if not boss then
        return
    end
    
    local bossPosition = boss:getPosition()
    for i = 1, 20 do
        local offsetX, offsetY = getRandomCircularOffset(bossIndex.spawnDistance)
        local x = bossPosition.x + offsetX
        local y = bossPosition.y + offsetY
        local targetPosition = Position(x, y, bossPosition.z)
        
        local tile = Tile(targetPosition)
        if tile and tile:isWalkable() then
            local ground = tile:getGround()
            if ground:getActionId() == 0 then
                local topVisibleCreature = tile:getTopVisibleCreature(boss)
                if not topVisibleCreature or not topVisibleCreature:isPlayer() then
                    if not bossIndex.requireLineOfSight or bossPosition:isSightClear(targetPosition) then
                        ground:setActionId(actionId)
                        bossPosition:sendDistanceEffect(targetPosition, buffs[actionId].tileSpawnShootEffect)
                        buffedTiles[targetPosition] = {bossId = boss:getId(), receivedAt = os.mtime()}
                        tileChecker(targetPosition, actionId)
                        return
                    end
                end
            end
        end
    end
end


local function calculateDamage(damage, damageType, percent, buffType)
    if damageType ~= 0 then
        if buffType == "damage mitigation" then
            return damage - (damage * (percent / 100))
        else
            return damage * ((percent + 100) / 100)
        end
    end
    return damage
end

local function damageCalculator(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType)
    local bossId = creature:isMonster() and creature:getId() or attacker:isMonster() and attacker:getId() or 0
    local index = activeBossBuffs[bossId]
    if index then
        local playerId = creature:isPlayer() and creature:getId() or attacker:isPlayer() and attacker:getId() or 0
        index = index[playerId]
        if index then
            local currentTime = os.mtime()
            local percent = 0
            local buffType = creature:isPlayer() and "damage mitigation" or "damage increase"
            -- loop through all buffs and find highest percent
            for i = #index, 1, -1 do
                local buff = buffs[index[i].actionId].buff
                if (buff.duration * 1000) + index[i].receivedAt > currentTime then
                    if buffType == buff.effect then
                        percent = percent < buff.percent and buff.percent or percent
                    end
                else
                    table.remove(index, i) -- remove inactive buffs
                end
            end
            if percent > 0 then
                primaryDamage = calculateDamage(primaryDamage, primaryType, percent, buffType)
                secondaryDamage = calculateDamage(secondaryDamage, secondaryType, percent, buffType)
            end
        end
    end
    return primaryDamage, secondaryDamage
end


local healthChange = CreatureEvent("onHealthChange_bossTileEffectBuffs")

function healthChange.onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
    primaryDamage, secondaryDamage = damageCalculator(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType)
    return primaryDamage, primaryType, secondaryDamage, secondaryType
end

healthChange:register()


local manaChange = CreatureEvent("onManaChange_bossTileEffectBuffs")

function manaChange.onManaChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
    primaryDamage, secondaryDamage = damageCalculator(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType)
    return primaryDamage, primaryType, secondaryDamage, secondaryType
end

manaChange:register()

local creatureevent = CreatureEvent("onDeath_bossTileEffectBuffs")

function creatureevent.onDeath(creature, corpse, killer, mostDamageKiller, lastHitUnjustified, mostDamageUnjustified)
    -- check if all bosses are dead
    local playerIds = {}
    for bossId, players in pairs(activeBossBuffs) do
        local creature = Creature(bossId)
        if creature then
            return true
        end
        for playerId, _ in pairs(players) do
            playerIds[playerId] = 0
        end
    end
    
    -- all bosses confirmed dead
    
    -- remove registered events from players
    for playerId, _ in pairs(playerIds) do
        local player = Player(playerId)
        if player then
            player:unregisterEvent("onHealthChange_bossTileEffectBuffs")
            player:unregisterEvent("onManaChange_bossTileEffectBuffs")
        end
    end
    
    -- remove any remaining active tiles
    for position, _ in pairs(buffedTiles) do
        local tile = Tile(position)
        if tile then
            local ground = tile:getGround()
            ground:removeAttribute(ITEM_ATTRIBUTE_ACTIONID)
        end
    end
    
    -- and reset all tables
    bossTracker = {}
    activeBossBuffs = {}
    buffedTiles = {}
    return true
end

creatureevent:register()


local creatureevent = CreatureEvent("onThink_bossTileEffectBuffs")

function creatureevent.onThink(creature, interval)
    local bossId = creature:getId()
    
    -- have to add this here, because onSpawn doesn't let you get the creatures Id, because it hasn't been created yet.. fully.  idk lol
    if not bossTracker[bossId] then
        local index = bosses[creature:getName():lower()]
        if index then
            local position = creature:getPosition()
            for i = 1, #index do
                if position:isInRange(index[i].location.from, index[i].location.to) then
                    bossTracker[creature:getId()] = {index = i, thinkTimer = 0}
                    break
                end
            end
        end
    end
    
    local bossIndex = bosses[creature:getName():lower()][bossTracker[bossId].index]
    
    bossTracker[bossId].thinkTimer = bossTracker[bossId].thinkTimer + interval
    if bossTracker[bossId].thinkTimer >= bossIndex.interval * 1000 then
        bossTracker[bossId].thinkTimer = bossTracker[bossId].thinkTimer - (bossIndex.interval * 1000)
        if math.random(100) <= bossIndex.chance then
            local actionId = bossIndex.actionIds[math.random(#bossIndex.actionIds)]
            createBuffTile(bossId, bossIndex, actionId)
        end
    end
    return true
end

creatureevent:register()


local moveevent = MoveEvent()

function moveevent.onStepIn(creature, item, position, fromPosition)
    if not isPlayer(creature:getId()) then
        return true
    end
    
    local tileActionId = item:getActionId()
    local index = buffs[tileActionId]
    item:removeAttribute(ITEM_ATTRIBUTE_ACTIONID)
    
    if not index then
        print("Error: ActionId not in table. (" .. tileActionId .. ")") -- shouldn't be possible.. but :shrug
        return true
    end
    
    -- work-around to ensure position tables are the same.
    for k, v in pairs(buffedTiles) do
        if position.x == k.x and position.y == k.y and position.z == k.z then
            position = k
        end
    end
    
    if not buffedTiles[position] then
        return true
    end
    
    local boss = Creature(buffedTiles[position].bossId)
    if not boss then
        buffedTiles[position] = nil
        return true
    end
    
    local currentTime = os.mtime()
    if currentTime > buffedTiles[position].receivedAt + (1000 * index.buff.tileDuration) then
        buffedTiles[position] = nil
        return true
    end
    
    local bossId = buffedTiles[position].bossId
    if not activeBossBuffs[bossId] then
        activeBossBuffs[bossId] = {}
    end
    
    local playerId = creature:getId()
    if not activeBossBuffs[bossId][playerId] then
        if not creature:hasEvent(CREATURE_EVENT_HEALTHCHANGE, "onHealthChange_bossTileEffectBuffs") then
            creature:registerEvent("onHealthChange_bossTileEffectBuffs")
        end
        if not creature:hasEvent(CREATURE_EVENT_MANACHANGE, "onManaChange_bossTileEffectBuffs") then
            creature:registerEvent("onManaChange_bossTileEffectBuffs")
        end
        activeBossBuffs[bossId][playerId] = {}
    end
    
    table.insert(activeBossBuffs[bossId][playerId], {receivedAt = currentTime, actionId = tileActionId})
    buffedTiles[position] = nil
    creature:say(buffs[tileActionId].buff.effect .. "\n" .. buffs[tileActionId].buff.duration .. " seconds", TALKTYPE_MONSTER_SAY)
    return true
end

for actionId, _ in pairs(buffs) do
    moveevent:aid(actionId)
end
moveevent:register()


local eventCallback = EventCallback

function eventCallback.onSpawn(creature, position, startup, artificial)
    local index = bosses[creature:getName():lower()]
    if index then
        for i = 1, #index do
            if position:isInRange(index[i].location.from, index[i].location.to) then
                creature:registerEvent("onDeath_bossTileEffectBuffs")
                creature:registerEvent("onHealthChange_bossTileEffectBuffs")
                creature:registerEvent("onThink_bossTileEffectBuffs")
                return true
            end
        end
    end
    return true
end

eventCallback:register(-666)
 
Last edited:
Markets
Something that has been in my mind for a long, long time..
Counters that act like "market stands" if that makes any sense.

So essentially you could have a few spots in a city assigned for players to be able to sell their items "afk"
without the need for a house or some non-immersive market in Depotbox.

4d945776478ed558604f59ff78c760da.jpg


I want to let players open a sort of market
- The seller places an item on the Counter, perhaps using an Auction Hammer on it and an "amount" window will be displayed where the seller states the sell price.
OR could create a container inside the Counter and then the seller uses a hammer on the Counter.
- The item will be put for sale for a certain amount of time let's say two hours (this can be put on the Auction Hammer?),
perhaps the price and ending time could be displayed on top of the item or on onLook.

  • If the owner of the item moves the item, the item will be removed from the "market" (perhaps with a confirmation message)
  • If a buyer moves the item, the item will be "sold" and it will take money from the player. (perhaps with a confirmation message)
  • If item is bought the gold is send to sellers bank account.
  • If the seller logs out the item will be taken off the market and send to depot.

Is this even possible with just storage and no database queries? ๐Ÿค”
0d0ed91e2707be77acc48a3cce3a0f58.png

------

EDIT: Perhaps it would be more immersive if you can perhaps ask a NPC that runs the market for you ๐Ÿค”
So behind the market stall there is an NPC that will do the actions for you?
 
Last edited:
I'm glad you're back. I'd like to ask a favor: create a script that, when the player uses the ring and dies, it removes the ring and is revived after about 5 seconds. Of course, upon dying and reviving, they would lose levels and skills normally, as it happens when dying in the usual way. If you could create a script for me, I would appreciate it. I'm using TFS 1.4.2, and it can be in revscripts. ๐Ÿ˜
Okay, so I've thought about this a lot, and it's basically a nightmare lol

Player having an equipped ring and dies -> simulate death

Lose experience -> find and replicate formula to lose experience.. checking blessings and items.. doable maybe
Lose skills -> same as above
Update Death list? -> unsure if you want that.. but would need to give other players experience possibly? cuz of pvp stuff
Dropping items -> unsure if you want that, probably easy to simulate.. but there would be no body

Then the big crux..
Removing buffs / creature events.. and then reapplying them? -> so many potential conflicts..
We basically need to simulate onLogout and onLogin

--
And then we move onto the really big problem. xD
Making the person stand there, as if they are basically offline and can't do anything.

We need to prevent..
-> the person from attacking
-> the person from follow locking
-> trading
-> items.. moving them around, using them, 'use with', item interaction at distance (source force walking us to attempt to interact)
-> walking and movement
-> talking
-> maybe other stuff?

--
Sorry to say, I really think this is impossible.
 
Okay, so I've thought about this a lot, and it's basically a nightmare lol

Player having an equipped ring and dies -> simulate death

Lose experience -> find and replicate formula to lose experience.. checking blessings and items.. doable maybe
Lose skills -> same as above
Update Death list? -> unsure if you want that.. but would need to give other players experience possibly? cuz of pvp stuff
Dropping items -> unsure if you want that, probably easy to simulate.. but there would be no body

Then the big crux..
Removing buffs / creature events.. and then reapplying them? -> so many potential conflicts..
We basically need to simulate onLogout and onLogin

--
And then we move onto the really big problem. xD
Making the person stand there, as if they are basically offline and can't do anything.

We need to prevent..
-> the person from attacking
-> the person from follow locking
-> trading
-> items.. moving them around, using them, 'use with', item interaction at distance (source force walking us to attempt to interact)
-> walking and movement
-> talking
-> maybe other stuff?

--
Sorry to say, I really think this is impossible.
Without the 5 second thing, you could just create a local table, kill the player and store their guid as the key and the death position
and onLogin check for the guid in the table and teleport to the death position, and ofc, if still exists, remove the ring.

But yeah, the whole effect of it happening would most likely require client changes, or a lot more thought.
 
Respawn at the nearest Respawn Statue/Tavern/Town

Sorry for asking the impossible again, probably going against your rules but it's also something that has been on my mind for quite a long time since traversing on my server will be somewhat hard on my huge-sized map and players getting back to their bodies is a problem I need to overcome.

Upon dying, players now respawn at their home town, but what if they are miles and miles away, it is very discouraging for players to travel such a long distance even if they could teleport their way back to the location through carriages and boats.

To the point:

- My original concept was to let players respawn at the closest Tavern upon dying not their home-town.
OR
- Upon dying players respawn next to an Object with actionid/uniqueid.
This way you can have this Respawn Object at any hunting ground and upon dying, the player spawns very near the place they died at.
Kind of like the Statues in Elden Ring or Bonfires in Dark Souls.

the lord of the rings elise GIF
 
Okay, so I've thought about this a lot, and it's basically a nightmare lol

Player having an equipped ring and dies -> simulate death

Lose experience -> find and replicate formula to lose experience.. checking blessings and items.. doable maybe
Lose skills -> same as above
Update Death list? -> unsure if you want that.. but would need to give other players experience possibly? cuz of pvp stuff
Dropping items -> unsure if you want that, probably easy to simulate.. but there would be no body

Then the big crux..
Removing buffs / creature events.. and then reapplying them? -> so many potential conflicts..
We basically need to simulate onLogout and onLogin

--
And then we move onto the really big problem. xD
Making the person stand there, as if they are basically offline and can't do anything.

We need to prevent..
-> the person from attacking
-> the person from follow locking
-> trading
-> items.. moving them around, using them, 'use with', item interaction at distance (source force walking us to attempt to interact)
-> walking and movement
-> talking
-> maybe other stuff?

--
Sorry to say, I really think this is impossible.
I appreciate your understanding; thank you for your dedicated efforts.

I acquired a foundational script from the PVP arena and subsequently made modifications. Now, both creatures and players have the capability to eliminate me; the demise is simulated, and I undergo revival after a brief 5-second interval. Following a span of several days, I discerned that there is no loss of levels or skills. Despite exerting efforts for over two weeks, there has been no tangible progressโ€”quite amusing, really. In due course, I am confident that I will attain success; a genuine script is in development.

Furthermore, the Revive system is already in existence, originating from the antiquated Evolution engine crafted by Xiadozu. They incorporated specific codes into the source. I intend to thoroughly investigate this matter and endeavor to integrate it into TFS 1.4.2. I extend my apologies and express gratitude for your ongoing commitment. Undoubtedly, you possess an exceptional level of expertise, standing out as a luminary within the Otland community. I am profoundly thankful for all your contributions.

kindly review the GIF to observe the outcome of the script; it is a somewhat improvised rendition. ๐Ÿ™‚
 
Last edited:
Good evening my dear, it's great to see collaborations like this!
I've been thinking about the following for a few days, my server is customized and I have 3 evolutions for each vocation, I would like: players with vocation 13 to 16, when they kill players with vocation 1 to 5, players with vocation 1 to 5 return to the temple position 1000 1000 7, without suffering any damage, as if it were a bit of a teleportation, and the killing player (vocation 13 to 16) does not receive any frag, or skull... it seems like a complex, would it be possible? Thank you very much in advance!
 
Alright, Let's try to respond to everyone here so we're all on the same page. :)

I can't edit posts because I don't have premium account on OtLand lol.

can you do a script killing a monster gives random 10% extra loot chance or 10% extra exp for 30 minutes and broadcast message
Doable. Added onto the ToDo list.

As an old school mapper who knew little about scripting i always felt basic confugrable "quest scripts" were a thing that were hard to come by. You could extract them out of other quests and scripts but a nice modern up to date generic set of quest scripts would be a cool thng to see & would go along way with newer people imo. Either you do one or a small "pack" either is fine.

  • Place X items on X Squares & Remove X walls/Objects
  • Flick Levers Remove Walls/Objects
  • Use X items on X objects to open a door
  • X Players Click X objects within X time to remove X walls/objects
  • X Players stand in X position to remove X walls/objects
  • Activae X levers in X seqence to remove walls/objects
  • Give NPC X Item and get X storage
All Doable.
I'll make them as a pack of single-use scripts, so people can just copy-paste the files, as they need more of them.
Added onto the ToDo list.

Wave spell that is based on three animations so starting/middle/ending for example what i mean is firstly it would execute starting effect once that effect ends it starts middle effect, after middle ends it starts ending effect so its like seamless one spell effect just combined from three sprites
This is mostly a timing issue with an addEvent.
There's no way to get the animation length via Lua function calls, so it's just trial and error until it looks seamless.
Added onto the ToDo list.

Markets
Something that has been in my mind for a long, long time..
Counters that act like "market stands" if that makes any sense.

So essentially you could have a few spots in a city assigned for players to be able to sell their items "afk"
without the need for a house or some non-immersive market in Depotbox.

4d945776478ed558604f59ff78c760da.jpg


I want to let players open a sort of market
- The seller places an item on the Counter, perhaps using an Auction Hammer on it and an "amount" window will be displayed where the seller states the sell price.
OR could create a container inside the Counter and then the seller uses a hammer on the Counter.
- The item will be put for sale for a certain amount of time let's say two hours (this can be put on the Auction Hammer?),
perhaps the price and ending time could be displayed on top of the item or on onLook.

  • If the owner of the item moves the item, the item will be removed from the "market" (perhaps with a confirmation message)
  • If a buyer moves the item, the item will be "sold" and it will take money from the player. (perhaps with a confirmation message)
  • If item is bought the gold is send to sellers bank account.
  • If the seller logs out the item will be taken off the market and send to depot.

Is this even possible with just storage and no database queries? ๐Ÿค”
0d0ed91e2707be77acc48a3cce3a0f58.png

------

EDIT: Perhaps it would be more immersive if you can perhaps ask a NPC that runs the market for you ๐Ÿค”
So behind the market stall there is an NPC that will do the actions for you?
Talked in private.
The idea being a lever + blackboard setup inside of a house, that would allow for house owner to set the price on the blackboard, and be able to sell the 'top most visible item'.
Untitled.png
Added onto the ToDo list.

Without the 5 second thing, you could just create a local table, kill the player and store their guid as the key and the death position
and onLogin check for the guid in the table and teleport to the death position, and ofc, if still exists, remove the ring.

But yeah, the whole effect of it happening would most likely require client changes, or a lot more thought.
Yeah, making a 'teleport to where I died' script is possible. xD
But was not the original request.

I appreciate your understanding; thank you for your dedicated efforts.

I acquired a foundational script from the PVP arena and subsequently made modifications. Now, both creatures and players have the capability to eliminate me; the demise is simulated, and I undergo revival after a brief 5-second interval. Following a span of several days, I discerned that there is no loss of levels or skills. Despite exerting efforts for over two weeks, there has been no tangible progressโ€”quite amusing, really. In due course, I am confident that I will attain success; a genuine script is in development.

Furthermore, the Revive system is already in existence, originating from the antiquated Evolution engine crafted by Xiadozu. They incorporated specific codes into the source. I intend to thoroughly investigate this matter and endeavor to integrate it into TFS 1.4.2. I extend my apologies and express gratitude for your ongoing commitment. Undoubtedly, you possess an exceptional level of expertise, standing out as a luminary within the Otland community. I am profoundly thankful for all your contributions.

kindly review the GIF to observe the outcome of the script; it is a somewhat improvised rendition. ๐Ÿ™‚
I have a feeling that this either requires source edits, or doesn't solve for any of the problems I listed earlier.
Sorry bud.

Respawn at the nearest Respawn Statue/Tavern/Town

Sorry for asking the impossible again, probably going against your rules but it's also something that has been on my mind for quite a long time since traversing on my server will be somewhat hard on my huge-sized map and players getting back to their bodies is a problem I need to overcome.

Upon dying, players now respawn at their home town, but what if they are miles and miles away, it is very discouraging for players to travel such a long distance even if they could teleport their way back to the location through carriages and boats.

To the point:

- My original concept was to let players respawn at the closest Tavern upon dying not their home-town.
OR
- Upon dying players respawn next to an Object with actionid/uniqueid.
This way you can have this Respawn Object at any hunting ground and upon dying, the player spawns very near the place they died at.
Kind of like the Statues in Elden Ring or Bonfires in Dark Souls.

the lord of the rings elise GIF
again, spoke in private to confirm details.

Will be making a tavern and respawn anchor system.
When you die, if you are inside of a respawn achor area, you'll respawn to that anchor.
If not in any respawn anchor area's, then respawn at closest tavern.
In the unlikely scenario that 2 taverns are equally close, it will respawn you at the first tavern found in the list of taverns.

Good evening my dear, it's great to see collaborations like this!
I've been thinking about the following for a few days, my server is customized and I have 3 evolutions for each vocation, I would like: players with vocation 13 to 16, when they kill players with vocation 1 to 5, players with vocation 1 to 5 return to the temple position 1000 1000 7, without suffering any damage, as if it were a bit of a teleportation, and the killing player (vocation 13 to 16) does not receive any frag, or skull... it seems like a complex, would it be possible? Thank you very much in advance!
Very doable. I will just prevent the death entirely, heal the player and teleport them to their home temple.
I assume this works for each evolution?

Tier 1, can kill anyone.
Tier 2, can kill tier 2 and tier 3. If kills tier 1, tier 1 goes to temple.
Tier 3, can only kill tier 3. If kills tier 1 or 2, they go to temple?

--
Thanks for all the replies guys.
I'll do my best. :)
 
Back
Top