• 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!
  • 2026 staff recruitment is open! Check it out and consider applying!

TFS 1.X+ weapon leveling 1.4.2 TFS

Ihavegunz

New Member
Joined
Feb 20, 2022
Messages
3
Reaction score
0
Hi, I have TFS 1.4.2 and I'd like to create a script that will allow all weapons to gain experience on impact. For example, I have a sword, and it gives me 1 experience on impact. At 100 experience, the sword will have +1 and will gain more attack and defense. My brother and I have been trying to write this script for 3 days, but TFS keeps throwing errors. can someone help me? Thank you.
 
Hi, I have TFS 1.4.2 and I'd like to create a script that will allow all weapons to gain experience on impact. For example, I have a sword, and it gives me 1 experience on impact. At 100 experience, the sword will have +1 and will gain more attack and defense. My brother and I have been trying to write this script for 3 days, but TFS keeps throwing errors. can someone help me? Thank you.
paste the script and the errors
 
hello brother, I don't fully understand how newer engines work, the engine automatically loads scripts from data/script and this is the script:

LUA:
local EXP_PER_LEVEL = 1000
local MAX_LEVEL = 10
local ATTACK_PER_LEVEL = 2
local EXP_GAIN_MIN = 1
local EXP_GAIN_MAX = 5

local ELEMENTS = {"fire", "ice", "earth", "death"}

local function updateWeaponDescription(item, level, exp)
    local percent = math.floor((exp / EXP_PER_LEVEL) * 100)
    item:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, string.format("Weapon Level: +%d\nEXP: %d/%d (%d%%)", level, exp, EXP_PER_LEVEL, percent))
end

local function applyWeaponBonuses(item, level)
    item:setAttack(item:getAttack() + ATTACK_PER_LEVEL)

    if level == 5 then
        item:setAttribute(ITEM_ATTRIBUTE_NAME, item:getName() .. " [Power+5]")
    elseif level == 10 then
        local element = ELEMENTS[math.random(#ELEMENTS)]
        item:setAttribute(ITEM_ATTRIBUTE_NAME, item:getName() .. " [" .. element .. "]")
        local holder = item:getHolder()
        if holder and holder:isPlayer() then
            Game.broadcastMessage(string.format("Gracz %s ulepszył broń do poziomu +10!", holder:getName()), MESSAGE_STATUS_WARNING)
        end
    end
end

function onStatsChange(creature, attacker, changetype, combat, value)
    if not attacker or not attacker:isPlayer() then return true end
    if changetype ~= STATSCHANGE_HEALTHLOSS then return true end

    local weapon = attacker:getSlotItem(CONST_SLOT_RIGHT)
    if not weapon or not weapon:isWeapon() then return true end

    local level = weapon:getCustomAttribute("weaponLevel") or 0
    local exp = weapon:getCustomAttribute("weaponExp") or 0

    if level >= MAX_LEVEL then return true end

    local gain = math.random(EXP_GAIN_MIN, EXP_GAIN_MAX)
    exp = exp + gain

    if exp >= EXP_PER_LEVEL then
        exp = exp - EXP_PER_LEVEL
        level = level + 1
        weapon:setCustomAttribute("weaponLevel", level)
        weapon:setCustomAttribute("weaponExp", exp)
        updateWeaponDescription(weapon, level, exp)
        applyWeaponBonuses(weapon, level)
    else
        weapon:setCustomAttribute("weaponExp", exp)
        updateWeaponDescription(weapon, level, exp)
    end

    return true
end

function onUse(player, item, fromPos, target, toPos, isHotkey)
    if not item:isWeapon() then
        return false
    end

    local level = item:getCustomAttribute("weaponLevel") or 0
    local exp = item:getCustomAttribute("weaponExp") or 0
    local percent = (exp / EXP_PER_LEVEL) * 100

    player:sendTextMessage(MESSAGE_INFO_DESCR,
        string.format("Twoja broń ma %d/%d EXP (%.1f%%) - Poziom: +%d", exp, EXP_PER_LEVEL, percent, level)
    )
    return true
end

creatureevent:register()
action:register()

and generates errors like this:
Code:
Lua Script Error: [Scripts Interface]
/root/forgottenserver/build/data/scripts/weapon_exp_system.lua
...forgottenserver/build/data/scripts/weapon_exp_system.lua:74: attempt to index global 'creatureevent' (a nil value)
stack traceback:
        [C]: in function '__index'
        ...forgottenserver/build/data/scripts/weapon_exp_system.lua:74: in main chunk
> weapon_exp_system.lua [error]

I'd also like to add that the previous conversion generated errors with data/lib/compat. Even if someone doesn't convert it, I'd appreciate some guidance. Thanks.

he also tried to do it this way:
LUA:
local EXP_PER_LEVEL = 1000
local MAX_LEVEL = 10
local ATTACK_PER_LEVEL = 2
local EXP_GAIN_MIN = 1
local EXP_GAIN_MAX = 5
local ELEMENTS = {"fire", "ice", "earth", "death"}

local function updateWeaponDescription(item, level, exp)
    local percent = math.floor((exp / EXP_PER_LEVEL) * 100)
    item:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION,
        string.format("Weapon Level: +%d\nEXP: %d/%d (%d%%)", level, exp, EXP_PER_LEVEL, percent))
end

local function applyWeaponBonuses(item, level)
    item:setAttack(item:getAttack() + ATTACK_PER_LEVEL)

    if level == 5 then
        item:setAttribute(ITEM_ATTRIBUTE_NAME, item:getName() .. " [Power+5]")
    elseif level == 10 then
        local element = ELEMENTS[math.random(#ELEMENTS)]
        item:setAttribute(ITEM_ATTRIBUTE_NAME, item:getName() .. " [" .. element .. "]")
        local holder = item:getHolder()
        if holder and holder:isPlayer() then
            Game.broadcastMessage(string.format("Gracz %s ulepszył broń do poziomu +10!", holder:getName()), MESSAGE_STATUS_WARNING)
        end
    end
end

local weaponExpEvent = CreatureEvent("WeaponExpGain")

function weaponExpEvent.onStatsChange(creature, attacker, changetype, combat, value)
    if not attacker or not attacker:isPlayer() then return true end
    if changetype ~= STATSCHANGE_HEALTHLOSS then return true end

    local weapon = attacker:getSlotItem(CONST_SLOT_RIGHT)
    if not weapon or not weapon:isWeapon() then return true end

    local level = weapon:getCustomAttribute("weaponLevel") or 0
    local exp = weapon:getCustomAttribute("weaponExp") or 0

    if level >= MAX_LEVEL then return true end

    local gain = math.random(EXP_GAIN_MIN, EXP_GAIN_MAX)
    exp = exp + gain

    if exp >= EXP_PER_LEVEL then
        exp = exp - EXP_PER_LEVEL
        level = level + 1
        weapon:setCustomAttribute("weaponLevel", level)
        weapon:setCustomAttribute("weaponExp", exp)
        updateWeaponDescription(weapon, level, exp)
        applyWeaponBonuses(weapon, level)
    else
        weapon:setCustomAttribute("weaponExp", exp)
        updateWeaponDescription(weapon, level, exp)
    end

    return true
end

weaponExpEvent:register()

local weaponClickAction = Action()

function weaponClickAction.onUse(player, item, fromPos, target, toPos, isHotkey)
    if not item:isWeapon() then return false end

    local level = item:getCustomAttribute("weaponLevel") or 0
    local exp = item:getCustomAttribute("weaponExp") or 0
    local percent = (exp / EXP_PER_LEVEL) * 100

    player:sendTextMessage(MESSAGE_INFO_DESCR,
        string.format("Twoja broń ma %d/%d EXP (%.1f%%) - Poziom: +%d", exp, EXP_PER_LEVEL, percent, level)
    )
    return true
end

weaponClickAction:id(2398, 2400, 2403, 2406, 7407, 2423, 2430, 7384, 7404)
weaponClickAction:register()

and then generates an error like this:
Code:
Lua Script Error: [Scripts Interface]
/root/forgottenserver/build/data/scripts/weapon_exp_system.lua
data/lib/compat/compat.lua:159: bad argument #1 to 'rawset' (table expected, got userdata)
stack traceback:
        [C]: in ?
        [C]: in function 'rawset'
        data/lib/compat/compat.lua:159: in function '__newindex'
        ...forgottenserver/build/data/scripts/weapon_exp_system.lua:34: in main chunk
> weapon_exp_system.lua [error]
 
Last edited:
hello brother, I don't fully understand how newer engines work, the engine automatically loads scripts from data/script and this is the script:

LUA:
local EXP_PER_LEVEL = 1000
local MAX_LEVEL = 10
local ATTACK_PER_LEVEL = 2
local EXP_GAIN_MIN = 1
local EXP_GAIN_MAX = 5

local ELEMENTS = {"fire", "ice", "earth", "death"}

local function updateWeaponDescription(item, level, exp)
    local percent = math.floor((exp / EXP_PER_LEVEL) * 100)
    item:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, string.format("Weapon Level: +%d\nEXP: %d/%d (%d%%)", level, exp, EXP_PER_LEVEL, percent))
end

local function applyWeaponBonuses(item, level)
    item:setAttack(item:getAttack() + ATTACK_PER_LEVEL)

    if level == 5 then
        item:setAttribute(ITEM_ATTRIBUTE_NAME, item:getName() .. " [Power+5]")
    elseif level == 10 then
        local element = ELEMENTS[math.random(#ELEMENTS)]
        item:setAttribute(ITEM_ATTRIBUTE_NAME, item:getName() .. " [" .. element .. "]")
        local holder = item:getHolder()
        if holder and holder:isPlayer() then
            Game.broadcastMessage(string.format("Gracz %s ulepszył broń do poziomu +10!", holder:getName()), MESSAGE_STATUS_WARNING)
        end
    end
end

function onStatsChange(creature, attacker, changetype, combat, value)
    if not attacker or not attacker:isPlayer() then return true end
    if changetype ~= STATSCHANGE_HEALTHLOSS then return true end

    local weapon = attacker:getSlotItem(CONST_SLOT_RIGHT)
    if not weapon or not weapon:isWeapon() then return true end

    local level = weapon:getCustomAttribute("weaponLevel") or 0
    local exp = weapon:getCustomAttribute("weaponExp") or 0

    if level >= MAX_LEVEL then return true end

    local gain = math.random(EXP_GAIN_MIN, EXP_GAIN_MAX)
    exp = exp + gain

    if exp >= EXP_PER_LEVEL then
        exp = exp - EXP_PER_LEVEL
        level = level + 1
        weapon:setCustomAttribute("weaponLevel", level)
        weapon:setCustomAttribute("weaponExp", exp)
        updateWeaponDescription(weapon, level, exp)
        applyWeaponBonuses(weapon, level)
    else
        weapon:setCustomAttribute("weaponExp", exp)
        updateWeaponDescription(weapon, level, exp)
    end

    return true
end

function onUse(player, item, fromPos, target, toPos, isHotkey)
    if not item:isWeapon() then
        return false
    end

    local level = item:getCustomAttribute("weaponLevel") or 0
    local exp = item:getCustomAttribute("weaponExp") or 0
    local percent = (exp / EXP_PER_LEVEL) * 100

    player:sendTextMessage(MESSAGE_INFO_DESCR,
        string.format("Twoja broń ma %d/%d EXP (%.1f%%) - Poziom: +%d", exp, EXP_PER_LEVEL, percent, level)
    )
    return true
end

creatureevent:register()
action:register()

and generates errors like this:
Code:
Lua Script Error: [Scripts Interface]
/root/forgottenserver/build/data/scripts/weapon_exp_system.lua
...forgottenserver/build/data/scripts/weapon_exp_system.lua:74: attempt to index global 'creatureevent' (a nil value)
stack traceback:
        [C]: in function '__index'
        ...forgottenserver/build/data/scripts/weapon_exp_system.lua:74: in main chunk
> weapon_exp_system.lua [error]

I'd also like to add that the previous conversion generated errors with data/lib/compat. Even if someone doesn't convert it, I'd appreciate some guidance. Thanks.

he also tried to do it this way:
LUA:
local EXP_PER_LEVEL = 1000
local MAX_LEVEL = 10
local ATTACK_PER_LEVEL = 2
local EXP_GAIN_MIN = 1
local EXP_GAIN_MAX = 5
local ELEMENTS = {"fire", "ice", "earth", "death"}

local function updateWeaponDescription(item, level, exp)
    local percent = math.floor((exp / EXP_PER_LEVEL) * 100)
    item:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION,
        string.format("Weapon Level: +%d\nEXP: %d/%d (%d%%)", level, exp, EXP_PER_LEVEL, percent))
end

local function applyWeaponBonuses(item, level)
    item:setAttack(item:getAttack() + ATTACK_PER_LEVEL)

    if level == 5 then
        item:setAttribute(ITEM_ATTRIBUTE_NAME, item:getName() .. " [Power+5]")
    elseif level == 10 then
        local element = ELEMENTS[math.random(#ELEMENTS)]
        item:setAttribute(ITEM_ATTRIBUTE_NAME, item:getName() .. " [" .. element .. "]")
        local holder = item:getHolder()
        if holder and holder:isPlayer() then
            Game.broadcastMessage(string.format("Gracz %s ulepszył broń do poziomu +10!", holder:getName()), MESSAGE_STATUS_WARNING)
        end
    end
end

local weaponExpEvent = CreatureEvent("WeaponExpGain")

function weaponExpEvent.onStatsChange(creature, attacker, changetype, combat, value)
    if not attacker or not attacker:isPlayer() then return true end
    if changetype ~= STATSCHANGE_HEALTHLOSS then return true end

    local weapon = attacker:getSlotItem(CONST_SLOT_RIGHT)
    if not weapon or not weapon:isWeapon() then return true end

    local level = weapon:getCustomAttribute("weaponLevel") or 0
    local exp = weapon:getCustomAttribute("weaponExp") or 0

    if level >= MAX_LEVEL then return true end

    local gain = math.random(EXP_GAIN_MIN, EXP_GAIN_MAX)
    exp = exp + gain

    if exp >= EXP_PER_LEVEL then
        exp = exp - EXP_PER_LEVEL
        level = level + 1
        weapon:setCustomAttribute("weaponLevel", level)
        weapon:setCustomAttribute("weaponExp", exp)
        updateWeaponDescription(weapon, level, exp)
        applyWeaponBonuses(weapon, level)
    else
        weapon:setCustomAttribute("weaponExp", exp)
        updateWeaponDescription(weapon, level, exp)
    end

    return true
end

weaponExpEvent:register()

local weaponClickAction = Action()

function weaponClickAction.onUse(player, item, fromPos, target, toPos, isHotkey)
    if not item:isWeapon() then return false end

    local level = item:getCustomAttribute("weaponLevel") or 0
    local exp = item:getCustomAttribute("weaponExp") or 0
    local percent = (exp / EXP_PER_LEVEL) * 100

    player:sendTextMessage(MESSAGE_INFO_DESCR,
        string.format("Twoja broń ma %d/%d EXP (%.1f%%) - Poziom: +%d", exp, EXP_PER_LEVEL, percent, level)
    )
    return true
end

weaponClickAction:id(2398, 2400, 2403, 2406, 7407, 2423, 2430, 7384, 7404)
weaponClickAction:register()

and then generates an error like this:
Code:
Lua Script Error: [Scripts Interface]
/root/forgottenserver/build/data/scripts/weapon_exp_system.lua
data/lib/compat/compat.lua:159: bad argument #1 to 'rawset' (table expected, got userdata)
stack traceback:
        [C]: in ?
        [C]: in function 'rawset'
        data/lib/compat/compat.lua:159: in function '__newindex'
        ...forgottenserver/build/data/scripts/weapon_exp_system.lua:34: in main chunk
> weapon_exp_system.lua [error]

As I understand it you want to create a revscript in weapon_exp_system.lua, and the error you are getting is caused by an incorrect action registration.

If you want to upgrade an item using another item you need to do it with an action script and register it like this for example:
LUA:
local action = Action()

function action.onUse(player, item, fromPosition, target, toPosition, isHotkey)
    -- your code here
end

action:id({item ID that should trigger the onUse script})

action:register()

You can use the script from this link as a reference:
 
I'd like to create a script that will allow all weapons to gain experience on impact
I think he does not want 'onUse' script, he wants script that detects, when weapon hits monster/player.

There is no Lua event for this in TFS 1.4.
You must add it in this line:
Code generated with AI in ~5 minutes - 2 prompts, first described what to do, second told AI to WRITE CODE, not a comment 'TODO later' after 2 minutes of coding :D (Cursor - The AI Code Editor (https://cursor.com/) - 20$/month):
C++:
        if (result) {
            lastAttack = OTSYS_TIME();

            if (tool) {
                // Get the custom attribute 'exp', add +1 or set to 1 if it does not exist
                const ItemAttributes::CustomAttribute* expAttr = tool->getCustomAttribute("exp");
                int64_t currentExp = 0;
             
                if (expAttr) {
                    // Create a non-const copy to call get<int64_t>()
                    ItemAttributes::CustomAttribute tempAttr = *expAttr;
                    currentExp = tempAttr.get<int64_t>();
                }
             
                // Create new value and set it
                ItemAttributes::CustomAttribute newExpAttr(currentExp + 1);
                std::string key = "exp";
                tool->setCustomAttribute(key, newExpAttr);
            }
        }
It compiled, IDK if it works. You must also add code to detect (below currentExp = tempAttr.get<int64_t>();), when it hits 100, reset it to 0 and add +1 attack/defense to weapon (tool).
Post automatically merged:

@Ihavegunz
I told AI to add event onUseWeapon(item). Prompt in polish in Cursor IDE:
mam rozne eventy w Lua dla gracza np. Player:onGainExperience(source, exp, rawExp)
chcial bym dodac podobny o nazwie Player:onUseWeapon(item) kiedy gracz atakuje przy uzyciu broni, uzycie broni jest obslugiwane przez Player::doAttacking(uint32_t) w player.cpp
Translated to english - just for OTLand - using AI:
I have various events in Lua for the player e.g. Player:onGainExperience(source, exp, rawExp)
I would like to add a similar one called Player:onUseWeapon(item) when the player attacks using a weapon, weapon usage is handled by Player::doAttacking(uint32_t) in player.cpp

and it did! All file changes:

Now you have to replace data/scripts/eventcallbacks/onUseWeapon_example.lua with your weapon upgrade logic.

1754507766461.webp


EDIT:
Of course AI hallucinated a bit, but luckily not in C++.
In onUseWeapon_example.lua file:
It says that 2456 is an axe and 2383 is a bow. 2383 is a spike sword (not bow) and 2456 is a bow (not axe).

It also return true -- Return true to continue normal operation from Lua function, that does not expect any return value in C++:
 
Last edited:
I added onUseWeapon_example.lua and did everything step by step, but I get an error in the engine:
">> Loading lua libs
[Warning - Events::load] Unknown player method: onUseWeapon"
I noticed that my engine doesn't have the
/doc/features/ directory
I added it myself and put the player-onUseWeapon-event.md file there.
does my engine not support this?
 
Back
Top