• 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.
[TFS 1.6] Mini Hunter System

[TFS 1.6] Mini Hunter System 1.0

No permission to download

Sarah Wesker

ƐƖєgαηт Sуηтαx ❤
Staff member
TFS Developer
Support Team
Joined
Mar 16, 2017
Messages
1,461
Solutions
163
Reaction score
2,184
Location
London
GitHub
MillhioreBT
YouTube
millhiorebt
Sarah Wesker submitted a new resource:

[TFS 1.6] Mini Hunter System - mini hunter system, where you can condemn your enemies for a price

This is a small hunter system with which players can use the !hunter command to give a price to the head of your enemies, the other players can go to their hunt and everyone who has participated in the death of the target will win a part calculated according to the damage done to the reward.

Any details regarding the system please notify me to fix possible errors.
Remember that this is for the latest version of TFS master, do not request downgrades for previous versions as they will not be...

Read more about this resource...
 
Hi Sarah, do you still take paid services? tried contacting you everywhere :/
 
Thank you Sarah
This version is compatible with TFS 1.5 Enjoy!
LUA:
--[[
    MARK: HunterSystem
    Author: @MillhioreBT {Sarah Wesker}
    Version: 1.0
    Conversion To: - TFS 1.5
    by : Abdala Ragab
]] --
---
---@enum HunterStatus
HunterStatus = {
    ACTIVE = 0,
    FINISHED = 1
}

HunterSystem = {
    command = "!hunter",
    commission = 10,
    refund = 50,
    chunkSize = 10,
    maxTimeHunted = 60 * 60 * 24 * 7,
    loaded = false,
    forceSave = false,
    cache = {}
}

local fmt = string.format

local function debugPrint(...)
    print("[HunterSystem]", ...)
end

local function createTable()
    local query = [[
        CREATE TABLE IF NOT EXISTS `hunter_system` (
            `id` INT NOT NULL AUTO_INCREMENT,
            `owner` INT NOT NULL,
            `target` INT NOT NULL,
            `status` INT NOT NULL,
            `reward_gold` INT NOT NULL,
            `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
            `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
            `finished_at` TIMESTAMP NULL,
            PRIMARY KEY (`id`),
            INDEX (`owner`),
            INDEX (`target`)
        );
    ]]

    if not db.query(query) then
        debugPrint("Error while creating table: hunter_system")
        return false
    end
    return true
end

local globalEvent = GlobalEvent("HunterSystemLoad")

function globalEvent.onStartup()
    if not createTable() then
        print("[HunterSystem] Error while creating table")
        return false
    end

    if not HunterSystem:loadAllHunters() then
        print("[HunterSystem] Warning while loading hunters")
    end
    return true
end

globalEvent:register()

function HunterSystem:loadHunters(owner)
    if self.cache[owner] then return true end

    local query = fmt([[
        SELECT `owner`, `target`, `status`, `reward_gold`,
        UNIX_TIMESTAMP(`created_at`) AS `created_at`,
        UNIX_TIMESTAMP(`updated_at`) AS `updated_at`,
        UNIX_TIMESTAMP(`finished_at`) AS `finished_at`
        FROM `hunter_system` WHERE `owner` = %d;
    ]], owner)

    local store = db.storeQuery(query)
    if not store then
        self.cache[owner] = {}
        return true
    end

    local hunters = {}
    repeat
        local hunter = setmetatable({
            owner = result.getNumber(store, "owner"),
            target = result.getNumber(store, "target"),
            status = result.getNumber(store, "status"),
            rewardGold = result.getNumber(store, "reward_gold"),
            createdAt = result.getNumber(store, "created_at"),
            updatedAt = result.getNumber(store, "updated_at"),
            finishedAt = result.getNumber(store, "finished_at")
        }, Hunter)
        hunters[#hunters + 1] = hunter
    until not result.next(store)
    result.free(store)

    self.cache[owner] = hunters
    return true
end

function HunterSystem:saveHunters(owner)
    local hunters = self.cache[owner]
    if not hunters then return true end

    local deleteQuery = fmt("DELETE FROM `hunter_system` WHERE `owner` = %d;", owner)
    if not db.query(deleteQuery) then
        debugPrint("Error while deleting hunters")
        return false
    end

    for _, hunter in ipairs(hunters) do
        if not hunter:isExpired() then
            local insertQuery = fmt([[
                INSERT INTO `hunter_system`
                (`owner`, `target`, `status`, `reward_gold`, `created_at`, `updated_at`, `finished_at`)
                VALUES (%d, %d, %d, %d, FROM_UNIXTIME(%d), FROM_UNIXTIME(%d), FROM_UNIXTIME(%d));
            ]], hunter.owner, hunter.target, hunter.status, hunter.rewardGold,
               hunter.createdAt, hunter.updatedAt, hunter.finishedAt)
            
            if not db.query(insertQuery) then
                debugPrint("Error while inserting hunter")
                return false
            end
        end
    end

    return true
end

function HunterSystem:addHunter(owner, target, rewardGold)
    local hunters = self.cache[owner]
    if not hunters then
        hunters = {}
        self.cache[owner] = hunters
    end

    for _, hunter in ipairs(hunters) do
        if hunter.target == target then
            return false
        end
    end

    local timeNow = os.time()
    local hunter = setmetatable({
        owner = owner,
        target = target,
        status = HunterStatus.ACTIVE,
        rewardGold = rewardGold,
        createdAt = timeNow,
        updatedAt = timeNow,
        finishedAt = timeNow + self.maxTimeHunted
    }, Hunter)

    hunters[#hunters + 1] = hunter
    return true
end

function HunterSystem:removeHunter(hunter)
    local hunters = self.cache[hunter.owner]
    if not hunters then
        return false
    end

    for i, h in ipairs(hunters) do
        if h == hunter then
            table.remove(hunters, i)
            return true
        end
    end
    return false
end

function HunterSystem:isExists(owner, target)
    local hunters = self.cache[owner]
    if hunters then
        for _, hunter in ipairs(hunters) do
            if hunter.status == HunterStatus.ACTIVE and not hunter:isExpired() and hunter.target == target then
                return true
            end
        end
    end
    return false
end

function HunterSystem:loadAllHunters()
    if self.loaded then return true end

    local owners = db.storeQuery("SELECT DISTINCT `owner` FROM `hunter_system`")
    if not owners then
        self.loaded = true
        return true
    end

    repeat
        self:loadHunters(result.getNumber(owners, "owner"))
    until not result.next(owners)
    result.free(owners)
    self.loaded = true
    return true
end

function HunterSystem:getOwnerByName(name)
    local player = Player(name)
    if player then return player:getGuid() end

    local store = db.storeQuery(fmt("SELECT `id` FROM `players` WHERE `name` = %s;",
        db.escapeString(name)))
    if not store then return nil end

    local id = result.getNumber(store, "id")
    result.free(store)
    return id
end

function HunterSystem:getNameByOwner(owner)
    local player = Player(owner)
    if player then return player:getName() end

    local store = db.storeQuery(fmt("SELECT `name` FROM `players` WHERE `id` = %d;", owner))
    if not store then return nil end

    local name = result.getString(store, "name")
    result.free(store)
    return name
end

function HunterSystem:getHuntersByTarget(target)
    local hunters = {}
    for _, hs in pairs(self.cache) do
        for _, hunter in ipairs(hs) do
            if hunter.status == HunterStatus.ACTIVE and not hunter:isExpired() and hunter.target == target then
                hunters[#hunters + 1] = hunter
            end
        end
    end
    return hunters
end

function HunterSystem:getValidHunters(owner)
    local hunters = self.cache[owner]
    if not hunters then return {} end

    local validHunters = {}
    for _, hunter in ipairs(hunters) do
        if hunter.status == HunterStatus.ACTIVE and not hunter:isExpired() then
            validHunters[#validHunters + 1] = hunter
        end
    end
    return validHunters
end

function HunterSystem:getAllValidHunters()
    local hunters = {}
    for _, hs in pairs(self.cache) do
        for _, hunter in ipairs(hs) do
            if hunter.status == HunterStatus.ACTIVE and not hunter:isExpired() then
                hunters[#hunters + 1] = hunter
            end
        end
    end
    return hunters
end

Hunter = {}
Hunter.__index = Hunter

function Hunter:isExpired()
    return os.time() >= self.finishedAt
end

local loginEvent = CreatureEvent("HunterSystemLogin")

function loginEvent.onLogin(player)
    player:registerEvent("HunterSystemDeath")
    player:registerEvent("HunterSystemLogout")
    HunterSystem:loadHunters(player:getGuid())
    return true
end

loginEvent:register()

local logoutEvent = CreatureEvent("HunterSystemLogout")

function logoutEvent.onLogout(player)
    HunterSystem:saveHunters(player:getGuid())
    return true
end

logoutEvent:register()

local function shuffle(t)
    local n = #t
    while n > 2 do
        local k = math.random(n)
        t[n], t[k] = t[k], t[n]
        n = n - 1
    end
    return t
end

local function checkCount(count)
    if not count then return -1 end
    return math.min(2147483647, count)
end

local function getCount(str)
    local b, e = str:find("%d+")
    if not b or not e then
        return -1
    end
    return checkCount(tonumber(str:sub(b, e)))
end

local function displayHuntersList(player, hunters, title)
    if #hunters == 0 then
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "No hunters found.")
        return
    end

    local message = title .. "\n"
    
    for i, hunter in ipairs(hunters) do
        local targetName = HunterSystem:getNameByOwner(hunter.target) or "Unknown"
        local ownerName = HunterSystem:getNameByOwner(hunter.owner) or "Unknown"
        message = message .. string.format("%d. %s -> %s | Reward: %d gold\n",
            i, ownerName, targetName, hunter.rewardGold)
    end
    
    player:popupFYI(message)
end

local talkAction = TalkAction(HunterSystem.command)

function talkAction.onSay(player, words, param)
    local split = {}
    for part in string.gmatch(param, "[^,]+") do
        split[#split + 1] = part:trim()
    end
    local action = split[1] and split[1]:lower()

    if action == "add" then
        if not split[2] or not split[3] then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Usage: !hunter add <player>,<amount>")
            return false
        end
        
        local target = HunterSystem:getOwnerByName(split[2])
        if not target then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Player not found.")
            return false
        end

        if target == player:getGuid() then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "You can't hunt yourself.")
            return false
        end

        if HunterSystem:isExists(player:getGuid(), target) then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "You are already hunting this player.")
            return false
        end

        local rewardGold = getCount(split[3])
        if rewardGold <= 0 then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Invalid reward gold.")
            return false
        end

        rewardGold = math.floor(rewardGold * (1 + HunterSystem.commission / 100))
        if checkCount(rewardGold) <= 0 then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Invalid reward gold.")
            return false
        end

        if player:getMoney() < rewardGold then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "You don't have enough gold coins.")
            return false
        end

        if not player:removeMoney(rewardGold) then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Error while removing gold coins.")
            return false
        end

        if not HunterSystem:addHunter(player:getGuid(), target, rewardGold) then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Error while adding hunter.")
            return false
        end

        local targetName = HunterSystem:getNameByOwner(target) or "Unknown"
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Hunter added successfully!")
        Game.broadcastMessage(string.format("%s started hunting %s for %d gold coins.",
            player:getName(), targetName, rewardGold), MESSAGE_EVENT_ADVANCE)
        
        HunterSystem:saveHunters(player:getGuid())
        return false
    end

    if action == "remove" then
        if not split[2] then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Usage: !hunter remove <player>")
            return false
        end
        
        local target = HunterSystem:getOwnerByName(split[2])
        if not target then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Player not found.")
            return false
        end

        local hunters = HunterSystem:getValidHunters(player:getGuid())
        if not hunters or #hunters == 0 then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "You are not hunting this player.")
            return false
        end

        local found = nil
        for _, hunter in ipairs(hunters) do
            if hunter.target == target then
                if not HunterSystem:removeHunter(hunter) then
                    player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Error while removing hunter.")
                    return false
                end
                found = hunter
                break
            end
        end

        if found then
            local refundGold = math.floor(found.rewardGold * HunterSystem.refund / 100)
            if checkCount(refundGold) <= 0 then
                player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Error while calculating refund.")
                return false
            end

            player:addMoney(refundGold)

            local targetName = HunterSystem:getNameByOwner(target) or "Unknown"
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE,
                string.format("Hunter removed. Refund: %d gold coins.", refundGold))
            Game.broadcastMessage(string.format("%s stopped hunting %s.", player:getName(), targetName),
                MESSAGE_EVENT_ADVANCE)

            HunterSystem:saveHunters(player:getGuid())
            return false
        end

        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "You are not hunting this player.")
        return false
    end

    if action == "list" then
        if not split[2] then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Usage: !hunter list <player>")
            return false
        end
        
        local target = HunterSystem:getOwnerByName(split[2])
        if not target then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Player not found.")
            return false
        end

        local hunters = HunterSystem:getValidHunters(target)
        if not hunters or #hunters == 0 then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "This player is not being hunted.")
            return false
        end

        local targetName = HunterSystem:getNameByOwner(target) or "Unknown"
        displayHuntersList(player, hunters, string.format("Players hunting %s:", targetName))
        return false
    end

    if action == "all" then
        if not HunterSystem:loadAllHunters() then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Error while loading hunters.")
            return false
        end

        local hunters = HunterSystem:getAllValidHunters()
        if #hunters == 0 then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "No active hunters found.")
            return false
        end

        hunters = shuffle(hunters)
        displayHuntersList(player, hunters, "All Active Hunters:")
        return false
    end

    if action == "my" then
        local hunters = HunterSystem:getValidHunters(player:getGuid())
        if #hunters == 0 then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "You are not hunting anyone.")
            return false
        end

        displayHuntersList(player, hunters, "Your active hunts:")
        return false
    end

    local helpText = [[
Hunter System Commands:

!hunter add <player>,<amount> - Place a bounty on a player
!hunter remove <player> - Remove bounty from a player
!hunter list <player> - Show who is hunting a player
!hunter all - Show all active bounties
!hunter my - Show your active hunts

Example: !hunter add PlayerName, 1000
]]

    player:popupFYI(helpText)
    return false
end

talkAction:separator(" ")
talkAction:register()

local deathEvent = CreatureEvent("HunterSystemDeath")

function deathEvent.onDeath(creature)
    local player = creature:getPlayer()
    if not player then return true end

    local timeNow = os.time()
    local killers = {}
    local totalDamage = 0
    
    local damageMap = player:getDamageMap()
    for uid, cb in pairs(damageMap) do
        local killer = Player(uid)
        if killer and killer ~= player and (timeNow - cb.ticks) <= 60000 then
            killers[#killers + 1] = {killer = killer, damage = cb.total}
            totalDamage = totalDamage + cb.total
        end
    end

    if #killers == 0 then return true end

    local hunters = HunterSystem:getHuntersByTarget(player:getGuid())
    if #hunters == 0 then return true end

    local maxHealth = player:getMaxHealth()
    if maxHealth <= 0 then maxHealth = 1 end

    for _, hunter in ipairs(hunters) do
        if hunter.status == HunterStatus.ACTIVE and not hunter:isExpired() then
            local rewardGold = math.max(1, math.floor(hunter.rewardGold * totalDamage / maxHealth))
            
            for _, cb in ipairs(killers) do
                cb.killer:addMoney(rewardGold)
                cb.killer:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE,
                    string.format("You received %d gold coins for hunting %s.", rewardGold, player:getName()))
            end

            hunter.status = HunterStatus.FINISHED
            hunter.updatedAt = timeNow
            hunter.finishedAt = timeNow

            HunterSystem:saveHunters(hunter.owner)
        end
    end

    return true
end

deathEvent:register()
 
Thank you Sarah
This version is compatible with TFS 1.5 Enjoy!
LUA:
--[[
    MARK: HunterSystem
    Author: @MillhioreBT {Sarah Wesker}
    Version: 1.0
    Conversion To: - TFS 1.5
    by : Abdala Ragab
]] --
---
---@enum HunterStatus
HunterStatus = {
    ACTIVE = 0,
    FINISHED = 1
}

HunterSystem = {
    command = "!hunter",
    commission = 10,
    refund = 50,
    chunkSize = 10,
    maxTimeHunted = 60 * 60 * 24 * 7,
    loaded = false,
    forceSave = false,
    cache = {}
}

local fmt = string.format

local function debugPrint(...)
    print("[HunterSystem]", ...)
end

local function createTable()
    local query = [[
        CREATE TABLE IF NOT EXISTS `hunter_system` (
            `id` INT NOT NULL AUTO_INCREMENT,
            `owner` INT NOT NULL,
            `target` INT NOT NULL,
            `status` INT NOT NULL,
            `reward_gold` INT NOT NULL,
            `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
            `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
            `finished_at` TIMESTAMP NULL,
            PRIMARY KEY (`id`),
            INDEX (`owner`),
            INDEX (`target`)
        );
    ]]

    if not db.query(query) then
        debugPrint("Error while creating table: hunter_system")
        return false
    end
    return true
end

local globalEvent = GlobalEvent("HunterSystemLoad")

function globalEvent.onStartup()
    if not createTable() then
        print("[HunterSystem] Error while creating table")
        return false
    end

    if not HunterSystem:loadAllHunters() then
        print("[HunterSystem] Warning while loading hunters")
    end
    return true
end

globalEvent:register()

function HunterSystem:loadHunters(owner)
    if self.cache[owner] then return true end

    local query = fmt([[
        SELECT `owner`, `target`, `status`, `reward_gold`,
        UNIX_TIMESTAMP(`created_at`) AS `created_at`,
        UNIX_TIMESTAMP(`updated_at`) AS `updated_at`,
        UNIX_TIMESTAMP(`finished_at`) AS `finished_at`
        FROM `hunter_system` WHERE `owner` = %d;
    ]], owner)

    local store = db.storeQuery(query)
    if not store then
        self.cache[owner] = {}
        return true
    end

    local hunters = {}
    repeat
        local hunter = setmetatable({
            owner = result.getNumber(store, "owner"),
            target = result.getNumber(store, "target"),
            status = result.getNumber(store, "status"),
            rewardGold = result.getNumber(store, "reward_gold"),
            createdAt = result.getNumber(store, "created_at"),
            updatedAt = result.getNumber(store, "updated_at"),
            finishedAt = result.getNumber(store, "finished_at")
        }, Hunter)
        hunters[#hunters + 1] = hunter
    until not result.next(store)
    result.free(store)

    self.cache[owner] = hunters
    return true
end

function HunterSystem:saveHunters(owner)
    local hunters = self.cache[owner]
    if not hunters then return true end

    local deleteQuery = fmt("DELETE FROM `hunter_system` WHERE `owner` = %d;", owner)
    if not db.query(deleteQuery) then
        debugPrint("Error while deleting hunters")
        return false
    end

    for _, hunter in ipairs(hunters) do
        if not hunter:isExpired() then
            local insertQuery = fmt([[
                INSERT INTO `hunter_system`
                (`owner`, `target`, `status`, `reward_gold`, `created_at`, `updated_at`, `finished_at`)
                VALUES (%d, %d, %d, %d, FROM_UNIXTIME(%d), FROM_UNIXTIME(%d), FROM_UNIXTIME(%d));
            ]], hunter.owner, hunter.target, hunter.status, hunter.rewardGold,
               hunter.createdAt, hunter.updatedAt, hunter.finishedAt)
           
            if not db.query(insertQuery) then
                debugPrint("Error while inserting hunter")
                return false
            end
        end
    end

    return true
end

function HunterSystem:addHunter(owner, target, rewardGold)
    local hunters = self.cache[owner]
    if not hunters then
        hunters = {}
        self.cache[owner] = hunters
    end

    for _, hunter in ipairs(hunters) do
        if hunter.target == target then
            return false
        end
    end

    local timeNow = os.time()
    local hunter = setmetatable({
        owner = owner,
        target = target,
        status = HunterStatus.ACTIVE,
        rewardGold = rewardGold,
        createdAt = timeNow,
        updatedAt = timeNow,
        finishedAt = timeNow + self.maxTimeHunted
    }, Hunter)

    hunters[#hunters + 1] = hunter
    return true
end

function HunterSystem:removeHunter(hunter)
    local hunters = self.cache[hunter.owner]
    if not hunters then
        return false
    end

    for i, h in ipairs(hunters) do
        if h == hunter then
            table.remove(hunters, i)
            return true
        end
    end
    return false
end

function HunterSystem:isExists(owner, target)
    local hunters = self.cache[owner]
    if hunters then
        for _, hunter in ipairs(hunters) do
            if hunter.status == HunterStatus.ACTIVE and not hunter:isExpired() and hunter.target == target then
                return true
            end
        end
    end
    return false
end

function HunterSystem:loadAllHunters()
    if self.loaded then return true end

    local owners = db.storeQuery("SELECT DISTINCT `owner` FROM `hunter_system`")
    if not owners then
        self.loaded = true
        return true
    end

    repeat
        self:loadHunters(result.getNumber(owners, "owner"))
    until not result.next(owners)
    result.free(owners)
    self.loaded = true
    return true
end

function HunterSystem:getOwnerByName(name)
    local player = Player(name)
    if player then return player:getGuid() end

    local store = db.storeQuery(fmt("SELECT `id` FROM `players` WHERE `name` = %s;",
        db.escapeString(name)))
    if not store then return nil end

    local id = result.getNumber(store, "id")
    result.free(store)
    return id
end

function HunterSystem:getNameByOwner(owner)
    local player = Player(owner)
    if player then return player:getName() end

    local store = db.storeQuery(fmt("SELECT `name` FROM `players` WHERE `id` = %d;", owner))
    if not store then return nil end

    local name = result.getString(store, "name")
    result.free(store)
    return name
end

function HunterSystem:getHuntersByTarget(target)
    local hunters = {}
    for _, hs in pairs(self.cache) do
        for _, hunter in ipairs(hs) do
            if hunter.status == HunterStatus.ACTIVE and not hunter:isExpired() and hunter.target == target then
                hunters[#hunters + 1] = hunter
            end
        end
    end
    return hunters
end

function HunterSystem:getValidHunters(owner)
    local hunters = self.cache[owner]
    if not hunters then return {} end

    local validHunters = {}
    for _, hunter in ipairs(hunters) do
        if hunter.status == HunterStatus.ACTIVE and not hunter:isExpired() then
            validHunters[#validHunters + 1] = hunter
        end
    end
    return validHunters
end

function HunterSystem:getAllValidHunters()
    local hunters = {}
    for _, hs in pairs(self.cache) do
        for _, hunter in ipairs(hs) do
            if hunter.status == HunterStatus.ACTIVE and not hunter:isExpired() then
                hunters[#hunters + 1] = hunter
            end
        end
    end
    return hunters
end

Hunter = {}
Hunter.__index = Hunter

function Hunter:isExpired()
    return os.time() >= self.finishedAt
end

local loginEvent = CreatureEvent("HunterSystemLogin")

function loginEvent.onLogin(player)
    player:registerEvent("HunterSystemDeath")
    player:registerEvent("HunterSystemLogout")
    HunterSystem:loadHunters(player:getGuid())
    return true
end

loginEvent:register()

local logoutEvent = CreatureEvent("HunterSystemLogout")

function logoutEvent.onLogout(player)
    HunterSystem:saveHunters(player:getGuid())
    return true
end

logoutEvent:register()

local function shuffle(t)
    local n = #t
    while n > 2 do
        local k = math.random(n)
        t[n], t[k] = t[k], t[n]
        n = n - 1
    end
    return t
end

local function checkCount(count)
    if not count then return -1 end
    return math.min(2147483647, count)
end

local function getCount(str)
    local b, e = str:find("%d+")
    if not b or not e then
        return -1
    end
    return checkCount(tonumber(str:sub(b, e)))
end

local function displayHuntersList(player, hunters, title)
    if #hunters == 0 then
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "No hunters found.")
        return
    end

    local message = title .. "\n"
   
    for i, hunter in ipairs(hunters) do
        local targetName = HunterSystem:getNameByOwner(hunter.target) or "Unknown"
        local ownerName = HunterSystem:getNameByOwner(hunter.owner) or "Unknown"
        message = message .. string.format("%d. %s -> %s | Reward: %d gold\n",
            i, ownerName, targetName, hunter.rewardGold)
    end
   
    player:popupFYI(message)
end

local talkAction = TalkAction(HunterSystem.command)

function talkAction.onSay(player, words, param)
    local split = {}
    for part in string.gmatch(param, "[^,]+") do
        split[#split + 1] = part:trim()
    end
    local action = split[1] and split[1]:lower()

    if action == "add" then
        if not split[2] or not split[3] then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Usage: !hunter add <player>,<amount>")
            return false
        end
       
        local target = HunterSystem:getOwnerByName(split[2])
        if not target then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Player not found.")
            return false
        end

        if target == player:getGuid() then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "You can't hunt yourself.")
            return false
        end

        if HunterSystem:isExists(player:getGuid(), target) then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "You are already hunting this player.")
            return false
        end

        local rewardGold = getCount(split[3])
        if rewardGold <= 0 then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Invalid reward gold.")
            return false
        end

        rewardGold = math.floor(rewardGold * (1 + HunterSystem.commission / 100))
        if checkCount(rewardGold) <= 0 then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Invalid reward gold.")
            return false
        end

        if player:getMoney() < rewardGold then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "You don't have enough gold coins.")
            return false
        end

        if not player:removeMoney(rewardGold) then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Error while removing gold coins.")
            return false
        end

        if not HunterSystem:addHunter(player:getGuid(), target, rewardGold) then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Error while adding hunter.")
            return false
        end

        local targetName = HunterSystem:getNameByOwner(target) or "Unknown"
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Hunter added successfully!")
        Game.broadcastMessage(string.format("%s started hunting %s for %d gold coins.",
            player:getName(), targetName, rewardGold), MESSAGE_EVENT_ADVANCE)
       
        HunterSystem:saveHunters(player:getGuid())
        return false
    end

    if action == "remove" then
        if not split[2] then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Usage: !hunter remove <player>")
            return false
        end
       
        local target = HunterSystem:getOwnerByName(split[2])
        if not target then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Player not found.")
            return false
        end

        local hunters = HunterSystem:getValidHunters(player:getGuid())
        if not hunters or #hunters == 0 then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "You are not hunting this player.")
            return false
        end

        local found = nil
        for _, hunter in ipairs(hunters) do
            if hunter.target == target then
                if not HunterSystem:removeHunter(hunter) then
                    player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Error while removing hunter.")
                    return false
                end
                found = hunter
                break
            end
        end

        if found then
            local refundGold = math.floor(found.rewardGold * HunterSystem.refund / 100)
            if checkCount(refundGold) <= 0 then
                player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Error while calculating refund.")
                return false
            end

            player:addMoney(refundGold)

            local targetName = HunterSystem:getNameByOwner(target) or "Unknown"
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE,
                string.format("Hunter removed. Refund: %d gold coins.", refundGold))
            Game.broadcastMessage(string.format("%s stopped hunting %s.", player:getName(), targetName),
                MESSAGE_EVENT_ADVANCE)

            HunterSystem:saveHunters(player:getGuid())
            return false
        end

        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "You are not hunting this player.")
        return false
    end

    if action == "list" then
        if not split[2] then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Usage: !hunter list <player>")
            return false
        end
       
        local target = HunterSystem:getOwnerByName(split[2])
        if not target then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Player not found.")
            return false
        end

        local hunters = HunterSystem:getValidHunters(target)
        if not hunters or #hunters == 0 then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "This player is not being hunted.")
            return false
        end

        local targetName = HunterSystem:getNameByOwner(target) or "Unknown"
        displayHuntersList(player, hunters, string.format("Players hunting %s:", targetName))
        return false
    end

    if action == "all" then
        if not HunterSystem:loadAllHunters() then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Error while loading hunters.")
            return false
        end

        local hunters = HunterSystem:getAllValidHunters()
        if #hunters == 0 then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "No active hunters found.")
            return false
        end

        hunters = shuffle(hunters)
        displayHuntersList(player, hunters, "All Active Hunters:")
        return false
    end

    if action == "my" then
        local hunters = HunterSystem:getValidHunters(player:getGuid())
        if #hunters == 0 then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "You are not hunting anyone.")
            return false
        end

        displayHuntersList(player, hunters, "Your active hunts:")
        return false
    end

    local helpText = [[
Hunter System Commands:

!hunter add <player>,<amount> - Place a bounty on a player
!hunter remove <player> - Remove bounty from a player
!hunter list <player> - Show who is hunting a player
!hunter all - Show all active bounties
!hunter my - Show your active hunts

Example: !hunter add PlayerName, 1000
]]

    player:popupFYI(helpText)
    return false
end

talkAction:separator(" ")
talkAction:register()

local deathEvent = CreatureEvent("HunterSystemDeath")

function deathEvent.onDeath(creature)
    local player = creature:getPlayer()
    if not player then return true end

    local timeNow = os.time()
    local killers = {}
    local totalDamage = 0
   
    local damageMap = player:getDamageMap()
    for uid, cb in pairs(damageMap) do
        local killer = Player(uid)
        if killer and killer ~= player and (timeNow - cb.ticks) <= 60000 then
            killers[#killers + 1] = {killer = killer, damage = cb.total}
            totalDamage = totalDamage + cb.total
        end
    end

    if #killers == 0 then return true end

    local hunters = HunterSystem:getHuntersByTarget(player:getGuid())
    if #hunters == 0 then return true end

    local maxHealth = player:getMaxHealth()
    if maxHealth <= 0 then maxHealth = 1 end

    for _, hunter in ipairs(hunters) do
        if hunter.status == HunterStatus.ACTIVE and not hunter:isExpired() then
            local rewardGold = math.max(1, math.floor(hunter.rewardGold * totalDamage / maxHealth))
           
            for _, cb in ipairs(killers) do
                cb.killer:addMoney(rewardGold)
                cb.killer:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE,
                    string.format("You received %d gold coins for hunting %s.", rewardGold, player:getName()))
            end

            hunter.status = HunterStatus.FINISHED
            hunter.updatedAt = timeNow
            hunter.finishedAt = timeNow

            HunterSystem:saveHunters(hunter.owner)
        end
    end

    return true
end

deathEvent:register()
!hunter add,Nick,1000 not !hunter add Nick, 1000

tfs 1.5 works. Thanks and best regards .
 
Back
Top