• 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,942
Solutions
589
Reaction score
5,606
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:
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
I didn't do "Use X items on X objects to open a door" because I couldn't see a need for it?

But I did the rest. (:
(Flick Lever Remove Walls/Objects, was already done in my previous thread, so you can find it there.)

And here they are, in no particular order.

npcQuest.gifonUse_leversSpecificOrder.gifonStepIn_multipleLocations_openPassageway.gifonUse_useMultipleObjects_openPassageway.gifplace_items_on_tiles_remove_object.gif

data/lib/core/core.lua -- add
LUA:
dofile('data/lib/core/xikiniCustomFunctions.lua')
data/lib/core/xikiniCustomFunctions.lua -- add
LUA:
function Position:isIdentical(position)
    if self.x == position.x and self.y == position.y and self.z == position.z then
        return true
    end
    return false
end

data/npc/Nugo.xml
XML:
<?xml version="1.0" encoding="UTF-8"?>
<npc name="Nugo" script="nugo.lua" walkinterval="2000" floorchange="0">
    <health now="100" max="100" />
    <look type="139" head="20" body="39" legs="45" feet="7" addons="0" />
</npc>
data/npc/scripts/nugo.lua
LUA:
local keywordHandler = KeywordHandler:new()
local npcHandler = NpcHandler:new(keywordHandler)
NpcSystem.parseParameters(npcHandler)

function onCreatureAppear(cid)              npcHandler:onCreatureAppear(cid)            end
function onCreatureDisappear(cid)           npcHandler:onCreatureDisappear(cid)         end
function onCreatureSay(cid, type, msg)      npcHandler:onCreatureSay(cid, type, msg)    end
function onThink()                          npcHandler:onThink()                        end

local function greetCallback(cid)
    local player = Player(cid)
    npcHandler.topic[cid] = 0
    return true
end

-- if you give the NPC 15 red apples, he will give you a storage
-- -1 means you haven't started the quest
-- 0 means you have started but not finished
-- 1 means you finished the quest
local config = {
    storageKey = 45020,
    requiredItemId = 2674, -- red apple
    requiredAmount = 15
}

local function creatureSayCallback(cid, type, msg)
    if not npcHandler:isFocused(cid) then
        return false
    end

    local player = Player(cid)
    local storageValue = player:getStorageValue(config.storageKey)
    
    if msgcontains(msg, "quest") then
        if storageValue == -1 then
            npcHandler:say("Would you like to help me?", cid)
            npcHandler.topic[cid] = 1
        elseif storageValue == 0 then
            npcHandler:say("Have you brought the 15 apples?", cid)
            npcHandler.topic[cid] = 1
        else
            npcHandler:say("Thanks for the help earlier, but I have nothing else for you.", cid)
            npcHandler.topic[cid] = 0
        end
        
    elseif npcHandler.topic[cid] == 1 then
        if storageValue == -1 then
            if msgcontains(msg, "yes") then
                npcHandler:say("Alright, thanks! Bring me 15 red apples and ask about {quest} again.", cid)
                player:setStorageValue(config.storageKey, 0)
            else
                npcHandler:say("Guess not.", cid)
            end
        else
            if msgcontains(msg, "yes") then
                if player:getItemCount(config.requiredItemId) >= config.requiredAmount then
                    player:removeItem(config.requiredItemId, config.requiredAmount)
                    player:setStorageValue(config.storageKey, 1)
                    npcHandler:say("Thanks! Here's your quest storage. Enjoy!", cid)
                else
                    npcHandler:say("You don't seem to have enough red apples. Come back when you have them.", cid)
                end
            else
                npcHandler:say("Guess not.", cid)
            end
        end
        npcHandler.topic[cid] = 0

    end
    return true
end

local function onAddFocus(cid)

end

local function onReleaseFocus(cid)

end

npcHandler:setCallback(CALLBACK_ONADDFOCUS, onAddFocus)
npcHandler:setCallback(CALLBACK_ONRELEASEFOCUS, onReleaseFocus)

npcHandler:setCallback(CALLBACK_GREET, greetCallback)
npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
npcHandler:addModule(FocusModule:new())

data/scripts/onUse_leversSpecificOrder.lua
LUA:
local config = {
    actionId = 45019,
    leverItemIds = {left = 1945, right = 1946},
    leverPositions = {
        Position(1004, 999, 7),
        Position(1005, 999, 7),
        Position(1006, 999, 7), -- use the levers in correct order
        Position(1007, 999, 7),
        Position(1008, 999, 7)
    },
    respawnTime = 5,
    removeItems = {
        {itemId = 1026, position = Position(1004, 998, 7)}, -- then all of these positions and items will get removed (use walls / items that can't be moved)
        {itemId = 1026, position = Position(1005, 998, 7)},
        {itemId = 1026, position = Position(1006, 998, 7)},
        {itemId = 1026, position = Position(1007, 998, 7)},
        {itemId = 1026, position = Position(1008, 998, 7)}
    }
}

local lastUsedLever = 0

local function resetAllLevers()
    lastUsedLever = 0
    for i = 1, #config.leverPositions do
        local tile = Tile(config.leverPositions[i])
        if not tile then
            print("Error, tile not found! Error: 45000888")
            return true
        end
        local tileItem = tile:getItemById(config.leverItemIds.right)
        if not tileItem then
            print("Error, lever not found! Error: 45000999")
            return true
        end
        tileItem:transform(config.leverItemIds.left)
    end
end



local leverActions = Action()

function leverActions.onUse(player, item, fromPosition, target, toPosition, isHotkey)
    if item:getId() == config.leverItemIds.right then
        return true
    end
    item:transform(config.leverItemIds.right)
    if config.leverPositions[lastUsedLever + 1]:isIdentical(toPosition) then
        lastUsedLever = lastUsedLever + 1
    end
    for i = 1, #config.leverPositions do
        local tile = Tile(config.leverPositions[i])
        if not tile then
            print("Error, tile not found! Error: 45000777")
            return true
        end
        local tileItem = tile:getItemById(config.leverItemIds.right)
        if not tileItem then
            return true
        end
    end
    if #config.leverPositions ~= lastUsedLever then
        addEvent(resetAllLevers, 1000)
        return true
    end
    for _, v in pairs(config.removeItems) do
        local tile = Tile(v.position)
        if not tile then
            print("Error, tile not found! Error: 45001111")
            return
        end
        local tileItem = tile:getItemById(v.itemId)
        if tileItem then
            tileItem:remove()
            addEvent(Game.createItem, config.respawnTime * 1000, v.itemId, 1, v.position)
        end
    end
    addEvent(resetAllLevers, config.respawnTime * 1000)
    return true
end

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

data/scripts/onStepIn_multipleLocations_openPassageway.lua
LUA:
local config = {
    requiredPositions = {
        Position(997, 995, 7),
        Position(997, 996, 7), -- if all these positions have a player.. remove items
        Position(998, 995, 7),
        Position(998, 996, 7)
    },
    respawnTime = 5,
    removeItems = {
        {itemId = 1050, position = Position(997, 993, 7)}, -- then all of these positions and items will get removed (use walls / items that can't be moved)
        {itemId = 1050, position = Position(998, 993, 7)},
    }
}

local moveevent = MoveEvent()

function moveevent.onStepIn(creature, item, position, fromPosition)
    if not creature:isPlayer() then
        return true
    end

    local doRemoveItems = true
    for _, _position in ipairs(config.requiredPositions) do
        local tile = Tile(_position)
        if not tile then
            print("Error, tile not found! Error: 45000444")
            return
        end
        local foundCreature = tile:getTopVisibleCreature(creature)
        if foundCreature and foundCreature:isPlayer() then
            _position:sendMagicEffect(CONST_ME_MAGIC_GREEN)
        else
            _position:sendMagicEffect(CONST_ME_MAGIC_RED)
            doRemoveItems = false
        end
    end
    
    if not doRemoveItems then
        return true
    end
    
    for _, v in pairs(config.removeItems) do
        local tile = Tile(v.position)
        if not tile then
            print("Error, tile not found! Error: 45000555")
            return
        end
        local tileItem = tile:getItemById(v.itemId)
        if tileItem then
            tileItem:remove()
            addEvent(Game.createItem, config.respawnTime * 1000, v.itemId, 1, v.position)
        end
    end
    return true
end

for _, position in ipairs(config.requiredPositions) do
    moveevent:position(position)
end
moveevent:register()

data/scripts/onUse_useMultipleObjects_openPassageway.lua
LUA:
--X Players Click X objects within X time to remove X walls/objects


local config = {
    actionId = 45018,
    useTime = 5000, -- milliseconds (if all objects used within 2 seconds, removeItems)
    clickableObjectPositions = {
        Position(1004, 993, 7),
        Position(1004, 996, 7),
        Position(1008, 993, 7),
        Position(1008, 996, 7)
    },
    respawnTime = 5, -- seconds
    removeItems = {
        {itemId = 1050, position = Position(1006, 993, 7)} -- then all of these positions and items will get removed (use walls / items that can't be moved)
    }
}

local usedObjects = { -- don't edit
--    [Position(1000, 1000, 7)] = {lastUsed = 0}
} 


local action = Action()

function action.onUse(player, item, fromPosition, target, toPosition, isHotkey)
    -- check if removeItems are there
    local tileItems = {}
    for _, v in pairs(config.removeItems) do
        local tile = Tile(v.position)
        if not tile then
            print("Error, tile not found! Error: 45000333")
            return true
        end
        local tileItem = tile:getItemById(v.itemId)
        if not tileItem then
            return true
        end
        tileItems[#tileItems + 1] = {item = tileItem, itemId = v.itemId, position = v.position}
    end

    local currentTime = os.mtime()
    for _, position in ipairs(config.clickableObjectPositions) do
        if toPosition:isIdentical(position) then
            usedObjects[position] = {lastUsed = currentTime}
            break
        end
    end
    toPosition:sendMagicEffect(CONST_ME_MAGIC_GREEN)
    
    local objectCount = 0
    local lowestTime = currentTime
    local highestTime = 0
    for k, v in pairs(usedObjects) do
        objectCount = objectCount + 1
        lowestTime = math.min(v.lastUsed, lowestTime)
        highestTime = math.max(v.lastUsed, highestTime)
    end
    
    if objectCount < #config.clickableObjectPositions then
        return true
    end
    
    if highestTime - lowestTime > config.useTime then
        return true
    end
    
    for i = 1, #tileItems do
        tileItems[i].item:remove()
        addEvent(Game.createItem, config.respawnTime * 1000, tileItems[i].itemId, 1, tileItems[i].position)
    end
    usedObjects = {}
    return true
end

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

data/scripts/place_items_on_tiles_remove_object.lua
LUA:
local config = {
    requiredItems = {
        {itemId = 2674, amount = 5, position = Position(1000, 995, 7)},
        {itemId = 2467, amount = 1, position = Position(1001, 995, 7)}, -- if all these positions have the required items
        {itemId = 2195, amount = 1, position = Position(1002, 995, 7)}
    },
    removeItems = {
        {itemId = 1026, position = Position(1001, 993, 7)} -- then all of these positions and items will get removed (use walls / items that can't be moved)
    }
}

local function addRemoveItems()
    local doRemoveItems = true
    for _, v in pairs(config.requiredItems) do
        local tile = Tile(v.position)
        if not tile then
            print("Error, tile not found! Error: 45000111")
            return
        end
        if tile:getItemCountById(v.itemId) < v.amount then
            doRemoveItems = false
            break
        end
    end
    
    for _, v in pairs(config.removeItems) do
        local tile = Tile(v.position)
        if not tile then
            print("Error, tile not found! Error: 45000222")
            return
        end
        local tileItem = tile:getItemById(v.itemId)
        if doRemoveItems and tileItem then
            tileItem:remove()
        end
        if not doRemoveItems and not tileItem then
            Game.createItem(v.itemId, 1, v.position)
        end
    end
end

local moveevent = MoveEvent()

function moveevent.onAddItem(moveitem, tileitem, position)
    addRemoveItems()
    return true
end

for _, v in pairs(config.requiredItems) do
    moveevent:position(v.position)
end
moveevent:register()


local moveevent = MoveEvent()

function moveevent.onRemoveItem(moveitem, tileitem, position)
    addRemoveItems()
    return true
end

for _, v in pairs(config.requiredItems) do
    moveevent:position(v.position)
end
moveevent:register()

data/scripts/onUse_leverActions.lua
LUA:
--[[
    "object" -> anything that is not a floor tile.
    "floor" floor tiles (roof/ground/hole)
    
    "add" / "remove" -> tells the script what to do when lever is on the left. (after it flops, it will do the opposite)
    "replace" -> transforms an item into another item, instead of adding/removing
    
]]--

local levers = {left = 1945, right = 1946}
local config = {
-- [actionid] = {"object", "add", position, itemid, relocate_direction}
-- [actionid] = {"object", "remove", position, itemid, relocate_direction}
-- [actionid] = {"object", "replace", position, {itemid_from, itemid_to}}

    [45011] = {"object", "add", Position(94, 93, 7), 1026, "south"},
    [45012] = {"object", "remove", Position(96, 93, 7), 1026, "south"},
    [45013] = {"object", "replace", Position(98, 93, 7), {1026, 1050}},
    
-- [actionid] = {"floor", "add", position, itemid}
-- [actionid] = {"floor", "remove", position, itemid}
-- [actionid] = {"floor", "replace", position, {itemid_from, itemid_to}, {relocate_from, relocate_to}}

    [45014] = {"floor", "add", Position(100, 93, 7), 406},
    [45015] = {"floor", "remove", Position(102, 93, 7), 406},
    [45016] = {"floor", "replace", Position(104, 93, 7), {406, 407}, {false, false}},
    [45017] = {"floor", "replace", Position(106, 93, 6), {461, 462}, {false, true}}, -- true means YES do relocate.
}

local relocateDirections = {
    ["north"] = {0, -1},
    ["east"]  = {1, 0},
    ["south"] = {0, 1},
    ["west"]  = {-1, 0},
}

local function transposeFields(fromPosition, toPosition, transpose)
    local tile = Tile(fromPosition)
    if tile then
        local items = tile:getItems()
        if items then
            for i, item in ipairs(items) do
                if transpose == true then
                    item:moveTo(toPosition)
                else
                    item:remove()
                end
            end
        end
    end
end

local leverActions = Action()

function leverActions.onUse(player, item, fromPosition, target, toPosition, isHotkey)
    local item_id = item.itemid
    local index = config[item.actionid]
    local tile = Tile(index[3])
    
    -- add
    if index[2] == "add" and item_id == levers.left or index[2] == "remove" and item_id == levers.right then
        if index[1] == "object" then
            local relocatePosition = Position(index[3].x + relocateDirections[index[5]][1], index[3].y + relocateDirections[index[5]][2], index[3].z)
            tile:relocateTo(relocatePosition)
            transposeFields(index[3], relocatePosition, true)
            Game.createItem(index[4], 1, index[3])
        elseif index[1] == "floor" then
            Game.createTile(index[3], true)
            Game.createItem(index[4], 1, index[3])
        end
        
    -- remove
    elseif index[2] == "remove" and item_id == levers.left or index[2] == "add" and item_id == levers.right then
        if index[1] == "object" then
            local object = tile:getItemById(index[4])
            object:remove()
        elseif index[1] == "floor" then
            local relocatePosition = Position(index[3].x, index[3].y, index[3].z + 1)
            tile:relocateTo(relocatePosition)
            transposeFields(index[3], relocatePosition, false)
            tile:getGround():remove()
        end
        
    -- replace
    elseif index[2] == "replace" then
        local transformTo = tile:getItemCountById(index[4][1]) > 0 and 2 or 1
        local object = tile:getItemById(transformTo == 2 and index[4][1] or index[4][2])            
        object:transform(index[4][transformTo])
        if index[1] == "floor" and index[5][transformTo] == true then
            local relocatePosition = Position(index[3].x, index[3].y, index[3].z + 1)
            tile:relocateTo(relocatePosition)
            transposeFields(index[3], relocatePosition, false)
        end
        
    end
    
    item:transform(item_id == levers.left and levers.right or levers.left)
    return true
end

for k, v in pairs(config) do
    leverActions:aid(k)
end
leverActions:register()
 
Hi @Xikini. So good to see you back on this! Here's my request ^^

An alternative for raid system, entirely based on revscriptsys. What I need is to spawn a list of creatures (example firstlist = demon, skeleton, troll, then secondlist = deer, rabbit, rat), with a desired number (from 1 to 50 creatures for example), inside the area x to y (frompos, topos), and turnable z (true if you wan't to spawn creatures on upper floors or lower floors, false if you want to spawn only at z: 7) and start every "x" hours/minutes/etc.

What is really important is to check if the raid has been already excequted inside the coordinates, to avoid possible crashes. So this ideas comes to my mind.

a) Create specific monsters for the raid, so it checks inside the area for the monstername (a.k.a Rat2, Demon2, or RatEvent, DemonEvent). This way the check can differ from normal monsters (ex. if there's a rat inside rat raid area). and delete them all if a new wave of the raid is going to start.

b) It really needs a method to spawn monsters in good positions, far from traps, this is really challenging so I don't think this going to be solved really quick. I asked to zbisu's Nimbus bot to see how can a monster be spawned far from walls, it sent me something like this:

LUA:
local function isPositionValid(position)
    -- Check if the position is far from traps, closed spaces, and walls
    -- Implement your logic here to determine if the position is valid
    -- You can use functions like Tile.isWalkable, Tile.getTopUseThing, etc.
    -- to check for traps, closed spaces, and walls
    return true -- Return true if the position is valid, false otherwise
end

local function getRandomValidPosition()
    local maxAttempts = 100 -- Maximum number of attempts to find a valid position
    local position = nil

    for i = 1, maxAttempts do
        -- Generate a random position
        position = Position(math.random(100, 200), math.random(100, 200), 7)

        -- Check if the position is valid
        if isPositionValid(position) then
            return position -- Return the valid position
        end
    end

    return nil -- Return nil if a valid position couldn't be found
end

local function spawnMonster()
    local monsterName = "Monster" -- Replace with the name of the monster you want to spawn
    local position = getRandomValidPosition()

    if position then
        -- Spawn the monster at the valid position
        Game.createMonster(monsterName, position)
    else
        print("Couldn't find a valid position to spawn the monster.")
    end
end

-- Call the spawnMonster function to spawn the monster
spawnMonster()

No idea if this is going to be a good starting point. Anyways, I guess I have attached the most important stuff, i'm sure that you will get a much better idea than me once you think it as code. Thanks in advance!

PS. If it is possible too, would like to request a talkaction to trigger the raid. That works along with the other parameters such as time, if is running or not, etc.
 
Last edited:
I didn't do "Use X items on X objects to open a door" because I couldn't see a need for it?

But I did the rest. :)
(Flick Lever Remove Walls/Objects, was already done in my previous thread, so you can find it there.)

And here they are, in no particular order.

View attachment 81677View attachment 81678View attachment 81679View attachment 81680View attachment 81681

data/lib/core/core.lua -- add
LUA:
dofile('data/lib/core/xikiniCustomFunctions.lua')
data/lib/core/xikiniCustomFunctions.lua -- add
LUA:
function Position:isIdentical(position)
    if self.x == position.x and self.y == position.y and self.z == position.z then
        return true
    end
    return false
end

data/npc/Nugo.xml
XML:
<?xml version="1.0" encoding="UTF-8"?>
<npc name="Nugo" script="nugo.lua" walkinterval="2000" floorchange="0">
    <health now="100" max="100" />
    <look type="139" head="20" body="39" legs="45" feet="7" addons="0" />
</npc>
data/npc/scripts/nugo.lua
LUA:
local keywordHandler = KeywordHandler:new()
local npcHandler = NpcHandler:new(keywordHandler)
NpcSystem.parseParameters(npcHandler)

function onCreatureAppear(cid)              npcHandler:onCreatureAppear(cid)            end
function onCreatureDisappear(cid)           npcHandler:onCreatureDisappear(cid)         end
function onCreatureSay(cid, type, msg)      npcHandler:onCreatureSay(cid, type, msg)    end
function onThink()                          npcHandler:onThink()                        end

local function greetCallback(cid)
    local player = Player(cid)
    npcHandler.topic[cid] = 0
    return true
end

-- if you give the NPC 15 red apples, he will give you a storage
-- -1 means you haven't started the quest
-- 0 means you have started but not finished
-- 1 means you finished the quest
local config = {
    storageKey = 45020,
    requiredItemId = 2674, -- red apple
    requiredAmount = 15
}

local function creatureSayCallback(cid, type, msg)
    if not npcHandler:isFocused(cid) then
        return false
    end

    local player = Player(cid)
    local storageValue = player:getStorageValue(config.storageKey)
   
    if msgcontains(msg, "quest") then
        if storageValue == -1 then
            npcHandler:say("Would you like to help me?", cid)
            npcHandler.topic[cid] = 1
        elseif storageValue == 0 then
            npcHandler:say("Have you brought the 15 apples?", cid)
            npcHandler.topic[cid] = 1
        else
            npcHandler:say("Thanks for the help earlier, but I have nothing else for you.", cid)
            npcHandler.topic[cid] = 0
        end
       
    elseif npcHandler.topic[cid] == 1 then
        if storageValue == -1 then
            if msgcontains(msg, "yes") then
                npcHandler:say("Alright, thanks! Bring me 15 red apples and ask about {quest} again.", cid)
                player:setStorageValue(config.storageKey, 0)
            else
                npcHandler:say("Guess not.", cid)
            end
        else
            if msgcontains(msg, "yes") then
                if player:getItemCount(config.requiredItemId) >= config.requiredAmount then
                    player:removeItem(config.requiredItemId, config.requiredAmount)
                    player:setStorageValue(config.storageKey, 1)
                    npcHandler:say("Thanks! Here's your quest storage. Enjoy!", cid)
                else
                    npcHandler:say("You don't seem to have enough red apples. Come back when you have them.", cid)
                end
            else
                npcHandler:say("Guess not.", cid)
            end
        end
        npcHandler.topic[cid] = 0

    end
    return true
end

local function onAddFocus(cid)

end

local function onReleaseFocus(cid)

end

npcHandler:setCallback(CALLBACK_ONADDFOCUS, onAddFocus)
npcHandler:setCallback(CALLBACK_ONRELEASEFOCUS, onReleaseFocus)

npcHandler:setCallback(CALLBACK_GREET, greetCallback)
npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
npcHandler:addModule(FocusModule:new())

data/scripts/onUse_leversSpecificOrder.lua
LUA:
local config = {
    actionId = 45019,
    leverItemIds = {left = 1945, right = 1946},
    leverPositions = {
        Position(1004, 999, 7),
        Position(1005, 999, 7),
        Position(1006, 999, 7), -- use the levers in correct order
        Position(1007, 999, 7),
        Position(1008, 999, 7)
    },
    respawnTime = 5,
    removeItems = {
        {itemId = 1026, position = Position(1004, 998, 7)}, -- then all of these positions and items will get removed (use walls / items that can't be moved)
        {itemId = 1026, position = Position(1005, 998, 7)},
        {itemId = 1026, position = Position(1006, 998, 7)},
        {itemId = 1026, position = Position(1007, 998, 7)},
        {itemId = 1026, position = Position(1008, 998, 7)}
    }
}

local lastUsedLever = 0

local function resetAllLevers()
    lastUsedLever = 0
    for i = 1, #config.leverPositions do
        local tile = Tile(config.leverPositions[i])
        if not tile then
            print("Error, tile not found! Error: 45000888")
            return true
        end
        local tileItem = tile:getItemById(config.leverItemIds.right)
        if not tileItem then
            print("Error, lever not found! Error: 45000999")
            return true
        end
        tileItem:transform(config.leverItemIds.left)
    end
end



local leverActions = Action()

function leverActions.onUse(player, item, fromPosition, target, toPosition, isHotkey)
    if item:getId() == config.leverItemIds.right then
        return true
    end
    item:transform(config.leverItemIds.right)
    if config.leverPositions[lastUsedLever + 1]:isIdentical(toPosition) then
        lastUsedLever = lastUsedLever + 1
    end
    for i = 1, #config.leverPositions do
        local tile = Tile(config.leverPositions[i])
        if not tile then
            print("Error, tile not found! Error: 45000777")
            return true
        end
        local tileItem = tile:getItemById(config.leverItemIds.right)
        if not tileItem then
            return true
        end
    end
    if #config.leverPositions ~= lastUsedLever then
        addEvent(resetAllLevers, 1000)
        return true
    end
    for _, v in pairs(config.removeItems) do
        local tile = Tile(v.position)
        if not tile then
            print("Error, tile not found! Error: 45001111")
            return
        end
        local tileItem = tile:getItemById(v.itemId)
        if tileItem then
            tileItem:remove()
            addEvent(Game.createItem, config.respawnTime * 1000, v.itemId, 1, v.position)
        end
    end
    addEvent(resetAllLevers, config.respawnTime * 1000)
    return true
end

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

data/scripts/onStepIn_multipleLocations_openPassageway.lua
LUA:
local config = {
    requiredPositions = {
        Position(997, 995, 7),
        Position(997, 996, 7), -- if all these positions have a player.. remove items
        Position(998, 995, 7),
        Position(998, 996, 7)
    },
    respawnTime = 5,
    removeItems = {
        {itemId = 1050, position = Position(997, 993, 7)}, -- then all of these positions and items will get removed (use walls / items that can't be moved)
        {itemId = 1050, position = Position(998, 993, 7)},
    }
}

local moveevent = MoveEvent()

function moveevent.onStepIn(creature, item, position, fromPosition)
    if not creature:isPlayer() then
        return true
    end

    local doRemoveItems = true
    for _, _position in ipairs(config.requiredPositions) do
        local tile = Tile(_position)
        if not tile then
            print("Error, tile not found! Error: 45000444")
            return
        end
        local foundCreature = tile:getTopVisibleCreature(creature)
        if foundCreature and foundCreature:isPlayer() then
            _position:sendMagicEffect(CONST_ME_MAGIC_GREEN)
        else
            _position:sendMagicEffect(CONST_ME_MAGIC_RED)
            doRemoveItems = false
        end
    end
   
    if not doRemoveItems then
        return true
    end
   
    for _, v in pairs(config.removeItems) do
        local tile = Tile(v.position)
        if not tile then
            print("Error, tile not found! Error: 45000555")
            return
        end
        local tileItem = tile:getItemById(v.itemId)
        if tileItem then
            tileItem:remove()
            addEvent(Game.createItem, config.respawnTime * 1000, v.itemId, 1, v.position)
        end
    end
    return true
end

for _, position in ipairs(config.requiredPositions) do
    moveevent:position(position)
end
moveevent:register()

data/scripts/onUse_useMultipleObjects_openPassageway.lua
LUA:
--X Players Click X objects within X time to remove X walls/objects


local config = {
    actionId = 45018,
    useTime = 5000, -- milliseconds (if all objects used within 2 seconds, removeItems)
    clickableObjectPositions = {
        Position(1004, 993, 7),
        Position(1004, 996, 7),
        Position(1008, 993, 7),
        Position(1008, 996, 7)
    },
    respawnTime = 5, -- seconds
    removeItems = {
        {itemId = 1050, position = Position(1006, 993, 7)} -- then all of these positions and items will get removed (use walls / items that can't be moved)
    }
}

local usedObjects = { -- don't edit
--    [Position(1000, 1000, 7)] = {lastUsed = 0}
}


local action = Action()

function action.onUse(player, item, fromPosition, target, toPosition, isHotkey)
    -- check if removeItems are there
    local tileItems = {}
    for _, v in pairs(config.removeItems) do
        local tile = Tile(v.position)
        if not tile then
            print("Error, tile not found! Error: 45000333")
            return true
        end
        local tileItem = tile:getItemById(v.itemId)
        if not tileItem then
            return true
        end
        tileItems[#tileItems + 1] = {item = tileItem, itemId = v.itemId, position = v.position}
    end

    local currentTime = os.mtime()
    for _, position in ipairs(config.clickableObjectPositions) do
        if toPosition:isIdentical(position) then
            usedObjects[position] = {lastUsed = currentTime}
            break
        end
    end
    toPosition:sendMagicEffect(CONST_ME_MAGIC_GREEN)
   
    local objectCount = 0
    local lowestTime = currentTime
    local highestTime = 0
    for k, v in pairs(usedObjects) do
        objectCount = objectCount + 1
        lowestTime = math.min(v.lastUsed, lowestTime)
        highestTime = math.max(v.lastUsed, highestTime)
    end
   
    if objectCount < #config.clickableObjectPositions then
        return true
    end
   
    if highestTime - lowestTime > config.useTime then
        return true
    end
   
    for i = 1, #tileItems do
        tileItems[i].item:remove()
        addEvent(Game.createItem, config.respawnTime * 1000, tileItems[i].itemId, 1, tileItems[i].position)
    end
    usedObjects = {}
    return true
end

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

data/scripts/place_items_on_tiles_remove_object.lua
LUA:
local config = {
    requiredItems = {
        {itemId = 2674, amount = 5, position = Position(1000, 995, 7)},
        {itemId = 2467, amount = 1, position = Position(1001, 995, 7)}, -- if all these positions have the required items
        {itemId = 2195, amount = 1, position = Position(1002, 995, 7)}
    },
    removeItems = {
        {itemId = 1026, position = Position(1001, 993, 7)} -- then all of these positions and items will get removed (use walls / items that can't be moved)
    }
}

local function addRemoveItems()
    local doRemoveItems = true
    for _, v in pairs(config.requiredItems) do
        local tile = Tile(v.position)
        if not tile then
            print("Error, tile not found! Error: 45000111")
            return
        end
        if tile:getItemCountById(v.itemId) < v.amount then
            doRemoveItems = false
            break
        end
    end
   
    for _, v in pairs(config.removeItems) do
        local tile = Tile(v.position)
        if not tile then
            print("Error, tile not found! Error: 45000222")
            return
        end
        local tileItem = tile:getItemById(v.itemId)
        if doRemoveItems and tileItem then
            tileItem:remove()
        end
        if not doRemoveItems and not tileItem then
            Game.createItem(v.itemId, 1, v.position)
        end
    end
end

local moveevent = MoveEvent()

function moveevent.onAddItem(moveitem, tileitem, position)
    addRemoveItems()
    return true
end

for _, v in pairs(config.requiredItems) do
    moveevent:position(v.position)
end
moveevent:register()


local moveevent = MoveEvent()

function moveevent.onRemoveItem(moveitem, tileitem, position)
    addRemoveItems()
    return true
end

for _, v in pairs(config.requiredItems) do
    moveevent:position(v.position)
end
moveevent:register()

data/scripts/onUse_leverActions.lua
LUA:
--[[
    "object" -> anything that is not a floor tile.
    "floor" floor tiles (roof/ground/hole)
   
    "add" / "remove" -> tells the script what to do when lever is on the left. (after it flops, it will do the opposite)
    "replace" -> transforms an item into another item, instead of adding/removing
   
]]--

local levers = {left = 1945, right = 1946}
local config = {
-- [actionid] = {"object", "add", position, itemid, relocate_direction}
-- [actionid] = {"object", "remove", position, itemid, relocate_direction}
-- [actionid] = {"object", "replace", position, {itemid_from, itemid_to}}

    [45011] = {"object", "add", Position(94, 93, 7), 1026, "south"},
    [45012] = {"object", "remove", Position(96, 93, 7), 1026, "south"},
    [45013] = {"object", "replace", Position(98, 93, 7), {1026, 1050}},
   
-- [actionid] = {"floor", "add", position, itemid}
-- [actionid] = {"floor", "remove", position, itemid}
-- [actionid] = {"floor", "replace", position, {itemid_from, itemid_to}, {relocate_from, relocate_to}}

    [45014] = {"floor", "add", Position(100, 93, 7), 406},
    [45015] = {"floor", "remove", Position(102, 93, 7), 406},
    [45016] = {"floor", "replace", Position(104, 93, 7), {406, 407}, {false, false}},
    [45017] = {"floor", "replace", Position(106, 93, 6), {461, 462}, {false, true}}, -- true means YES do relocate.
}

local relocateDirections = {
    ["north"] = {0, -1},
    ["east"]  = {1, 0},
    ["south"] = {0, 1},
    ["west"]  = {-1, 0},
}

local function transposeFields(fromPosition, toPosition, transpose)
    local tile = Tile(fromPosition)
    if tile then
        local items = tile:getItems()
        if items then
            for i, item in ipairs(items) do
                if transpose == true then
                    item:moveTo(toPosition)
                else
                    item:remove()
                end
            end
        end
    end
end

local leverActions = Action()

function leverActions.onUse(player, item, fromPosition, target, toPosition, isHotkey)
    local item_id = item.itemid
    local index = config[item.actionid]
    local tile = Tile(index[3])
   
    -- add
    if index[2] == "add" and item_id == levers.left or index[2] == "remove" and item_id == levers.right then
        if index[1] == "object" then
            local relocatePosition = Position(index[3].x + relocateDirections[index[5]][1], index[3].y + relocateDirections[index[5]][2], index[3].z)
            tile:relocateTo(relocatePosition)
            transposeFields(index[3], relocatePosition, true)
            Game.createItem(index[4], 1, index[3])
        elseif index[1] == "floor" then
            Game.createTile(index[3], true)
            Game.createItem(index[4], 1, index[3])
        end
       
    -- remove
    elseif index[2] == "remove" and item_id == levers.left or index[2] == "add" and item_id == levers.right then
        if index[1] == "object" then
            local object = tile:getItemById(index[4])
            object:remove()
        elseif index[1] == "floor" then
            local relocatePosition = Position(index[3].x, index[3].y, index[3].z + 1)
            tile:relocateTo(relocatePosition)
            transposeFields(index[3], relocatePosition, false)
            tile:getGround():remove()
        end
       
    -- replace
    elseif index[2] == "replace" then
        local transformTo = tile:getItemCountById(index[4][1]) > 0 and 2 or 1
        local object = tile:getItemById(transformTo == 2 and index[4][1] or index[4][2])           
        object:transform(index[4][transformTo])
        if index[1] == "floor" and index[5][transformTo] == true then
            local relocatePosition = Position(index[3].x, index[3].y, index[3].z + 1)
            tile:relocateTo(relocatePosition)
            transposeFields(index[3], relocatePosition, false)
        end
       
    end
   
    item:transform(item_id == levers.left and levers.right or levers.left)
    return true
end

for k, v in pairs(config) do
    leverActions:aid(k)
end
leverActions:register()
Insane work man i really think people under-estimate the value of these scripts. Any mapper with some creativity & basic scripting knowledge can make some fun and interesting quests. This should almost be in its own thread. If i had these 10 years ago it would of really added alot of depth to my server when it was in its infancy. Great job.
 
Script regarding players in guilds.

If a player is in a guild and beats someone who has another guild - he doesn't get a frag.
If he kills someone from his guild, he has a frag.
Of course, if someone from a guild kills players without a guild and vice versa, he also gets a frag.
 
Insane work man i really think people under-estimate the value of these scripts. Any mapper with some creativity & basic scripting knowledge can make some fun and interesting quests. This should almost be in its own thread. If i had these 10 years ago it would of really added alot of depth to my server when it was in its infancy. Great job.

Exactly! A lot of scripts can be worked around to suit the needs of the server just requires some imagination!
imagination GIF
 
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

triple effect spell.gif

data/lib/core/core.lua -- add
LUA:
dofile('data/lib/core/xikiniCustomFunctions.lua')
data/lib/core/xikiniCustomFunctions.lua -- add
LUA:
function sendAreaEffect(centrePosition, effectTable, magicEffect, direction)
    
    local centre = 0
    local length = #effectTable   -- north/south (amount of arrays in main array)
    local width = #effectTable[1] --  west/east (amount of items in each array)
    
    for _length = 1, length do
        for _width = 1, width do
            if effectTable[_length][_width] > 1 then
                centre = {length = _length, width = _width}
                print(_length, _width)
                break
            end
        end
        if centre ~= 0 then
            break
        end
    end
    
    if centre == 0 then
        print("Lua Error: no centre found for effectTable. (3 = centre + effect | 2 = centre | 1 = effect | 0 = no effect)")
        return
    end
    
    if not Position(centrePosition) then -- Idk if this works or not. Would have to test lol
        print("Lua Error: a valid position was not used.")
        return
    end
    
    for _length = 1, length do
        for _width = 1, width do
            local effectType = effectTable[_length][_width]
            if effectType == 1 or effectType == 3 then
                local deltaX = _width - centre.width
                local deltaY = _length - centre.length
                
                if direction then
                    if direction == DIRECTION_NORTH then
                        deltaY = deltaY - 1
                    elseif direction == DIRECTION_SOUTH then
                        deltaY = -deltaY + 1
                    elseif direction == DIRECTION_EAST then
                        local temp = deltaX
                        deltaX = -deltaY + 1
                        deltaY = temp
                    elseif direction == DIRECTION_WEST then
                        local temp = deltaX
                        deltaX = deltaY - 1
                        deltaY = -temp
                    else
                        print("Lua Error: not a valid direction. use 0,1,2,3 or DIRECTION_SOUTH / WEST / EAST / NORTH")
                        return
                    end
                end
                local effectPosition = Position(centrePosition.x + deltaX, centrePosition.y + deltaY, centrePosition.z)
                effectPosition:sendMagicEffect(magicEffect)
            end
        end
    end 
end
data/scripts/tripleEffectSpell.lua (make sure to use a spell:id that doesn't exist on your server)
LUA:
local combat = Combat()
combat:setParameter(COMBAT_PARAM_TYPE, COMBAT_ENERGYDAMAGE)
combat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_MAGIC_BLUE)
combat:setParameter(COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_ENERGY)
combat:setArea(createCombatArea(AREA_SQUAREWAVE5))

function onGetFormulaValues(player, level, magicLevel)
    local min = (level / 5) + (magicLevel * 4.5) + 20
    local max = (level / 5) + (magicLevel * 7.6) + 48
    return -min, -max
end

combat:setCallback(CALLBACK_PARAM_LEVELMAGICVALUE, "onGetFormulaValues")


local function castDelayedSpell(creatureId, position, lookDir, area, areaEffect, shootEffect)
    local creature = Creature(creatureId)
    if not creature then
        return
    end
    local offset = Position.directionOffset[lookDir]
    position:sendDistanceEffect(Position(position.x + offset.x, position.y + offset.y, position.z), shootEffect)
    sendAreaEffect(position, area, areaEffect, lookDir)
end

local spell = Spell("instant")

function spell.onCastSpell(creature, variant)
    local creatureId = creature:getId()
    local creaturePosition = creature:getPosition()    
    local originalDirection = creature:getDirection()
    combat:execute(creature, variant)
    addEvent(castDelayedSpell, 500, creatureId, creaturePosition, originalDirection, AREA_SQUAREWAVE5, CONST_ME_MAGIC_RED, CONST_ANI_FIRE)
    addEvent(castDelayedSpell, 1000, creatureId, creaturePosition, originalDirection, AREA_SQUAREWAVE5, CONST_ME_MAGIC_GREEN, CONST_ANI_POISON)
    return 
end

spell:name("triple effect spell")
spell:words("triple effect spell")
spell:group("attack")
spell:id(179) -- MAKE SURE TO ADJUST FOR YOUR SERVER (check data/spells/spells.xml)
spell:cooldown(1000)
spell:groupCooldown(1000)
spell:level(1)
spell:mana(10)
spell:isSelfTarget(false)
spell:isPremium(true)
spell:needLearn(false)
spell:needDirection(true)
spell:register()
 
Hello thanks and for your time!

[Script Request]
A script that will show top 3 players clones, name, outfit and level as a monster?

Players name [LvL: 999]

Thank you!
 
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
 
Last edited:
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
Got some help in support board to find a cleaner solution, using the default spell/combat system.
You can find that solution here.

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?

bandicam2024-01-3000-05-55-638-ezgif.com-video-to-gif-converter.gif

Lot's of safety checks not shown in vid.
Anti-griefing of houses is already setup in my sources.
If you don't have that, maybe consider installing this one.

Setup similar to this, so that players outside of the house do not have access to the items, only the levers.
Make sure the walls with blackboards are also house tiles, or the prices won't save.

Untitled.png

data/lib/core/core.lua -- add
LUA:
dofile('data/lib/core/xikiniCustomFunctions.lua')
data/lib/core/xikiniCustomFunctions.lua -- add
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/houseMarket.lua
LUA:
local config = {
    blackboard = {   
        ids = {1811},    --add more stationary text objects here
        actionId = 45021 -- set on all blackboards
    },
    lever = {
        ids = {
            {left = 1945, right = 1946},
            --{left = 1945, right = 1946} -- if you have other levels / objects you want to use
        }
    },
    leverConfirmSpeed = 2, -- seconds (how fast lever resets if purchase isn't confirmed)
    leverData = {
        [45022] = {
            offset = {
                blackboard = {x = 0, y = -2}, -- tells us where the blackboard is in relation to the lever
                sellTile = {x = 0, y = -1}    -- same as above, but for the item tile
            }
        },
        --[00000] = {
        --    offset = {
        --        blackboard = {x = 0, y = 0}, -- x -> negative is left, positive is right
        --        sellTile = {x = 0, y = 0}    -- y -> negative is up, positive is down
        --    }
        --},
    },
    useDepot = false,
    usePlayerTown = true -- if set to false, then will use 'no town' depot (aka: linked depots.. instead of 'thais depot') (only used if useDepot is true)
}
-- END OF CONFIG, don't edit under here

local activateLevers = {
--    ["Position(1000, 1000, 7)"] = {playerId = playerId, eventId = event}
}

local function isStringANumber(str)
    return str:match("^%d+$") ~= nil
end

local function removeSpaces(str)
    return str:gsub("%s+", "")
end

local function positionToString(position)
    return "Position(" .. position.x .. ", " .. position.y .. ", " .. position.z .. ")"
end

local function sendDelayedMessage(playerId, messageType, message)
    local player = Player(playerId)
    if not player then
        return
    end
    player:sendTextMessage(messageType, message)
end

local function modifyBlackboardText(position, itemId, textAddition)
    local tile = Tile(position)
    if not tile then
        print("modifyBlackboardText -> blackboard not on a tile " .. positionToString(position))
        return false
    end
    local textItem = tile:getItemById(itemId)
    if not textItem then
        print("modifyBlackboardText -> Unable to find blackboard " .. positionToString(position))
        return false
    end
    local text = textItem:getAttribute(ITEM_ATTRIBUTE_TEXT)
    textItem:setAttribute(ITEM_ATTRIBUTE_TEXT, text .. textAddition)
    return true
end

local function verifyTextWasWrittenByCurrentHouseOwner(item, eraseBoard) -- bad function, don't use for other scenario's xD
    local position = item:getPosition()
    local tile = Tile(position)
    if not tile then
        print("verifyTextWasWrittenByCurrentOwner -> blackboard not on a tile " .. positionToString(position))
        return false
    end
    local house = tile:getHouse()
    if not house then
        print("verifyTextWasWrittenByCurrentOwner -> no house here " .. positionToString(position))
        return false
    end
   
    local text = item:getAttribute(ITEM_ATTRIBUTE_TEXT)
    local splitResult = string.split(text, ",")
   
    if house:getOwnerGuid() ~= tonumber(splitResult[2]) then
        item:removeAttribute(ITEM_ATTRIBUTE_TEXT)
        item:setAttribute(ITEM_ATTRIBUTE_TEXT, "")
        return false
    end
   
    if eraseBoard and tonumber(splitResult[1]) then
        item:removeAttribute(ITEM_ATTRIBUTE_TEXT)
        item:setAttribute(ITEM_ATTRIBUTE_TEXT, "")
        return "erased"
    end
    return true
end

local function getBlackboardPrice(position)
    local tile = Tile(position)
    if not tile then
        print("getBlackboardPrice -> blackboard not on a tile " .. positionToString(position))
        return false
    end
    local textItem
    for _, itemId in pairs(config.blackboard.ids) do
        textItem = tile:getItemById(itemId)
        if textItem then
            break
        end
    end
    if not textItem then
        print("getBlackboardPrice -> Unable to find blackboard " .. positionToString(position))
        return false
    end
   
    verifyTextWasWrittenByCurrentHouseOwner(textItem)

    local text = textItem:getAttribute(ITEM_ATTRIBUTE_TEXT)
    local splitResult = string.split(text, ",")
    local price = tonumber(splitResult[1]) or 0
    return price
end

local function resetLever(position, leverLeftId, leverRightId)
    activateLevers[positionToString(position)] = nil
    local lever = Tile(position):getItemById(leverRightId)
    if not lever then
        return
    end
    lever:transform(leverLeftId)
end

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

function formatNumberWithCommas(number)
    local formatted = tostring(number)
    local k
    while true do 
        formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
        if k == 0 then break end
    end
    return formatted
end

local blackboard_action = Action()

function blackboard_action.onUse(player, item, fromPosition, target, toPosition, isHotkey)
    local erased = verifyTextWasWrittenByCurrentHouseOwner(item, true)
    if erased == "erased" then
        player:say("Price removed successfully.", TALKTYPE_MONSTER_SAY, nil, player)
        return true
    end
    if not player:hasEvent(CREATURE_EVENT_TEXTEDIT, "onTextEdit_HouseMarket") then
        player:registerEvent("onTextEdit_HouseMarket")
    end
    return false
end

blackboard_action:aid(config.blackboard.actionId)
blackboard_action:register()


local lever_action = Action()

function lever_action.onUse(player, item, fromPosition, target, toPosition, isHotkey)
    local activatedLever = activateLevers[positionToString(toPosition)]
    if activatedLever and activatedLever.playerId ~= player:getId() then
        player:say("Another player is attempting to purchase this item.", TALKTYPE_MONSTER_SAY, nil, player)
        return true
    end

    local leverId = item:getId()
    local leverId_index = 0
    for i = 1, #config.lever.ids do
        if leverId == config.lever.ids[i].left or leverId == config.lever.ids[i].right then
            leverId_index = i
            break
        end
    end
   
    if leverId_index == 0 then
        player:say("Lever id not found. Contact Gamemaster.", TALKTYPE_MONSTER_SAY, nil, player)
        return true
    end
   
    if config.lever.ids[leverId_index].left == leverId then
        local leverIndex = config.leverData[item:getActionId()]
        local blackboard_position = Position(toPosition.x + leverIndex.offset.blackboard.x, toPosition.y + leverIndex.offset.blackboard.y, toPosition.z)
        local price = getBlackboardPrice(blackboard_position)
       
        if price == 0 then
            player:say("Item unavailable. No price has been set.", TALKTYPE_MONSTER_SAY, nil, player)
            return true
        end
       
        local thingPosition = Position(toPosition.x + leverIndex.offset.sellTile.x, toPosition.y + leverIndex.offset.sellTile.y, toPosition.z)
        local tile = Tile(thingPosition)
        if not tile then
            player:say("sellTile not found. Contact Gamemaster.", TALKTYPE_MONSTER_SAY, nil, player)
            return true
        end
       
        local thing = tile:getTopDownItem()
        if not thing then
            player:say("No item is available for purchase.", TALKTYPE_MONSTER_SAY, nil, player)
            return true
        end
       
        item:transform(config.lever.ids[leverId_index].right)
        local event = addEvent(resetLever, 1000 * config.leverConfirmSpeed, toPosition, config.lever.ids[leverId_index].left, config.lever.ids[leverId_index].right)
        activateLevers[positionToString(toPosition)] = {playerId = player:getId(), eventId = event, price = price, thing = thing}
       
        player:say("Would you like to purchase " .. getItemNameString(thing) .. " for " .. formatNumberWithCommas(price) .. " gold coins?", TALKTYPE_MONSTER_SAY, nil, player)
    else
        stopEvent(activatedLever.eventId)
        activateLevers[positionToString(toPosition)] = nil -- we have a copy of the table already, so can set to nil here
        item:transform(config.lever.ids[leverId_index].left)
       
        local leverIndex = config.leverData[item:getActionId()]
        local blackboard_position = Position(toPosition.x + leverIndex.offset.blackboard.x, toPosition.y + leverIndex.offset.blackboard.y, toPosition.z)
        local price = getBlackboardPrice(blackboard_position)
       
        if price == 0 then
            player:say("Item unavailable. No price has been set.", TALKTYPE_MONSTER_SAY, nil, player)
            return true
        end
       
        local thingPosition = Position(toPosition.x + leverIndex.offset.sellTile.x, toPosition.y + leverIndex.offset.sellTile.y, toPosition.z)
        local tile = Tile(thingPosition)
        if not tile then
            player:say("sellTile not found. Contact Gamemaster.", TALKTYPE_MONSTER_SAY, nil, player)
            return true
        end
       
        local thing = tile:getTopDownItem()
        if not thing then
            player:say("No item is available for purchase.", TALKTYPE_MONSTER_SAY, nil, player)
            return true
        end
       
        if price ~= activatedLever.price then
            player:say("Price of the item has changed.", TALKTYPE_MONSTER_SAY, nil, player)
            return true
        end
       
        if thing ~= activatedLever.thing then
            player:say("Item is no longer available for purchase.", TALKTYPE_MONSTER_SAY, nil, player)
            return true
        end
       
        if player:getTotalMoney() < activatedLever.price then
            player:say("You do not have sufficient funds to purchase this item.", TALKTYPE_MONSTER_SAY, nil, player)
            return true
        end
       
        player:removeTotalMoney(activatedLever.price)
       
        local ownerGUID = tile:getHouse():getOwnerGuid()
        local targetPlayer = Player(ownerGUID)
        if targetPlayer then
            targetPlayer:setBankBalance(targetPlayer:getBankBalance() + activatedLever.price)
        else
            db.query("UPDATE `players` SET `balance` = `balance` + " .. activatedLever.price .. " WHERE `id` = '" .. ownerGUID .. "'")
        end
       
        thingPosition:sendMagicEffect(CONST_ME_FERUMBRAS)
        player:say("You have purchased " .. getItemNameString(thing) .. ".", TALKTYPE_MONSTER_SAY, nil, player)
       
        local soldItem = thing:clone()
        if config.useDepot then
            local townId = config.usePlayerTown and player:getTown():getId() or 0
            local depot = player:getDepotChest(townId, true)
            depot:addItemEx(soldItem, INDEX_WHEREEVER, FLAG_NOLIMIT)
            player:say("Item delivered to " .. Town(townId):getName() .. ".", TALKTYPE_MONSTER_SAY, nil, player)
        else
            player:getInbox():addItemEx(soldItem, INDEX_WHEREEVER, FLAG_NOLIMIT)
            player:say("Item delivered to inbox.", TALKTYPE_MONSTER_SAY, nil, player)
        end
        thing:remove()
    end
    return true
end

for actionId, _ in pairs(config.leverData) do
    lever_action:aid(actionId)
end
lever_action:register()


local creatureevent = CreatureEvent("onTextEdit_HouseMarket")

function creatureevent.onTextEdit(player, item, text)
    if not table.contains(config.blackboard.ids, item:getId()) then
        return true
    end
    if item:getActionId() ~= config.blackboard.actionId then
        return true
    end
   
    local position = item:getPosition()
    local tile = Tile(position)
   
    if not tile then
        player:say("Error! Please contact gamemaster.", TALKTYPE_MONSTER_SAY, nil, player)
        print("onTextEdit_HouseMarket -> blackboard not on a tile " .. positionToString(position))
        return false
    end
   
    local house = tile:getHouse()
    if not house then
        player:say("Error! Please contact gamemaster.", TALKTYPE_MONSTER_SAY, nil, player)
        print("onTextEdit_HouseMarket -> blackboard not on a house tile " .. positionToString(position))
        return false
    end
   
    if house:getOwnerGuid() ~= player:getGuid() then
        player:say("Failed to set price!\nOnly the house owner can set prices.", TALKTYPE_MONSTER_SAY, nil, player)
        return false
    end
   
    text = removeSpaces(text)
   
    if text == "" then
        player:say("Price removed successfully.", TALKTYPE_MONSTER_SAY, nil, player)
        return true
    end
   
    if not isStringANumber(text) then
        player:say("Failed to set price!\nCan only use numbers.", TALKTYPE_MONSTER_SAY, nil, player)
        return false
    end
   
    player:say("Price set successfully.", TALKTYPE_MONSTER_SAY, nil, player)
    addEvent(modifyBlackboardText, 0, position, item:getId(), "," .. house:getOwnerGuid())
    return true
end

creatureevent:register()


local ec = EventCallback

ec.onLook = function(self, thing, position, distance, description)
    if thing:isItem() and thing:getActionId() == config.blackboard.actionId then
        verifyTextWasWrittenByCurrentHouseOwner(thing)       
        local description = "The price is " .. formatNumberWithCommas(getBlackboardPrice(position)) .. " gold coins."
        addEvent(sendDelayedMessage, 0, self:getId(), MESSAGE_INFO_DESCR, description)
        return false
    end
    return description
end

ec:register()
 
Last edited:
Hi,
I would need a code that will show in the onLook function how many kills a person has (since the beginning of his game on the server).

Additionally, I have an old frag system (3 kills to RS).
I would like it to:

1.) If a player who has a guild, kills a person from another guild he will not receive any frag.
2.) If a person having a guild kills a player from his guild he will receive a frag.
3.) If a person without a guild kills a person who has a guild and vice versa, also receives a fraga.

In summary, if a person participates in a guild and kills a person from another guild, he does not receive a frage.
Statistical kills must be counted normally, but are not penalized with a frag.
 
Hi there, I have a request if its possible to make.

Info:
  • TFS 1.4.2
  • Client 10.98

When a player reaches a certain lvl they will receive an item (this system is already in place). This item they can right-click to open a list of items. They can only choose ONE out of these items. When they have chosen the item they want (confirm button) , the chosen item will be added to the players backpack, and maybe a text will be displayed with the name of the item they choose. Then the item they right-clicked on will be deleted.
There would also need to be a cancel button where the item they right-clicked (the original reward) is not deleted, and can be opened at a later point.

It would also be a great feature if the player could click on any of the items in the list and press a "Details" button to get more info about the items before making their choice.

------------------------------------
I've tried editing an already existing crafting script where a lot of this is already possible, but I don't need it to be a "crafting-script" per say where you need ingredients etc.. And the script I tried to edit also doesn't show any information about the item, only the name and the ingredients it takes to craft it.

Hopefully this is a challenge you are up for and doesn't prove impossible :D

Ask me if anything is unclear.


- Erik
 
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

bandicam2024-01-3009-25-39-124-ezgif.com-video-to-gif-converter.gif

data/scripts/respawnAnchorSystem.lua
LUA:
local deathStorage = {
    x = 45023,
    y = 45024,
    z = 45025
}

local taverns = {
    Position(1000, 1000, 7),
    Position(980, 1020, 7),  -- if not within a respawn anchor, will respawn at closest tavern
    Position(1020, 1020, 7)
}

local respawnAnchors = {
    {    respawnAnchor = Position(1000, 1016, 7),
        anchorMap = {
            {from = Position(1004, 1013, 7), to = Position(1011, 1016, 7)},
            --{from = Position(1000, 1000, 7), to = Position(1001, 1001, 7)},
            --{from = Position(1000, 1000, 7), to = Position(1001, 1001, 7)}
    }    },
--    {    respawnAnchor = Position(1000, 1000, 7),
--        anchorMap = {
--            {from = Position(1000, 1000, 7), to = Position(1001, 1001, 7)},
--            {from = Position(1000, 1000, 7), to = Position(1001, 1001, 7)},
--            {from = Position(1000, 1000, 7), to = Position(1001, 1001, 7)}
--    }    },
--    {    respawnAnchor = Position(1000, 1000, 7),
--        anchorMap = {
--            {from = Position(1000, 1000, 7), to = Position(1001, 1001, 7)},
--            {from = Position(1000, 1000, 7), to = Position(1001, 1001, 7)},
--            {from = Position(1000, 1000, 7), to = Position(1001, 1001, 7)}
--    }    },
}

local function respawn(player)
    local x = player:getStorageValue(deathStorage.x)
    local y = player:getStorageValue(deathStorage.y)
    local z = player:getStorageValue(deathStorage.z)
    local playerPosition = Position(x, y, z)
    for _, anchor in pairs(respawnAnchors) do
        for _, anchorMap in pairs(anchor.anchorMap) do
            if playerPosition:isInRange(anchorMap.from, anchorMap.to) then
                player:teleportTo(anchor.respawnAnchor)
                return
            end
        end
    end
    
    local closestTavern = 0
    local nearestdistance = -1
    for i = 1, #taverns do
        local distance = playerPosition:getDistance(taverns[i])
        if nearestdistance == -1 or distance < nearestdistance then
            closestTavern = i
            nearestdistance = distance
        end
    end
    player:teleportTo(taverns[closestTavern])
end

local creatureevent = CreatureEvent("onDeath_respawnAnchorSystem")

function creatureevent.onDeath(creature, corpse, killer, mostDamageKiller, lastHitUnjustified, mostDamageUnjustified)
    local deathPosition = creature:getPosition()
    creature:setStorageValue(deathStorage.x, deathPosition.x)
    creature:setStorageValue(deathStorage.y, deathPosition.y)
    creature:setStorageValue(deathStorage.z, deathPosition.z)
    return true
end

creatureevent:register()

local creatureevent = CreatureEvent("onLogin_respawnAnchorSystem")

function creatureevent.onLogin(player)
    player:registerEvent("onDeath_respawnAnchorSystem")
    if player:getStorageValue(deathStorage.x) ~= -1 then
        respawn(player)
        player:setStorageValue(deathStorage.x, -1)
        player:setStorageValue(deathStorage.y, -1)
        player:setStorageValue(deathStorage.z, -1)
    end
    return true
end

creatureevent:register()
 
GIF by SpongeBob SquarePants

Ohmygod you managed to do it!
I had little faith in the Market system and even less in the Respawn but this will be huge!

Thank you!
- Now players can AFK sell items with their own market in a somewhat more immersive way.
-
And don't have to walk ages to get back to the spot where they died and respawn at the closest tavern or respawn anchor, perfect!
 
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!

Some notes at the bottom, but otherwise, enjoy. :)

bandicam2024-01-3011-25-38-369-ezgif.com-video-to-gif-converter.gif

data/scripts/onPrepareDeath_vocationProtection.lua
LUA:
local config = {
--    [{vocationIds}] = {cannotKill = {vocationIds}},   
    [{1, 2, 3, 4, 5}] = {cannotKill = {}},
    [{6, 7, 8, 9, 10}] = {cannotKill = {1, 2, 3, 4, 5}},
    [{11, 12, 13, 14, 15}] = {cannotKill = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}},
}

local templePosition = Position(1000, 1000, 7)
local gracePeriod = 120 -- seconds (creatures that harmed this player greater then 2 minutes ago are not calculated.)

local creatureevent = CreatureEvent("onPrepareDeath_vocationProtection")

function creatureevent.onPrepareDeath(creature, killer)
   
    -- setup damage map
    local damageMap = {}
    for creatureId, damage in pairs(creature:getDamageMap()) do
        local _creature = Creature(creatureId)
        if _creature then
            local master = _creature:getMaster()
            if master and master:isPlayer() then
                _creature = master
            end
            if _creature:isPlayer() then
                local name = _creature:getName()
                if not damageMap[name] then
                    damageMap[name] = {damage = 0, ticks = 0, vocationId = _creature:getVocation():getId()}
                end
                damageMap[name].damage = damageMap[name].damage + damage.total
                damageMap[name].ticks = damage.ticks
            end
        end
    end
   
    local currentTime = os.mtime()
    local dismissThreshold = currentTime - (gracePeriod * 1000)
    for k, v in pairs(damageMap) do
        if v.ticks > dismissThreshold then
            local vocationId = creature:getVocation():getId()
            for vocations, _v in pairs(config) do
                if table.contains(vocations, v.vocationId) then -- vocation doing damage
                    if table.contains(_v.cannotKill, vocationId) then -- vocation taking damage
                        creature:addHealth(creature:getMaxHealth())
                        creature:teleportTo(templePosition)
                        return false
                    end
                end
            end
        end
    end
   
    return true
end

creatureevent:register()

local creatureevent = CreatureEvent("onLogin_vocationProtection")

function creatureevent.onLogin(player)
    player:registerEvent("onPrepareDeath_vocationProtection")
    return true
end

creatureevent:register()

So basically it works like this..

If the player dies, and they were attacked by a player that cannot kill that vocation within the past 2 minutes, they will be teleported to the temple.

The skull system is entirely ignored, and only focuses on the vocations that are dealing damage to each other.
So if a tier 3 kills a tier 1 player, then the tier 3 will remain with their white skull as normal, until they can exit combat, but since the tier 1 player doesn't die, they won't obtain a 'frag'.
-> Although depending on pvp settings.. they might get a frag if the tier 1 went and died to a monster after like 3 minutes or something? I'm not sure how long the grace period for the skull system is on that. You could increase the grace period timer in the script until that is no longer an issue though.

Some caveats.. if you have 'tick damage', like a fire field or something, then even after teleporting, you'll still have those effects on the player.
-> Even if it's strong enough to kill the player, they'll just teleport again, so kind of a non-issue, aside from losing hp.

(player abuse scenario)
If some tier 3 vocation gives some 'tickle damage' to a tier 1 or 2 player.. then they are basically immune to dying.
I mean, if they die, they'd be sent to the temple.. but basically risk-free questing or something.

-----
These are kind of edge-cases though. So probably not worth worrying about.
All of that aside, it works quite nicely.
 
hey man,

i am wondering if you could make a rev script with this function:

there is an amulet (xxxx) id, when you use (x) id item on this amulet it gains stats.

there should be 6 diffrent stones 1 for each element (fire, earth, ice, energy, death and holy) each stone should give 0.5% if thats possible else 1% stats to this amulet stacking up to max 10% protection.

if rev script isnt possible then other things could work aswell.

thx in advance! :)
 
hey man,

i am wondering if you could make a rev script with this function:

there is an amulet (xxxx) id, when you use (x) id item on this amulet it gains stats.

there should be 6 diffrent stones 1 for each element (fire, earth, ice, energy, death and holy) each stone should give 0.5% if thats possible else 1% stats to this amulet stacking up to max 10% protection.

if rev script isnt possible then other things could work aswell.

thx in advance! :)
There's tons of systems like that on the forum ready to copy and run. :)
 
1.- spell that spams an alert before being cast (tutorialsquares).

bandicam2024-01-3120-36-10-522-ezgif.com-video-to-gif-converter.gif

data/scripts/alertSpell.lua
LUA:
local combat = Combat()
combat:setParameter(COMBAT_PARAM_TYPE, COMBAT_HOLYDAMAGE)
combat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_HOLYAREA)
combat:setArea(createCombatArea(AREA_CIRCLE3X3))

function onGetFormulaValues(player, level, magicLevel)
    local min = (level / 5) + (magicLevel * 5) + 25
    local max = (level / 5) + (magicLevel * 6.2) + 45
    return -min, -max
end

combat:setCallback(CALLBACK_PARAM_LEVELMAGICVALUE, "onGetFormulaValues")

local warning = Combat()
warning:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_TUTORIALSQUARE)
warning:setArea(createCombatArea(AREA_CIRCLE3X3))

local function delayedCast(creatureId, variant, combat)
    local creature = Creature(creatureId)
    if not creature then
        return
    end
    combat:execute(creature, variant)
end

local spell = Spell(SPELL_INSTANT)

function spell.onCastSpell(creature, variant)
    warning:execute(creature, variant)
    addEvent(delayedCast, 1500, creature:getId(), variant, combat)
    return true
end

spell:name("alert spell")
spell:words("alert spell")
spell:group("attack")
spell:id(180)
spell:cooldown(1000)
spell:groupCooldown(1000)
spell:level(1)
spell:mana(10)
spell:isSelfTarget(false)
spell:isPremium(true)
spell:needLearn(false)
spell:needDirection(false)
spell:register()
Post automatically merged:

Hello!
Need a script something like miniworld changes but....

The idea is that there will be several teleports in different places on the map.
If a player will have a party with a minimum of 1 person and it will be in the player's range then the player will be able to enter the teleport the same way the person who is in the party.
If he loses this party or the person will not be in range to the party expshare for, for example, 10 minutes then it throws both people out of the party to a certain position.
In addition, the teleport can always work if the player has the given storage.

Well, and one more point... Teleports are activated randomly, for example, a maximum of 3 different for one day, after 24 hours access for players is closed and opens another random 3 portals.

+ Some information which places are currently active.
This is how it reads.. is this correct?

When server start, choose 1-3 random teleports, and add to the map.

When entering the portal
- if don't have a party, just go solo
- if you have a party, will pull in nearby party members.
-> Party Leader is only one who can enter portal
-> if anyone loses party (leaves, or dies?) then everyone is kicked out to a specific position.
-> portal only works once per server save per person?
-> Unless you have a storage, then can enter always?

Questions.. When do you stop checking the party?
What is the win/exit condition of this portal?

I need more clarification to script this.
 
Last edited:
I have seen a lot of scripts that raise the critical attribute.
But none that adds a weapon such an attribute that doesn't have it.

Could you write such a script for TFS 1.4.2?

E.g. use an item on a weapon, then it will add a critical hit chance and an item description +% on critical hit.
 
Back
Top