/*
* CSB Bounty
* Copyright (C) 2026 counter-strike-boost.com
*
* Players (or the server) can place a cash bounty on another player's head.
* Bounties are stored per SteamID in a Trie, shown in the HUD, announced on
* placement / increase / claim, and paid to whoever kills the target. An
* anti-farm cooldown blocks payouts for repeatedly killing the same victim.
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version. It is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See for details.
*/
#include
#include
#include
new const PLUGIN[] = "CSB Bounty"
new const VERSION[] = "1.0.0"
new const AUTHOR[] = "counter-strike-boost.com"
new Trie:g_tBounty
new g_pEnabled, g_pMin, g_pCooldown
new g_szAuth[33][35]
new Float:g_fLastKill[33][33]
new g_iSync
public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR)
g_tBounty = TrieCreate()
g_pEnabled = register_cvar("csb_bounty_enabled", "1")
g_pMin = register_cvar("csb_bounty_min", "500")
g_pCooldown = register_cvar("csb_bounty_cooldown", "45")
register_clcmd("say", "cmdSay")
register_clcmd("say_team", "cmdSay")
register_concmd("amx_bounty", "cmdAdminBounty", ADMIN_LEVEL_A, " - place a server bounty")
register_event("DeathMsg", "eventDeath", "a", "1>0")
register_logevent("eventRoundStart", 2, "1=Round_Start")
g_iSync = CreateHudSyncObj()
set_task(1.0, "taskHud", 0, _, _, "b")
}
public plugin_end()
{
TrieDestroy(g_tBounty)
}
public client_authorized(id)
{
get_user_authid(id, g_szAuth[id], charsmax(g_szAuth[]))
}
public client_disconnected(id)
{
if (g_szAuth[id][0])
TrieDeleteKey(g_tBounty, g_szAuth[id])
g_szAuth[id][0] = 0
}
getBounty(id)
{
if (!g_szAuth[id][0])
return 0
new amount
if (TrieGetCell(g_tBounty, g_szAuth[id], amount))
return amount
return 0
}
setBounty(id, amount)
{
if (!g_szAuth[id][0])
return
if (amount <= 0)
TrieDeleteKey(g_tBounty, g_szAuth[id])
else
TrieSetCell(g_tBounty, g_szAuth[id], amount)
}
public cmdSay(id)
{
new args[128]
read_args(args, charsmax(args))
remove_quotes(args)
if (containi(args, "/bounty") != 0)
return PLUGIN_CONTINUE
if (!get_pcvar_num(g_pEnabled))
{
client_print(id, print_chat, "[CSB] Bounties are disabled.")
return PLUGIN_HANDLED
}
/* format: /bounty */
new rest[96]
copy(rest, charsmax(rest), args[7])
trim(rest)
new targetName[32], amountStr[16]
strtok(rest, targetName, charsmax(targetName), amountStr, charsmax(amountStr), ' ')
trim(targetName)
trim(amountStr)
if (!targetName[0] || !amountStr[0])
{
client_print(id, print_chat, "[CSB] Usage: /bounty ")
return PLUGIN_HANDLED
}
new target = findByName(targetName)
if (!target)
{
client_print(id, print_chat, "[CSB] No player matching '%s'.", targetName)
return PLUGIN_HANDLED
}
if (target == id)
{
client_print(id, print_chat, "[CSB] You cannot put a bounty on yourself.")
return PLUGIN_HANDLED
}
new amount = str_to_num(amountStr)
new minB = get_pcvar_num(g_pMin)
if (amount < minB)
{
client_print(id, print_chat, "[CSB] Minimum bounty is $%d.", minB)
return PLUGIN_HANDLED
}
new money = cs_get_user_money(id)
if (money < amount)
{
client_print(id, print_chat, "[CSB] You only have $%d.", money)
return PLUGIN_HANDLED
}
cs_set_user_money(id, money - amount)
new was = getBounty(target)
setBounty(target, was + amount)
new placer[32], victim[32]
get_user_name(id, placer, charsmax(placer))
get_user_name(target, victim, charsmax(victim))
if (was > 0)
client_print(0, print_chat, "[CSB] %s raised the bounty on %s to $%d!", placer, victim, was + amount)
else
client_print(0, print_chat, "[CSB] %s placed a $%d bounty on %s!", placer, amount, victim)
return PLUGIN_HANDLED
}
public cmdAdminBounty(id, level, cid)
{
if (!cmd_access(id, level, cid, 3))
return PLUGIN_HANDLED
new arg1[32], arg2[16]
read_argv(1, arg1, charsmax(arg1))
read_argv(2, arg2, charsmax(arg2))
new target = findByName(arg1)
if (!target)
{
console_print(id, "[CSB] No player matching '%s'.", arg1)
return PLUGIN_HANDLED
}
new amount = str_to_num(arg2)
if (amount <= 0)
{
console_print(id, "[CSB] Amount must be positive.")
return PLUGIN_HANDLED
}
new was = getBounty(target)
setBounty(target, was + amount)
new victim[32]
get_user_name(target, victim, charsmax(victim))
client_print(0, print_chat, "[CSB] The server placed a $%d bounty on %s!", amount, victim)
return PLUGIN_HANDLED
}
findByName(const name[])
{
new players[32], num
get_players(players, num, "ch")
for (new i = 0; i < num; i++)
{
new pn[32]
get_user_name(players[i], pn, charsmax(pn))
if (containi(pn, name) != -1)
return players[i]
}
return 0
}
public eventDeath()
{
new killer = read_data(1)
new victim = read_data(2)
if (victim < 1 || victim > 32)
return
new reward = getBounty(victim)
if (reward <= 0)
return
if (killer < 1 || killer > 32 || killer == victim)
{
/* suicide / world: bounty stays */
return
}
/* anti-farm: same killer killing same victim too soon pays nothing */
new Float:cd = get_pcvar_float(g_pCooldown)
if (get_gametime() - g_fLastKill[killer][victim] < cd)
{
setBounty(victim, 0)
return
}
g_fLastKill[killer][victim] = get_gametime()
new money = cs_get_user_money(killer)
new give = money + reward
if (give > 16000)
give = 16000
cs_set_user_money(killer, give)
setBounty(victim, 0)
new kn[32], vn[32]
get_user_name(killer, kn, charsmax(kn))
get_user_name(victim, vn, charsmax(vn))
client_print(0, print_chat, "[CSB] %s claimed the $%d bounty on %s!", kn, reward, vn)
}
public eventRoundStart()
{
TrieClear(g_tBounty)
}
public taskHud()
{
new best = 0, bestVal = 0
new players[32], num, pid
get_players(players, num, "ch")
for (new i = 0; i < num; i++)
{
pid = players[i]
new b = getBounty(pid)
if (b > bestVal)
{
bestVal = b
best = pid
}
}
if (!best)
return
new name[32]
get_user_name(best, name, charsmax(name))
set_hudmessage(255, 200, 0, 0.02, 0.20, 0, 0.0, 1.1, 0.0, 0.0, -1)
ShowSyncHudMsg(0, g_iSync, "TOP BOUNTY: %s - $%d", name, bestVal)
}