• 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,834
Solutions
586
Reaction score
5,424
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:
Based on your description, this is the best I could come up with.

The people who use the chest, get reward chances based on their vocation.

Lua:
local rewardChestId = 1746
local rewards = {
    vocations = {
        [{1, 5}] = { -- sorc & master sorc
            chanceType = "sorcerer",
            ["sorcerer"] = {itemChanceModifier = 1},
            ["druid"] = {itemChanceModifier = 2}, -- 2x harder to get druid items, if your class is sorcerer
            ["paladin"] = {itemChanceModifier = 3},
            ["knight"] = {itemChanceModifier = 3}
        },
        [{2, 6}] = { -- druid & elder druid
            chanceType = "druid",
            ["sorcerer"] = {itemChanceModifier = 2},
            ["druid"] = {itemChanceModifier = 1},
            ["paladin"] = {itemChanceModifier = 3},
            ["knight"] = {itemChanceModifier = 3}
        },
        [{3, 7}] = { -- paladin & royal paladin
            chanceType = "paladin",
            ["sorcerer"] = {itemChanceModifier = 3},
            ["druid"] = {itemChanceModifier = 3},
            ["paladin"] = {itemChanceModifier = 1},
            ["knight"] = {itemChanceModifier = 2}
        },
        [{4, 8}] = { -- knight & elite knight
            chanceType = "knight",
            ["sorcerer"] = {itemChanceModifier = 3},
            ["druid"] = {itemChanceModifier = 3},
            ["paladin"] = {itemChanceModifier = 2},
            ["knight"] = {itemChanceModifier = 1}
        }
    },
    rewardAmount = {min = 100, max = 100}, -- item rewards you receive for each chest
    itemList = {
        {itemId = 2160, amount = {min = 1, max = 1}, chance = 10000, chanceType = "sorcerer"}, -- chance 10000 = 100% | 100 = 1% | 1 = 0.01%
        {itemId = 2152, amount = {min = 1, max = 1}, chance = 10000, chanceType = "druid"},
        {itemId = 2148, amount = {min = 1, max = 1}, chance = 10000, chanceType = "paladin"},
        {itemId = 8302, amount = {min = 1, max = 1}, chance = 10000, chanceType = "knight"}
    }
}

local action = Action()

function action.onUse(player, item, fromPosition, target, toPosition, isHotkey)
    local vocation = player:getVocation():getId()
    local chanceTypes
    for k, v in pairs(rewards.vocations) do
        if table.contains(k, vocation) then
            chanceTypes = {
                ["sorcerer"] = v["sorcerer"].itemChanceModifier,
                ["druid"] = v["druid"].itemChanceModifier,
                ["paladin"] = v["paladin"].itemChanceModifier,
                ["knight"] = v["knight"].itemChanceModifier
            }
            vocation = v.chanceType
            break
        end
    end
    local rewardAmount = math.random(rewards.rewardAmount.min, rewards.rewardAmount.max)
    local _rewards = {}
    while #_rewards < rewardAmount do
        for k, v in pairs(rewards.itemList) do
            local chance = 10000 * chanceTypes[v.chanceType]
            if math.random(chance) <= v.chance then
                _rewards[#_rewards + 1] = {itemId = v.itemId, amount = math.random(v.amount.min, v.amount.max)}
            end
        end
    end
    for i = 1, rewardAmount do
        local rand = math.random(#_rewards)
        player:addItem(_rewards[rand].itemId, _rewards[rand].amount, true)
        table.remove(_rewards[rand])
    end
    item:remove(1)
    return true
end

action:id(rewardChestId)
action:register()
Thanks man! I really appreciate it, It will be so useful!
 
Lua:
local skillStages = {
    [SKILL_FIST] = {
        [{  1,   8}] = {multiplier = 5},
        [{  9,  20}] = {multiplier = 4},
        [{ 21,  50}] = {multiplier = 3},
        [{ 51, 100}] = {multiplier = 2},
        [{101}]      = {multiplier = 1}
    },
    [SKILL_CLUB] = {
        [{  1,   8}] = {multiplier = 5},
        [{  9,  20}] = {multiplier = 4},
        [{ 21,  50}] = {multiplier = 3},
        [{ 51, 100}] = {multiplier = 2},
        [{101}]      = {multiplier = 1}
    },
    [SKILL_SWORD] = {
        [{  1,   8}] = {multiplier = 5},
        [{  9,  20}] = {multiplier = 4},
        [{ 21,  50}] = {multiplier = 3},
        [{ 51, 100}] = {multiplier = 2},
        [{101}]      = {multiplier = 1}
    },
    [SKILL_AXE] = {
        [{  1,   8}] = {multiplier = 5},
        [{  9,  20}] = {multiplier = 4},
        [{ 21,  50}] = {multiplier = 3},
        [{ 51, 100}] = {multiplier = 2},
        [{101}]      = {multiplier = 1}
    },
    [SKILL_DISTANCE] = {
        [{  1,   8}] = {multiplier = 5},
        [{  9,  20}] = {multiplier = 4},
        [{ 21,  50}] = {multiplier = 3},
        [{ 51, 100}] = {multiplier = 2},
        [{101}]      = {multiplier = 1}
    },
    [SKILL_SHIELD] = {
        [{  1,   8}] = {multiplier = 5},
        [{  9,  20}] = {multiplier = 4},
        [{ 21,  50}] = {multiplier = 3},
        [{ 51, 100}] = {multiplier = 2},
        [{101}]      = {multiplier = 1}
    },
    [SKILL_FISHING] = {
        [{  1,   8}] = {multiplier = 5},
        [{  9,  20}] = {multiplier = 4},
        [{ 21,  50}] = {multiplier = 3},
        [{ 51, 100}] = {multiplier = 2},
        [{101}]      = {multiplier = 1}
    },
    [SKILL_MAGLEVEL] = {
        [{  1,   8}] = {multiplier = 5},
        [{  9,  20}] = {multiplier = 4},
        [{ 21,  50}] = {multiplier = 3},
        [{ 51, 100}] = {multiplier = 2},
        [{101}]      = {multiplier = 1}
    }
}


local ec = EventCallback

ec.onGainSkillTries = function(self, skill, tries)
    if not skillStages[skill] then
        return tries
    end
    local playerLevel = skill == SKILL_MAGLEVEL and self:getMagicLevel() or self:getSkillLevel(skill)
    for k, v in pairs(skillStages[skill]) do
        if playerLevel >= k[1] then
            if not k[2] or playerLevel <= k[2] then
                tries = tries * v.multiplier
            end
        end
    end
    return tries
end

ec:register()
Post automatically merged:


Added.

Go to the post and find the uploaded script, as per usual. lol

Thank you so much for the adjustments! Ive included a serverinfo.lua talkaction adjustment here for anyone that needs it. Right now it can only be used if you are setting all your skill rates to the exact same config settings since I need to include the table info inside the script as getSkillRate and getMagicRate cant be used with this script to get current rates on the fly. Just a little contribution back for yours <3

Lua:
local skillStages = {
    [SKILL_FIST] = {
        [{  1,   40}] = {multiplier = 12},
        [{ 41,  80}] = {multiplier = 10},
        [{ 81, 100}] = {multiplier = 8},
        [{101}]      = {multiplier = 4}
    },
    [SKILL_CLUB] = {
        [{  1,   40}] = {multiplier = 12},
        [{ 41,  80}] = {multiplier = 10},
        [{ 81, 100}] = {multiplier = 8},
        [{101}]      = {multiplier = 4}
    },
    [SKILL_SWORD] = {
        [{  1,   40}] = {multiplier = 12},
        [{ 41,  80}] = {multiplier = 10},
        [{ 81, 100}] = {multiplier = 8},
        [{101}]      = {multiplier = 4}
    },
    [SKILL_AXE] = {
        [{  1,   40}] = {multiplier = 12},
        [{ 41,  80}] = {multiplier = 10},
        [{ 81, 100}] = {multiplier = 8},
        [{101}]      = {multiplier = 4}
    },
    [SKILL_DISTANCE] = {
        [{  1,   40}] = {multiplier = 12},
        [{ 41,  80}] = {multiplier = 10},
        [{ 81, 100}] = {multiplier = 8},
        [{101}]      = {multiplier = 4}
    },
    [SKILL_SHIELD] = {
        [{  1,   40}] = {multiplier = 12},
        [{ 41,  80}] = {multiplier = 10},
        [{ 81, 100}] = {multiplier = 8},
        [{101}]      = {multiplier = 4}
    },
    [SKILL_MAGLEVEL] = {
        [{  1,   40}] = {multiplier = 6},
        [{ 41,  80}] = {multiplier = 5},
        [{ 81, 100}] = {multiplier = 4},
        [{101}]      = {multiplier = 3}
    }
}

local lootRateConfigKey = configKeys.RATE_LOOT

function getRateText(player, skillStages)
    local skillRates = {}
  
    -- Get the player's skill levels
    local skills = {
        SKILL_FIST, SKILL_CLUB, SKILL_SWORD, SKILL_AXE, SKILL_DISTANCE, SKILL_SHIELD
    }

    for _, skill in ipairs(skills) do
        local skillLevel = player:getSkillLevel(skill)
        local stages = skillStages[skill]

        for k, v in pairs(stages) do
            if skillLevel >= k[1] then
                if not k[2] or skillLevel <= k[2] then
                    -- Append the level range to the skill rate message
                    local range = k[2] and string.format("from level %d to %d", k[1], k[2]) or string.format("from level %d to infinite", k[1])
                    skillRates[skill] = string.format("%dx (%s)", v.multiplier, range)
                    break
                end
            end
        end

        -- If skill rate not found, set to "Unknown"
        skillRates[skill] = skillRates[skill] or "Unknown"
    end
  
    -- Get the player's magic level
    local magicLevel = player:getMagicLevel()
    local magicStages = skillStages[SKILL_MAGLEVEL]
    local magicRate = getRateForMagicLevel(magicLevel, magicStages)

    -- Append the level range for magic skill
    local magicRange = ""
    for k, v in pairs(magicStages) do
        if magicLevel >= k[1] then
            if not k[2] or magicLevel <= k[2] then
                magicRange = k[2] and string.format("from level %d to %d", k[1], k[2]) or string.format("from level %d to infinite", k[1])
                break
            end
        end
    end

    -- If magic rate not found, set to "Unknown"
    magicRate = magicRate or "Unknown"

    return skillRates, magicRate, magicRange
end

function getRateForMagicLevel(magicLevel, magicStages)
    for k, v in pairs(magicStages) do
        if magicLevel >= k[1] then
            if not k[2] or magicLevel <= k[2] then
                return string.format("%dx", v.multiplier)
            end
        end
    end
    return "Unknown"
end

function onSay(player, words, param)
    local message = "Server Info:\n"
  
    -- Append exp rate to the message
    local expRate = string.format("%.1f", Game.getExperienceStage(player:getLevel())) -- Format to one decimal point for low xp stages
    message = message .. string.format("Exp rate: %sx\n", expRate)

    -- Get the skill rates for each skill and the magic rate
    local skillRates, magicRate, magicRange = getRateText(player, skillStages)

    -- Append skill rates for each skill to the message
    for skill, rate in pairs(skillRates) do
        local skillName = getSkillName(skill):gsub("^%l", string.upper)
        skillName = skillName:gsub(" fighting$", "") -- Remove " fighting" suffix if present
        message = message .. string.format("%s rate: %s\n", skillName, rate)
    end

    -- Append magic rate and range to the message
    message = message .. string.format("Magic rate: %s (%s)\n", magicRate, magicRange)

    -- Append loot rate to the message
    local lootRate = configManager.getNumber(lootRateConfigKey)
    message = message .. string.format("Loot rate: %dx", lootRate)

    -- Send the message to the player
    player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, message)
    return false
end

Make sure you copy your table from skillStages.lua and replace the table in the serverinfo.lua talkaction to ensure values line up!

Thanks Thank You GIF by BLKBOK
 
Last edited:
I've got a few ideas on the list ^^, so here is the next one, if you could do it, it'd be great!

"Ticket/Pass" Arena & Tournaments:

- Players need a "Ticket/Pass" to enter a 1vs1 or 4vs4 fight map (Arena Kolisseum), 1vs1 or 4vs4 tournament, or massive fight (all vs all), but all are PVP game modes.

-When a player kills another, earn the killed player's Ticket/Pass directly (Tickets are transferred from the dead player to the Player killer).

-When it is a 4vs4 team fight, the winning team will receive the enemy's Tickets equally (Each player will get 1 Ticket even if they didn't make any frag/last hit.

  • When there is a massive fight (all vs all--war mode) there is no assistance mode, only last hit counts.
  • Massive event starts by teleporting all players (let's say 10) to different rooms. The last standing player gets all the Tickets.

-Tickets/Pass can be used in special shops, Quests, buffs, or whatever devs like :)


Thank you again and hope you can do it!

PD: (I will give you all possible likes on this forum in my free time ;)
 
Changing spell colldown (attack) when player attack player (mouser target)

etc. If player attack monsters have default spell colldown but Its attack player all attack spells have colldowns 2s.
 
View attachment 82234

The Final Bounty Hunter System to Rule Them All
I've been looking around and there have been some systems made in the past but either they were 0.3/0.4 or were not up to par.​

For this system, I had the following in mind as a basic skeleton:
  • Player A goes to a specific NPC and asks to place a bounty on Player B at XXX amount of gold.​
  • Player B is listed on a bounty list, which can be checked by looking on a Blackboard (or any writable/hangable). (I couldn't care less about displaying it on the website)​
  • Player C kills Player B and receives the gold.​

To make it advanced and better than all the others:
  • Ability for Player B to buy off his bounty.​
  • If two players participate in a kill, the gold is shared, except if one of the attackers dies, then only the one that survived gets all the gold (or giving the hunted a chance to get away).​
  • If a Player receives a Red Skull, the system automatically puts a bounty of XXX gold on a player.​
  • Players are able to increase existing bounties.​
  • Make players with a certain storage immune to be targeted by regular players, although if a player has a certain storage value, they can put a bounty on that player.​
This way I can categorize races (vocations), and whatnot.

Important notes:
- Everything done must be through an NPC, rather not use a talkaction to put a bounty on someone.

The Lord Of The Rings GIF by Maudit
Scared Cringe GIF by O&O, Inc
lord of the rings queue GIF


Don't hate me xD

bandicam2024-02-1909-20-55-587-ezgif.com-video-to-gif-converter.gif

data/scripts/bountyHunterSystem.lua
Lua:
GLOBAL_BOUNTY_INFO = {
    redSkullBounty = 10000,
    bountyStorage = 45084,
    bountyPayoutStorage = 45086,
    bountyStartTimeStorage = 45088,
    bountyBuyoffDelay = 24 * 60 * 60,
    skullStorage = 45085,
    bountyActionId = 45087 -- put actionId on items you want (onLook function, for telling about bounties)
}


function getBountyStartTime(player)
    return player:getStorageValue(GLOBAL_BOUNTY_INFO.bountyStartTimeStorage)
end

function getBountyPayout(player)
    local bountyAmount = player:getStorageValue(GLOBAL_BOUNTY_INFO.bountyPayoutStorage)
    bountyAmount = bountyAmount > 0 and bountyAmount or 0
    return bountyAmount
end

function getBounty(player)
    local bountyAmount = player:getStorageValue(GLOBAL_BOUNTY_INFO.bountyStorage)
    bountyAmount = bountyAmount > 0 and bountyAmount or 0
    return bountyAmount
end

function changeBounty(player, amount)
    local bountyAmount = getBounty(player) + amount
    player:setStorageValue(GLOBAL_BOUNTY_INFO.bountyStorage, bountyAmount)
    player:sendTextMessage(MESSAGE_STATUS_WARNING, "Your bounty has been " .. (amount > 0 and "increased" or "decreased") .. " to " .. bountyAmount .. ".")
    if amount > 0 and getBountyStartTime(player) == -1 then
        player:setStorageValue(GLOBAL_BOUNTY_INFO.bountyStartTimeStorage, os.time())
    end
end

local function checkPreviousSkull(playerId, currentSkull)
    local player = Player(playerId)
    if not player then
        return
    end
    if player:getSkull() ~= SKULL_RED then
        return
    end
    local previousSkull = currentSkull and currentSkull or player:getStorageValue(GLOBAL_BOUNTY_INFO.skullStorage)
    if previousSkull == SKULL_RED then
        return
    end
    changeBounty(player, GLOBAL_BOUNTY_INFO.redSkullBounty)
    player:setStorageValue(GLOBAL_BOUNTY_INFO.bountyStartTimeStorage, os.time())
end


local creatureevent = CreatureEvent("onKill_BountySystem")

function creatureevent.onKill(creature, target)
    if not target:isPlayer() then
        return true
    end
    local bountyAmount = getBounty(target)
    if bountyAmount > 0 then
        local damageMap = {}
        local playerCount = 0
        for creatureId, damage in pairs(target: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 playerId = _creature:getId()
                    damageMap[playerId] = (damageMap[playerId] or 0) + damage.total
                    playerCount = playerCount + 1
                end
            end
        end
        local payout = math.ceil(bountyAmount / playerCount)
        for playerId, _ in pairs(damageMap) do
            local player = Player(playerId)
            if player then
                local bountyPayout = getBountyPayout(player) + payout
                player:setStorageValue(GLOBAL_BOUNTY_INFO.bountyPayoutStorage, bountyPayout)
                player:sendTextMessage(MESSAGE_STATUS_WARNING, "Bounty Receieved: " .. payout)
            end
        end
        target:say("BOUNTIED PLAYER!\n" .. bountyAmount, TALKTYPE_MONSTER_SAY, false, nil, target:getPosition())
        target:setStorageValue(GLOBAL_BOUNTY_INFO.bountyStorage, 0)
        target:setStorageValue(GLOBAL_BOUNTY_INFO.bountyStartTimeStorage, -1)
    end
    addEvent(checkPreviousSkull, 0, creature:getId(), creature:getSkull())
    return true
end

creatureevent:register()


local creatureevent = CreatureEvent("onLogin_BountySystem")

function creatureevent.onLogin(player)
    addEvent(checkPreviousSkull, 0, player:getId())
    player:registerEvent("onKill_BountySystem")
    return true
end

creatureevent:register()


local creatureevent = CreatureEvent("onLogout_BountySystem")

function creatureevent.onLogout(player)
    player:setStorageValue(GLOBAL_BOUNTY_INFO.skullStorage, player:getSkull())
    return true
end

creatureevent:register()



local ec = EventCallback

ec.onLook = function(self, thing, position, distance, description)   
    if thing:isItem() and thing:getActionId() == GLOBAL_BOUNTY_INFO.bountyActionId then
        local bounties = {}
        for _, player in ipairs(Game.getPlayers()) do
            local bounty = getBounty(player)
            if bounty > 0 then
                bounties[#bounties + 1] = {bounty = bounty, playerName = player:getName()}
            end
        end
        table.sort(bounties, function(a,b)
            return a.bounty > b.bounty
        end)
        description = "You see a list of available bounties."
        if distance > 1 then
            for i = 1, #bounties do
                if i <= 3 then
                    description = description .. "\n" .. bounties[i].playerName .. " " .. bounties[i].bounty
                else
                    description = description .. "\n\n(I could see more bounties if I moved closer)"
                end
                if description ~= "You see a list of available bounties." then
                    description = "\n" .. description
                else
                    description = description  .. "\n\nNobody has a bounty."
                end
            end
        else
            local text = ""
            for i = 1, #bounties do
                if text ~= "" then
                    text = text .. "\n"
                end
                text = text .. bounties[i].playerName .. " " .. bounties[i].bounty
            end
            if text == "" then
                text = text  .. "\n\nNobody has a bounty."
            end
            self:showTextDialog(thing:getId(), "List of available bounties\n\n" .. text)
        end
    end
    return description
end

ec:register(1)

data/npc/Bounty Person.xml
XML:
<?xml version="1.0" encoding="UTF-8"?>
<npc name="Bounty Person" script="bountyperson.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/bountyperson.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 bounty = {}
local minimumBounty = 500

local function greetCallback(cid)
    local player = Player(cid)
    npcHandler.topic[cid] = 0
    bounty[cid] = {bountyType = "", bountyAmount = 0, bountyTarget = ""}
    return true
end

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

    local player = Player(cid)
    
    if msgcontains(msg, "bounty") then
        npcHandler:say("Are you wanting to {add} a bounty or {buyoff} or {check} a bounty? Or are you wanting to {collect} your received bounties?", cid)
        npcHandler.topic[cid] = 1
        
    elseif npcHandler.topic[cid] == 1 then
        if not (msgcontains(msg, "buyoff") or msgcontains(msg, "add") or msgcontains(msg, "check") or msgcontains(msg, "collect")) then
            npcHandler:say("Maybe another time, then.", cid)
            npcHandler.topic[cid] = 0
            return true
        end
        
        if msgcontains(msg, "collect") then
            local uncollectedBounty = getBountyPayout(player)
            if uncollectedBounty < 1 then
                npcHandler:say("You do not have any uncollected bounties at this time.", cid)
                npcHandler.topic[cid] = 0
                return true
            end
            npcHandler:say("You currently have " .. uncollectedBounty .. " of uncollected bounty. Would you like to collect it now?", cid)
            npcHandler.topic[cid] = 5
            return true
        end
        
        bounty[cid].bountyType = msgcontains(msg, "add") and "add" or msgcontains(msg, "buyoff") and "buyoff" or "check"
        npcHandler:say("Who do you want to " .. bounty[cid].bountyType .. " a bounty for?", cid)
        npcHandler.topic[cid] = 2
        
    elseif npcHandler.topic[cid] == 2 then
        local bountyPlayer = Player(msg)
        if not bountyPlayer then
            npcHandler:say("The player (" .. msg .. ") is offline, or does not exist.", cid)
            return true
        end
        
        bounty[cid].bountyTarget = bountyPlayer:getName()
        local bountyAmount = getBounty(bountyPlayer)
        
        if bounty[cid].bountyType == "check" then
            npcHandler:say(bounty[cid].bountyTarget .. " has a bounty of " .. bountyAmount .. ".", cid)
            bounty[cid] = {bountyType = "", bountyAmount = 0, bountyTarget = ""}
            npcHandler.topic[cid] = 0
            return true
        end
        
        if bounty[cid].bountyType == "buyoff" and bountyAmount < 1 then
            npcHandler:say("The player (" .. bounty[cid].bountyTarget .. ") has no bounty to buyoff.", cid)
            bounty[cid] = {bountyType = "", bountyAmount = 0, bountyTarget = ""}
            npcHandler.topic[cid] = 0
            return true
        end
        
        if bounty[cid].bountyType == "buyoff" and getBountyStartTime(player) + GLOBAL_BOUNTY_INFO.bountyBuyoffDelay > os.time() then
            npcHandler:say("The player (" .. bounty[cid].bountyTarget .. ")'s bounty is too recent and unable to be bought off.", cid)
            bounty[cid] = {bountyType = "", bountyAmount = 0, bountyTarget = ""}
            npcHandler.topic[cid] = 0
            return true
        end
        
        npcHandler:say("How much bounty would you like to " .. (bounty[cid].bountyType == "add" and "add to" or "buyoff from") .. " " .. bounty[cid].bountyTarget .. "? They currently have a bounty of " .. bountyAmount .. ".", cid)
        npcHandler.topic[cid] = 3
        
    elseif npcHandler.topic[cid] == 3 then
        local bountyPlayer = Player(bounty[cid].bountyTarget)
        if not bountyPlayer then
            npcHandler:say("The player (" .. bounty[cid].bountyTarget .. ") has went offline. Unable to continue with transaction.", cid)
            bounty[cid] = {bountyType = "", bountyAmount = 0, bountyTarget = ""}
            npcHandler.topic[cid] = 0
            return true
        end
        
        local amount = tonumber(msg)
        if not amount or amount ~= math.floor(amount) or amount < 1 then
            npcHandler:say("That is not a valid number.", cid)
            return true
        elseif bounty[cid].bountyType == "add" and amount < minimumBounty then
            npcHandler:say("Minimum bounty that can be added is " .. minimumBounty .. ".", cid)
            return true
        end
        
        if bounty[cid].bountyType == "buyoff" and amount > getBounty(bountyPlayer) then
            amount = getBounty(bountyPlayer)
        end
        bounty[cid].bountyAmount = amount
        npcHandler:say("To confirm, you would you like to " .. (bounty[cid].bountyType == "add" and "add " .. bounty[cid].bountyAmount .. " to" or "buyoff " .. bounty[cid].bountyAmount .. " from") .. " " .. bounty[cid].bountyTarget .. "'s bounty amount?", cid)
        npcHandler.topic[cid] = 4
        
    elseif npcHandler.topic[cid] == 4 then
        if not msgcontains(msg, "yes") then
            npcHandler:say("Maybe another time, then.", cid)
            bounty[cid] = {bountyType = "", bountyAmount = 0, bountyTarget = ""}
            npcHandler.topic[cid] = 0
            return true
        end
    
        local bountyPlayer = Player(bounty[cid].bountyTarget)
        if not bountyPlayer then
            npcHandler:say("The player (" .. bounty[cid].bountyTarget .. ") has went offline. Unable to continue with transaction.", cid)
            bounty[cid] = {bountyType = "", bountyAmount = 0, bountyTarget = ""}
            npcHandler.topic[cid] = 0
            return true
        end
        
        
        local bountyAmount = getBounty(bountyPlayer)
        if bounty[cid].bountyType == "buyoff" and bountyAmount == 0 then
            npcHandler:say("The player (" .. bounty[cid].bountyTarget .. ") no longer has a bounty to buyoff.", cid)
            bounty[cid] = {bountyType = "", bountyAmount = 0, bountyTarget = ""}
            npcHandler.topic[cid] = 0
            return true
        end
        
        local amount = bounty[cid].bountyAmount
        if bounty[cid].bountyType == "buyoff" and amount > bountyAmount then
            amount = bountyAmount
        end
        bounty[cid].bountyAmount = amount
        
        if player:getTotalMoney() < bounty[cid].bountyAmount then
            npcHandler:say("You do not have the required currency available for this transaction.", cid)
            bounty[cid] = {bountyType = "", bountyAmount = 0, bountyTarget = ""}
            npcHandler.topic[cid] = 0
            return true
        end
        
        player:removeTotalMoney(bounty[cid].bountyAmount)
        if bounty[cid].bountyType == "add" then
            bountyAmount = bountyAmount + bounty[cid].bountyAmount
            changeBounty(bountyPlayer, amount)
        else
            bountyAmount = bountyAmount - bounty[cid].bountyAmount
            changeBounty(bountyPlayer, -amount)
            if bountyAmount == 0 then
                bountyPlayer:setStorageValue(GLOBAL_BOUNTY_INFO.bountyStartTimeStorage, -1)
            end
        end
        
        
        npcHandler:say("Transaction successful. " .. bounty[cid].bountyTarget .. "'s bounty is now " .. bountyAmount .. ".", cid)
        bounty[cid] = {bountyType = "", bountyAmount = 0, bountyTarget = ""}
        npcHandler.topic[cid] = 0
        
    elseif npcHandler.topic[cid] == 5 then
        if not msgcontains(msg, "yes") then
            npcHandler:say("Maybe another time, then.", cid)
            npcHandler.topic[cid] = 0
            return true
        end
        
        local uncollectedBounty = getBountyPayout(player)
        player:setBankBalance(player:getBankBalance() + uncollectedBounty)
        player:setStorageValue(GLOBAL_BOUNTY_INFO.bountyPayoutStorage, 0)
        npcHandler:say(uncollectedBounty .. " in accumulated bounties has been sent to your bank.", cid)
        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())
 
Last edited:
View attachment 82245


data/scripts/eventcallbacks/player/default_onLook.lua

add to very top
Lua:
local conditions = {
    "life increase",
    "mana increase",
    "speed",
    "magic",
    "melee",
    "fist",
    "club",
    "sword",
    "axe",
    "distance",
    "shield",
    "fishing",
    "critical hit chance",
    "critical hit damage",
    "life leech chance",
    "life leech amount",
    "mana leech chance",
    "mana leech amount",
    "life increase percent",
    "mana increase percent",
    "magic percent",
    "melee percent",
    "fist percent",
    "club percent",
    "sword percent",
    "axe percent",
    "distance percent",
    "shield percent",
    "fishing percent",
    "life regen",
    "mana regen",
    "soul regen"
}
add under local description = "You see " .. thing:getDescription(distance)
Lua:
    if thing:isItem() then
        for i = 1, #conditions do
            local currentStatAmount = thing:getCustomAttribute(conditions[i])
            currentStatAmount = currentStatAmount and currentStatAmount or 0
            if currentStatAmount > 0 then
                description = description .. "\n" .. conditions[i] .. ": " .. currentStatAmount
            end
        end
    end
data/scripts/itemUpgradeSystem.lua
Lua:
local conditionSubId = 45083 -- must be a unique subId not used for other buffs in your server
local config = {
    ["statMain"] = {
        -- flat_bonus_stats
        {statType = "life increase"},
        {statType = "mana increase"},
        {statType = "magic"},
        {statType = "fist"},
        {statType = "melee"},
        {statType = "distance"},
        {statType = "shield"},
        {statType = "fishing"},
        {statType = "critical hit chance"},
        {statType = "critical hit damage"},
        {statType = "life leech chance"},
        {statType = "life leech amount"},
        {statType = "mana leech chance"},
        {statType = "mana leech amount"}
    },
    ["statSpeed"] = {
        {statType = "speed"}
    },
    ["statRegen"] = {
        {statType = "life regen", ticks = 1000}, -- ticks in milliseconds}
        {statType = "mana regen", ticks = 1000}  -- can't go lower then 1000
    },
    ["statSoulRegen"] = {
        {statType = "soul regen", ticks = 1000}
    },
 
    maxUpgradesPerItem = 5,
    upgradeItems = {
        [8302] = {statType = "reset item"},
        [8303] = {statType = "life increase", value = 100},
    },
    specialDisallowedItems = {} -- any items that shouldn't be upgradeable, add them here
}

-- Choose Flat or Percentage stats. Cannot use both.

-- Flat Stats
--[[
    ["statMain"] = {
        -- flat_bonus_stats
        {statType = "life increase"},
        {statType = "mana increase"},
        {statType = "magic"},
        {statType = "fist"},
        {statType = "melee"},
        {statType = "distance"},
        {statType = "shield"},
        {statType = "fishing"},
        {statType = "critical hit chance"},
        {statType = "critical hit damage"},
        {statType = "life leech chance"},
        {statType = "life leech amount"},
        {statType = "mana leech chance"},
        {statType = "mana leech amount"}
    },
    ["statSpeed"] = {
        {statType = "speed"}
    },
    ["statRegen"] = {
        {statType = "life regen", ticks = 5000}, -- ticks in milliseconds}
        {statType = "mana regen", ticks = 5000}  -- can't go lower then 1000
    },
    ["statSoulRegen"] = { -- you can remove entire categories
        {statType = "soul regen", ticks = 5000} -- or individual stats, if you don't want to use them
    },
]]--


-- Percent Stats
--[[
    ["statMain"] = {
        -- percent_bonus_stats
        {statType = "life increase percent"},
        {statType = "mana increase percent"},
        {statType = "magic percent"},
        {statType = "fist percent"},
        {statType = "melee percent"},
        {statType = "distance percent"},
        {statType = "shield percent"},
        {statType = "fishing percent"},
        {statType = "critical hit chance"},
        {statType = "critical hit damage"},
        {statType = "life leech chance"},
        {statType = "life leech amount"},
        {statType = "mana leech chance"},
        {statType = "mana leech amount"}
    },
    ["statSpeed"] = {
        {statType = "speed"}
    },
    ["statRegen"] = {
        {statType = "life regen", ticks = 5000}, -- ticks in milliseconds}
        {statType = "mana regen", ticks = 5000}  -- can't go lower then 1000
    },
    ["statSoulRegen"] = {
        {statType = "soul regen", ticks = 5000}
    },
]]--


-- END OF CONFIG

local choiceDictionary = {}

local conditions = {
    ["life increase"] = {CONDITION_PARAM_STAT_MAXHITPOINTS},
    ["mana increase"] = {CONDITION_PARAM_STAT_MAXMANAPOINTS},
    ["speed"] = {CONDITION_PARAM_SPEED},
    ["magic"] = {CONDITION_PARAM_STAT_MAGICPOINTS},
    ["melee"] = {CONDITION_PARAM_SKILL_MELEE},
    ["fist"] = {CONDITION_PARAM_SKILL_FIST},
    ["club"] = {CONDITION_PARAM_SKILL_CLUB},
    ["sword"] = {CONDITION_PARAM_SKILL_SWORD},
    ["axe"] = {CONDITION_PARAM_SKILL_AXE},
    ["distance"] = {CONDITION_PARAM_SKILL_DISTANCE},
    ["shield"] = {CONDITION_PARAM_SKILL_SHIELD},
    ["fishing"] = {CONDITION_PARAM_SKILL_FISHING},
    ["critical hit chance"] = {CONDITION_PARAM_SPECIALSKILL_CRITICALHITCHANCE},
    ["critical hit damage"] = {CONDITION_PARAM_SPECIALSKILL_CRITICALHITAMOUNT},
    ["life leech chance"] = {CONDITION_PARAM_SPECIALSKILL_LIFELEECHCHANCE},
    ["life leech amount"] = {CONDITION_PARAM_SPECIALSKILL_LIFELEECHAMOUNT},
    ["mana leech chance"] = {CONDITION_PARAM_SPECIALSKILL_MANALEECHCHANCE},
    ["mana leech amount"] = {CONDITION_PARAM_SPECIALSKILL_MANALEECHAMOUNT},
    ["life increase percent"] = {CONDITION_PARAM_STAT_MAXHITPOINTSPERCENT},
    ["mana increase percent"] = {CONDITION_PARAM_STAT_MAXMANAPOINTSPERCENT},
    ["magic percent"] = {CONDITION_PARAM_STAT_MAGICPOINTSPERCENT},
    ["melee percent"] = {CONDITION_PARAM_SKILL_MELEEPERCENT},
    ["fist percent"] = {CONDITION_PARAM_SKILL_FISTPERCENT},
    ["club percent"] = {CONDITION_PARAM_SKILL_CLUBPERCENT},
    ["sword percent"] = {CONDITION_PARAM_SKILL_SWORDPERCENT},
    ["axe percent"] = {CONDITION_PARAM_SKILL_AXEPERCENT},
    ["distance percent"] = {CONDITION_PARAM_SKILL_DISTANCEPERCENT},
    ["shield percent"] = {CONDITION_PARAM_SKILL_SHIELDPERCENT},
    ["fishing percent"] = {CONDITION_PARAM_SKILL_FISHINGPERCENT},
    ["life regen"] = {CONDITION_PARAM_HEALTHGAIN, CONDITION_PARAM_HEALTHTICKS},
    ["mana regen"] = {CONDITION_PARAM_MANAGAIN, CONDITION_PARAM_MANATICKS},
    ["soul regen"] = {CONDITION_PARAM_SOULGAIN, CONDITION_PARAM_SOULTICKS}
}

local main_attributes = {CONDITION_ATTRIBUTES, CONDITION_HASTE, CONDITION_REGENERATION, CONDITION_SOUL}
local main_stats = {"statMain", "statSpeed", "statRegen", "statSoulRegen"}

local function updateStatBonus(playerId)
    local player = Player(playerId)
    if not player then
        return
    end
 
    -- remove all previous buffs
    for i = 1, 4 do
        if player:getCondition(main_attributes[i], conditionSubId) then
            player:removeCondition(main_attributes[i], conditionSubId)
        end
    end
 
    local equippedItems = {}
    for slot = 1, 10 do
        local slotItem = player:getSlotItem(slot)
        if slotItem then
            equippedItems[#equippedItems + 1] = slotItem
        end
    end
 
    -- add all buffs
    for i = 1, 4 do
        local statCategory = main_stats[i]
        if config[statCategory] then
            local condition = Condition(main_attributes[i], conditionSubId)
            condition:setParameter(CONDITION_PARAM_TICKS, -1)
            for _, stat in ipairs(config[statCategory]) do
                local statValue = 0
                for i = 1, #equippedItems do
                    local itemValue = equippedItems[i]:getCustomAttribute(stat.statType)
                    statValue = itemValue and itemValue + statValue or statValue
                end
                if statValue > 0 then
                    for conditionParam = 1, #conditions[stat.statType] do
                        condition:setParameter(conditions[stat.statType][conditionParam], statValue)
                    end
                    player:addCondition(condition)
                end
            end
        end
    end
    return true
end


local slotTypes = {
    48, -- weapon or shield
    49, -- helmet
    50, -- amulet
    52, -- backpack
    56, -- armor
    112, -- legs
    176, -- boots
    304, -- ring
    560, -- ammo
    2096 -- two handed
}

local function isItemUpgradeable(item)
    if not item:isItem() then
        return false
    end
    local itemType = ItemType(item:getId())
    if not table.contains(slotTypes, itemType:getSlotPosition()) then
        return false
    end
    if itemType:isStackable() then
        return false
    end
    if table.contains(config.specialDisallowedItems, item:getId()) then
        return false
    end
    return true
end



local action = Action()

function action.onUse(player, item, fromPosition, target, toPosition, isHotkey)
    if not isItemUpgradeable(target) then
        player:sendTextMessage(MESSAGE_STATUS_SMALL, "Not a valid item to apply upgrades too.")
        player:getPosition():sendMagicEffect(CONST_ME_POFF, player)
        return true
    end
 
    local itemIndex = config.upgradeItems[item:getId()]
    local itemUpgradeCount = target:getCustomAttribute("Upgrade Counter")
    itemUpgradeCount = itemUpgradeCount and itemUpgradeCount or 0
 
    if itemIndex.statType == "reset item" then
        if itemUpgradeCount == 0 then
            player:sendTextMessage(MESSAGE_STATUS_SMALL, "Item has no upgrades to reset.")
            target:getPosition():sendMagicEffect(CONST_ME_POFF, player)
            return true
        end
        for k, v in pairs(conditions) do
            target:removeCustomAttribute(k)
        end
        target:removeCustomAttribute("Upgrade Counter")
        target:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE, player)
        player:sendTextMessage(MESSAGE_STATUS_SMALL, "All upgrades have been removed from this item.")
        item:remove(1)
        if toPosition.x == CONTAINER_POSITION and toPosition.y <= 10 then
            addEvent(updateStatBonus, 0, player:getId())
        end
        return true
    end
 
    if itemUpgradeCount >= config.maxUpgradesPerItem then
        player:sendTextMessage(MESSAGE_STATUS_SMALL, "Item has reached max upgrades.")
        target:getPosition():sendMagicEffect(CONST_ME_POFF, player)
        return true
    end
 
    local currentStatAmount = target:getCustomAttribute(itemIndex.statType)
    currentStatAmount = currentStatAmount and currentStatAmount or 0
    target:setCustomAttribute(itemIndex.statType, currentStatAmount + itemIndex.value)
    target:setCustomAttribute("Upgrade Counter", itemUpgradeCount + 1)
 
    target:getPosition():sendMagicEffect(CONST_ME_MAGIC_GREEN, player)
    player:sendTextMessage(MESSAGE_STATUS_SMALL, "Item has been upgraded.")
    item:remove(1)
 
    if toPosition.x == CONTAINER_POSITION and toPosition.y <= 10 then
        addEvent(updateStatBonus, 0, player:getId())
    end
    return true
end

for itemId, _ in pairs(config.upgradeItems) do
    action:id(itemId)
end
action:register()


local creatureevent = CreatureEvent("onLogin_updateItemStatBonus")

function creatureevent.onLogin(player)
    updateStatBonus(player:getId())
    return true
end

creatureevent:register()


local ec = EventCallback

ec.onMoveItem = function(self, item, count, fromPosition, toPosition, fromCylinder, toCylinder)
    if not (toPosition.x == CONTAINER_POSITION and toPosition.y <= 10 or fromPosition.x == CONTAINER_POSITION and fromPosition.y <= 10) then
        return RETURNVALUE_NOERROR
    end
    addEvent(updateStatBonus, 0, self:getId())
    return RETURNVALUE_NOERROR
end

ec:register()
Post automatically merged:


Lua:
local skillStages = {
    [SKILL_FIST] = {
        [{  1,   8}] = {multiplier = 5},
        [{  9,  20}] = {multiplier = 4},
        [{ 21,  50}] = {multiplier = 3},
        [{ 51, 100}] = {multiplier = 2},
        [{101}]      = {multiplier = 1}
    },
    [SKILL_CLUB] = {
        [{  1,   8}] = {multiplier = 5},
        [{  9,  20}] = {multiplier = 4},
        [{ 21,  50}] = {multiplier = 3},
        [{ 51, 100}] = {multiplier = 2},
        [{101}]      = {multiplier = 1}
    },
    [SKILL_SWORD] = {
        [{  1,   8}] = {multiplier = 5},
        [{  9,  20}] = {multiplier = 4},
        [{ 21,  50}] = {multiplier = 3},
        [{ 51, 100}] = {multiplier = 2},
        [{101}]      = {multiplier = 1}
    },
    [SKILL_AXE] = {
        [{  1,   8}] = {multiplier = 5},
        [{  9,  20}] = {multiplier = 4},
        [{ 21,  50}] = {multiplier = 3},
        [{ 51, 100}] = {multiplier = 2},
        [{101}]      = {multiplier = 1}
    },
    [SKILL_DISTANCE] = {
        [{  1,   8}] = {multiplier = 5},
        [{  9,  20}] = {multiplier = 4},
        [{ 21,  50}] = {multiplier = 3},
        [{ 51, 100}] = {multiplier = 2},
        [{101}]      = {multiplier = 1}
    },
    [SKILL_SHIELD] = {
        [{  1,   8}] = {multiplier = 5},
        [{  9,  20}] = {multiplier = 4},
        [{ 21,  50}] = {multiplier = 3},
        [{ 51, 100}] = {multiplier = 2},
        [{101}]      = {multiplier = 1}
    },
    [SKILL_FISHING] = {
        [{  1,   8}] = {multiplier = 5},
        [{  9,  20}] = {multiplier = 4},
        [{ 21,  50}] = {multiplier = 3},
        [{ 51, 100}] = {multiplier = 2},
        [{101}]      = {multiplier = 1}
    },
    [SKILL_MAGLEVEL] = {
        [{  1,   8}] = {multiplier = 5},
        [{  9,  20}] = {multiplier = 4},
        [{ 21,  50}] = {multiplier = 3},
        [{ 51, 100}] = {multiplier = 2},
        [{101}]      = {multiplier = 1}
    }
}


local ec = EventCallback

ec.onGainSkillTries = function(self, skill, tries)
    if not skillStages[skill] then
        return tries
    end
    local playerLevel = skill == SKILL_MAGLEVEL and self:getMagicLevel() or self:getSkillLevel(skill)
    for k, v in pairs(skillStages[skill]) do
        if playerLevel >= k[1] then
            if not k[2] or playerLevel <= k[2] then
                tries = tries * v.multiplier
            end
        end
    end 
    return tries
end

ec:register()
Post automatically merged:


Added.

Go to the post and find the uploaded script, as per usual. lol
Since you make such clean and handsome scripts, could you also add like physical and elemental damage?
That and capacity increase it 2 additions that would be dope in this script!

/Thanks in advance, Erna.
 
Hi,
Need script talkactions for players.
1.) Player can check the same player maximum 3x for hours.
2.) Player get exhaust for talkctions 1 minute.
3.) If player use !checkbot PLAYERNAME - target player have 1 minutes to answer (code or something like this).
4.) If player target not answer (send text to first player (BOT Confirmed, you can kill him without frag).
5.) If player taget is bot, first player can kill him without frag.
6.) If target player target answer true, send message to first player (this is no bot).

*Player first can check target only is located at a maximum distance of 5 sqm.
 
Hello,
I tried for hours to find these scripts so i will request 2 talkaction revscripts for players that can do

"!item magic plate armor" to say what monsters is dropping that item in chat example
Demon, Orcshabal,

and a "!monster rat" to see all drops like example,
Cheese [0.01%]
1-12x Gold Coin [40%]
 
Hi Xikini, I have an idea of a script for NPC, to make them feel more natural.

If certain hour comes, lets say 20:00, NPC will walk away walking certain path and finally stop on a given coordinates and will stay inactive for some time, for example until 08:00, and then walk back into its "day" coordinates and become active again. Would be dope if it would walk into the bed and disappear, making bed change its sprites into occupied version.

Another idea are inactive NPCs that would talk to each other from time to time.

Thank you in advance!

There's something very borked in the sources for creature:setMovementBlocked(true)
It doesn't seem to work correctly for monsters or npc's.

So, set npc walkinterval to 1500+ when walking npc's along a path.

Monsters 'work' as well, but they try to run to nearby players, and make the path really glitchy looking.

In the gif below, I have 'testPath' enabled, so you can see where the npc is trying to walk.

function list
Lua:
-- for single position walking
-- pauseState = true/false
-- canMove = true/false -- if you creatures to move after pausing and before resuming
-- nodeLooseness = 0+ -- how close to destination creature needs to be, to consider the task finished
-- testPath = true/false/nil -- set to true if you want to see intended path, effects.
Creature:isWalkingCreatureTo()
Creature:stopWalkCreatureTo()
Creature:setPauseWalkCreatureTo(pauseState, canMove)
Creature:startWalkCreatureTo(position, nodeLooseness, testPath)


-- for complicated paths (aka nodes)

-- nodeList -- example..
-- changeElevation = true/false/nil -- set to true if going up/down stairs/holes/ladders/teleporting
--[[
nodeList = {
    {position = Position(1000, 1000, 7), changeElevation = true},
    {position = Position(1001, 1001, 7), nodeLooseness = 2},
    {position = Position(1002, 1002, 7)},
    {position = Position(1003, 1003, 7)}
}

]]--
Creature:isWalkingCreatureAlongNodes()
Creature:stopWalkCreatureAlongNodes()
Creature:setPauseWalkCreatureAlongNodes(pauseState, canMove)
Creature:startWalkCreatureAlongNodes(nodeList, testPath, nodeTimeoutDuration)

bandicam2024-02-2013-04-09-666-ezgif.com-video-to-gif-converter.gif
bandicam2024-02-2013-46-28-251-ezgif.com-video-to-gif-converter.gif
View attachment bandicam 2024-02-21 13-02-31-400.mp4

Yes, this is the longest script of your life, so that no source edits are required.
data/lib/core/xikiniCustomFunctions.lua
Lua:
-- check attached document

example usage, as shown in the 2nd gif
data/scripts/test.lua
Lua:
local creature = 0

local action = Action()

function action.onUse(player, item, fromPosition, target, toPosition, isHotkey)
    local target = Creature(creature)
    if not target then
        creature = Game.createNpc("bounty person", player:getPosition())
        if creature then
            creature = creature:getId()
            target = Creature(creature)
        end
    end
    if target then
        local nodeList = {
            {position = Position(1001, 1006, 7)},
            {position = Position(1006, 1006, 7)},
            {position = Position(1008, 1006, 6), changeElevation = true},
            {position = Position(1013, 1006, 6)},
            {position = Position(1015, 1006, 7), changeElevation = true},
            {position = Position(1019, 1006, 7)},
        }
        walkCreatureAlongNodes(target:getId(), nodeList, true)
        -- walkCreatureAlongNodes(creatureId, nodeList, testPath, nodeLooseness, nodeTimeoutDuration, nodeDuration, nodeActive)
    end
    return true
end

action:id(2173)
action:register()
And more complicated example, as shown in the video.
Lua:
local function doStuff(creatureId, eventTimeline, originalSpeed)
    local creature = Creature(creatureId)
    if not creature then
        return
    end
    
    local campfireLocation = Position(1002, 1006, 7)
    
    if eventTimeline == 0 then
        local nodeList = {
            {position = Position(1012, 1014, 7), nodeLooseness = 2},
            {position = Position(1017, 1010, 7), nodeLooseness = 1},
            {position = Position(1016, 1006, 7)},
            {position = Position(1015, 1006, 7)},
            {position = Position(1013, 1006, 6), changeElevation = true},
            {position = Position(1008, 1006, 6)},
            {position = Position(1006, 1006, 7), changeElevation = true},
            {position = Position(1001, 1006, 7)}
        }
        creature:startWalkCreatureAlongNodes(nodeList)
        addEvent(doStuff, 500, creatureId, eventTimeline + 1, originalSpeed)
        return
    elseif eventTimeline == 1 then
        if creature:isWalkingCreatureAlongNodes() then
            if not originalSpeed and creature:getPosition():getDistance(Position(1017, 1012, 7)) <= 1 then
                creature:setPauseWalkCreatureAlongNodes(true, false)
                originalSpeed = 1 -- reusing this, cuz not being used yet
                addEvent(doStuff, getStepDuration(creature), creatureId, eventTimeline, originalSpeed)
                return
            elseif originalSpeed == 1 then
                creature:setDirection(DIRECTION_EAST)
                creature:say("Hello other me!")
                originalSpeed = 2
                addEvent(doStuff, 5000, creatureId, eventTimeline, originalSpeed)
                return
            elseif originalSpeed == 2 then
                creature:setPauseWalkCreatureAlongNodes(false)
                originalSpeed = 3
            end
            addEvent(doStuff, 500, creatureId, eventTimeline, originalSpeed)
            return
        end
        -- reached destination
        local speed = creature:getSpeed()
        creature:changeSpeed(-speed)
        originalSpeed = speed
        creature:setDirection(DIRECTION_EAST)
        addEvent(doStuff, 500, creatureId, eventTimeline + 1, originalSpeed)
        return
    elseif eventTimeline == 2 then
        campfireLocation:sendMagicEffect(CONST_ME_POFF)
        Game.createItem(1421, 1, campfireLocation)
        addEvent(doStuff, 500, creatureId, eventTimeline + 1, originalSpeed)
        return
    elseif eventTimeline == 3 then
        campfireLocation:sendMagicEffect(CONST_ME_POFF)
        local thing = Tile(campfireLocation):getItemById(1421)
        thing:transform(1422)
        addEvent(doStuff, 500, creatureId, eventTimeline + 1, originalSpeed)
        return
    elseif eventTimeline == 4 then
        campfireLocation:sendMagicEffect(CONST_ME_FIREAREA)
        local thing = Tile(campfireLocation):getItemById(1422)
        thing:transform(1423)
        addEvent(doStuff, 10000, creatureId, eventTimeline + 1, originalSpeed)
        return
    elseif eventTimeline == 5 then
        campfireLocation:sendMagicEffect(CONST_ME_POFF)
        local thing = Tile(campfireLocation):getItemById(1423)
        thing:transform(1422)
        addEvent(doStuff, 500, creatureId, eventTimeline + 1, originalSpeed)
        return
    elseif eventTimeline == 6 then
        campfireLocation:sendMagicEffect(CONST_ME_POFF)
        local thing = Tile(campfireLocation):getItemById(1422)
        thing:transform(1421)
        addEvent(doStuff, 500, creatureId, eventTimeline + 1, originalSpeed)
        return
    elseif eventTimeline == 7 then
        campfireLocation:sendMagicEffect(CONST_ME_POFF)
        local thing = Tile(campfireLocation):getItemById(1421)
        thing:remove()
        addEvent(doStuff, 500, creatureId, eventTimeline + 1, originalSpeed)
        return
    elseif eventTimeline == 8 then
        local nodeList = {
            {position = Position(1001, 1016, 7)}
        }
        creature:changeSpeed(originalSpeed)
        creature:startWalkCreatureAlongNodes(nodeList)
        addEvent(doStuff, 500, creatureId, eventTimeline + 1, originalSpeed)
        return
    elseif eventTimeline == 9 then
        if creature:isWalkingCreatureAlongNodes() then
            addEvent(doStuff, 500, creatureId, eventTimeline, originalSpeed)
            return
        end
        creature:teleportTo(Position(1000, 1017, 7), true)
        addEvent(doStuff, 200, creatureId, eventTimeline + 1, originalSpeed)
    elseif eventTimeline == 10 then
        local thing = Tile(Position(1000, 1017, 7)):getItemById(5500)
        thing:transform(5498)
        thing = Tile(Position(1001, 1017, 7)):getItemById(5501)
        thing:transform(5499)
        creature:getPosition():sendMagicEffect(CONST_ME_POFF)
        creature:remove()
    end        
end

local creature = 0

local action = Action()

function action.onUse(player, item, fromPosition, target, toPosition, isHotkey)
    local target = Creature(creature)
    if not target then
        creature = Game.createNpc("bounty person", Position(1001, 1016, 7))
        if creature then
            creature = creature:getId()
            target = Creature(creature)
        end
        
        local thing = Tile(Position(1000, 1017, 7)):getItemById(5498)
        if thing then
            thing:transform(5500)
            thing = Tile(Position(1001, 1017, 7)):getItemById(5499)
            thing:transform(5501)
        end

        doStuff(target:getId(), 0)
    end
    return true
end

action:id(2173)
action:register()
 

Attachments

  • xikiniCustomFunctions.lua
    39.1 KB · Views: 9 · VirusTotal
Last edited:
@Xikini

I have question can you help me achieve this?

I would like to create arena in which creatures for example Demon appears and after you kill this demon. Demon [lvl 2] Will spawn with 10% more health experience and damage?. I do know i can achieve that by just adding more creatures in to lua with those names.. but is there any other way I can achieve this without modifications in my source code?

Any advise on that?
 
There's something very borked in the sources for creature:setMovementBlocked(true)
It doesn't seem to work correctly for monsters or npc's.

So, set npc walkinterval to 1500+ when walking npc's along a path.

Monsters 'work' as well, but they try to run to nearby players, and make the path really glitchy looking.

In the gif below, I have 'testPath' enabled, so you can see where the npc is trying to walk.

View attachment 82325
View attachment 82332

Yes, this is the longest script of your life, so that no source edits are required.
data/lib/core/xikiniCustomFunctions.lua
Lua:
-- check attached document

example usage, as shown in the gif
data/scripts/test.lua
Lua:
local creature = 0

local action = Action()

function action.onUse(player, item, fromPosition, target, toPosition, isHotkey)
    local target = Creature(creature)
    if not target then
        creature = Game.createNpc("bounty person", player:getPosition())
        if creature then
            creature = creature:getId()
            target = Creature(creature)
        end
    end
    if target then
        local nodeList = {
            {position = Position(1001, 1006, 7)},
            {position = Position(1006, 1006, 7)},
            {position = Position(1008, 1006, 6), changeElevation = true},
            {position = Position(1013, 1006, 6)},
            {position = Position(1015, 1006, 7), changeElevation = true},
            {position = Position(1019, 1006, 7)},
        }
        walkCreatureAlongNodes(target:getId(), nodeList, true)
        -- walkCreatureAlongNodes(creatureId, nodeList, testPath, nodeLooseness, nodeTimeoutDuration, nodeDuration, nodeActive)
    end
    return true
end

action:id(2173)
action:register()

He doesn't quit folks, we watched this man go through compiling and modifying tfs sources (something he said he wouldn't do) just to make this for you guys! Yall need to bow down and kiss his feet 🙌 Great work as always brother!
 
There's something very borked in the sources for creature:setMovementBlocked(true)
It doesn't seem to work correctly for monsters or npc's.

So, set npc walkinterval to 1500+ when walking npc's along a path.

Monsters 'work' as well, but they try to run to nearby players, and make the path really glitchy looking.

In the gif below, I have 'testPath' enabled, so you can see where the npc is trying to walk.

View attachment 82325
View attachment 82332

Yes, this is the longest script of your life, so that no source edits are required.
data/lib/core/xikiniCustomFunctions.lua
Lua:
-- check attached document

example usage, as shown in the gif
data/scripts/test.lua
Lua:
local creature = 0

local action = Action()

function action.onUse(player, item, fromPosition, target, toPosition, isHotkey)
    local target = Creature(creature)
    if not target then
        creature = Game.createNpc("bounty person", player:getPosition())
        if creature then
            creature = creature:getId()
            target = Creature(creature)
        end
    end
    if target then
        local nodeList = {
            {position = Position(1001, 1006, 7)},
            {position = Position(1006, 1006, 7)},
            {position = Position(1008, 1006, 6), changeElevation = true},
            {position = Position(1013, 1006, 6)},
            {position = Position(1015, 1006, 7), changeElevation = true},
            {position = Position(1019, 1006, 7)},
        }
        walkCreatureAlongNodes(target:getId(), nodeList, true)
        -- walkCreatureAlongNodes(creatureId, nodeList, testPath, nodeLooseness, nodeTimeoutDuration, nodeDuration, nodeActive)
    end
    return true
end

action:id(2173)
action:register()
I feel really bad because Xikini spent like 10 hours of his time to do that after I just mentioned rather humble, little walking NPC, but he did whole fucking walking system for all creatures, initially even tried compilig sources...

I feel like all his recent work has done more for the community than the entire TFS development team has done in years. I have no words!

breathtaking-youre-breathtaking.gif
 
Probably needs more features added, such as limits to each stat, and possibly restrictions per vocation..
But the base system is there.

View attachment 82140

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/statSystem.lua
Lua:
local conditionSubId = 45064 -- must be a unique subId not used for other buffs in your server
local config  = {
    statStorageKey = 45064, -- give players points with this storage
    ["statMain"] = {
        -- flat_bonus_stats
        {storageKey = 45065, statType = "life increase", value = 50},
        {storageKey = 45066, statType = "mana increase", value = 50},
        {storageKey = 45067, statType = "magic", value = 1},
        {storageKey = 45068, statType = "fist", value = 1},
        {storageKey = 45069, statType = "melee", value = 1},
        {storageKey = 45070, statType = "distance", value = 1},
        {storageKey = 45071, statType = "shield", value = 1},
        {storageKey = 45072, statType = "fishing", value = 1},
        {storageKey = 45073, statType = "critical hit chance", value = 1},
        {storageKey = 45074, statType = "critical hit damage", value = 1},
        {storageKey = 45075, statType = "life leech chance", value = 1},
        {storageKey = 45076, statType = "life leech amount", value = 1},
        {storageKey = 45077, statType = "mana leech chance", value = 1},
        {storageKey = 45078, statType = "mana leech amount", value = 1}
    },
    ["statSpeed"] = {
        {storageKey = 45079, statType = "speed", value = 2}
    },
    ["statRegen"] = {
        {storageKey = 45080, statType = "life regen", value = 5, ticks = 5000}, -- ticks in milliseconds}
        {storageKey = 45081, statType = "mana regen", value = 5, ticks = 5000}  -- can't go lower then 1000
    },
    ["statSoulRegen"] = { -- you can remove entire categories
        {storageKey = 45082, statType = "soul regen", value = 1, ticks = 5000} -- or individual stats, if you don't want to use them
    },
 
    modalWindow = {
        id = 1002, -- must be unique
        title = "Stat System",
        message = "",
        eventText = "ModalWindow_XikiniStatSystem",
        buttons = {
            {text = "Reset"},
            {text = "Remove"},
            {text = "Add", defaultEnterButton = true},
            {text = "Close", defaultEscapeButton = true},
        }
    }
}

-- Choose Flat or Percentage stats. Cannot use both.

-- Flat Stats
--[[
    ["statMain"] = {
        -- flat_bonus_stats
        {storageKey = 45065, statType = "life increase", value = 50},
        {storageKey = 45066, statType = "mana increase", value = 50},
        {storageKey = 45067, statType = "magic", value = 1},
        {storageKey = 45068, statType = "fist", value = 1},
        {storageKey = 45069, statType = "melee", value = 1},
        {storageKey = 45070, statType = "distance", value = 1},
        {storageKey = 45071, statType = "shield", value = 1},
        {storageKey = 45072, statType = "fishing", value = 1},
        {storageKey = 45073, statType = "critical hit chance", value = 1},
        {storageKey = 45074, statType = "critical hit damage", value = 1},
        {storageKey = 45075, statType = "life leech chance", value = 1},
        {storageKey = 45076, statType = "life leech amount", value = 1},
        {storageKey = 45077, statType = "mana leech chance", value = 1},
        {storageKey = 45078, statType = "mana leech amount", value = 1}
    },
    ["statSpeed"] = {
        {storageKey = 45079, statType = "speed", value = 2}
    },
    ["statRegen"] = {
        {storageKey = 45080, statType = "life regen", value = 5, ticks = 5000}, -- ticks in milliseconds}
        {storageKey = 45081, statType = "mana regen", value = 5, ticks = 5000}  -- can't go lower then 1000
    },
    ["statSoulRegen"] = { -- you can remove entire categories
        {storageKey = 45082, statType = "soul regen", value = 1, ticks = 5000} -- or individual stats, if you don't want to use them
    },
]]--


-- Percent Stats
--[[
    ["statMain"] = {
        -- percent_bonus_stats
        {storageKey = 45065, statType = "life increase percent", value = 1},
        {storageKey = 45066, statType = "mana increase percent", value = 1},
        {storageKey = 45067, statType = "magic percent", value = 1},
        {storageKey = 45068, statType = "fist percent", value = 1},
        {storageKey = 45069, statType = "melee percent", value = 1},
        {storageKey = 45070, statType = "distance percent", value = 1},
        {storageKey = 45071, statType = "shield percent", value = 1},
        {storageKey = 45072, statType = "fishing percent", value = 1},
        {storageKey = 45073, statType = "critical hit chance", value = 1},
        {storageKey = 45074, statType = "critical hit damage", value = 1},
        {storageKey = 45075, statType = "life leech chance", value = 1},
        {storageKey = 45076, statType = "life leech amount", value = 1},
        {storageKey = 45077, statType = "mana leech chance", value = 1},
        {storageKey = 45078, statType = "mana leech amount", value = 1}
    },
    ["statSpeed"] = {
        {storageKey = 45079, statType = "speed", value = 2}
    },
    ["statRegen"] = {
        {storageKey = 45080, statType = "life regen", value = 5, ticks = 5000}, -- ticks in milliseconds}
        {storageKey = 45081, statType = "mana regen", value = 5, ticks = 5000}  -- can't go lower then 1000
    },
    ["statSoulRegen"] = {
        {storageKey = 45082, statType = "soul regen", value = 1, ticks = 5000}
    },
]]--


-- END OF CONFIG

local choiceDictionary = {}

local conditions = {
    ["life increase"] = {CONDITION_PARAM_STAT_MAXHITPOINTS},
    ["mana increase"] = {CONDITION_PARAM_STAT_MAXMANAPOINTS},
    ["speed"] = {CONDITION_PARAM_SPEED},
    ["magic"] = {CONDITION_PARAM_STAT_MAGICPOINTS},
    ["melee"] = {CONDITION_PARAM_SKILL_MELEE},
    ["fist"] = {CONDITION_PARAM_SKILL_FIST},
    ["club"] = {CONDITION_PARAM_SKILL_CLUB},
    ["sword"] = {CONDITION_PARAM_SKILL_SWORD},
    ["axe"] = {CONDITION_PARAM_SKILL_AXE},
    ["distance"] = {CONDITION_PARAM_SKILL_DISTANCE},
    ["shield"] = {CONDITION_PARAM_SKILL_SHIELD},
    ["fishing"] = {CONDITION_PARAM_SKILL_FISHING},
    ["critical hit chance"] = {CONDITION_PARAM_SPECIALSKILL_CRITICALHITCHANCE},
    ["critical hit damage"] = {CONDITION_PARAM_SPECIALSKILL_CRITICALHITAMOUNT},
    ["life leech chance"] = {CONDITION_PARAM_SPECIALSKILL_LIFELEECHCHANCE},
    ["life leech amount"] = {CONDITION_PARAM_SPECIALSKILL_LIFELEECHAMOUNT},
    ["mana leech chance"] = {CONDITION_PARAM_SPECIALSKILL_MANALEECHCHANCE},
    ["mana leech amount"] = {CONDITION_PARAM_SPECIALSKILL_MANALEECHAMOUNT},
    ["life increase percent"] = {CONDITION_PARAM_STAT_MAXHITPOINTSPERCENT},
    ["mana increase percent"] = {CONDITION_PARAM_STAT_MAXMANAPOINTSPERCENT},
    ["magic percent"] = {CONDITION_PARAM_STAT_MAGICPOINTSPERCENT},
    ["melee percent"] = {CONDITION_PARAM_SKILL_MELEEPERCENT},
    ["fist percent"] = {CONDITION_PARAM_SKILL_FISTPERCENT},
    ["club percent"] = {CONDITION_PARAM_SKILL_CLUBPERCENT},
    ["sword percent"] = {CONDITION_PARAM_SKILL_SWORDPERCENT},
    ["axe percent"] = {CONDITION_PARAM_SKILL_AXEPERCENT},
    ["distance percent"] = {CONDITION_PARAM_SKILL_DISTANCEPERCENT},
    ["shield percent"] = {CONDITION_PARAM_SKILL_SHIELDPERCENT},
    ["fishing percent"] = {CONDITION_PARAM_SKILL_FISHINGPERCENT},
    ["life regen"] = {CONDITION_PARAM_HEALTHGAIN, CONDITION_PARAM_HEALTHTICKS},
    ["mana regen"] = {CONDITION_PARAM_MANAGAIN, CONDITION_PARAM_MANATICKS},
    ["soul regen"] = {CONDITION_PARAM_SOULGAIN, CONDITION_PARAM_SOULTICKS}
}

local main_attributes = {CONDITION_ATTRIBUTES, CONDITION_HASTE, CONDITION_REGENERATION, CONDITION_SOUL}
local main_stats = {"statMain", "statSpeed", "statRegen", "statSoulRegen"}

-- choiceDictionary Setup
for i = 1, 4 do
    local statCategory = main_stats[i]
    if config[statCategory] then
        for index, stat in ipairs(config[statCategory]) do
            choiceDictionary[#choiceDictionary + 1] = config[statCategory][index]
        end
    end
end

local function updateStatBonus(player)
    -- remove all previous buffs
    for i = 1, 4 do
        if player:getCondition(main_attributes[i], conditionSubId) then
            player:removeCondition(main_attributes[i], conditionSubId)
        end
    end
 
    -- add all buffs
    for i = 1, 4 do
        local statCategory = main_stats[i]
        if config[statCategory] then
            local condition = Condition(main_attributes[i], conditionSubId)
            condition:setParameter(CONDITION_PARAM_TICKS, -1)
            for _, stat in ipairs(config[statCategory]) do
                local storageValue = player:getStorageValue(stat.storageKey)
                if storageValue > 0 then
                    for conditionParam = 1, #conditions[stat.statType] do
                        condition:setParameter(conditions[stat.statType][conditionParam], (stat.value * storageValue))
                    end
                    player:addCondition(condition)
                end
            end
        end
    end
    return true
end

local creatureevent = CreatureEvent("onLogin_updateStatBonus")

function creatureevent.onLogin(player)
    updateStatBonus(player)
    return true
end

creatureevent:register()



local function createStatWindow(playerId)
    local player = Player(playerId)
    if not player then
        return
    end

    if player:hasEvent(CREATURE_EVENT_MODALWINDOW, config.modalWindow.eventText) then
        player:unregisterEvent(config.modalWindow.eventText)
    end
    player:registerEvent(config.modalWindow.eventText)
 
    local storageValue = player:getStorageValue(config.statStorageKey)
    local modalWindow = ModalWindow(config.modalWindow.id, config.modalWindow.title, "You have " .. (storageValue > 0 and storageValue or 0) .. " stat points to spend.")
 
    for id, button in ipairs(config.modalWindow.buttons) do
        modalWindow:addButton(id, button.text)
        if button.defaultEscapeButton then
            modalWindow:setDefaultEscapeButton(id)
        elseif button.defaultEnterButton then
            modalWindow:setDefaultEnterButton(id)
        end
    end
 
    for id, stat in ipairs(choiceDictionary) do
        local storageValue = player:getStorageValue(stat.storageKey)
        storageValue = storageValue > 0 and storageValue or 0
        modalWindow:addChoice(id, "[" .. storageValue .. "] " .. stat.statType)
    end
 
    modalWindow:hasPriority()
    modalWindow:sendToPlayer(player)
end


local creatureevent = CreatureEvent(config.modalWindow.eventText)

function creatureevent.onModalWindow(player, modalWindowId, buttonId, choiceId)
    player:unregisterEvent(config.modalWindow.eventText)
 
    if modalWindowId == config.modalWindow.id then
        local buttonChoice = config.modalWindow.buttons[buttonId].text
    
        local statStorageValue = player:getStorageValue(config.statStorageKey)
        if buttonChoice == "Add" then
            if statStorageValue > 0 then
                local stat = choiceDictionary[choiceId]
                local storageValue = player:getStorageValue(stat.storageKey)
                storageValue = storageValue > 0 and storageValue or 0
                player:setStorageValue(stat.storageKey, storageValue + 1)
                player:setStorageValue(config.statStorageKey, statStorageValue - 1)
            end
        elseif buttonChoice == "Remove" then
            if statStorageValue > 0 then
                local stat = choiceDictionary[choiceId]
                local storageValue = player:getStorageValue(stat.storageKey)
                storageValue = storageValue > 0 and storageValue or 0
                if storageValue > 0 then
                    player:setStorageValue(stat.storageKey, storageValue - 1)
                    player:setStorageValue(config.statStorageKey, statStorageValue + 1)
                end
            end
        elseif buttonChoice == "Reset" then
            local totalResetPoints = 0
            for id, stat in ipairs(choiceDictionary) do
                local storageValue = player:getStorageValue(stat.storageKey)
                storageValue = storageValue > 0 and storageValue or 0
                player:setStorageValue(stat.storageKey, 0)
                totalResetPoints = totalResetPoints + storageValue
            end
            player:setStorageValue(config.statStorageKey, statStorageValue + totalResetPoints)
        else
            -- "Close" button
            return true
        end
    
        updateStatBonus(player)
        addEvent(createStatWindow, 0, player:getId())
    end
    return true
end

creatureevent:register()


local talkaction = TalkAction("!stats")

function talkaction.onSay(player, words, param, type)
    createStatWindow(player:getId())
    return false
end

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



from script here

change -- line 1
Lua:
local amuletItemId = 2173
to
Lua:
local upgradeableItems = {2173}


change -- line 68
Lua:
if target:getId() ~= amuletItemId then
to
Lua:
if not table.contains(upgradeableItems, target:getId())
Post automatically merged:



View attachment 82141

data/scripts/holyTible.lua
Lua:
local holyTibleId = 1970

local positions = {
--  [actionId] = destination
    [45083] = Position(982, 993, 7),
    [45084] = Position(981, 992, 7)
}

local moveevent = MoveEvent()

function moveevent.onStepIn(creature, item, position, fromPosition)
    if not creature:isPlayer() then
        return true
    end
    if creature:getItemCount(holyTibleId) == 0 then
        creature:teleportTo(fromPosition, true)
        return true
    end
    position:sendMagicEffect(CONST_ME_TELEPORT)
    creature:teleportTo(positions[item:getActionId()])
    creature:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
    return true
end

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

I've decided to improve item descriptions in Xikini's protection upgrade script, because it was removing all the existing additional descriptions that upgradable item previously had, like those from XML file or custom descriptions from another scripts/systems as well.

xikini_upgrade.gif

In Xikini's script do the following changes:
in line 2
Lua:
local maximumUpgradePercent
change to:
Code:
maximumUpgradePercent

remove
setProtectionDescription(target)

and remove whole setProtectionDescription function.

open data/scripts/eventcallbacks/player/default_onLook.lua and before return description paste:
Lua:
if thing:isItem() then
    local text = ""
    local isMaxUpgraded = true
    local upgrades = false
    local order = {"physical", "energy", "earth", "fire", "ice", "holy", "death"}
    for _, element in ipairs(order) do
        local attributeValue = thing:getCustomAttribute(element) or 0
        if attributeValue > 0 then
            if text ~= "" then
                text = text .. ", "
            end
            text = text .. element .. " +" .. attributeValue .. "%"
        end
        if attributeValue ~= maximumUpgradePercent then
            isMaxUpgraded = false
        end

        if attributeValue > 0 then
            upgrades = true
        end
       
    end

    if isMaxUpgraded then
        text = "[Additional Protections: All Damage Types +" .. maximumUpgradePercent .. "%]"
    else
        text = "[Additional Protections: " .. text .. "]"
    end
   
    if upgrades then
    description = description ..  "\n\n" .. text
    else
    description = description
    end

end

Big <3 Xikini!
 
Last edited:
open data/scripts/eventcallbacks/player/default_onLook.lua
Just curious, what's the difference between the folders... one has 'data/event/player.lua' with 'onLook,' and the other has 'data/scripts/eventcallbacks/player/default_onLook'? Could you provide a brief explanation so I can understand better? I've always only added data/event/player.lua... haha.
 
Just curious, what's the difference between the folders... one has 'data/event/player.lua' with 'onLook,' and the other has 'data/scripts/eventcallbacks/player/default_onLook'? Could you provide a brief explanation so I can understand better? I've always only added data/event/player.lua... haha.
Im deffinitely not an expert for such questions, but how I understand it is the data/event/player.lua with its "onLook" function provides the basic feature that returns info about stuff when you look at something, and the other one in data/scripts/eventcallbacks/player/default_onLook is like everything beyond that, what you want to add to the onLook function.

The first one (data/event/player.lua) triggers the second one. First one SENDS description to the player so that you can see this green text, second one CONTAINS additional descriptions if you add them there.
 
Requesting a more unique loot stats for all weapons, and equipment but not to complicated with attributes and such.

Weapons can have a boost of max +3 in atk and defense.

Armors/legs/shield can have a max boost of +2

helmets/boots/amulets/rings can have a max boost of +1 def

Lets use the katana as an example, common stats are atk:16 def: 12 so with the more unique loot stats I mean you are able to loot katanas with the max stats of +3 of the regular stats so the max would be atk:19 def: 15 but can be anything in between. Same with for example studded armor: 5 max can be armor: 7.

And no fancy names like common, rare and legendary just
 
View attachment 82028

data/scripts/capacityIncreaserItems.lua
Lua:
local config = {
    [2643] = { -- leather boots
        capacityIncrease = 209, -- 200 increase + 9 for weight of boots
        slot = "feet" -- ("head", "necklace", "backpack", "armor", "hand", "legs", "feet", "ring", "ammo")
    },
    [10522] = { -- crown backpack
        capacityIncrease = 1017, -- 1000 increase + 17 for weight of backpack
        slot = "backpack"
    }
}

-- END OF CONFIG

local startingLevelofVocations = 8 -- only edit this if you have a non-vanilla setup
local baseCapacity = 400

local function calculateTotalCapacity(player) 
    local playerLevel = player:getLevel()
    local newCapacity = baseCapacity * 100
    if playerLevel <= startingLevelofVocations then
        newCapacity = newCapacity + (Vocation(0):getCapacityGain() * (playerLevel - 1))
    else
        newCapacity = newCapacity + (Vocation(0):getCapacityGain() * (startingLevelofVocations - 1))
    end
    if playerLevel > startingLevelofVocations then
        newCapacity = newCapacity + (player:getVocation():getCapacityGain() * (player:getLevel() - startingLevelofVocations))
    end

    for i = 1, 10 do
        local slotItem = player:getSlotItem(i)
        if slotItem then
            local index = config[slotItem:getId()]
            if index then
                newCapacity = newCapacity + (index.capacityIncrease * 100)
            end
        end
    end 

    if player:getCapacity() ~= newCapacity then
        player:setCapacity(newCapacity)
    end
end

for itemId, v in pairs(config) do

    local moveevent = MoveEvent()
 
    function moveevent.onEquip(player, item, slot, isCheck)
        if not isCheck then
            calculateTotalCapacity(player)
        end
        return true
    end
 
    moveevent:slot(v.slot)
    moveevent:id(itemId)
     
    moveevent:register()
 
 
    local moveevent = MoveEvent()

    function moveevent.onDeEquip(player, item, slot, isCheck)
        if not isCheck then
            calculateTotalCapacity(player)
        end
        return true
    end
 
    moveevent:slot(v.slot)
    moveevent:id(itemId)
     
    moveevent:register()
end


local creatureevent = CreatureEvent("onLogin_capacityIncreaserItems")

function creatureevent.onLogin(player)
    calculateTotalCapacity(player)
    return true
end

creatureevent:register()
Just a little question, using slot = "backpack, ammo" will work? Because backpacks can be equiped (generally) on ammo and backpack slots. Just tested it as this:
1708525383678.png
but it only triggers the ammo slot in this case. There's also a test that must be done in case that both slots get enabled, just a little tip check if equip and deequip events with the two avaible slots for the same item can cause addition of infinite capacity. Regards!
 
Okay, so I disliked how incomplete the system was before.. so It's been updated.

And here's an example of what you can do with it, including the new functions.

View attachment bandicam 2024-02-21 13-02-31-400.mp4

Lua:
local function doStuff(creatureId, eventTimeline, originalSpeed)
    local creature = Creature(creatureId)
    if not creature then
        return
    end
 
    local campfireLocation = Position(1002, 1006, 7)
 
    if eventTimeline == 0 then
        local nodeList = {
            {position = Position(1012, 1014, 7), nodeLooseness = 2},
            {position = Position(1017, 1010, 7), nodeLooseness = 1},
            {position = Position(1016, 1006, 7)},
            {position = Position(1015, 1006, 7)},
            {position = Position(1013, 1006, 6), changeElevation = true},
            {position = Position(1008, 1006, 6)},
            {position = Position(1006, 1006, 7), changeElevation = true},
            {position = Position(1001, 1006, 7)}
        }
        creature:startWalkCreatureAlongNodes(nodeList)
        addEvent(doStuff, 500, creatureId, eventTimeline + 1, originalSpeed)
        return
    elseif eventTimeline == 1 then
        if creature:isWalkingCreatureAlongNodes() then
            if not originalSpeed and creature:getPosition():getDistance(Position(1017, 1012, 7)) <= 1 then
                creature:setPauseWalkCreatureAlongNodes(true, false)
                originalSpeed = 1 -- reusing this, cuz not being used yet
                addEvent(doStuff, getStepDuration(creature), creatureId, eventTimeline, originalSpeed)
                return
            elseif originalSpeed == 1 then
                creature:setDirection(DIRECTION_EAST)
                creature:say("Hello other me!")
                originalSpeed = 2
                addEvent(doStuff, 5000, creatureId, eventTimeline, originalSpeed)
                return
            elseif originalSpeed == 2 then
                creature:setPauseWalkCreatureAlongNodes(false)
                originalSpeed = 3
            end
            addEvent(doStuff, 500, creatureId, eventTimeline, originalSpeed)
            return
        end
        -- reached destination
        local speed = creature:getSpeed()
        creature:changeSpeed(-speed)
        originalSpeed = speed
        creature:setDirection(DIRECTION_EAST)
        addEvent(doStuff, 500, creatureId, eventTimeline + 1, originalSpeed)
        return
    elseif eventTimeline == 2 then
        campfireLocation:sendMagicEffect(CONST_ME_POFF)
        Game.createItem(1421, 1, campfireLocation)
        addEvent(doStuff, 500, creatureId, eventTimeline + 1, originalSpeed)
        return
    elseif eventTimeline == 3 then
        campfireLocation:sendMagicEffect(CONST_ME_POFF)
        local thing = Tile(campfireLocation):getItemById(1421)
        thing:transform(1422)
        addEvent(doStuff, 500, creatureId, eventTimeline + 1, originalSpeed)
        return
    elseif eventTimeline == 4 then
        campfireLocation:sendMagicEffect(CONST_ME_FIREAREA)
        local thing = Tile(campfireLocation):getItemById(1422)
        thing:transform(1423)
        addEvent(doStuff, 10000, creatureId, eventTimeline + 1, originalSpeed)
        return
    elseif eventTimeline == 5 then
        campfireLocation:sendMagicEffect(CONST_ME_POFF)
        local thing = Tile(campfireLocation):getItemById(1423)
        thing:transform(1422)
        addEvent(doStuff, 500, creatureId, eventTimeline + 1, originalSpeed)
        return
    elseif eventTimeline == 6 then
        campfireLocation:sendMagicEffect(CONST_ME_POFF)
        local thing = Tile(campfireLocation):getItemById(1422)
        thing:transform(1421)
        addEvent(doStuff, 500, creatureId, eventTimeline + 1, originalSpeed)
        return
    elseif eventTimeline == 7 then
        campfireLocation:sendMagicEffect(CONST_ME_POFF)
        local thing = Tile(campfireLocation):getItemById(1421)
        thing:remove()
        addEvent(doStuff, 500, creatureId, eventTimeline + 1, originalSpeed)
        return
    elseif eventTimeline == 8 then
        local nodeList = {
            {position = Position(1001, 1016, 7)}
        }
        creature:changeSpeed(originalSpeed)
        creature:startWalkCreatureAlongNodes(nodeList)
        addEvent(doStuff, 500, creatureId, eventTimeline + 1, originalSpeed)
        return
    elseif eventTimeline == 9 then
        if creature:isWalkingCreatureAlongNodes() then
            addEvent(doStuff, 500, creatureId, eventTimeline, originalSpeed)
            return
        end
        creature:teleportTo(Position(1000, 1017, 7), true)
        addEvent(doStuff, 200, creatureId, eventTimeline + 1, originalSpeed)
    elseif eventTimeline == 10 then
        local thing = Tile(Position(1000, 1017, 7)):getItemById(5500)
        thing:transform(5498)
        thing = Tile(Position(1001, 1017, 7)):getItemById(5501)
        thing:transform(5499)
        creature:getPosition():sendMagicEffect(CONST_ME_POFF)
        creature:remove()
    end     
end

local creature = 0

local action = Action()

function action.onUse(player, item, fromPosition, target, toPosition, isHotkey)
    local target = Creature(creature)
    if not target then
        creature = Game.createNpc("bounty person", Position(1001, 1016, 7))
        if creature then
            creature = creature:getId()
            target = Creature(creature)
        end
     
        local thing = Tile(Position(1000, 1017, 7)):getItemById(5498)
        if thing then
            thing:transform(5500)
            thing = Tile(Position(1001, 1017, 7)):getItemById(5499)
            thing:transform(5501)
        end

        doStuff(target:getId(), 0)
    end
    return true
end

action:id(2173)
action:register()
 
Last edited:
Back
Top