/*
* CSB Freeze Tag
* Copyright (C) 2026 counter-strike-boost.com
*
* A freeze-tag game mode. A lethal hit does not kill: instead the victim is
* frozen in place as a glowing ice statue (maxspeed pinned, glow-shell render)
* and the attacker scores a freeze. A living teammate who stands next to a
* frozen player for a few seconds thaws them. A team wins the round when every
* living opponent is frozen. Kills are intercepted with Ham_TakeDamage.
*
* 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
#include
new const PLUGIN[] = "CSB Freeze Tag"
new const VERSION[] = "1.0.0"
new const AUTHOR[] = "counter-strike-boost.com"
#define TASK_SCAN 6300
new g_pEnabled, g_pReviveTime, g_pRadius
new bool:g_bFrozen[33]
new Float:g_fSavedSpeed[33]
new Float:g_fReviveProg[33]
new bool:g_bRoundOver = false
public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR)
g_pEnabled = register_cvar("csb_ft_enabled", "1")
g_pReviveTime = register_cvar("csb_ft_revive_time", "3.0")
g_pRadius = register_cvar("csb_ft_revive_radius", "90.0")
RegisterHam(Ham_TakeDamage, "player", "hamTakeDamage")
register_logevent("eventNewRound", 2, "1=Round_Start")
set_task(0.5, "taskScan", TASK_SCAN, _, _, "b")
}
public client_disconnected(id)
{
g_bFrozen[id] = false
g_fReviveProg[id] = 0.0
}
public hamTakeDamage(victim, inflictor, attacker, Float:damage, damagebits)
{
if (!get_pcvar_num(g_pEnabled))
return HAM_IGNORED
if (victim < 1 || victim > 32 || !is_user_alive(victim))
return HAM_IGNORED
/* frozen statues take no further damage */
if (g_bFrozen[victim])
return HAM_SUPERCEDE
/* would this hit be lethal? if so, freeze instead of kill */
if (damage >= float(get_user_health(victim)))
{
freezePlayer(victim)
if (attacker >= 1 && attacker <= 32 && attacker != victim && is_user_connected(attacker))
{
new an[32], vn[32]
get_user_name(attacker, an, charsmax(an))
get_user_name(victim, vn, charsmax(vn))
client_print(0, print_chat, "^x04[CSB]^x03 %s^x01 froze^x03 %s^x01!", an, vn)
}
return HAM_SUPERCEDE
}
return HAM_IGNORED
}
freezePlayer(id)
{
g_bFrozen[id] = true
g_fReviveProg[id] = 0.0
g_fSavedSpeed[id] = get_user_maxspeed(id)
set_user_maxspeed(id, 1.0)
/* icy blue glow shell */
set_user_rendering(id, kRenderFxGlowShell, 20, 120, 255, kRenderNormal, 25)
}
thawPlayer(id, bool:announce)
{
g_bFrozen[id] = false
g_fReviveProg[id] = 0.0
if (!is_user_connected(id))
return
new Float:spd = g_fSavedSpeed[id]
if (spd < 10.0)
spd = 250.0
set_user_maxspeed(id, spd)
set_user_rendering(id)
if (announce && is_user_alive(id))
{
new vn[32]
get_user_name(id, vn, charsmax(vn))
client_print(0, print_chat, "^x04[CSB]^x03 %s^x01 was thawed by a teammate.", vn)
}
}
public eventNewRound()
{
g_bRoundOver = false
for (new id = 1; id <= 32; id++)
{
if (g_bFrozen[id])
thawPlayer(id, false)
g_bFrozen[id] = false
g_fReviveProg[id] = 0.0
}
}
public taskScan()
{
if (!get_pcvar_num(g_pEnabled))
return
new players[32], num, pid
get_players(players, num, "a")
new Float:radius = get_pcvar_float(g_pRadius)
new Float:needTime = get_pcvar_float(g_pReviveTime)
new tAlive[3], tFrozen[3] // index 1 = T, 2 = CT
tAlive[1] = tAlive[2] = 0
tFrozen[1] = tFrozen[2] = 0
new origin[3], other[3]
for (new i = 0; i < num; i++)
{
pid = players[i]
new team = get_user_team(pid)
if (team == 1 || team == 2)
{
tAlive[team]++
if (g_bFrozen[pid])
tFrozen[team]++
}
if (!g_bFrozen[pid])
continue
/* look for a living teammate standing close */
get_user_origin(pid, origin)
new bool:reviving = false
for (new j = 0; j < num; j++)
{
new mate = players[j]
if (mate == pid || g_bFrozen[mate])
continue
if (get_user_team(mate) != team)
continue
get_user_origin(mate, other)
if (float(get_distance(origin, other)) <= radius)
{
reviving = true
break
}
}
if (reviving)
{
g_fReviveProg[pid] += 0.5
set_hudmessage(20, 160, 255, -1.0, 0.65, 0, 0.0, 0.6, 0.0, 0.0, -1)
show_hudmessage(pid, "Thawing... %d%%", floatround(g_fReviveProg[pid] / needTime * 100.0))
if (g_fReviveProg[pid] >= needTime)
thawPlayer(pid, true)
}
else if (g_fReviveProg[pid] > 0.0)
{
g_fReviveProg[pid] = 0.0
}
}
checkWin(tAlive, tFrozen)
}
checkWin(tAlive[3], tFrozen[3])
{
if (g_bRoundOver)
return
for (new team = 1; team <= 2; team++)
{
new foe = (team == 1) ? 2 : 1
/* this team has living players and all of them are frozen -> they lose */
if (tAlive[team] > 0 && tFrozen[team] >= tAlive[team] && tAlive[foe] > 0)
{
g_bRoundOver = true
client_print(0, print_chat, "^x04[CSB]^x01 %s froze the whole enemy team! Round over.",
(foe == 1) ? "The Terrorists" : "The Counter-Terrorists")
for (new id = 1; id <= 32; id++)
if (g_bFrozen[id])
thawPlayer(id, false)
set_task(2.0, "taskRestart")
return
}
}
}
public taskRestart()
{
server_cmd("sv_restartround 1")
server_exec()
}