diff --git a/FunMode/scripting/FunMode.sp b/FunMode/scripting/FunMode.sp new file mode 100644 index 00000000..20991708 --- /dev/null +++ b/FunMode/scripting/FunMode.sp @@ -0,0 +1,881 @@ +#pragma semicolon 1 + +#include +#include +#include +#include +#include +#include + +#include "TeamManager.inc" +#include "zr_grenade_effects.inc" + +#pragma newdecls required + +#define VOTE_NO "###no###" +#define VOTE_YES "###yes###" + +enum +{ + Hit_Undefined = -1, + Hit_NotAPlayer, + Hit_Teammate, + Hit_Enemy +} + +ArrayList g_hLiveKnives = null; + +ConVar g_cvarKnifeDamage = null; +ConVar g_cvarKnifeDamageHS = null; +ConVar g_cvarKnifeTrail = null; +ConVar g_cvarStartingKnivesMother = null; +ConVar g_cvarStartingKnives = null; +ConVar g_cvarMaxKnives = null; +ConVar g_cvarKnifeRegenTime = null; +ConVar g_cvarVotePercent = null; + +Handle g_hNotifyTimer = null; +Handle g_hKnifeRegenerationTimer[MAXPLAYERS + 1] = { null, ... }; + +bool g_bLoadedLate = false; +bool g_bEnabled = false; +bool g_bInWarmup = false; +bool g_bTeamManagerLoaded = false; +bool g_bIgnoredFirstUpdate[MAXPLAYERS + 1] = { false, ... }; + +int g_iTimeUntilNextKnife[MAXPLAYERS + 1] = { -1, ... }; +int g_iPlayerKnives[MAXPLAYERS + 1] = { 0, ... }; +int g_iKnifeModelIdx = 0; +int g_iLastKnifeDeath = 0; + +public Plugin myinfo = +{ + name = "Fun Mode", + author = "Obus, idea by D()G@N", + description = "", + version = "1.1.2", + url = "" +} + +public APLRes AskPluginLoad2(Handle hThis, bool bLoadedLate, char[] error, int err_max) +{ + g_bLoadedLate = bLoadedLate; + + return APLRes_Success; +} + +public void OnPluginStart() +{ + LoadTranslations("common.phrases"); + LoadTranslations("basevotes.phrases"); + + g_hLiveKnives = new ArrayList(2); + + g_cvarKnifeDamage = CreateConVar("sm_funmode_knifedamage", "10", "How much damage a knife hit shall deal"); + g_cvarKnifeDamageHS = CreateConVar("sm_funmode_knifedamagehs", "20", "How much damage a knife headshot shall deal"); + g_cvarKnifeTrail = CreateConVar("sm_funmode_knifetrail", "1", "Enables knife trails"); + g_cvarStartingKnivesMother = CreateConVar("sm_funmode_startingknivesmother", "3", "How many knives mother zm shall spawn with"); + g_cvarStartingKnives = CreateConVar("sm_funmode_startingknives", "0", "How many knives normal zm shall spawn with"); + g_cvarMaxKnives = CreateConVar("sm_funmode_maxknives", "5", "Maximum number of knives a zm can carry"); + g_cvarKnifeRegenTime = CreateConVar("sm_funmode_regentime", "45", "How long it takes to regenerate 1 knife"); + g_cvarVotePercent = CreateConVar("sm_funmode_votepercent", "0.6", "Percentage of votes requires to enable FunMode"); + + AutoExecConfig(true, "plugin.FunMode"); + + g_cvarMaxKnives.AddChangeHook(ConVarChanged_Max_Regen); + g_cvarKnifeRegenTime.AddChangeHook(ConVarChanged_Max_Regen); + + HookEvent("player_death", EventHook_PlayerDeath, EventHookMode_Pre); + HookEvent("round_start", EventHook_RoundStart, EventHookMode_Post); + + g_hNotifyTimer = CreateTimer(0.25, Timer_UpdateKnives, _, TIMER_REPEAT); + + AddNormalSoundHook(NormalSHook_SmokeImpact); +} + +public void OnAllPluginsLoaded() +{ + g_bTeamManagerLoaded = LibraryExists("TeamManager"); + + if (!g_bLoadedLate) + return; + + if (!g_bTeamManagerLoaded || (g_bTeamManagerLoaded && !TeamManager_InWarmup())) + { + CreateTimer(1.0, Timer_OnWarmupEnd, _, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); + } +} + +public void OnLibraryAdded(const char[] sName) +{ + if (!strcmp(sName, "TeamManager", false)) + g_bTeamManagerLoaded = true; +} + +public void OnLibraryRemoved(const char[] sName) +{ + if (!strcmp(sName, "TeamManager", false)) + g_bTeamManagerLoaded = false; +} + +public void OnPluginEnd() +{ + if (g_hNotifyTimer != null) + delete g_hNotifyTimer; + + for (int i = 1; i <= MaxClients; i++) + { + if (!IsClientInGame(i) || IsFakeClient(i)) + continue; + + OnClientDisconnect(i); + } + + UnhookEvent("player_death", EventHook_PlayerDeath, EventHookMode_Pre); + UnhookEvent("round_start", EventHook_RoundStart, EventHookMode_Post); + + g_cvarMaxKnives.RemoveChangeHook(ConVarChanged_Max_Regen); + g_cvarKnifeRegenTime.RemoveChangeHook(ConVarChanged_Max_Regen); +} + +public Action ZR_OnGrenadeEffect(int client, int grenade) +{ + if (!g_bEnabled) + return Plugin_Continue; + + if (!IsValidClient(client) || !IsValidEntity(grenade)) + return Plugin_Continue; + + if (g_hLiveKnives.FindValue(grenade) != -1) + return Plugin_Stop; + + return Plugin_Continue; +} + +public void OnMapStart() +{ + if (g_bTeamManagerLoaded) + g_bInWarmup = true; + + g_iKnifeModelIdx = PrecacheModel("models/weapons/w_knife_t.mdl"); + + for (int i = 0; i < MAXPLAYERS + 1; i++) + { + g_bIgnoredFirstUpdate[i] = false; + } +} + +public void OnMapEnd() +{ + for (int i = 1; i <= MaxClients; i++) + { + if (g_hKnifeRegenerationTimer[i] != null) + delete g_hKnifeRegenerationTimer[i]; + } +} + +public void OnClientPutInServer(int client) +{ + if (!g_bEnabled) + return; + + if (g_hKnifeRegenerationTimer[client] != null) + delete g_hKnifeRegenerationTimer[client]; + + g_iPlayerKnives[client] = 0; + g_bIgnoredFirstUpdate[client] = false; + + if (g_bInWarmup) + return; + + SDKHook(client, SDKHook_SpawnPost, SDKHookCB_ClientSpawnPost); + + MenuSource iOpenMenu = GetClientMenu(client); + + if (iOpenMenu != MenuSource_None && iOpenMenu == MenuSource_RawPanel) //probably failnadefriday + return; + + Panel hNotifyPanel = new Panel(GetMenuStyleHandle(MenuStyle_Radio)); + hNotifyPanel.DrawItem("Freezenades vs. Throwing Knives Mode is enabled on this map.", ITEMDRAW_RAWLINE); + hNotifyPanel.DrawItem("", ITEMDRAW_SPACER); + hNotifyPanel.DrawItem("There are different classes enabled than usual. Check them out by typing !zclass.", ITEMDRAW_RAWLINE); + hNotifyPanel.DrawItem("Watch out for zombies spawning in between humans!", ITEMDRAW_RAWLINE); + hNotifyPanel.DrawItem("", ITEMDRAW_SPACER); + hNotifyPanel.DrawItem("As a Human you have one Freezenade (Smoke). Use it wisely!", ITEMDRAW_RAWLINE); + hNotifyPanel.DrawItem("", ITEMDRAW_SPACER); + hNotifyPanel.DrawItem("As a Zombie you can throw knives using your knives secondary attack.", ITEMDRAW_RAWLINE); + hNotifyPanel.DrawItem("The maximum amount of throwing knives you can hold at any time is five.", ITEMDRAW_RAWLINE); + hNotifyPanel.DrawItem("You will generate one throwing knife after a certain period of time has passed.", ITEMDRAW_RAWLINE); + hNotifyPanel.DrawItem("", ITEMDRAW_SPACER); + hNotifyPanel.DrawItem("Have fun!", ITEMDRAW_RAWLINE); + hNotifyPanel.DrawItem("1. Got it!", ITEMDRAW_RAWLINE); + hNotifyPanel.SetKeys(1023); + + hNotifyPanel.Send(client, MenuHandler_NotifyPanel, 0); + + delete hNotifyPanel; +} + +public void SDKHookCB_ClientSpawnPost(int client) +{ + RequestFrame(RequestFrame_ClientSpawnPost, client); +} + +public void RequestFrame_ClientSpawnPost(int client) +{ + static int iTickCount = 0; + + if (iTickCount <= 16) + { + iTickCount++; + RequestFrame(RequestFrame_ClientSpawnPost, client); + return; + } + + iTickCount = 0; + + if (!IsClientInGame(client) || GetClientTeam(client) != CS_TEAM_CT) + return; + + int ent = GivePlayerItem(client, "weapon_smokegrenade"); + EquipPlayerWeapon(client, ent); + + //PrintToChatAll("%N spawned in and got a smoke", client); + + char sHumanClassName[64]; + ZRT_GetClientClassSectionName(client, sHumanClassName, sizeof(sHumanClassName)); + if (StrEqual(sHumanClassName, "freeze_human")) + { + int ent = GivePlayerItem(client, "weapon_smokegrenade"); // needs ammo_smokegrenade_max 2 + EquipPlayerWeapon(client, ent); + //PrintToChatAll("%N spawned in as the freeze human and got another smoke", client); + } + +} + +int MenuHandler_NotifyPanel(Menu hMenu, MenuAction iAction, int iParam1, int iParam2) +{ + switch (iAction) + { + case MenuAction_Select, MenuAction_Cancel: + delete hMenu; + } +} + +public void OnClientDisconnect(int client) +{ + if (g_hKnifeRegenerationTimer[client] != null) + delete g_hKnifeRegenerationTimer[client]; + + g_iPlayerKnives[client] = 0; + g_bIgnoredFirstUpdate[client] = false; + SDKUnhook(client, SDKHook_SpawnPost, SDKHookCB_ClientSpawnPost); +} + +public void OnEntityCreated(int entity, const char[] sClassName) +{ + if (!g_bEnabled) + return; + + if (!strncmp(sClassName[7], "knife", 5)) + RequestFrame(RequestFrame_OnEntityCreated_Knife, entity); + + if (!strcmp(sClassName, "smokegrenade_projectile")) + RequestFrame(RequestFrame_OnEntityCreated_SmokeProjectile, entity); +} + +public void RequestFrame_OnEntityCreated_SmokeProjectile(int entity) +{ + if (!IsValidEntity(entity)) + return; + + if (g_hLiveKnives.FindValue(entity) == -1) + return; + + SetEntProp(entity, Prop_Send, "m_CollisionGroup", 0, 4); +} + +public void RequestFrame_OnEntityCreated_Knife(int entity) +{ + if (!IsValidEntity(entity)) + return; + + if (!HasEntProp(entity, Prop_Send, "m_hOwnerEntity")) + return; + + int iOwner = GetEntPropEnt(entity, Prop_Send, "m_hOwnerEntity"); + + if (!IsValidClient(iOwner)) + return; + + g_bIgnoredFirstUpdate[iOwner] = false; +} + +public void OnEntityDestroyed(int entity) +{ + int idx = -1; + + if ((idx = g_hLiveKnives.FindValue(entity)) != -1) + g_hLiveKnives.Erase(idx); +} + +public void ZR_OnClientInfected(int client, int attacker, bool bMotherInfect, bool bRespawnOverride, bool bRespawn) +{ + if (!g_bEnabled) + return; + + if (bMotherInfect) + { + int iCurHealth = GetClientHealth(client); + + SetEntProp(client, Prop_Send, "m_iHealth", iCurHealth * 2); + + g_iPlayerKnives[client] = g_cvarStartingKnivesMother.IntValue; + } + else + { + g_iPlayerKnives[client] = g_cvarStartingKnives.IntValue; + } + + if (g_hKnifeRegenerationTimer[client] != null) + { + delete g_hKnifeRegenerationTimer[client]; + g_hKnifeRegenerationTimer[client] = null; + } + + g_hKnifeRegenerationTimer[client] = CreateTimer(g_cvarKnifeRegenTime.FloatValue, Timer_RegenerateKnives, client, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); + g_iTimeUntilNextKnife[client] = GetTime() + g_cvarKnifeRegenTime.IntValue; +} + +public void TeamManager_WarmupEnd() +{ + if (!g_bInWarmup || g_bEnabled) + return; + + CreateTimer(1.0, Timer_OnWarmupEnd, _, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); +} + +public Action OnPlayerRunCmd(int client, int &buttons) +{ + static float fLastSecondaryAttack[MAXPLAYERS + 1]; + + if (!g_bEnabled) + return Plugin_Continue; + + if (!(buttons & IN_ATTACK2) || !IsValidClient(client) || !ZR_IsClientZombie(client)) + return Plugin_Continue; + + int iKnife = GetPlayerWeaponSlot(client, CS_SLOT_KNIFE); + + if (iKnife <= 0) + return Plugin_Continue; + + char sWeapon[32]; + GetClientWeapon(client, sWeapon, sizeof(sWeapon)); + + if (strncmp(sWeapon[7], "knife", 5)) + return Plugin_Continue; + + float fNextSecondaryAttack = GetEntPropFloat(iKnife, Prop_Send, "m_flNextSecondaryAttack"); + + if (fNextSecondaryAttack == fLastSecondaryAttack[client] || !g_bIgnoredFirstUpdate[client]) + { + g_bIgnoredFirstUpdate[client] = true; + return Plugin_Continue; + } + + fLastSecondaryAttack[client] = fNextSecondaryAttack; + + DispatchKnife(client); + + return Plugin_Continue; +} + +public Action EventHook_PlayerDeath(Event hEvent, const char[] sName, bool bDontBroadcast) +{ + if (!g_bEnabled) + return Plugin_Continue; + + int victim = GetClientOfUserId(hEvent.GetInt("userid")); + char sWeapon[32]; + + hEvent.GetString("weapon", sWeapon, sizeof(sWeapon)); + + if (victim == g_iLastKnifeDeath) + { + g_iLastKnifeDeath = 0; + return Plugin_Handled; + } + + return Plugin_Continue; +} + +public Action EventHook_RoundStart(Event hEvent, const char[] sName, bool bDontBroadcast) +{ + if (g_bInWarmup || !g_bEnabled) + return Plugin_Continue; + + RequestFrame(RequestFrame_OnRoundStart, 16); + + return Plugin_Continue; +} + +public void RequestFrame_OnRoundStart(int iTicksToWait) +{ + static int iTickCount = 0; + + if (iTickCount <= iTicksToWait) + { + iTickCount++; + RequestFrame(RequestFrame_OnRoundStart, 16); + return; + } + + iTickCount = 0; + + for (int i = 1; i <= MaxClients; i++) + { + if (!IsClientInGame(i) || IsFakeClient(i)) + continue; + + if (GetClientTeam(i) != CS_TEAM_CT) + continue; + + //int ent = GivePlayerItem(i, "weapon_smokegrenade"); // is done on post-spawn now + //EquipPlayerWeapon(i, ent); + } +} + +public Action Timer_OnWarmupEnd(Handle hThis) +{ + static int iTimePassed = 0; + static bool bDelayed = false; + + if (iTimePassed++ >= 5) + { + if (IsVoteInProgress()) + { + iTimePassed = 0; + bDelayed = true; + PrintCenterTextAll("FunMode vote delayed, retrying in %d", 5); + return Plugin_Continue; + } + + Menu hFunModeVote = new Menu(MenuHandler_FunModeVote); + hFunModeVote.SetTitle("Enable Freezenades vs Throwing Knives?"); + hFunModeVote.AddItem(VOTE_YES, "Yes"); + hFunModeVote.AddItem(VOTE_NO, "No"); + hFunModeVote.OptionFlags = MENUFLAG_BUTTON_NOVOTE; + hFunModeVote.ExitButton = false; + + hFunModeVote.DisplayVoteToAll(20); + + bDelayed = false; + iTimePassed = 0; + + return Plugin_Stop; + } + + if (!bDelayed) + PrintCenterTextAll("FunMode vote in %d", 6 - iTimePassed); + else + PrintCenterTextAll("FunMode vote delayed, retrying in %d", 6 - iTimePassed); + + return Plugin_Continue; +} + +public int MenuHandler_FunModeVote(Menu menu, MenuAction action, int param1, int param2) //i copypasted it again mom look +{ + if (action == MenuAction_End) + { + delete menu; + } + else if (action == MenuAction_DisplayItem) + { + char display[64]; + menu.GetItem(param2, "", 0, _, display, sizeof(display)); + + if (strcmp(display, VOTE_NO) == 0 || strcmp(display, VOTE_YES) == 0) + { + char buffer[255]; + Format(buffer, sizeof(buffer), "%T", display, param1); + + return RedrawMenuItem(buffer); + } + } + else if (action == MenuAction_VoteCancel && param1 == VoteCancel_NoVotes) + { + PrintToChatAll("[SM] %t", "No Votes Cast"); + } + else if (action == MenuAction_VoteEnd) + { + char item[64], display[64]; + float percent, limit; + int votes, totalVotes; + + GetMenuVoteInfo(param2, votes, totalVotes); + menu.GetItem(param1, item, sizeof(item), _, display, sizeof(display)); + + if (strcmp(item, VOTE_NO) == 0) + { + votes = totalVotes - votes; + } + + limit = g_cvarVotePercent.FloatValue; + percent = float(votes) / float(totalVotes); + + if ((strcmp(item, VOTE_YES) == 0 && FloatCompare(percent, limit) < 0) || strcmp(item, VOTE_NO) == 0) + { + PrintToChatAll("[SM] %t", "Vote Failed", RoundToNearest(100.0 * limit), RoundToNearest(100.0 * percent), totalVotes); + } + else + { + PrintToChatAll("[SM] %t", "Vote Successful", RoundToNearest(100.0 * percent), totalVotes); + + ServerCommand("exec funmodeload"); + + g_bInWarmup = false; + g_bEnabled = true; + + CreateTimer(1.0, Timer_RestartRound, _, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); + + if (strcmp(item, VOTE_NO) == 0 || strcmp(item, VOTE_YES) == 0) + { + strcopy(item, sizeof(item), display); + } + } + } + + return 0; +} + +public Action Timer_RestartRound(Handle hThis) +{ + static int iCountDown = 0; + + if (iCountDown++ >= 3) + { + iCountDown = 0; + float fDelay = 3.0; + CS_TerminateRound(fDelay, CSRoundEnd_GameStart, false); + CreateTimer(2.0, Timer_FireOnClientPutInServer, _, TIMER_FLAG_NO_MAPCHANGE); + return Plugin_Stop; + } + + PrintCenterTextAll("FunMode vote passed, restarting game in: %d", 4 - iCountDown); + + return Plugin_Continue; +} + +public Action Timer_FireOnClientPutInServer(Handle hThis) +{ + for (int i = 1; i <= MaxClients; i++) + { + if (!IsClientInGame(i) || IsFakeClient(i)) + continue; + + int iDefaultClassZM = ZR_GetClassByName("Funmode Zombie 1"); + ZR_SelectClientClass(i, iDefaultClassZM); + + int iDefaultClassHuman = ZR_GetClassByName("Funmode Human 1"); + ZR_SelectClientClass(i, iDefaultClassHuman); + + OnClientPutInServer(i); + } + + return Plugin_Handled; +} + +public Action Timer_UpdateKnives(Handle hThis) +{ + if (!g_bEnabled) + return Plugin_Continue; + + for (int i = 1; i <= MaxClients; i++) + { + if (!IsValidClient(i) || IsFakeClient(i)) + continue; + + if (!ZR_IsClientZombie(i)) + continue; + + if (g_hKnifeRegenerationTimer[i] != null) + PrintHintText(i, "Throwing knives: %d/%d [%d]", g_iPlayerKnives[i], g_cvarMaxKnives.IntValue, g_iTimeUntilNextKnife[i] - GetTime()); + else + PrintHintText(i, "Throwing knives: %d/%d", g_iPlayerKnives[i], g_cvarMaxKnives.IntValue); + + StopSound(i, SNDCHAN_STATIC, "UI/hint.wav"); + } + + return Plugin_Continue; +} + +public Action NormalSHook_SmokeImpact(int clients[MAXPLAYERS], int &numClients, char sample[PLATFORM_MAX_PATH], int &entity, int &channel, float &volume, int &level, int &pitch, int &flags, char soundEntry[PLATFORM_MAX_PATH], int &seed) +{ + if (!g_bEnabled) + return Plugin_Continue; + + int idx = -1; + + if ((idx = g_hLiveKnives.FindValue(entity)) != -1 && !strncmp(sample, "weapons/smokegrenade/grenade_hit", 32)) + { + int iHitType = g_hLiveKnives.Get(idx, 1, false); + + switch (iHitType) + { + case Hit_Undefined, Hit_Teammate: + { + return Plugin_Handled; + } + + case Hit_NotAPlayer: + { + return Plugin_Continue; + } + + case Hit_Enemy: + { + EmitSoundToAll("physics/flesh/flesh_impact_bullet4.wav", entity, channel, level, flags, volume, pitch); + return Plugin_Handled; + } + + default: + { + return Plugin_Continue; //monkaS + } + } + } + + return Plugin_Continue; +} + +void DispatchKnife(int iOwner) +{ + if (g_iPlayerKnives[iOwner] <= 0) + return; + + int iKnife = CreateEntityByName("smokegrenade_projectile"); + + if (iKnife <= 0 || !DispatchSpawn(iKnife)) + return; + + SetEntPropEnt(iKnife, Prop_Send, "m_hOwnerEntity", iOwner); + SetEntPropEnt(iKnife, Prop_Send, "m_hThrower", iOwner); + SetEntProp(iKnife, Prop_Send, "m_iTeamNum", GetClientTeam(iOwner)); + SetEntProp(iKnife, Prop_Send, "m_nModelIndex", g_iKnifeModelIdx); + SetEntPropFloat(iKnife, Prop_Send, "m_flModelScale", 1.0); + SetEntPropFloat(iKnife, Prop_Send, "m_flElasticity", 0.2); + SetEntPropFloat(iKnife, Prop_Data, "m_flGravity", 1.0); + + float vecOrigin[3]; + float vecAngles[3]; + float vecVelocity[3]; + float vecOwnerVelocity[3]; + float vecKnifeSpin[3] = { 2000.0, 0.0, 0.0 }; + + GetClientEyePosition(iOwner, vecOrigin); + GetClientEyeAngles(iOwner, vecAngles); + GetAngleVectors(vecAngles, vecVelocity, NULL_VECTOR, NULL_VECTOR); + ScaleVector(vecVelocity, 2000.0); + GetEntPropVector(iOwner, Prop_Data, "m_vecVelocity", vecOwnerVelocity); + AddVectors(vecVelocity, vecOwnerVelocity, vecVelocity); + SetEntPropVector(iKnife, Prop_Data, "m_vecAngVelocity", vecKnifeSpin); + + SetEntProp(iKnife, Prop_Data, "m_nNextThinkTick", -1); + DispatchKeyValue(iKnife, "OnUser1", "!self,Kill,,10.0,-1"); + AcceptEntityInput(iKnife, "FireUser1"); + + if (g_cvarKnifeTrail.BoolValue) + { + int iColor[4] = { 255, ... }; + TE_SetupBeamFollow(iKnife, PrecacheModel("sprites/bluelaser1.vmt"), 0, 0.5, 8.0, 1.0, 0, iColor); + TE_SendToAll(); + } + + TeleportEntity(iKnife, vecOrigin, vecAngles, vecVelocity); + SDKHook(iKnife, SDKHook_Touch, SDKHookCB_OnKnifeStartTouch); + + g_iPlayerKnives[iOwner]--; + + //PrintToServer("[DispatchKnife] %N threw knife %d | %d remaining", iOwner, iKnife, g_iPlayerKnives[iOwner]); + g_hLiveKnives.Set(g_hLiveKnives.Push(iKnife), Hit_Undefined, 1, false); + + if (g_hKnifeRegenerationTimer[iOwner] == null) + { + g_hKnifeRegenerationTimer[iOwner] = CreateTimer(g_cvarKnifeRegenTime.FloatValue, Timer_RegenerateKnives, iOwner, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); + g_iTimeUntilNextKnife[iOwner] = GetTime() + g_cvarKnifeRegenTime.IntValue; + } +} + +public Action SDKHookCB_OnKnifeStartTouch(int iKnife, int iHitEntity) +{ + if (IsValidClient(iHitEntity)) + { + int iAttacker = GetEntPropEnt(iKnife, Prop_Send, "m_hThrower"); + + if (!IsValidClient(iAttacker)) + { + AcceptEntityInput(iKnife, "Kill"); + return Plugin_Continue; + } + + if (iHitEntity == iAttacker) + return Plugin_Continue; + + if (GetClientTeam(iAttacker) == GetClientTeam(iHitEntity)) + { + int idx = -1; + + if ((idx = g_hLiveKnives.FindValue(iKnife)) != -1) + g_hLiveKnives.Set(idx, Hit_Teammate, 1, false); + + SetEntProp(iKnife, Prop_Send, "m_CollisionGroup", 2, 4); + CreateTimer(0.050, Timer_KillKnife, iKnife, TIMER_FLAG_NO_MAPCHANGE); + return Plugin_Continue; + } + + int idx = -1; + + if ((idx = g_hLiveKnives.FindValue(iKnife)) != -1) + g_hLiveKnives.Set(idx, Hit_Enemy, 1, false); + + int iDamageToDeal = g_cvarKnifeDamage.IntValue; + float vecVictimEyePos[3]; + float vecKnifeOrigin[3]; + float vecAttackerOrigin[3]; + + GetClientEyePosition(iHitEntity, vecVictimEyePos); + GetEntPropVector(iKnife, Prop_Send, "m_vecOrigin", vecKnifeOrigin); + GetClientAbsOrigin(iAttacker, vecAttackerOrigin); + + Handle hDamageMsg = StartMessageOne("Damage", iHitEntity, USERMSG_RELIABLE); + BfWriteByte(hDamageMsg, iDamageToDeal); + BfWriteVecCoord(hDamageMsg, vecAttackerOrigin); + EndMessage(); + + float fDistToEyePos = GetVectorDistance(vecKnifeOrigin, vecVictimEyePos); + bool bHeadShot = fDistToEyePos <= 22.5; //close enough :^) + iDamageToDeal = bHeadShot ? g_cvarKnifeDamageHS.IntValue : iDamageToDeal; + + int iVictimHealth = GetClientHealth(iHitEntity); + + if (iVictimHealth - iDamageToDeal <= 0) + { + g_iLastKnifeDeath = iHitEntity; + + ForcePlayerSuicide(iHitEntity); + + Event hPlayerDeathEvent = CreateEvent("player_death"); + hPlayerDeathEvent.SetInt("userid", GetClientUserId(iHitEntity)); + hPlayerDeathEvent.SetInt("attacker", GetClientUserId(iAttacker)); + hPlayerDeathEvent.SetString("weapon", "throwing_knife"); + hPlayerDeathEvent.SetBool("headshot", bHeadShot); + hPlayerDeathEvent.Fire(); + } + else + { + SetEntProp(iHitEntity, Prop_Send, "m_iHealth", iVictimHealth - iDamageToDeal); + + Event hPlayerHurtEvent = CreateEvent("player_hurt"); + hPlayerHurtEvent.SetInt("userid", GetClientUserId(iHitEntity)); + hPlayerHurtEvent.SetInt("attacker", GetClientUserId(iAttacker)); + hPlayerHurtEvent.SetInt("health", iVictimHealth - iDamageToDeal); + hPlayerHurtEvent.SetInt("armor", GetClientArmor(iHitEntity)); + hPlayerHurtEvent.SetString("weapon", "throwing_knife"); + hPlayerHurtEvent.SetInt("dmg_health", iDamageToDeal); + hPlayerHurtEvent.SetInt("dmg_armor", 0); + hPlayerHurtEvent.SetInt("hitgroup", bHeadShot?1:2); + hPlayerHurtEvent.Fire(); + } + + SetEntPropFloat(iHitEntity, Prop_Send, "m_flStamina", 1000.0); // ¯\_(?)_/¯ + + SetEntProp(iKnife, Prop_Send, "m_CollisionGroup", 2, 4); + CreateTimer(0.050, Timer_KillKnife, iKnife, TIMER_FLAG_NO_MAPCHANGE); + + return Plugin_Continue; + } + + char sClsName[64]; + + GetEntityClassname(iHitEntity, sClsName, sizeof(sClsName)); + + if (!strncmp(sClsName, "trigger_", 8) || !strncmp(sClsName, "func_", 5)) + return Plugin_Continue; + + int idx = -1; + + if ((idx = g_hLiveKnives.FindValue(iKnife)) != -1) + g_hLiveKnives.Set(idx, Hit_NotAPlayer, 1, false); + + SetEntProp(iKnife, Prop_Send, "m_CollisionGroup", 2, 4); + CreateTimer(0.050, Timer_KillKnife, iKnife, TIMER_FLAG_NO_MAPCHANGE); + + return Plugin_Continue; +} + +public Action Timer_KillKnife(Handle hTimer, int iKnife) +{ + if (!IsValidEntity(iKnife)) + return Plugin_Handled; + + AcceptEntityInput(iKnife, "Kill"); + + return Plugin_Handled; +} + +public Action Timer_RegenerateKnives(Handle hTimer, int iOwner) +{ + if (!g_bEnabled) + return Plugin_Continue; + + g_iPlayerKnives[iOwner]++; + + if (g_iPlayerKnives[iOwner] >= g_cvarMaxKnives.IntValue) + { + g_hKnifeRegenerationTimer[iOwner] = null; + return Plugin_Stop; + } + + g_iTimeUntilNextKnife[iOwner] = GetTime() + g_cvarKnifeRegenTime.IntValue; + + return Plugin_Continue; +} + +public void ConVarChanged_Max_Regen(ConVar cvar, const char[] sOldVal, const char[] sNewVal) +{ + if (!g_bEnabled) + return; + + if (cvar == g_cvarMaxKnives) + { + int iNewVal = StringToInt(sNewVal); + + for (int i = 0; i <= MaxClients; i++) + { + if (g_iPlayerKnives[i] <= iNewVal) + continue; + + g_iPlayerKnives[i] = iNewVal; + } + } + else + { + float fNewVal = StringToFloat(sNewVal); + + for (int i = 0; i <= MaxClients; i++) + { + if (g_hKnifeRegenerationTimer[i] == null) + continue; + + delete g_hKnifeRegenerationTimer[i]; + g_hKnifeRegenerationTimer[i] = CreateTimer(fNewVal, Timer_RegenerateKnives, i, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); + g_iTimeUntilNextKnife[i] = GetTime() + g_cvarKnifeRegenTime.IntValue; + } + } +} + +stock bool IsValidClient(int client) +{ + return (client > 0 && client <= MaxClients && IsClientInGame(client) && IsPlayerAlive(client)); +} \ No newline at end of file diff --git a/includes/zr_grenade_effects.inc b/includes/zr_grenade_effects.inc new file mode 100644 index 00000000..3bebf126 --- /dev/null +++ b/includes/zr_grenade_effects.inc @@ -0,0 +1,48 @@ +/** + * Called when a player is about to be freezed by a grenade + * + * @param client The victim index + * @param attacker The client index who threw the grenade + * @param duration The freeze duration, set by reference + * @return Plugin_Changed to apply new values, Plugin_Contninue to allow as is and >= Plugin_Handled to block + */ +forward Action:ZR_OnClientFreeze(client, attacker, &Float:duration); + +/** + * Called when a player has been freezed by a grenade + * + * @param client The victim index + * @param attacker The client index who threw the grenade + * @param duration The freeze duration + * @noreturn + */ +forward ZR_OnClientFreezed(client, attacker, Float:duration); + +/** + * Called when a player is about to be ignited by a grenade + * + * @param client The victim index + * @param attacker The client index who threw the grenade + * @param duration The ignite duration, set by reference + * @return Plugin_Changed to apply new values, Plugin_Contninue to allow as is and >= Plugin_Handled to block + */ +forward Action:ZR_OnClientIgnite(client, attacker, &Float:duration); + +/** + * Called when a player has been ignited by a grenade + * + * @param client The victim index + * @param attacker The client index who threw the grenade + * @param duration The freeze duration + * @noreturn + */ +forward ZR_OnClientIgnited(client, attacker, Float:duration); + +/** + * Called when a grenade will get his effect + * + * @param client Client that throw the grenade + * @param grenade Grenade index + * @return Plugin_Continue to allow as is and Plugin_Handled to block effect in the grenade + */ +forward Action:ZR_OnGrenadeEffect(client, grenade); \ No newline at end of file diff --git a/includes/zr_tools.inc b/includes/zr_tools.inc new file mode 120000 index 00000000..ddb1277f --- /dev/null +++ b/includes/zr_tools.inc @@ -0,0 +1 @@ +../zr_tools/scripting/include/zr_tools.inc \ No newline at end of file