#include <amxmodx>
#include <fakemeta>
#include <reapi>
#include <hamsandwich>
#include <xs>
new const PLUGIN_NAME[] = "Snow WarmUp";
new const PLUGIN_VERSION[] = "0.1";
new const PLUGIN_AUTHOR[] = "bizon";
new const SNOWBALL_MODEL_VIEW[] = "models/snow_warmup/v_snowball.mdl";
new const SNOWBALL_MODEL_PLAYER[] = "models/snow_warmup/p_snowball.mdl";
new const SNOWBALL_MODEL_WORLD[] = "models/snow_warmup/w_snowball.mdl";
//
new const SNOWBALL_SOUND_FREEZE[] = "snow_warmup/freeze.wav";
new const SNOWBALL_SOUND_UNFREEZE[] = "snow_warmup/unfreeze.wav";
new const AMBIENCE_SOUND_FIND[] = "ambience/wind1.wav"; // valve/sound/...
//
const TASK_USER_FREEZE = 0xD134;
enum any: CvarsStruct
{
CVAR_SWU_TIME,
CVAR_SWU_IS_FREEZE_USER,
CVAR_SWU_FREEZE_TIME_USER,
CVAR_SWU_DAMAGE_USER,
CVAR_SWU_START_HEALTH_USER,
}
new
HookChain: g_hRoundEnd,
HookChain: g_hRestartRound,
HookChain: g_hPlayerSpawn,
HookChain: g_hKilled,
HookChain: g_hCanHavePlayerItem,
HookChain: g_hThrowSmokeGrenade,
HookChain: g_hDefaultDeploy,
HookChain: g_hRadio,
bool: bIsSnowWarmUp,
g_iTimer,
//
g_iCvarRoundInfinite,
g_iCvarForseRespawn,
g_member_bCTCantBuy,
g_member_bTCantBuy,
//
g_iSpriteTrail,
//
g_eCvar[CvarsStruct];
public plugin_precache()
{
precache_model(SNOWBALL_MODEL_VIEW);
precache_model(SNOWBALL_MODEL_PLAYER);
precache_model(SNOWBALL_MODEL_WORLD);
precache_sound(SNOWBALL_SOUND_FREEZE);
precache_sound(SNOWBALL_SOUND_UNFREEZE);
precache_sound(AMBIENCE_SOUND_FIND);
g_iSpriteTrail = precache_model("sprites/zbeam3.spr");
}
public client_disconnected(pPlayer)
{
remove_task(pPlayer + TASK_USER_FREEZE);
}
public plugin_init()
{
register_plugin(PLUGIN_NAME, PLUGIN_VERSION, PLUGIN_AUTHOR);
g_hRoundEnd = RegisterHookChain(RG_RoundEnd, "@RoundEnd_Pre");
@cvars_attach();
}
@RoundEnd_Pre(WinStatus:status, ScenarioEventEndRound:event, Float:tmDelay)
{
if(event != ROUND_GAME_COMMENCE)
return;
g_hRestartRound = RegisterHookChain(RG_CSGameRules_RestartRound, "@CSGameRules_RestartRound_Pre");
g_hPlayerSpawn = RegisterHookChain(RG_CSGameRules_PlayerSpawn, "@CSGameRules_PlayerSpawn_Post", true); // избегаем блок вызова RG_CBasePlayer_OnSpawnEquip вышестоящими плагинами
g_hKilled = RegisterHookChain(RG_CBasePlayer_Killed, "@CBasePlayer_Killed_Post", true);
g_hCanHavePlayerItem = RegisterHookChain(RG_CSGameRules_CanHavePlayerItem, "@CSGameRules_CanHavePlayerItem_Pre");
g_hThrowSmokeGrenade = RegisterHookChain(RG_ThrowSmokeGrenade, "@ThrowSmokeGrenade_Post", true);
g_hDefaultDeploy = RegisterHookChain(RG_CBasePlayerWeapon_DefaultDeploy, "@CBasePlayerWeapon_DefaultDeploy_Pre");
g_hRadio = RegisterHookChain(RG_CBasePlayer_Radio, "@CBasePlayer_Radio_Pre");
g_iCvarRoundInfinite = get_cvar_num("mp_round_infinite");
g_iCvarForseRespawn = get_cvar_num("mp_forcerespawn");
set_cvar_num("mp_round_infinite", 1);
set_cvar_num("mp_forcerespawn", 1);
g_member_bCTCantBuy = get_member_game(m_bCTCantBuy);
g_member_bTCantBuy = get_member_game(m_bTCantBuy);
set_member_game(m_bCTCantBuy, true);
set_member_game(m_bTCantBuy, true);
DisableHookChain(g_hRoundEnd);
}
@CSGameRules_RestartRound_Pre()
{
if(bIsSnowWarmUp)
{
DisableHookChain(g_hRestartRound);
DisableHookChain(g_hPlayerSpawn);
DisableHookChain(g_hKilled);
DisableHookChain(g_hCanHavePlayerItem);
DisableHookChain(g_hThrowSmokeGrenade);
DisableHookChain(g_hDefaultDeploy);
DisableHookChain(g_hRadio);
set_cvar_num("mp_round_infinite", g_iCvarRoundInfinite);
set_cvar_num("mp_forcerespawn", g_iCvarForseRespawn);
set_member_game(m_bCTCantBuy, g_member_bCTCantBuy);
set_member_game(m_bTCantBuy, g_member_bTCantBuy);
set_dhudmessage(0, 255, 255, -1.0, 0.04, .holdtime = 5.0);
show_dhudmessage(0, "Зимняя разминка окончена^nПриятной игры");
new iFwd;
iFwd = CreateMultiForward("swu_end", ET_CONTINUE);
ExecuteForward(iFwd);
}else{
g_iTimer = g_eCvar[CVAR_SWU_TIME];
set_task(1.0, "@func_hud_info", 0xC0DE, .flags = "b");
rg_send_audio(0, AMBIENCE_SOUND_FIND, PITCH_NORM);
new iFwd;
iFwd = CreateMultiForward("swu_start", ET_CONTINUE);
ExecuteForward(iFwd);
}
bIsSnowWarmUp = true;
}
@CSGameRules_PlayerSpawn_Post(pPlayer)
{
rg_remove_all_items(pPlayer);
DisableHookChain(g_hCanHavePlayerItem);
new iItem;
iItem = rg_give_custom_item(pPlayer, "weapon_smokegrenade");
if(!is_nullent(iItem))
{
rg_set_iteminfo(iItem, ItemInfo_iMaxAmmo1, 999);
rg_set_user_bpammo(pPlayer, get_member(iItem, m_iId), 999);
}
EnableHookChain(g_hCanHavePlayerItem);
set_entvar(pPlayer, var_health, Float: float(g_eCvar[CVAR_SWU_START_HEALTH_USER]));
}
@CBasePlayer_Killed_Post(pVictim, pAttacker, iGibs)
{
rg_set_rendering(pVictim);
rg_reset_maxspeed(pVictim);
remove_task(pVictim + TASK_USER_FREEZE);
}
@CSGameRules_CanHavePlayerItem_Pre(pPlayer, iItem)
{
if(is_nullent(iItem))
return HC_CONTINUE;
rg_remove_entity(iItem)
SetHookChainReturn(ATYPE_INTEGER, 0);
return HC_SUPERCEDE;
}
@ThrowSmokeGrenade_Post(pPlayer, Float: vecStart[3], Float: vecVelocity[3], Float: flTime, const usEvent)
{
new eEnt;
eEnt = GetHookChainReturn(ATYPE_INTEGER);
if(!is_nullent(eEnt))
rg_remove_entity(eEnt);
new
Float: fOriginStart[3],
Float: fDest[3],
Float: fVelocity[3];
// 28 - "Bip01 R Hand" - правая рука (кость скелета)
// https://wiki.alliedmods.net/FakeMeta_Functions_Detailed_Descriptions#EngFunc_GetBonePosition
static const iBoneRightHand = 28;
engfunc(EngFunc_GetBonePosition, pPlayer, iBoneRightHand, fOriginStart, fDest);
// получаем координаты перед рукой на N юнитов
static const Float: fDistShiftFrontUnit = 5.0;
fDest[0] = 0.0
engfunc(EngFunc_MakeVectors, fDest);
global_get(glb_v_forward, fDest);
xs_vec_mul_scalar(fDest, fDistShiftFrontUnit, fDest);
xs_vec_add(fOriginStart, fDest, fDest);
engfunc(EngFunc_TraceLine, fOriginStart, fDest, DONT_IGNORE_MONSTERS, pPlayer, 0);
get_tr2(0, TR_vecEndPos, fOriginStart);
// расчёт векторной скорости полёта снежка
get_origin_aiming(pPlayer, fDest);
xs_vec_sub(fDest, fOriginStart, fVelocity);
xs_vec_normalize(fVelocity, fVelocity);
xs_vec_mul_scalar(fVelocity, 1999.0, fVelocity);
// создаем снежок
eEnt = rg_create_entity("info_target");
engfunc(EngFunc_SetModel, eEnt, SNOWBALL_MODEL_WORLD);
engfunc(EngFunc_SetOrigin, eEnt, fOriginStart);
engfunc(EngFunc_SetSize, eEnt, Float:{-3.0, -3.0, -3.0}, Float:{3.0, 3.0, 3.0});
set_entvar(eEnt, var_classname, "snowball");
set_entvar(eEnt, var_owner, pPlayer);
set_entvar(eEnt, var_movetype, MOVETYPE_TOSS);
set_entvar(eEnt, var_solid, SOLID_TRIGGER);
set_entvar(eEnt, var_velocity, fVelocity);
SetTouch(eEnt, "@touch_snowball");
set_msg_beamfollow(eEnt, g_iSpriteTrail, 3, 5, 0, 255, 255, 150);
}
@CBasePlayerWeapon_DefaultDeploy_Pre(iItem, szViewModel[], szWeaponModel[], iAnim, szAnimExt[], skiplocal)
{
if(!is_nullent(iItem) && get_member(iItem, m_iId) == WEAPON_SMOKEGRENADE)
{
SetHookChainArg(2, ATYPE_STRING, SNOWBALL_MODEL_VIEW);
SetHookChainArg(3, ATYPE_STRING, SNOWBALL_MODEL_PLAYER);
}
}
@CBasePlayer_Radio_Pre(pPlayer, iMsgIndex, sMsgVerbose[], iPitch, bool: bIsShowIcon)
{
SetHookChainReturn(ATYPE_INTEGER, 0);
return HC_SUPERCEDE;
}
@touch_snowball(eEnt, pPlayer)
{
new pOwner;
pOwner = get_entvar(eEnt, var_owner);
if(is_user_connected(pPlayer) && pOwner != pPlayer)
{
if(is_user_connected(pOwner) && rg_is_player_can_takedamage(pOwner, pPlayer))
{
emit_sound(pPlayer, CHAN_STATIC, SNOWBALL_SOUND_FREEZE, VOL_NORM, ATTN_NORM, 0, PITCH_NORM);
ExecuteHamB(Ham_TakeDamage, pPlayer, eEnt, pOwner, float(g_eCvar[CVAR_SWU_DAMAGE_USER]), DMG_FREEZE);
if(g_eCvar[CVAR_SWU_IS_FREEZE_USER])
{
remove_task(pPlayer + TASK_USER_FREEZE);
set_task(float(g_eCvar[CVAR_SWU_FREEZE_TIME_USER]), "@func_reset_user_freeze", pPlayer + TASK_USER_FREEZE);
rg_set_rendering(pPlayer, kRenderFxGlowShell, 0, 255, 255, kRenderNormal, 16);
set_entvar(pPlayer, var_maxspeed, -1.0);
}
}
}
rg_remove_entity(eEnt);
}
@func_hud_info(iTask)
{
if(g_iTimer <= 0)
{
remove_task(iTask);
server_cmd("sv_restart 1");
}else{
new sSeconds[16];
correct_word_seconds(g_iTimer, sSeconds, charsmax(sSeconds));
set_dhudmessage(0, 255, 255, -1.0, 0.04, .holdtime = 0.8);
show_dhudmessage(0, "Зимняя разминка^nДо окончания: %i %s", g_iTimer, sSeconds);
g_iTimer--;
}
}
@func_reset_user_freeze(pPlayer)
{
pPlayer -= TASK_USER_FREEZE;
rg_set_rendering(pPlayer);
rg_reset_maxspeed(pPlayer);
emit_sound(pPlayer, CHAN_STATIC, SNOWBALL_SOUND_UNFREEZE, VOL_NORM, ATTN_NORM, 0, PITCH_NORM);
}
@cvars_attach()
{
bind_pcvar_num(
create_cvar(
"swu_time", "100", FCVAR_SERVER,
.description = "Время разминки (в секундах)",
.has_min = false, .min_val = 0.0,
.has_max = false, .max_val = 0.0
), g_eCvar[CVAR_SWU_TIME]
);
bind_pcvar_num(
create_cvar(
"swu_is_freeze_user", "1", FCVAR_SERVER,
.description = "Замораживать ли игрока при попадании в него снежком?^n1 - да, 0 - нет",
.has_min = true, .min_val = 0.0,
.has_max = true, .max_val = 1.0
), g_eCvar[CVAR_SWU_IS_FREEZE_USER]
);
bind_pcvar_num(
create_cvar(
"swu_freeze_time_user", "3", FCVAR_SERVER,
.description = "На сколько секунд замораживать игрока при попадании в него снежком?",
.has_min = false, .min_val = 0.0,
.has_max = false, .max_val = 0.0
), g_eCvar[CVAR_SWU_FREEZE_TIME_USER]
);
bind_pcvar_num(
create_cvar(
"swu_damage_user", "55", FCVAR_SERVER,
.description = "Сколько урона получает игрок при попадании в него снежком?",
.has_min = false, .min_val = 0.0,
.has_max = false, .max_val = 0.0
), g_eCvar[CVAR_SWU_DAMAGE_USER]
);
bind_pcvar_num(
create_cvar(
"swu_start_health_user", "100", FCVAR_SERVER,
.description = "Изначальное здоровье игрока на разминке",
.has_min = false, .min_val = 0.0,
.has_max = false, .max_val = 0.0
), g_eCvar[CVAR_SWU_START_HEALTH_USER]
);
AutoExecConfig(true);
}
stock get_origin_front(pPlayer, Float: fOriginEnd[3], Float: fDist)
{
static Float: fOriginStart[3];
get_entvar(pPlayer, var_origin, fOriginStart);
get_entvar(pPlayer, var_v_angle, fOriginEnd);
fOriginEnd[0] = 0.0;
engfunc(EngFunc_MakeVectors, fOriginEnd);
global_get(glb_v_forward, fOriginEnd);
xs_vec_mul_scalar(fOriginEnd, fDist, fOriginEnd);
xs_vec_add(fOriginStart, fOriginEnd, fOriginEnd);
engfunc(EngFunc_TraceLine, fOriginStart, fOriginEnd, DONT_IGNORE_MONSTERS, pPlayer, 0);
get_tr2(0, TR_vecEndPos, fOriginEnd);
}
stock get_origin_aiming(pPlayer, Float: fOrigin[3])
{
static Float: fDest[3];
get_entvar(pPlayer, var_origin, fOrigin);
get_entvar(pPlayer, var_view_ofs, fDest);
xs_vec_add(fOrigin, fDest, fOrigin);
get_entvar(pPlayer, var_v_angle, fDest);
engfunc(EngFunc_MakeVectors, fDest);
global_get(glb_v_forward, fDest);
xs_vec_mul_scalar(fDest, 8192.0, fDest);
xs_vec_add(fOrigin, fDest, fDest);
engfunc(EngFunc_TraceLine, fOrigin, fDest, DONT_IGNORE_MONSTERS, pPlayer, 0);
get_tr2(0, TR_vecEndPos, fOrigin);
}
stock rg_remove_entity(eEnt)
{
set_entvar(eEnt, var_nextthink, -1.0);
set_entvar(eEnt, var_flags, FL_KILLME);
}
stock rg_set_rendering(eEnt, fx = kRenderFxNone, r = 255, g = 255, b = 255, render = kRenderNormal, amount = 16)
{
new Float: fRenderColor[3];
fRenderColor[0] = float(r);
fRenderColor[1] = float(g);
fRenderColor[2] = float(b);
set_entvar(eEnt, var_renderfx, fx);
set_entvar(eEnt, var_rendercolor, fRenderColor);
set_entvar(eEnt, var_rendermode, render);
set_entvar(eEnt, var_renderamt, float(amount));
}
stock set_msg_beamfollow(eEnt, iSpriteIndex, iLifeTime, iSize, iRed, iGreen, iBlue, iBrightness)
{
message_begin(MSG_BROADCAST, SVC_TEMPENTITY);
write_byte(TE_BEAMFOLLOW);
write_short(eEnt);
write_short(iSpriteIndex);
write_byte(iLifeTime * 10);
write_byte(iSize);
write_byte(iRed);
write_byte(iGreen);
write_byte(iBlue);
write_byte(iBrightness);
message_end();
}
enum _:
{
Plural,
Singular,
Nominative
};
stock correct_word_seconds(iSeconds, sWords[], iWordsLen)
{
switch(get_numerical_noun_form(iSeconds))
{
case Plural: format(sWords, iWordsLen, "секунд");
case Singular: format(sWords, iWordsLen, "секунды");
case Nominative: format(sWords, iWordsLen, "секунда");
}
}
// https://dev-cs.ru/threads/222/#post-2364
// by hunter
stock get_numerical_noun_form(iNum)
{
if(iNum > 10 && ((iNum % 100) / 10) == 1)
return Plural;
switch (iNum % 10)
{
case 1: return Nominative;
case 2, 3, 4: return Singular;
}
return Plural;
}