• 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!
  • New resources must be posted under Resources tab. A discussion thread will be created automatically, you can't open threads manually anymore.

[System] Weapon Hit Rank!

Critico

Sexy
Joined
Mar 25, 2010
Messages
370
Reaction score
179
credits:

Jhon & Vodkart

Version : 8.6+


How it works:

It is a simple system, shows the rank of high hit from the server, ex: axe, sword, club, wand, SD, etc ...

Shows the name of the player - [Hit] - Name of weapon


images:

rank axe
L7EiY.png

rank club
Jd53C.png

rank sd
1pmY2.png


ddzqv.png


note: as the normal rank system, level, etc, to appear in rank the player has to relog or give serversalve.


Mods:

RankHit.xml
LUA:
<?xml version="1.0" encoding="UTF-8"?>  
<mod name="RankHit" version="1.0" author="Vodkart e Jhon" contact="xtibia.com" enabled="yes">  
<config name="rank_func"><![CDATA[
WEAPON_WAND = {2190, 2191, 2188, 8921, 2189, 2187, 8920, 8922, 2184, 7414, 2453} -- id wands
WEAPON_ROD = {2182, 2186, 2185, 8911, 2181, 2183, 8912, 8910, 7958, 12609} -- id rods
storage_mostdamage = {
CLUB = {155201,156201},
SWORD = {155202,156202},
AXE = {155203,156203},
WAND = {155204,156204},
ROD = {155205,156205},
DISTANCE = {155206,156206},
MAGIC_SD = {155207,156207,157207}
}
function setSdHit(cid, damage)
if damage > getPlayerStorageValue(cid, storage_mostdamage.MAGIC_SD[1]) then
setPlayerStorageValue(cid, storage_mostdamage.MAGIC_SD[1], damage)
setPlayerStorageValue(cid, storage_mostdamage.MAGIC_SD[2], 2268)
end
return true
end
function setPlayerMostHitMage(cid, damage, weapon)
if isInArray(WEAPON_WAND, getPlayerSlotItem(cid, CONST_SLOT_LEFT).itemid) == TRUE or isInArray(WEAPON_WAND, getPlayerSlotItem(cid, CONST_SLOT_RIGHT).itemid) == TRUE then
if damage > getPlayerStorageValue(cid, storage_mostdamage.WAND[1]) then setPlayerStorageValue(cid, storage_mostdamage.WAND[1], damage) setPlayerStorageValue(cid, storage_mostdamage.WAND[2], weapon) end
elseif isInArray(WEAPON_ROD, getPlayerSlotItem(cid, CONST_SLOT_LEFT).itemid) == TRUE or isInArray(WEAPON_ROD, getPlayerSlotItem(cid, CONST_SLOT_RIGHT).itemid) == TRUE then
if damage > getPlayerStorageValue(cid, storage_mostdamage.ROD[1]) then setPlayerStorageValue(cid, storage_mostdamage.ROD[1], damage) setPlayerStorageValue(cid, storage_mostdamage.ROD[2], weapon) end
end
return true
end
function getRankHit(cid, value, wvalue, max, RankName) -- by vodka
local str,arm ="",""
str = "--[".. (RankName == nil and "RANK STORAGE" or ""..RankName.."") .."]--\n\n"
local query = db.getResult("SELECT `player_id`, `value` FROM `player_storage` WHERE `key` = "..value.." ORDER BY cast(value as INTEGER) DESC;")
if (query:getID() ~= -1) then k = 1 repeat if k > max then break end
local getweapon = db.getResult("SELECT `value` FROM `player_storage` WHERE `player_id` = ".. query:getDataString("player_id") .." AND `key` = "..wvalue)
if (getweapon:getID() ~= -1) then
arm = getItemNameById(getweapon:getDataString("value"))
end
str = str .. "\n " .. k .. ". "..getPlayerNameByGUID(query:getDataString("player_id")).." - [" .. query:getDataInt("value") .. "] - "..arm..""
k = k + 1 until not query:next() end return doPlayerPopupFYI(cid, str)
end
function haveWeapon(cid) -- by vodka
local armas = {1,2,3,5,6}
if getPlayerSlotItem(cid, CONST_SLOT_RIGHT).itemid > 0 and isInArray(armas, getItemWeaponType(getPlayerSlotItem(cid, CONST_SLOT_RIGHT).uid)) or getPlayerSlotItem(cid, CONST_SLOT_LEFT).itemid > 0 and isInArray(armas, getItemWeaponType(getPlayerSlotItem(cid, CONST_SLOT_LEFT).uid)) then
return true
end
return false
end
function getWeaponType(cid) -- by vodka
return getPlayerSlotItem(cid, CONST_SLOT_LEFT).itemid == 0 and getItemWeaponType(getPlayerSlotItem(cid, CONST_SLOT_RIGHT).uid) or getItemWeaponType(getPlayerSlotItem(cid, CONST_SLOT_LEFT).uid)
end
]]></config>
<talkaction words="/rankhit;!rankhit" event="buffer"><![CDATA[
domodlib('rank_func')
local rank_hit = {
["axe"] = {storage_mostdamage.AXE[1],storage_mostdamage.AXE[2]},
["sword"] = {storage_mostdamage.SWORD[1],storage_mostdamage.SWORD[2]},
["club"] = {storage_mostdamage.CLUB[1],storage_mostdamage.CLUB[2]},
["wand"] = {storage_mostdamage.WAND[1],storage_mostdamage.WAND[2]},
["rod"] = {storage_mostdamage.ROD[1],storage_mostdamage.ROD[2]},
["distance"] = {storage_mostdamage.DISTANCE[1],storage_mostdamage.DISTANCE[2]},
["sd"] = {storage_mostdamage.MAGIC_SD[1],storage_mostdamage.MAGIC_SD[2]}
}
local param = string.lower(param)
if (param == "") then
local str = ""
str = str .. ""..getCreatureName(cid).." Hit\'s\:\n\n"
for hit, item in pairs(rank_hit) do
str = str..string.upper(hit)..": ".. (getPlayerStorageValue(cid, item[1]) ~= -1 and "["..getPlayerStorageValue(cid, item[1]).."] - "..getItemNameById(getPlayerStorageValue(cid, item[2])).."" or "None") .."\n"
end
str = str .. ""
doPlayerPopupFYI(cid,str)
return true
end
if not rank_hit[param] then
return doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE,"digite o nome correto.")
end
return getRankHit(cid, rank_hit[param][1],rank_hit[param][2], 10, "Rank "..param)
]]></talkaction>
<event type="login" name="Rank Hit" event="script"><![CDATA[  
domodlib('rank_func')
function onLogin(cid)
registerCreatureEvent(cid, "RankhitCombat")
return true  
end  
]]></event>
<event type="combat" name="RankhitCombat" event="script"><![CDATA[
domodlib('rank_func')
                                 registerCreatureEvent(target, "RankhitStats")
return true
]]></event>
<event type="statschange" name="RankhitStats" event="script"><![CDATA[
domodlib('rank_func')
if type == STATSCHANGE_HEALTHLOSS and isPlayer(attacker) and haveWeapon(attacker) then
t = {
[1] = {storage_mostdamage.SWORD[1],storage_mostdamage.SWORD[2]},
[2] = {storage_mostdamage.CLUB[1],storage_mostdamage.CLUB[2]},
[3] = {storage_mostdamage.AXE[1],storage_mostdamage.AXE[2]},
[5] = {storage_mostdamage.DISTANCE[1],storage_mostdamage.DISTANCE[2]}
}
MyWeapon = getPlayerSlotItem(attacker, CONST_SLOT_LEFT).itemid == 0 and getPlayerSlotItem(attacker, CONST_SLOT_RIGHT).itemid or getPlayerSlotItem(attacker, CONST_SLOT_LEFT).itemid
if (getPlayerStorageValue(attacker, storage_mostdamage.MAGIC_SD[3]) >= os.time()) then
setSdHit(attacker,value)
elseif t[getWeaponType(attacker)] then
if value > getPlayerStorageValue(attacker, t[getWeaponType(attacker)][1]) then
setPlayerStorageValue(attacker, t[getWeaponType(attacker)][1], value)
setPlayerStorageValue(attacker, t[getWeaponType(attacker)][2], MyWeapon)
end
else
setPlayerMostHitMage(attacker, value, MyWeapon)
end  
end
return true
]]></event>
</mod>

In data > spells > scripts > attack > sudden death.lua add:
Code:
setPlayerStorageValue(cid, 157207, os.time()+3)

thereby:
LUA:
function onCastSpell(cid, var)
setPlayerStorageValue(cid, 157207, os.time()+3)
return doCombat(cid, combat, var)
end


!rankhit -- show ranks weapons cid
!rankhit axe -- show rank axe server
!rankhit sword -- show rank sword server
!rankhit sd -- show rank sword server

etc...
 
Last edited:
i got this:

[26/06/2012 22:54:34] mysql_real_query(): SELECT `player_id`, `value` FROM `player_storage` WHERE `key` = 155207 ORDER BY cast(value as INTEGER) DESC; - MYSQL ERROR: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INTEGER) DESC' at line 1 (1064)
 
It's because your server is MySQL, but we will solve this ...


Change this line:

Code:
local query = db.getResult("SELECT `player_id`, `value` FROM `player_storage` WHERE `key` = "..value.." ORDER BY cast(value as INTEGER) DESC;")

for this:

Code:
local query = db.getResult("SELECT `player_id`, `value` FROM `player_storage` WHERE `key` = "..value.." ORDER BY cast(value as SIGNED) DESC;")
 
Awsome script, but im getting tons of error's when trying to add the script.. TFS 0.3.7 crying damson

Here are the errors i get


i was thinking of trying to take it from a mod and turn it into lua scripts... cauz i have no clue wtf to do.
 
@Critico
I've got weird problem, First one this mod not saving each hit cuz my last save was 2340 and when i attacked with 3600 it doesn't save it
second one when i tried to re-log i found my attack like 5200 :S and i didn't attack with this :(
And when i attacked with another character with sword too, i couldn't find my rank only first rank showed
xcmckx.jpg

TFS 0.3.6
 
Hello. I need this in tfs 1.5. Have Anyone for 1.5?
 
Hello. I need this in tfs 1.5. Have Anyone for 1.5?
Tested with TFS 1.5 downgraded by Nekiro for 8.6
I think it has string building bug and doesn't account for SD damage and split between rods and wands, but otherwise it works.. feel free to update it since I dont have much time right now..

LUA:
local RankHit = {
    top = 3,

    storages = {
        sword =    { damage = 51001, weapon = 51002, label = "Sword" },
        club =     { damage = 51003, weapon = 51004, label = "Club" },
        axe =      { damage = 51005, weapon = 51006, label = "Axe" },
        distance = { damage = 51007, weapon = 51008, label = "Distance" },
        wand =     { damage = 51009, weapon = 51010, label = "Wand" }
    },

    order = { "sword", "club", "axe", "distance", "wand" },

    weaponTypeToCategory = {
        [WEAPON_SWORD] = "sword",
        [WEAPON_CLUB] = "club",
        [WEAPON_AXE] = "axe",
        [WEAPON_DISTANCE] = "distance",
        [WEAPON_WAND] = "wand"
    }
}

local function trim(str)
    return str:match("^%s*(.-)%s*$")
end

local function getItemNameSafe(itemId)
    if not itemId or itemId <= 0 then
        return "Unknown"
    end

    local itemType = ItemType(itemId)
    if not itemType then
        return "Unknown"
    end

    local name = itemType:getName()
    return (name and name ~= "") and name or "Unknown"
end

local function getTrackedWeapon(player)
    local left = player:getSlotItem(CONST_SLOT_LEFT)
    if left then
        local leftWeaponType = ItemType(left:getId()):getWeaponType()
        if RankHit.weaponTypeToCategory[leftWeaponType] then
            return left, leftWeaponType
        end
    end

    local right = player:getSlotItem(CONST_SLOT_RIGHT)
    if right then
        local rightWeaponType = ItemType(right:getId()):getWeaponType()
        if RankHit.weaponTypeToCategory[rightWeaponType] then
            return right, rightWeaponType
        end
    end

    return nil, nil
end

local function detectCategory(player, origin)
    local weapon, weaponType = getTrackedWeapon(player)
    if not weapon or not weaponType then
        return nil, nil
    end

    if origin == ORIGIN_MELEE then
        if weaponType == WEAPON_SWORD or weaponType == WEAPON_CLUB or weaponType == WEAPON_AXE then
            return RankHit.weaponTypeToCategory[weaponType], weapon:getId()
        end
        return nil, nil
    end

    if origin == ORIGIN_RANGED then
        if weaponType == WEAPON_DISTANCE then
            return "distance", weapon:getId()
        end
        return nil, nil
    end

    if origin == ORIGIN_WAND then
        if weaponType == WEAPON_WAND then
            return "wand", weapon:getId()
        end
        return nil, nil
    end

    return nil, nil
end

local function saveBestHit(player, category, damage, weaponId)
    local cfg = RankHit.storages[category]
    if not cfg or damage <= 0 then
        return
    end

    local current = player:getStorageValue(cfg.damage)
    if current < damage then
        player:setStorageValue(cfg.damage, damage)
        player:setStorageValue(cfg.weapon, weaponId)
    end
end

local function buildPersonalStats(player)
    local text = {
        "--[Your Best Hits]--",
        ""
    }

    for _, category in ipairs(RankHit.order) do
        local cfg = RankHit.storages[category]
        local damage = player:getStorageValue(cfg.damage)
        local weaponId = player:getStorageValue(cfg.weapon)

        if damage and damage > 0 then
            text[#text + 1] = string.format("%s: [%d] - %s", cfg.label, damage, getItemNameSafe(weaponId))
        else
            text[#text + 1] = string.format("%s: None", cfg.label)
        end
    end

    return table.concat(text, "\n")
end

local function buildTopList(category)
    local cfg = RankHit.storages[category]
    if not cfg then
        return "Invalid category."
    end

    local query = string.format([[
        SELECT
            p.name AS player_name,
            CAST(ps.value AS SIGNED) AS hit_damage,
            COALESCE(CAST(psw.value AS SIGNED), 0) AS weapon_id
        FROM player_storage ps
        INNER JOIN players p
            ON p.id = ps.player_id
        LEFT JOIN player_storage psw
            ON psw.player_id = ps.player_id
           AND psw.`key` = %d
        WHERE ps.`key` = %d
          AND CAST(ps.value AS SIGNED) > 0
        ORDER BY CAST(ps.value AS SIGNED) DESC, p.name ASC
        LIMIT %d
    ]], cfg.weapon, cfg.damage, RankHit.top)

    local resultId = db.storeQuery(query)
    if not resultId then
        return string.format("--[Top %d %s Hits]--\n\nNo entries yet.", RankHit.top, cfg.label)
    end

    local text = {
        string.format("--[Top %d %s Hits]--", RankHit.top, cfg.label),
        ""
    }

    local place = 1
    repeat
        local name = result.getString(resultId, "player_name")
        local damage = result.getNumber(resultId, "hit_damage")
        local weaponId = result.getNumber(resultId, "weapon_id")

        text[#text + 1] = string.format("%d. %s - [%d] - %s", place, name, damage, getItemNameSafe(weaponId))
        place = place + 1
    until not result.next(resultId)

    result.free(resultId)
    return table.concat(text, "\n")
end

local trackEvent = CreatureEvent("RankHitTrack")

function trackEvent.onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
    if not attacker or not attacker:isPlayer() then
        return primaryDamage, primaryType, secondaryDamage, secondaryType
    end

    local totalDamage = 0
    if primaryDamage > 0 then
        totalDamage = totalDamage + primaryDamage
    end
    if secondaryDamage > 0 then
        totalDamage = totalDamage + secondaryDamage
    end

    if totalDamage <= 0 then
        return primaryDamage, primaryType, secondaryDamage, secondaryType
    end

    local category, weaponId = detectCategory(attacker, origin)
    if category then
        saveBestHit(attacker, category, totalDamage, weaponId)
    end

    return primaryDamage, primaryType, secondaryDamage, secondaryType
end

trackEvent:register()

local loginEvent = CreatureEvent("RankHitLogin")

function loginEvent.onLogin(player)
    player:registerEvent("RankHitTrack")
    return true
end

loginEvent:register()

local startupEvent = GlobalEvent("RankHitStartup")

function startupEvent.onStartup()
    for _, monsterType in pairs(Game.getMonsterTypes()) do
        monsterType:registerEvent("RankHitTrack")
    end
    return true
end

startupEvent:register()

local function showRank(player, param)
    param = trim(param:lower())

    if param == "" then
        player:popupFYI(buildPersonalStats(player))
        return false
    end

    if not RankHit.storages[param] then
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Usage: !rankhit sword, !rankhit club, !rankhit axe, !rankhit distance, !rankhit wand")
        return false
    end

    player:popupFYI(buildTopList(param))
    return false
end

local rankTalk = TalkAction("!rankhit")

function rankTalk.onSay(player, words, param)
    return showRank(player, param)
end

rankTalk:separator(" ")
rankTalk:register()
 
@up
I'll try to fix it tomorrow. It shows window and but it doesn't update.
rank.webp
thanks for reply!
 
Back
Top