/* * 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) } }