• 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!

TFS 1.X Bounty Hunter System

imkingran

Learning everyday.
Premium User
Joined
Jan 15, 2014
Messages
1,318
Solutions
35
Reaction score
435
Hello OtLand Community, today I will release the upgraded Bounty Hunter system for TFS 1.X

Original System: TalkAction - Bounty Hunters System (Player Hunt System) (http://otland.net/threads/bounty-hunters-system-player-hunt-system.27721/)

Thanks to @kamilwxx for helping me understand how to store db results back in 2014 when this thread was started.

Customized for TFS 1.x, here we go:
- At the top of the Library file and the PHP file there is a small CONFIG section. I wrote comments on all the spots so you know what each thing means.
- Supports Coins/PremiumPoints and a Custom Currency if you choose it

library:
Code:
-- create table in database if one does not already exist
db.query("CREATE TABLE IF NOT EXISTS `bounty_hunter_system` (`id` int(11) NOT NULL auto_increment, `hunter_id` int(11) NOT NULL, `target_id` int(11) NOT NULL, `killer_id` int(11) NOT NULL, `prize` bigint(20) NOT NULL, `currencyType` varchar(32) NOT NULL, `dateAdded` int(15) NOT NULL, `killed` int(11) NOT NULL, `dateKilled` int(15) NOT NULL, PRIMARY KEY  (`id`)) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;")

--------------------------------------
---------- START OF CONFIG -----------
--------------------------------------
local customCurrency = '' -- by default bank balance and premium points are included but you can add other stackable currencies like gold nuggets etc here eg, 'gold nugget' or you can use the itemID or the item name
local config = {
    ipCheck = true, -- players from same IP can not place bounty on each other
    minLevelToAddBounty = 20, -- min lvl req to place a bounty
    minLevelToBeTargeted = 20, -- min lvl req to be targeted by a bounty
    broadcastKills = true, -- Should it broadcast a message to the whole game-world when someone was killed?
    broadcastHunt = false, -- Should it broadcast a message to the whole game-world when someone is added to the bounty list?
    mailbox_position = Position(32351,32223,6), -- If you are using a custom currency then we will send it to the players Mailbox, in order to do it you just need to put the location of one mailbox from your map here, doesn't matter which
    currencies = {
        ['gold'] = {  
            minAmount = 1000000, -- Min amount of Gold allowed
            maxAmount = 1000000000, -- Max amount of gold allowed
            func =
                function(player, prize, currency)
                    return player:setBankBalance(player:getBankBalance() + prize)
                end,
            check =
                function(player, amount, currency)
                    if player:getBankBalance() >= amount then
                        return player:setBankBalance(player:getBankBalance() - amount)
                    end
                    return false
                end,
        },
        ['points'] = {
            minAmount = 10, -- Min amount of premium points allowed
            maxAmount = 500, -- Max amount of premium points allowed
            func =
                function(player, prize, currency)
                    return player:addPremiumPoints(prize)
                end,
            check =
                function(player, prize, currency)
                    if player:getPremiumPoints() > prize then
                        return player:removePremiumPoints(prize)
                    end
                    return false
                end
        },
        [customCurrency] = {
            minAmount = 10, -- Min amount of custom item allowed
            maxAmount = 3000, -- Max amount of custom item allowed
            func =
                function(player, prize, currency)
                    return player:sendParcel(prize)
                end,
            check =
                function(player, amount, currency)
                    local itemID = ItemType(customCurrency):getId()
                    if itemID > 0 and player:getItemCount(itemID) >= amount then
                        player:removeItem(itemID, amount)
                        return true
                    end
                    return false
                end,
        }
    }
}
--------------------------------------
----------- END OF CONFIG ------------
--------------------------------------
-- Only edit below if you know what you are doing --

local function trimString(str)
  return (str:gsub("^%s*(.-)%s*$", "%1"))
end

local function addItemsToBag(bpID, itemID, count)
    local masterBag = Game.createItem(bpID,1)
    local stackable = ItemType(itemID):isStackable()

    if stackable then
        if count > 2000 then
            local bp = Game.createItem(bpID,1)
            masterBag:addItemEx(bp)
            for i = 1, count do
                if bp:getEmptySlots() < 1 then
                    bp = Game.createItem(bpID,1)
                    masterBag:addItemEx(bp)
                end
                bp:addItem(itemID)
            end
        end
        return masterBag
    end

    if count > 20 then
        local bp = Game.createItem(bpID,1)
        masterBag:addItemEx(bp)
        for i = 1, count do
            if bp:getEmptySlots() < 1 then
                bp = Game.createItem(bpID,1)
                masterBag:addItemEx(bp)
            end
            bp:addItem(itemID)
        end
    return masterBag
    end

    for i = 1, count do
        masterBag:addItem(itemID)
    end
    return masterBag
end

function Player:sendParcel(amount)
    local itemID = ItemType(customCurrency):getId()
    if itemID == 0 then
        print('Error in sending parcel. Custom currency was not set properly double check the spelling.')
        return
    end
    local container = Game.createItem(2595, 1)
    container:setAttribute(ITEM_ATTRIBUTE_NAME, 'Bounty Hunters Mail')
    local label = container:addItem(2599, 1)
    label:setAttribute(ITEM_ATTRIBUTE_TEXT, self:getName())
    label:setAttribute(ITEM_ATTRIBUTE_WRITER, "Bounty Hunters Mail")
    local parcel = addItemsToBag(1988, itemID, amount)
    container:addItemEx(parcel)
    container:moveTo(config.mailbox_position)
end

function Player:getPremiumPoints(points)
    local points = db.storeQuery("SELECT `premium_points` FROM `accounts` WHERE `id` = " .. self:getAccountId() .. ";")
    if points then
        local pointTotal = result.getDataInt(points, "premium_points")
        result.free(points)
    return pointTotal
    end
    return 0
end

function Player:addPremiumPoints(points)
    return db.query("UPDATE accounts SET premium_points = premium_points + "..points.." where id = "..self:getAccountId()..";")
end

function Player:removePremiumPoints(points)
    return db.query("UPDATE accounts SET premium_points = premium_points - "..points.." where id = "..self:getAccountId()..";")
end

function Player:getBountyInfo()
    local result_plr = db.storeQuery("SELECT prize, id, currencyType FROM `bounty_hunter_system` WHERE `target_id` = "..self:getGuid().." AND `killed` = 0;")
    if (result_plr == false) then
        return {false, 0, 0, 0, 0}
    end
    local prize = tonumber(result.getDataInt(result_plr, "prize"))
    local id = tonumber(result.getDataInt(result_plr, "id"))
    local bounty_type = tostring(result.getDataString(result_plr, "currencyType"))
    result.free(result_plr)
    return {true, prize, id, bounty_type, currency}
end

local function addBountyKill(killer, target, prize, id, bounty_type, currency)
    if not config.currencies[bounty_type] then
        print('error in adding bounty prize')
        return true
    end
    config.currencies[bounty_type].func(killer, prize, currency)
    db.query("UPDATE `bounty_hunter_system` SET `killed` = 1, `killer_id`="..killer:getGuid()..", `dateKilled` = " .. os.time() .. " WHERE `id`  = "..id..";")
    killer:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE,'[BOUNTY HUNTER SYSTEM] You killed ' .. target:getName() .. ' and earned a reward of ' .. prize .. ' ' .. bounty_type .. 's!')
    if config.broadcastKills then
        Game.broadcastMessage("Bounty Hunter Update:\n " .. killer:getName() .. " has killed " .. target:getName() .. " and earned a reward of " .. prize .. " " .. bounty_type .. "!", MESSAGE_EVENT_ADVANCE)
    end
    return true
end

local function addBountyHunt(player, target, amount, currencyType)
    db.query("INSERT INTO `bounty_hunter_system` VALUES (NULL," .. player:getGuid() .. "," .. target:getGuid() .. ",0," .. amount .. ", '" .. currencyType .. "', " .. os.time() .. ", 0, 0);")
    player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "[BOUNTY HUNTER SYSTEM] You have placed bounty on " .. target:getName() .. " for a reward of " .. amount .. " " .. currencyType .. "!")
    if config.broadcastHunt then
        Game.broadcastMessage("[BOUNTY_HUNTER_SYSTEM]\n " .. player:getName() .. " has put a bounty on " .. target:getName() .. " for " .. amount .. " " .. t[2] .. ".", MESSAGE_EVENT_ADVANCE)
    end
return false
end

function onBountyHunterSay(player, words, param)
    if player:getLevel() < config.minLevelToAddBounty then
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, '[BOUNTY_HUNTER_SYSTEM] You need level ' .. config.minLevelToAddBounty .. ' to use this command.')
        return false
    end
    local t = param:split(",")
    local name = t[1]
    local currencyType = t[2] and trimString(t[2]) or nil
    local amount = t[3] and tonumber(t[3]) or nil

    if not (name and currencyType and amount) then
        local item = ItemType(customCurrency)
        local text = '[BOUNTY HUNTER SYSTEM GUIDE]\n\nCommand Usage:\n!hunt playerName, type(gold/' .. customCurrency .. '/points), amount' .. '\n\n' .. 'Hunting for Gold:\n' .. '--> !hunt Joe,gold,150000\n' .. '--> Placed a bounty on Joe for the amount of 150,000 gps.' .. '\n\n' .. 'Hunting for Premium Points:\n' .. '--> !hunt Joe,points,100\n' .. '--> Placed a bounty on Joe for the amount of 100 premium points.'
        text = text .. (item:getId() > 0 and ('\n\n' .. 'Hunting for ' .. item:getPluralName() .. ':\n' .. '--> !hunt Joe,' .. customCurrency .. ',50\n' .. '--> Placed a bounty on Joe for the amount of 50 ' .. item:getPluralName()) or '')
        player:popupFYI(text)
        return false
    end

    local target = Player(name)
    if not target then
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, '[BOUNTY_HUNTER_SYSTEM] A player with the name of ' .. name .. ' is not online.')
    return false
    end

    if target:getGuid() == player:getGuid() then
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, '[BOUNTY_HUNTER_SYSTEM] You may not place a bounty on yourself!')
    return false
    end

    if config.ipCheck and target:getIp() == player:getIp() then
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, '[BOUNTY_HUNTER_SYSTEM] You may not place a bounty on a player from the same IP Address!')
    return false
    end

    if target:getLevel() < config.minLevelToBeTargeted then
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, '[BOUNTY_HUNTER_SYSTEM] You may only target players level ' .. config.minLevelToBeTargeted .. ' and above!')
    return false
    end

    local info = target:getBountyInfo()
    if info[1] then
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "[BOUNTY HUNTER SYSTEM] This player has already been hunted.")
        return false
    end

    local typ = config.currencies[currencyType]
    if not typ then
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, '[BOUNTY_HUNTER_SYSTEM] The currency type "' .. currencyType .. '" is not a valid bounty currency. [Currencies: gold/points' .. (customCurrency ~= '' and '/'..customCurrency..'' or '') .. ']')
    return false
    end

    local minA, maxA = typ.minAmount, typ.maxAmount
    if amount < minA or amount > maxA then
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, '[BOUNTY_HUNTER_SYSTEM] The currency type of "' .. currencyType .. '" allows the amount to be in the range of ' .. minA .. ' --> ' .. maxA .. '.')
    return false
    end

    if not typ.check(player, amount, currencyType) then
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, '[BOUNTY_HUNTER_SYSTEM] You do not have ' .. amount .. ' ' .. currencyType .. '. [Error: Insufficient Funds]')
    return false
    end

    return addBountyHunt(player, target, amount, currencyType)
end



function onBountyHunterKill(creature, target)
    if not target:isPlayer() then
        return true
    end

    if creature:getTile():hasFlag(TILESTATE_PVPZONE) then
        return true
    end

    local info = target:getBountyInfo()
    if not info[1] then
        return true
    end

    return addBountyKill(creature, target, info[2], info[3], info[4], info[5])
end

data/talkactions/talkactions.xml
Code:
<talkaction words="!hunt" separator=" " script="bounty_hunter.lua" />

data/talkactions/scripts/bounty_hunter.lua
Code:
function onSay(player, words, param)
    return onBountyHunterSay(player, words, param)
end

data/creaturescripts/creaturescripts.xml
Code:
<event type="kill" name="BountyHunterKill" script="bounty_hunter.lua"/>

data/creaturescripts/scripts/bounty_hunter.lua
Code:
function onKill(player, target)
    return onBountyHunterKill(player, target)
end

data/creaturescripts/scripts/others/login.lua
Code:
player:registerEvent('BountyHunterKill')

Web Page:
PHP:
<style>
    .bounty_hunter_header {
        font-size: 22px;
        font-weight: bold;
        text-align: center;
    }
    .bounty_hunter_table_header {
        font-size: 14px;
        color: white;
        background-color: #505050;
        text-align: center;
        font-weight: bold;
    }
    .lightRow {
        font-size: 14px;
        color: black;
        background-color: #F1E0C6;
        text-align: center;
    }
   .darkRow {
        font-size: 14px;
        color: black;
        background-color: #D4C0A1;
        text-align: center;
    }
    .bh_name {
        color: maroon;
    }
    .bh_type {
        color: blue;
    }
    .bh_amount {
        color: green;
    }
</style>
<?php
////////////// CONFIG START /////////////////////////
$customCurrency = ''; // If you are using a custom currency add the name here
$imgPath = 'http://outfit-images.ots.me/animatedOutfits1090/animoutfit.php'; // Path to your images (default is Gesior 10.90 animated outfit images)
////////////// CONFIG END /////////////////////////
$main_content .= '
<center><h1>Bounty Hunters</h1><br/>
<table border="2" cellpadding="4" cellspacing="1" width="100%">
    <tr class="bounty_hunter_table_header">
        <td class="bounty_hunter_header" colspan="2">General Information</td>
    </tr>
    <tr class="lightRow">
        <td><strong>Command:</strong></td>
        <td><strong>!hunt <span class="bh_name">playerName</span>, <span class="bh_type">points/gold'. (($customCurrency != '') ? '/'. $customCurrency.'' : '') . '</span>, <span class="bh_amount">amount</span></strong></td>
    </tr>
    <tr class="bounty_hunter_table_header">
        <td class="bounty_hunter_header" colspan="2">Examples</td>
    </tr>
    <tr class="lightRow">
        <td><strong>Add by points</strong></td>
        <td><strong>!hunt <span class="bh_name">John</span>, <span class="bh_type">points</span>, <span class="bh_amount">10</span></strong><br/>Add bounty on player named John worth 10 points.</td>
    </tr>
    <tr class="darkRow">
        <td><strong>Add by gold</strong></td>
        <td><strong>!hunt <span class="bh_name">John</span>, <span class="bh_type">gold</span>, <span class="bh_amount">100000</span></strong><br/>Add bounty on player named John worth 100,000 gold coins.</td>
    </tr>
    ';
    if ($customCurrency != '') {
        $main_content .= '
        <tr class="lightRow">
            <td><strong>Add by '.$customCurrency.'</strong></td>
            <td><strong>!hunt <span class="bh_name">John</span>, <span class="bh_type">'.$customCurrency.'</span>, <span class="bh_amount">20</span></strong><br/>Add bounty on player named John worth 20 '.$customCurrency.'.</td>
        </tr>
        ';   
    }
$main_content .= '
</table><br/><br/>
';

$main_content .= '
<table border="2" cellpadding="4" cellspacing="1" width="100%">
    <tr class="bounty_hunter_table_header">
        <td class="bounty_hunter_header" colspan="5">Bounty List</td>
    </tr>
    <tr class="bounty_hunter_table_header">
        <td>Hunted by</td>
        <td>Reward</td>
        <td>Player Hunted</td>
        <td>Outfit</td>
        <td>Killed by</td>
    </tr>';
$num = 0;
$bountys = $SQL->query('SELECT A.* , B.name AS hunted_by, C.name AS player_hunted, D.name AS killed_by
                        FROM bounty_hunter_system AS A
                        LEFT JOIN players AS B ON A.hunter_id = B.id
                        LEFT JOIN players AS C ON A.target_id = C.id
                        LEFT JOIN players AS D ON A.killer_id = D.id
                        ORDER BY A.killed,A.prize DESC');
if ($bountys->rowCount() > 0) {
    foreach($bountys as $bounty) 
    {
        if ($bounty['killed_by']){
            $killed_by = '<a href="?subtopic=characters&name='.$bounty['killed_by'].'">'.$bounty['killed_by'].'</a>';
        } else {
            $killed_by = 'still alive';
        }
        $skill = $SQL->query('SELECT * FROM '.$SQL->tableName('players').' WHERE '.$SQL->fieldName('id').' = '.$bounty['target_id'].'')->fetch();
        $main_content .= '
            <tr class="'.(($num % 2 == 0) ? "lightRow" : "darkRow").'">
                <td><a href="?subtopic=characters&name='.$bounty['hunted_by'].'">'.$bounty['hunted_by'].'</a></td>
                <td>'.$bounty['prize'].' '.$bounty['currencyType'].'</td>
                <td><a href="?subtopic=characters&name='.$bounty['player_hunted'].'">'.$bounty['player_hunted'].'</a></td>
                <td><div style="position: relative; width: 32px; height: 32px;"><div style="background-image: url(\''.$imgPath.'?id='.$skill['looktype'].'&addons='.$skill['lookaddons'].'&head='.$skill['lookhead'].'&body='.$skill['lookbody'].'&legs='.$skill['looklegs'].'&feet='.$skill['lookfeet'].'\'); position: absolute; width: 64px; height: 80px; background-position: bottom right; background-repeat: no-repeat; right: 0px; bottom: 0px;"></div></div></td>
                <td>'.$killed_by.'</td>
            </tr>';
        $num++;
    }
} else {
    $main_content.='<tr class="lightRow"><td colspan=5>Currently there are not any bounty hunter offers.</td></tr>';
}
$main_content .='</table>';
?>


List of things to do:
  1. Add in global broadcast that was requested somewhere in this thread
  2. Add in Bounty NPC
  3. Add in talkaction to check for active bounties
  4. Make webpage look prettier
  5. Allow players to add multiple bounties to 1 person
  6. Open for ideas
Change Log
  • Fixed not removing Bank Balance
  • Fixed not checking Premium Points balance before removing. (Was putting into negative values)
  • No longer able to place bounty on self
  • Added option in config to check for ips (add ipCheck = true/false to your config or re-copy library)
 
Last edited:
Try this one:
Code:
function onSay(cid, words, param)
    if(param == "") then
        doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "[BOUNTY HUNTERS] Use: \"!hunt [prize],[nick]\" where prize is for example 1(k).")
        return TRUE
    end
    local t = string.split(param, ",")
    if(not t[2]) then
        doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "[BOUNTY HUNTERS] Use: \"!hunt [prize],[nick]\" where prize is for example 1(k).")
        return TRUE
    end
 
    local sp_id = getPlayerGUIDByName(t[2])
    if sp_id == nil then
        doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "[BOUNTY HUNTERS] This player doesn't exists.")     
        return TRUE
    end
 
    local result_plr = db.storeQuery("SELECT * FROM `bounty_hunters` WHERE `sp_id` = "..sp_id.."  AND `killed` = 0;")
    if(result_plr ~= false) then
        is = tonumber(result.getDataInt(result_plr, "sp_id"))
        result.free(result_plr)
    else
        is = 0
    end
    prize = tonumber(t[1])

    if(prize == nil or prize < 1) then
        doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "[BOUNTY HUNTERS] Use: \"!hunt [prize],[nick]\" where prize is for example 1(k).")
        return TRUE
    end
 
    if(prize >= 100000000000000000000) then
        doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "[BOUNTY HUNTERS] Sorry, you typed too big number!")
        return TRUE
    end

    if is ~= 0 then
        doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "[BOUNTY HUNTERS] This player has already hunted.") 
        return TRUE
    end

    if doPlayerRemoveMoney(cid, prize*1000) == TRUE then
        db.query("INSERT INTO `bounty_hunters` VALUES (NULL,"..getPlayerGUID(cid)..","..sp_id..",0," .. os.time() .. ","..prize..",0,0);")
        doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "[BOUNTY HUNTERS] Hunt has been added!")         
    else
        doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "[BOUNTY HUNTERS] You haven't got enough money!")         
    end
 
 
    return 1
end
 
I edited the Talkaction to fix a few minor bugs that allowed players to enter a bounty even with an invalid name and the minimum bounty must be 1kk. You can edit the minimum prize as you like on this line:

Code:
    if(prize == nil or prize < 1000000) then
        doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "[BOUNTY HUNTERS] The prize can not be less than 1kk.")
        return TRUE
    end

I also edited the Creaturescript so that the money is now sent directly to the players bank instead of there backpack.

Also both the addition of a bounty and the kill will now be broadcasted.

adding outfit on the players might be great
It might be frustrating if you can't wear the outfit of your choice because other players keep adding a bounty to you. :(
 
Last edited:
i mean in the php not online lol
and other thing letting the target know that he is bounty
 
Back
Top