All pastes #2045165 Raw Edit

Stuff

public cpp v1 · immutable
#2045165 ·published 2011-04-11 17:01 UTC
rendered paste body
/* * ============================================================================ * *  Rotoblin * *  File:			rotoblin.survivorexploitfixes.sp *  Type:			Module *  Description:	Fixes some exploits for the suvivor ledge grabs * *  Copyright (C) 2011 pwn <ajparise@gmail.com> * *  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. * *  This program 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 the *  GNU General Public License for more details. * *  You should have received a copy of the GNU General Public License *  along with this program.  If not, see <http://www.gnu.org/licenses/>. * * ============================================================================ */// --------------------//       Private// --------------------enum exploitState{	PLAYER_PULLED,						// 1 or 0 indicating if the player is pulled	PLAYER_PULLED_BY,					// User ID of smoker who caused the pull	PLAYER_HANGING,						// 1 or 0 indicating if the player is hanging	PLAYER_WAS_INCAPPED,				// 1 or 0 indicating if the player was incapped by the pulled ledge hang	HEALTH_SOLID,						// Solid health of the player prior to the ledge (only if they were pulled)	HEALTH_HANGING_OR_RELEASE,			// Incap health of the player when they first went off the ledge OR after tongue has been release	HEALTH_REVIVE_START,				// Incap health of the player on revival	HEALTH_SOLID_REVIVE,				// Calculated solid health the player should have when revived	Float:HEALTH_TEMP_REVIVE,			// Calculated temp health the player should have when revive	HANDLE_REVIVE_HEALTH,				// 1 or 0 indicating if we should handle solid health on revive	HANDLE_REVIVE_TEMP_HEALTH,			// 1 or 0 indicating if we should handle temporary health on revive	Float:HEALTH_BUFFER,				// Health buffer value when the player grabbed the ledge	Float:HEALTH_BUFFER_TIME,			// Health buffer time value when the player grabbed the ledge	Handle:TIMER_HEALTH_TRACKER,		// Handle of pulled and hanging timer (calculates health)	Handle:TIMER_HEALTH_UPDATER			// Handle of health update timer (restores health and bonus)}static 			playerState[MAXPLAYERS+1][exploitState];static			Float:SMOKER_CLAW_DAMAGE				= 3.0;static					g_iDebugChannel					= 0;static	const	String:	DEBUG_CHANNEL_NAME[]			= "SurvivorExploitFixes";// **********************************************//                   Forwards// **********************************************/** * Plugin is starting. * * @noreturn */public _SurvExploitFixes_OnPluginStart(){	HookPublicEvent(EVENT_ONPLUGINENABLE, _SEF_OnPluginEnable);	HookPublicEvent(EVENT_ONPLUGINDISABLE, _SEF_OnPluginDisable);		g_iDebugChannel = DebugAddChannel(DEBUG_CHANNEL_NAME);		DebugPrintToAllEx("Module is now setup");}/** * Plugin is now enabled. * * @noreturn */public _SEF_OnPluginEnable(){	// Smoker events	HookEvent("choke_start", _SEF_ChokeStart_Event);	HookEvent("tongue_grab", _SEF_TongueGrab_Event);	HookEvent("tongue_release", _SEF_TongueRelease_Event);		// Round events	HookEvent("round_start", _SEF_RoundStart_Event);	HookEvent("round_end", _SEF_RoundEnd_Event);		// Player events	HookEvent("heal_success", _SEF_HealSuccess_Event);	HookEvent("player_death", _SEF_PlayerDeath_Event);		HookEvent("player_ledge_grab", _SEF_PlayerLedgeGrab_Event);			HookEvent("revive_begin", _SEF_ReviveBegin_Event);	HookEvent("revive_success", _SEF_ReviveSuccess_Event);		HookEvent("player_bot_replace", _SEF_BotReplacePlayer_Event);	HookEvent("bot_player_replace", _SEF_PlayerReplaceBot_Event);	DebugPrintToAllEx( "Module is now loaded");	}/** * Plugin is now disabled. * * @noreturn */public _SEF_OnPluginDisable(){	// Smoker events	UnhookEvent("choke_start", _SEF_ChokeStart_Event);	UnhookEvent("tongue_grab", _SEF_TongueGrab_Event);	UnhookEvent("tongue_release", _SEF_TongueRelease_Event);		// Round events	UnhookEvent("round_start", _SEF_RoundStart_Event);	UnhookEvent("round_end", _SEF_RoundEnd_Event);		// Player events	UnhookEvent("heal_success", _SEF_HealSuccess_Event);	UnhookEvent("player_death", _SEF_PlayerDeath_Event);		UnhookEvent("player_ledge_grab", _SEF_PlayerLedgeGrab_Event);			UnhookEvent("revive_begin", _SEF_ReviveBegin_Event);	UnhookEvent("revive_success", _SEF_ReviveSuccess_Event);	UnhookEvent("player_bot_replace", _SEF_BotReplacePlayer_Event);	UnhookEvent("bot_player_replace", _SEF_PlayerReplaceBot_Event);			DebugPrintToAllEx( "Module is now unloaded");}// Copy player states between bot - player and player - bot switches public _SEF_BotReplacePlayer_Event(Handle:event, const String:name[], bool:dontBroadcast){		new player = GetClientOfUserId(GetEventInt(event, "player"));	new bot = GetClientOfUserId(GetEventInt(event, "bot"));	CopyPlayerState(player, bot);	}public _SEF_PlayerReplaceBot_Event(Handle:event, const String:name[], bool:dontBroadcast){		new player = GetClientOfUserId(GetEventInt(event, "player"));	new bot = GetClientOfUserId(GetEventInt(event, "bot"));	CopyPlayerState(bot, player);	}public _SEF_TongueGrab_Event(Handle:event, const String:name[], bool:dontBroadcast){	new userid = GetEventInt(event, "victim");	new client = GetClientOfUserId(userid);		new attacker = GetEventInt(event, "attacker");		// Start the monitor	StartPulledHealthMonitor(client, attacker);}public _SEF_TongueRelease_Event(Handle:event, const String:name[], bool:dontBroadcast){	new userid = GetEventInt(event, "victim");	new client = GetClientOfUserId(userid);		// Mark the user as no longer being pulled	playerState[client][PLAYER_PULLED] = 0;		if (playerState[client][PLAYER_HANGING] == 1)	{		// Capture this to calculate additional damage done from hanging				CaptureHangingHealth(client);			}		DebugPrintToAllEx( "Client %i: \"%N\" just released from pull", client, client);}public _SEF_ChokeStart_Event(Handle:event, const String:name[], bool:dontBroadcast){	new userid = GetEventInt(event, "victim");	new client = GetClientOfUserId(userid);		// Mark the user as no longer being pulled	playerState[client][PLAYER_PULLED] = 0;		DebugPrintToAllEx( "Client %i: \"%N\" is being choked", client, client);}public _SEF_PlayerLedgeGrab_Event(Handle:event, const String:name[], bool:dontBroadcast){	// Determine which survivor is on ledge	new userid = GetEventInt(event, "userid");	new client = GetClientOfUserId(userid);		// Mark the user as hanging	playerState[client][PLAYER_HANGING] = 1;		// Capture the current hanging health and temporary health	CaptureHangingHealth(client);	CaptureTemporaryHealth(client);		}public _SEF_HealSuccess_Event(Handle:event, const String:name[], bool:dontBroadcast){	new userid = GetEventInt(event, "subject");	new client = GetClientOfUserId(userid);		ResetPlayerState(client);}public _SEF_ReviveBegin_Event(Handle:event, const String:name[], bool:dontBroadcast){	new userid = GetEventInt(event, "subject");	new client = GetClientOfUserId(userid);		// Capture the current incap health	playerState[client][HEALTH_REVIVE_START] = GetSurvivorPermanentHealth(client);	DebugPrintToAllEx( "Client %i: \"%N\" revive started at %i", client, client, playerState[client][HEALTH_REVIVE_START]);}public _SEF_ReviveSuccess_Event(Handle:event, const String:name[], bool:dontBroadcast){	new userid = GetEventInt(event, "subject");	new client = GetClientOfUserId(userid);	StartSetHealthTimer(client);		DebugPrintToAllEx( "Client %i: \"%N\" revive success", client, client);}public _SEF_RoundStart_Event(Handle:event, const String:name[], bool:dontBroadcast){	DebugPrintToAllEx( "Survivor Exploit Fix Loaded");}public _SEF_PlayerDeath_Event(Handle:event, const String:name[], bool:dontBroadcast){	new userid = GetEventInt(event, "userid");	new client = GetClientOfUserId(userid);		if(client && GetClientTeam(client) != TEAM_INFECTED)		ResetPlayerState(client);}public _SEF_RoundEnd_Event(Handle:event, const String:name[], bool:dontBroadcast){	ResetAllPlayerStates();}/** * Starts the set health timer *  * @param timer			Handle to the timer object * @param client		Client object * @noreturn */StartSetHealthTimer(client){		if (playerState[client][PLAYER_HANGING] == 1)	{		DebugPrintToAllEx("Starting revive timer for %i \"%N\"", client, client);		playerState[client][Handle:TIMER_HEALTH_UPDATER] = CreateTimer(0.01, SetHealthTimer, client);	}}/** * Called after revive success event if a player was hanging *  * @param timer			Handle to the timer object * @param client		Client object * @noreturn */public Action:SetHealthTimer(Handle:timer, any:client){	AdjustPermanentReviveHealth(client);		AdjustTemporaryReviveHealth(client);	ResetPlayerState(client);}/** * Adjusts (if necessary) and Sets permanent health * Adjusts permanent health according to damage taken between tongue release and revive  * Scenario 1 - Player is smoked off the ledge * 		1a) Player was incapacitated * 				- Set health to pre-calculated health + 1 * 				- Issue hurtme command for 1 damage (this is necessary for incaps - otherwise the player will glitch until player is hurt) * 		1b) Player was not incapacitated * 				- Adjust pre-calculated health to account for normal ledge damage * 				- Set health to pre-calculated health + 1 * 				- Issue hurtme command for 1 damage (this is necessary for incaps - otherwise the player will glitch until player is hurt) * @param client		Client to set temporary health for * @noreturn */AdjustPermanentReviveHealth(client){	// Permanent health should be modified due to being pulled off a ledge	if (playerState[client][HANDLE_REVIVE_HEALTH] == 1)	{		// Only modify revive health if player was not incapped		if (playerState[client][PLAYER_WAS_INCAPPED] != 1)		{			// Modify calculated health based on amount of damage taken between tongue release and revive			new ledgeReviveHealth = RoundToFloor(CalculateLedgeReviveHealth(client, float(playerState[client][HEALTH_SOLID_REVIVE])));			playerState[client][HEALTH_SOLID_REVIVE] = ledgeReviveHealth;					}			SetEntityHealth(client, playerState[client][HEALTH_SOLID_REVIVE] + 1); // Note we are adding 1 to enable HurtMe to fire without effecting health		HurtMe(client); // Must send this - otherwise health may be restored improperly if perm health drops to 0 with temp health				DebugPrintToAllEx( "Set survivor health: %i", playerState[client][HEALTH_SOLID_REVIVE]); 	}	else	{		// Could handle temp health ledge hang issue		// Players are not damaged until incap health drops below 300		// Could modify health accordingly if HB is in effect	}	}/** * Adjusts (if necessary) and Sets temporary health * Adjusts temporary health according to damage taken between tongue release and revive  * Scenario 1 - Player fell off ledge with temporary health * 		1a) Permanent health is <= 1 * 				- Set temp health with damage based on normal ledge damage rates with normal decay * 		1b) Permanent health is > 1 * 				- Set original health bonus with normal decay * Scenario 2 - Player was incapacitated while on ledge * 		2a) Player Didn't die from incap * 				- Add 1 to incap count * 				- Set temp health to 31 * 				- Issue hurt me command for 1 damage (this is necessary - otherwise the player will glitch until player is hurt) * 		2b) Player died from incap (NOT HANDLED HERE) * 				- Is handled in PulledHealthMonitor * @param client		Client to set temporary health for * @noreturn */AdjustTemporaryReviveHealth(client){	// Temporary health should be modified due to ledge hange (could be with or without a smoker)		if (playerState[client][HANDLE_REVIVE_TEMP_HEALTH] == 1)	{		// Modifies temporary health based on time between tongue release and revive		if (playerState[client][PLAYER_WAS_INCAPPED] == 0)		{			DebugPrintToAllEx( "Doing temp health without incap - %i", GetClientHealth(client));						// Additional damage to temp health (only if solid health <= 1)			if (GetClientHealth(client) <= 1)			{				playerState[client][Float:HEALTH_TEMP_REVIVE] = CalculateLedgeReviveHealth(client, playerState[client][Float:HEALTH_TEMP_REVIVE]);				SetSurvivorTempHealth(client, playerState[client][Float:HEALTH_TEMP_REVIVE], playerState[client][Float:HEALTH_BUFFER_TIME]);				DebugPrintToAllEx( "Updated HB: %f at %f", playerState[client][Float:HEALTH_TEMP_REVIVE], playerState[client][Float:HEALTH_BUFFER_TIME]);			}			else // Don't damage temp health further, just restore for decay rate			{				SetSurvivorTempHealth(client, playerState[client][Float:HEALTH_BUFFER], playerState[client][Float:HEALTH_BUFFER_TIME]);				DebugPrintToAllEx( "Reset HB: %f at %f", playerState[client][Float:HEALTH_BUFFER], playerState[client][Float:HEALTH_BUFFER_TIME]);						}		}		else // Tick incap count and set revive health		{			new currentIncapCount = GetSurvivorIncapCount(client);			currentIncapCount++;			SetEntProp(client, Prop_Send, "m_currentReviveCount", currentIncapCount);						SetSurvivorTempHealth(client, playerState[client][Float:HEALTH_TEMP_REVIVE], 0.0);					HurtMe(client);						DebugPrintToAllEx( "Incap count: %i | Incapped Health Bonus: %f", currentIncapCount, playerState[client][Float:HEALTH_TEMP_REVIVE]);		}	}	}/** * Calculates damage done to health from hanging * Used to adjust revive health calculated from a ledge pull * * @param client		Client to use for calculations * @return 				Modified health value based on hanging health and starting revive health */Float:CalculateLedgeReviveHealth(client, Float:health){	// Modify calculated health based on amount of damage taken between tongue release and revive	new initialLedgeHealth = playerState[client][HEALTH_HANGING_OR_RELEASE]; 	// Health on tongue release	new reviveLedgeHealth = playerState[client][HEALTH_REVIVE_START];			// Health on revive start		// Calculate percentage of health that should remain	new Float:dmgPercent = float(reviveLedgeHealth) / float(initialLedgeHealth);		new Float:preciseHealth = health * dmgPercent;	DebugPrintToAllEx( "ILH: %i, RLH: %i, CH: %f DP: %f, NH: %f", initialLedgeHealth, reviveLedgeHealth, health, dmgPercent, preciseHealth);		return preciseHealth;}/** * Called on tongue drag event to monitor player's health (before ledge fall) * * @param client		Client whose health should be monitored * @noreturn */StartPulledHealthMonitor(client, attackerId){	ResetPlayerState(client);		playerState[client][PLAYER_PULLED_BY] = attackerId;	playerState[client][PLAYER_PULLED] = 1;			CapturePermanentHealth(client);			playerState[client][Handle:TIMER_HEALTH_TRACKER] = CreateTimer(0.05, PulledHealthMonitor, client, TIMER_FLAG_NO_MAPCHANGE|TIMER_REPEAT);		DebugPrintToAllEx( "Health monitor started for \"%N\"", client);}/** * Monitors health while a survivor is being pulled * Stops when a survivor is incapacitated, dies or is choked * * @param timer			Handle to the timer object. * @param client		Client index of pulled player * @noreturn */public Action:PulledHealthMonitor(Handle:timer, any:client){	new bool:isPulled = playerState[client][PLAYER_PULLED] == 1;	new bool:isIncapped = playerState[client][PLAYER_WAS_INCAPPED] == 1;	new bool:isHanging = playerState[client][PLAYER_HANGING] == 1;		if (!isIncapped && isPulled) // Stop on incap or when pull is over	{		if (!isHanging) // Capture permanent health prior to ledge		{			CapturePermanentHealth(client);			DebugPrintToAllEx( "Logging player health: %i", playerState[client][HEALTH_SOLID]);		}		else // Calculate health while being pulled on a ledge		{			// Handle health on revive			playerState[client][HANDLE_REVIVE_HEALTH] = 1;						new Float:currentHealth = CalculateSmokedHangHealth(client);						if (currentHealth < 1.0) // Player should be incapacitated or dead			{				IncapacitateSurvivor(client);				playerState[client][Handle:TIMER_HEALTH_TRACKER] = INVALID_HANDLE;				return Plugin_Stop;			}		}				return Plugin_Continue;	}	else	{		playerState[client][Handle:TIMER_HEALTH_TRACKER] = INVALID_HANDLE;		return Plugin_Stop;	}}/** * Calculates remaining health of survivor being pulled while hanging on a ledge * Used by the PulledHealthMonitor * * @param client		Client index of pulled player * @return				Total health remaining */Float:CalculateSmokedHangHealth(client){		// Determine total damage done by smoker so far	new Float:startingHangingHealth = float(playerState[client][HEALTH_HANGING_OR_RELEASE]);	new Float:currentHangingHealth = float(GetSurvivorPermanentHealth(client));		new Float:totalDamage = (startingHangingHealth - currentHangingHealth) / SMOKER_CLAW_DAMAGE;		// Estimate current solid health using solid health remaining right before ledge grab		new Float:solidHealth = float(playerState[client][HEALTH_SOLID]);		new Float:newSolidHealth = solidHealth - totalDamage;		// Estimate current temp health	// Only damage temp health if damage exceeds solid health	new Float:tempHealth = GetSurvivorTempHealth(client);	new Float:tempHealthDmg = totalDamage - solidHealth;	new Float:newTempHealth = tempHealth - (tempHealthDmg > 0.0 ? tempHealthDmg : 0.0);		// Capture calculated health	playerState[client][HEALTH_SOLID_REVIVE] = newSolidHealth < 0.0 ? 0 : RoundToFloor(newSolidHealth);	playerState[client][Float:HEALTH_TEMP_REVIVE] = newTempHealth < 0.0 ? 0.0 : newTempHealth;		DebugPrintToAllEx( "TD: %f | TPH: %i | TTH %f | TH: %f", totalDamage, playerState[client][HEALTH_SOLID_REVIVE], playerState[client][Float:HEALTH_TEMP_REVIVE], float(playerState[client][HEALTH_SOLID_REVIVE]) + playerState[client][Float:HEALTH_TEMP_REVIVE]);		// Return total remaining health	return float(playerState[client][HEALTH_SOLID_REVIVE]) + playerState[client][Float:HEALTH_TEMP_REVIVE];}/** * Determines what to do when a player gets incapacitated by a smoker while on a ledge * Scenario 1 - Incap count < 2 * 		-	Set flags to enable plugin to adjust health on revive * 		-	Send message to chat to let everyone know player was incapacitated *  * Scenario 2 - Incap count >= 2 * 		-	Set flags to release player from ledge and make them fall * 		-	Adjust health buffer to 0.0 (fixes remaining buffer incase game thinks they have more) * 		-	Kill player * 		-	Send message to chat to let everyone know player has died *  * @param client		Client index of player */IncapacitateSurvivor(client){		// Get incap count and determine if action needs to be taken	new currentIncapCount = GetSurvivorIncapCount(client);	if (currentIncapCount < 2)	{		currentIncapCount++;				playerState[client][HANDLE_REVIVE_HEALTH] = 1;		playerState[client][HANDLE_REVIVE_TEMP_HEALTH] = 1;		playerState[client][HEALTH_SOLID_REVIVE] = 0;		playerState[client][Float:HEALTH_TEMP_REVIVE] = 31.0;		playerState[client][PLAYER_WAS_INCAPPED] = 1;				new attacker = playerState[client][PLAYER_PULLED_BY];		if (attacker)			 _SEF_SmokerIncappedPlayer_Event(client, attacker);		else			 _SEF_SmokerIncappedPlayer_Event(client, attacker);	}	else	{		DebugPrintToAllEx( "Player %i \"%N\" should be dead", client, client);		SetEntProp(client, Prop_Send, "m_isIncapacitated", 0);		SetEntProp(client, Prop_Send, "m_isHangingFromLedge", 0);		SetEntProp(client, Prop_Send, "m_isFallingFromLedge", 1);		SetEntPropFloat(client, Prop_Send, "m_healthBuffer", 0.0);		ForcePlayerSuicide(client);				new attacker = playerState[client][PLAYER_PULLED_BY];		if (attacker)			CPrintToChatAll("{green}%N {red}killed {green}%N" , attacker, client);		else			CPrintToChatAll("{green}%s {red}killed {green}%N", "Smoker", client);			}}/* Temporary health functions */Float:GetSurvivorTempHealth(client){	new Float:temphp = GetEntPropFloat(client, Prop_Send, "m_healthBuffer") - ((GetGameTime() - GetEntPropFloat(client, Prop_Send, "m_healthBufferTime")) * GetConVarFloat(FindConVar("pain_pills_decay_rate"))) - 1.0;	return Float:temphp > 0.0 ? temphp : 0.0;}/** * Sets a clients temporary health * * @param client			Client index of player to snapshot * @param Float:health		Amount of temporary health * @param Float:startTime	Starting decay time (0.0 defaults to current game time) */SetSurvivorTempHealth(client, Float:health, Float:startTime){	if (startTime <= 0.0)		startTime = GetGameTime();		SetEntPropFloat(client, Prop_Send, "m_healthBuffer", health);	SetEntPropFloat(client, Prop_Send, "m_healthBufferTime", startTime);}/** * Takes a snapshot of a clients temporary health * * @param client		Client index of player to snapshot */CaptureTemporaryHealth(client){	// Capture health buffer	playerState[client][Float:HEALTH_TEMP_REVIVE] = GetSurvivorTempHealth(client);	playerState[client][Float:HEALTH_BUFFER] = GetEntPropFloat(client, Prop_Send, "m_healthBuffer");	playerState[client][Float:HEALTH_BUFFER_TIME] = GetEntPropFloat(client, Prop_Send, "m_healthBufferTime");	DebugPrintToAllEx("Stored temporary health: TH: %f HB: %f | HBT: %f", playerState[client][Float:HEALTH_TEMP_REVIVE], playerState[client][Float:HEALTH_BUFFER], playerState[client][Float:HEALTH_BUFFER_TIME]);}/** * Gets permanent health * * @param client		Client index of player */GetSurvivorPermanentHealth(client){	return GetEntProp(client, Prop_Send, "m_iHealth");}/** * Sets permanent health * * @param client		Client index of player *///SetSurvivorPermanentHealth(client, health)//{// 	health = health > 0 ? health : 0;// 	SetEntProp(client, Prop_Send, "m_iHealth", health);//}/**  * Takes a snapshot of a clients permanent health * * @param client		Client index of player to snapshot */CapturePermanentHealth(client){	playerState[client][HEALTH_SOLID] = GetSurvivorPermanentHealth(client);	return playerState[client][HEALTH_SOLID];}/** * Takes a snapshot of a clients hanging healh * * @param client		Client index of player to snapshot */CaptureHangingHealth(client){	playerState[client][HEALTH_HANGING_OR_RELEASE] = GetSurvivorPermanentHealth(client);	DebugPrintToAllEx("Storing hanging health: %i", playerState[client][HEALTH_HANGING_OR_RELEASE]);}/** * Gets a survivors incap count * * @param client		Client index of player */GetSurvivorIncapCount(client){	return GetEntProp(client, Prop_Send, "m_currentReviveCount");}/** * Resets player state tracked by the plugin * * @param client		Client index of player to reset * @noreturn */ResetPlayerState(client){			playerState[client][PLAYER_PULLED] = 0;	playerState[client][PLAYER_HANGING] = 0;	playerState[client][PLAYER_WAS_INCAPPED] = 0;		playerState[client][HEALTH_SOLID] = 0;	playerState[client][HEALTH_HANGING_OR_RELEASE] = 0;	playerState[client][HEALTH_REVIVE_START] = 0;	playerState[client][HEALTH_SOLID_REVIVE] = -1;	playerState[client][Float:HEALTH_TEMP_REVIVE] = -1.0;		playerState[client][HANDLE_REVIVE_HEALTH] = 0;	playerState[client][HANDLE_REVIVE_TEMP_HEALTH] = 1; // Set to 1 to always handle temp health on revive		playerState[client][Float:HEALTH_BUFFER] = 0.0;	playerState[client][Float:HEALTH_BUFFER_TIME] = 0.0;		DebugPrintToAllEx("Reset player state for client %i \"%N\"", client, client);}/** * Resets all player states tracked by the plugin * * @noreturn */ResetAllPlayerStates(){	for(new i = 0; i < MAXPLAYERS+1; i++)	{		ResetPlayerState(i);	}}/** * Copies player state from one player to another * * @param clientFrom		Client to copy from * @param clientTo			Client to copy to * @noreturn */CopyPlayerState(clientFrom, clientTo){	playerState[clientTo][PLAYER_PULLED] = playerState[clientFrom][PLAYER_PULLED];	playerState[clientTo][PLAYER_PULLED_BY] = playerState[clientFrom][PLAYER_PULLED_BY];	playerState[clientTo][PLAYER_HANGING] = playerState[clientFrom][PLAYER_HANGING];	playerState[clientTo][PLAYER_WAS_INCAPPED] = playerState[clientFrom][PLAYER_WAS_INCAPPED];			playerState[clientTo][HEALTH_SOLID] = playerState[clientFrom][HEALTH_SOLID];	playerState[clientTo][HEALTH_HANGING_OR_RELEASE] = playerState[clientFrom][HEALTH_HANGING_OR_RELEASE];	playerState[clientTo][HEALTH_REVIVE_START] = playerState[clientFrom][HEALTH_REVIVE_START];	playerState[clientTo][HEALTH_SOLID_REVIVE] = playerState[clientFrom][HEALTH_SOLID_REVIVE];	playerState[clientTo][Float:HEALTH_TEMP_REVIVE] = playerState[clientFrom][Float:HEALTH_TEMP_REVIVE];		playerState[clientTo][HANDLE_REVIVE_HEALTH] = playerState[clientFrom][HANDLE_REVIVE_HEALTH];	playerState[clientTo][HANDLE_REVIVE_TEMP_HEALTH] = playerState[clientFrom][HANDLE_REVIVE_TEMP_HEALTH];		playerState[clientTo][Float:HEALTH_BUFFER] = playerState[clientFrom][Float:HEALTH_BUFFER];	playerState[clientTo][Float:HEALTH_BUFFER_TIME] = playerState[clientFrom][Float:HEALTH_BUFFER_TIME];		DebugPrintToAllEx("Copied player state to client %i from client %i", clientFrom, clientTo);	// Restart Timers	if (playerState[clientFrom][PLAYER_PULLED] == 1 && playerState[clientFrom][Handle:TIMER_HEALTH_TRACKER] != INVALID_HANDLE)	{		CloseHandle(playerState[clientFrom][Handle:TIMER_HEALTH_TRACKER]);		playerState[clientFrom][Handle:TIMER_HEALTH_TRACKER] = INVALID_HANDLE;				StartPulledHealthMonitor(clientTo, playerState[clientTo][PLAYER_PULLED_BY]);	}		ResetPlayerState(clientFrom);}/** * Hurt client for 1 damage * Used to fix incaps on revive * * @param clientFrom		Client to copy from * @param clientTo			Client to copy to * @param client			Client index of player * @noreturn */HurtMe(client){	new String:command[] = "hurtme";	new flags = GetCommandFlags(command);		SetCommandFlags(command, flags & ~FCVAR_CHEAT);		FakeClientCommand(client, "hurtme 1");		// restore z_spawn	SetCommandFlags(command, flags);}/** * Wrapper for printing a debug message without having to define channel index * everytime. * * @param format		Formatting rules. * @param ...			Variable number of format parameters. * @noreturn */static DebugPrintToAllEx(const String:format[], any:...){	decl String:buffer[DEBUG_MESSAGE_LENGTH];	VFormat(buffer, sizeof(buffer), format, 2);	DebugPrintToAll(g_iDebugChannel, buffer);} stock _SEF_SmokerIncappedPlayer_Event(client, atttacker){new Handle:event = CreateEvent("player_incapacitated", true);SetEventInt(event, "userid", GetClientUserId(client));SetEventInt(event, "attacker", GetClientUserId(attacker));SetEventInt(event, "attackerentid", 0);SetEventString(event, "weapon", "smoker_claw");FireEvent(event, false);}