/*
* CSB Chicken Grenade
* Copyright (C) 2026 counter-strike-boost.com
*
* Turns the HE grenade into a chicken bomb: the thrown grenade wears a chicken
* model, and just before it detonates it hatches several chicken entities that
* scatter and pop one by one, each dealing scaled blast damage around it.
*
* Inspired by the classic Chicken Grenade / Chicken Mod idea. Independent GPL
* re-implementation; no original code is reused.
*
* 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 Chicken Grenade"
new const VERSION[] = "1.0.0"
new const AUTHOR[] = "counter-strike-boost.com"
#define TE_EXPLOSION 3
#define CHICK_CLASS "csb_chick"
new const g_szChickenModel[] = "models/chick.mdl"
new const g_szHeModel[] = "models/w_hegrenade.mdl"
new const g_szCluckSound[] = "player/pl_duck1.wav"
new g_pEnabled, g_pCount, g_pDamage, g_pRadius
new g_iSprExplode
new g_iMaxPlayers
public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR)
g_pEnabled = register_cvar("csb_chick_enabled", "1")
g_pCount = register_cvar("csb_chick_count", "5")
g_pDamage = register_cvar("csb_chick_damage", "50")
g_pRadius = register_cvar("csb_chick_radius", "140")
register_forward(FM_SetModel, "fwSetModel")
register_forward(FM_Think, "fwThink")
g_iMaxPlayers = get_maxplayers()
}
public plugin_precache()
{
g_iSprExplode = precache_model("sprites/zerogxplode.spr")
precache_model(g_szChickenModel)
precache_sound(g_szCluckSound)
}
public fwSetModel(ent, const model[])
{
if (!get_pcvar_num(g_pEnabled) || !pev_valid(ent))
return FMRES_IGNORED
if (!equal(model, g_szHeModel))
return FMRES_IGNORED
/* it is a live HE grenade: dress it as a chicken */
engfunc(EngFunc_SetModel, ent, g_szChickenModel)
new Float:fDmgTime
pev(ent, pev_dmgtime, fDmgTime)
new Float:delay = fDmgTime - get_gametime() - 0.1
if (delay < 0.2)
delay = 0.2
set_task(delay, "taskHatch", ent)
return FMRES_IGNORED
}
public taskHatch(ent)
{
if (!pev_valid(ent))
return
static Float:fOrigin[3]
pev(ent, pev_origin, fOrigin)
new owner = pev(ent, pev_owner)
new count = get_pcvar_num(g_pCount)
for (new i = 0; i < count; i++)
spawnChick(fOrigin, owner)
}
spawnChick(const Float:origin[3], owner)
{
new ent = engfunc(EngFunc_CreateNamedEntity, engfunc(EngFunc_AllocString, "info_target"))
if (!pev_valid(ent))
return
set_pev(ent, pev_classname, CHICK_CLASS)
engfunc(EngFunc_SetModel, ent, g_szChickenModel)
set_pev(ent, pev_movetype, MOVETYPE_TOSS)
set_pev(ent, pev_solid, SOLID_TRIGGER)
set_pev(ent, pev_owner, owner)
static Float:mins[3], Float:maxs[3]
mins[0] = -4.0; mins[1] = -4.0; mins[2] = 0.0
maxs[0] = 4.0; maxs[1] = 4.0; maxs[2] = 8.0
engfunc(EngFunc_SetSize, ent, mins, maxs)
engfunc(EngFunc_SetOrigin, ent, origin)
/* random scatter velocity */
static Float:vel[3]
vel[0] = float(random_num(-180, 180))
vel[1] = float(random_num(-180, 180))
vel[2] = float(random_num(180, 320))
set_pev(ent, pev_velocity, vel)
/* hatch-to-pop delay via Think */
new Float:life = random_float(0.7, 1.5)
set_pev(ent, pev_nextthink, get_gametime() + life)
}
public fwThink(ent)
{
if (!pev_valid(ent))
return FMRES_IGNORED
static cls[32]
pev(ent, pev_classname, cls, charsmax(cls))
if (!equal(cls, CHICK_CLASS))
return FMRES_IGNORED
static Float:origin[3]
pev(ent, pev_origin, origin)
new owner = pev(ent, pev_owner)
/* visual explosion */
message_begin_f(MSG_PVS, SVC_TEMPENTITY, origin)
write_byte(TE_EXPLOSION)
write_coord(floatround(origin[0]))
write_coord(floatround(origin[1]))
write_coord(floatround(origin[2]))
write_short(g_iSprExplode)
write_byte(12)
write_byte(20)
write_byte(0)
message_end()
emit_sound(ent, CHAN_STATIC, g_szCluckSound, 1.0, ATTN_NORM, 0, PITCH_NORM)
blastDamage(origin, owner)
engfunc(EngFunc_RemoveEntity, ent)
return FMRES_IGNORED
}
blastDamage(const Float:origin[3], owner)
{
new Float:radius = get_pcvar_float(g_pRadius)
new Float:maxDmg = get_pcvar_float(g_pDamage)
new attacker = (owner >= 1 && owner <= g_iMaxPlayers) ? owner : 0
for (new i = 1; i <= g_iMaxPlayers; i++)
{
if (!is_user_alive(i))
continue
static Float:pOrigin[3]
pev(i, pev_origin, pOrigin)
new Float:dist = get_distance_f(origin, pOrigin)
if (dist > radius)
continue
new Float:scale = 1.0 - (dist / radius)
new Float:dmg = maxDmg * scale
if (dmg < 1.0)
continue
new inflictor = (attacker > 0) ? attacker : i
ExecuteHamB(Ham_TakeDamage, i, inflictor, attacker > 0 ? attacker : i, dmg, DMG_BLAST)
}
}