340 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			SourcePawn
		
	
	
	
	
	
			
		
		
	
	
			340 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			SourcePawn
		
	
	
	
	
	
| #pragma semicolon 1
 | |
| #pragma newdecls required
 | |
| 
 | |
| #include <sourcemod>
 | |
| #include <sdkhooks>
 | |
| #include <sdktools>
 | |
| #include <dhooks>
 | |
| #include <cstrike>
 | |
| #include <CSSFixes>
 | |
| #include <zombiereloaded>
 | |
| 
 | |
| public Plugin myinfo =
 | |
| {
 | |
| 	name 			= "DamageProxy",
 | |
| 	author 			= "BotoX",
 | |
| 	description 	= "",
 | |
| 	version 		= "0.0",
 | |
| 	url 			= ""
 | |
| };
 | |
| 
 | |
| bool g_bLateLoad = false;
 | |
| 
 | |
| Handle g_hFireBulletDetour;
 | |
| int g_hVelocityModifier;
 | |
| int g_hActiveWeapon;
 | |
| 
 | |
| int g_LastAttacker = 0;
 | |
| int g_LastVictim = 0;
 | |
| 
 | |
| char g_iPhysboxToClient[2048];
 | |
| 
 | |
| int g_iSpecialKnife[MAXPLAYERS + 1];
 | |
| bool g_bNoSlowdown[MAXPLAYERS + 1];
 | |
| bool g_bRestoreHP[MAXPLAYERS + 1];
 | |
| bool g_bFullKnife[MAXPLAYERS + 1];
 | |
| 
 | |
| KeyValues g_Config;
 | |
| 
 | |
| public void OnPluginStart()
 | |
| {
 | |
| 	Handle hGameData = LoadGameConfigFile("DamageProxy.games");
 | |
| 	if(!hGameData)
 | |
| 		SetFailState("Failed to load DamageProxy gamedata.");
 | |
| 
 | |
| 	g_hFireBulletDetour = DHookCreateFromConf(hGameData, "CCSPlayer__FireBullet");
 | |
| 	if(!g_hFireBulletDetour)
 | |
| 		SetFailState("Failed to setup detour for CCSPlayer__FireBullet");
 | |
| 
 | |
| 	delete hGameData;
 | |
| 
 | |
| 	if(!DHookEnableDetour(g_hFireBulletDetour, false, Detour_OnFireBullet))
 | |
| 		SetFailState("Failed to detour CCSPlayer__FireBullet.");
 | |
| 
 | |
| 	g_hVelocityModifier = FindSendPropInfo("CCSPlayer", "m_flVelocityModifier");
 | |
| 	if(g_hVelocityModifier == -1)
 | |
| 		SetFailState("Couldn't find CCSPlayer::m_flVelocityModifier");
 | |
| 
 | |
| 	g_hActiveWeapon = FindSendPropInfo("CBaseCombatCharacter", "m_hActiveWeapon");
 | |
| 	if(g_hActiveWeapon == -1)
 | |
| 		SetFailState("Couldn't find CBaseCombatCharacter::m_hActiveWeapon");
 | |
| 
 | |
| 	PhysboxToClientMap(g_iPhysboxToClient, true);
 | |
| }
 | |
| 
 | |
| public void OnPluginEnd()
 | |
| {
 | |
| 	PhysboxToClientMap(g_iPhysboxToClient, false);
 | |
| }
 | |
| 
 | |
| public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
 | |
| {
 | |
| 	g_bLateLoad = late;
 | |
| 	return APLRes_Success;
 | |
| }
 | |
| 
 | |
| public void OnMapStart()
 | |
| {
 | |
| 	bool bLate = g_bLateLoad;
 | |
| 	g_bLateLoad = false;
 | |
| 
 | |
| 	if(g_Config)
 | |
| 		delete g_Config;
 | |
| 
 | |
| 	char sMapName[PLATFORM_MAX_PATH];
 | |
| 	GetCurrentMap(sMapName, sizeof(sMapName));
 | |
| 
 | |
| 	char sConfigFile[PLATFORM_MAX_PATH];
 | |
| 	BuildPath(Path_SM, sConfigFile, sizeof(sConfigFile), "configs/damageproxy/%s.cfg", sMapName);
 | |
| 	if(!FileExists(sConfigFile))
 | |
| 	{
 | |
| 		LogMessage("Could not find mapconfig: \"%s\"", sConfigFile);
 | |
| 		return;
 | |
| 	}
 | |
| 	LogMessage("Found mapconfig: \"%s\"", sConfigFile);
 | |
| 
 | |
| 	g_Config = new KeyValues("items");
 | |
| 	if(!g_Config.ImportFromFile(sConfigFile))
 | |
| 	{
 | |
| 		delete g_Config;
 | |
| 		LogError("ImportFromFile() failed!");
 | |
| 		return;
 | |
| 	}
 | |
| 	g_Config.Rewind();
 | |
| 
 | |
| 	if(!bLate)
 | |
| 		return;
 | |
| 
 | |
| 	/* Late Load */
 | |
| 	for(int client = 1; client <= MaxClients; client++)
 | |
| 	{
 | |
| 		if(!IsClientInGame(client))
 | |
| 			continue;
 | |
| 
 | |
| 		OnClientPutInServer(client);
 | |
| 
 | |
| 		int iKnife = GetPlayerWeaponSlot(client, CS_SLOT_KNIFE);
 | |
| 		if(iKnife > 0)
 | |
| 			OnWeaponEquipped(client, iKnife);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| public void OnClientPutInServer(int client)
 | |
| {
 | |
| 	g_iSpecialKnife[client] = 0;
 | |
| 	g_bNoSlowdown[client] = false;
 | |
| 	g_bRestoreHP[client] = false;
 | |
| 	g_bFullKnife[client] = false;
 | |
| 
 | |
| 	if(g_Config)
 | |
| 	{
 | |
| 		SDKHook(client, SDKHook_WeaponEquipPost, OnWeaponEquipped);
 | |
| 		SDKHook(client, SDKHook_WeaponDropPost, OnWeaponDropped);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| public void OnWeaponEquipped(int client, int entity)
 | |
| {
 | |
| 	if(!g_Config)
 | |
| 		return;
 | |
| 
 | |
| 	if(!IsValidEntity(entity))
 | |
| 		return;
 | |
| 
 | |
| 	char sClassname[64];
 | |
| 	GetEntityClassname(entity, sClassname, sizeof(sClassname));
 | |
| 	if(strcmp(sClassname, "weapon_knife", false) != 0)
 | |
| 		return;
 | |
| 
 | |
| 	int iHammerID = GetEntProp(entity, Prop_Data, "m_iHammerID");
 | |
| 	if(!iHammerID)
 | |
| 		return;
 | |
| 
 | |
| 	char sHammerID[16] = "z";
 | |
| 	if(ZR_IsClientHuman(client))
 | |
| 		sHammerID[0] = 'h';
 | |
| 	IntToString(iHammerID, sHammerID[1], sizeof(sHammerID) - 1);
 | |
| 
 | |
| 	g_Config.Rewind();
 | |
| 	if(!g_Config.JumpToKey(sHammerID))
 | |
| 		return;
 | |
| 
 | |
| 	float fKnockbackScale = g_Config.GetFloat("KnockbackScale", 1.0);
 | |
| 	ZR_SetClientKnockbackScale(client, fKnockbackScale);
 | |
| 
 | |
| 	float fKnockbackMaxForce = g_Config.GetFloat("KnockbackMaxForce", 0.0);
 | |
| 	if(fKnockbackMaxForce > 0.0)
 | |
| 		ZR_SetClientKnockbackMaxForce(client, fKnockbackMaxForce);
 | |
| 
 | |
| 	float fKnockbackMaxVel = g_Config.GetFloat("KnockbackMaxVel", -1.0);
 | |
| 	if(fKnockbackMaxVel >= 0.0)
 | |
| 		ZR_SetClientKnockbackMaxVelocity(client, fKnockbackMaxVel);
 | |
| 
 | |
| 	g_bNoSlowdown[client] = view_as<bool>(g_Config.GetNum("NoSlowDown", 0));
 | |
| 
 | |
| 	g_bRestoreHP[client] = view_as<bool>(g_Config.GetNum("RestoreHP", 1));
 | |
| 
 | |
| 	g_bFullKnife[client] = view_as<bool>(g_Config.GetNum("FullKnife", 0));
 | |
| 
 | |
| 	g_iSpecialKnife[client] = entity;
 | |
| 
 | |
| 	CheckChildren(client, entity);
 | |
| }
 | |
| 
 | |
| public void OnWeaponDropped(int client, int entity)
 | |
| {
 | |
| 	if(entity == g_iSpecialKnife[client])
 | |
| 	{
 | |
| 		ZR_SetClientKnockbackScale(client, 1.0);
 | |
| 		ZR_SetClientKnockbackMaxForce(client, 0.0);
 | |
| 		ZR_SetClientKnockbackMaxVelocity(client, -1.0);
 | |
| 		g_iSpecialKnife[client] = 0;
 | |
| 		g_bNoSlowdown[client] = false;
 | |
| 		g_bRestoreHP[client] = false;
 | |
| 		g_bFullKnife[client] = false;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| public void OnEntityDestroyed(int entity)
 | |
| {
 | |
| 	if(entity >= 0 && entity < sizeof(g_iPhysboxToClient))
 | |
| 		g_iPhysboxToClient[entity] = 0;
 | |
| }
 | |
| 
 | |
| void CheckChildren(int client, int parent)
 | |
| {
 | |
| 	bool bKillPush = view_as<bool>(g_Config.GetNum("KillPush", 1));
 | |
| 
 | |
| 	int entity = INVALID_ENT_REFERENCE;
 | |
| 	while((entity = FindEntityByClassname(entity, "*")) != INVALID_ENT_REFERENCE)
 | |
| 	{
 | |
| 		if(GetEntPropEnt(entity, Prop_Data, "m_pParent") != parent)
 | |
| 			continue;
 | |
| 
 | |
| 		char sClassname[64];
 | |
| 		GetEntityClassname(entity, sClassname, sizeof(sClassname));
 | |
| 
 | |
| 		char sTargetname[64];
 | |
| 		GetEntPropString(entity, Prop_Data, "m_iName", sTargetname, sizeof(sTargetname));
 | |
| 
 | |
| 		int iTemplateLoc = FindCharInString(sTargetname, '&', true);
 | |
| 		if(iTemplateLoc != -1)
 | |
| 			sTargetname[iTemplateLoc] = 0;
 | |
| 
 | |
| 		// -1 = Never kill
 | |
| 		// 0 = Default
 | |
| 		// 1 = Always kill
 | |
| 		int iKill = 0;
 | |
| 		if(sTargetname[0])
 | |
| 			iKill = g_Config.GetNum(sTargetname, 0);
 | |
| 
 | |
| 		PrintToConsoleAll("%d child: \"%s\" %d (%s) kill:%d", client, sClassname, entity, sTargetname, iKill);
 | |
| 
 | |
| 		if(!strncmp(sClassname, "func_physbox", 12, false) || !strcmp(sClassname, "func_breakable", false))
 | |
| 		{
 | |
| 			g_iPhysboxToClient[entity] = client;
 | |
| 			SDKHook(entity, SDKHook_OnTakeDamage, OnTakeDamage);
 | |
| 
 | |
| 			PrintToConsoleAll("Hooking \"%s\" %d (%s) OnTakeDamage", sClassname, entity, sTargetname);
 | |
| 		}
 | |
| 		else if((bKillPush && !strcmp(sClassname, "trigger_push", false)) || iKill > 0)
 | |
| 		{
 | |
| 			if((bKillPush || iKill > 0) && iKill >= 0)
 | |
| 			{
 | |
| 				AcceptEntityInput(entity, "Kill");
 | |
| 				PrintToConsoleAll("Killing \"%s\" %d (%s)", sClassname, entity, sTargetname);
 | |
| 			}
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			CheckChildren(client, entity);
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3], float angles[3], int &weapon, int &subtype, int &cmdnum, int &tickcount, int &seed, int mouse[2])
 | |
| {
 | |
| 	// because knifes don't call FireBullet we reset it here too
 | |
| 	g_LastAttacker = 0;
 | |
| 	g_LastVictim = 0;
 | |
| }
 | |
| 
 | |
| public MRESReturn Detour_OnFireBullet(int pThis, Handle hReturn, Handle hParams)
 | |
| {
 | |
| 	// need to reset it per fired bullet else only one of the shotgun pellets would be able to hit the same player
 | |
| 	g_LastAttacker = 0;
 | |
| 	g_LastVictim = 0;
 | |
| 	return MRES_Handled;
 | |
| }
 | |
| 
 | |
| public Action OnTakeDamage(int victim, int &attacker, int &inflictor, float &damage, int &damagetype, int &weapon, float damageForce[3], float damagePosition[3], int damagecustom)
 | |
| {
 | |
| 	if(attacker <= 0 || attacker > MAXPLAYERS)
 | |
| 		return Plugin_Continue;
 | |
| 
 | |
| 	int client = g_iPhysboxToClient[victim];
 | |
| 	if(client <= 0)
 | |
| 	{
 | |
| 		PrintToConsoleAll("!!! %d client = %d no physbox !!!", victim, client);
 | |
| 		return Plugin_Continue;
 | |
| 	}
 | |
| 
 | |
| 	int knife = g_iSpecialKnife[client];
 | |
| 	if(knife <= 0)
 | |
| 	{
 | |
| 		PrintToConsoleAll("!!! %d client = %d no knife !!!", victim, client);
 | |
| 		return Plugin_Continue;
 | |
| 	}
 | |
| 
 | |
| 	// fix multiple penetration
 | |
| 	if(g_LastAttacker == attacker && g_LastVictim == client)
 | |
| 		return Plugin_Handled;
 | |
| 
 | |
| 	g_LastAttacker = attacker;
 | |
| 	g_LastVictim = client;
 | |
| 
 | |
| 	int flags = ZR_KNOCKBACK_CUSTOM | ZR_KNOCKBACK_SCALE | ZR_KNOCKBACK_LIMITFORCE | ZR_KNOCKBACK_LIMITVEL;
 | |
| 
 | |
| 	if(g_bFullKnife[client])
 | |
| 	{
 | |
| 		char sWeaponClassname[64];
 | |
| 		if(inflictor == attacker)
 | |
| 		{
 | |
| 			int iWeapon = GetClientActiveWeapon(attacker);
 | |
| 			if(iWeapon > 0)
 | |
| 				GetEdictClassname(iWeapon, sWeaponClassname, sizeof(sWeaponClassname));
 | |
| 		}
 | |
| 		else
 | |
| 			GetEdictClassname(inflictor, sWeaponClassname, sizeof(sWeaponClassname));
 | |
| 
 | |
| 		if(StrEqual(sWeaponClassname, "weapon_knife"))
 | |
| 			flags &= ~(ZR_KNOCKBACK_SCALE | ZR_KNOCKBACK_LIMITFORCE | ZR_KNOCKBACK_LIMITVEL);
 | |
| 	}
 | |
| 
 | |
| 	float flVelocityModifier;
 | |
| 	if(g_bNoSlowdown[client])
 | |
| 		flVelocityModifier = GetEntDataFloat(client, g_hVelocityModifier);
 | |
| 
 | |
| 	int iClientHealth;
 | |
| 	if(g_bRestoreHP[client])
 | |
| 		iClientHealth = GetClientHealth(client);
 | |
| 
 | |
| 	// Don't damage kevlar.
 | |
| 	damagetype |= DMG_RADIATION;
 | |
| 
 | |
| 	SDKHooks_TakeDamage(client, inflictor, attacker, damage, damagetype, weapon, damageForce, damagePosition, flags);
 | |
| 
 | |
| 	if(g_bNoSlowdown[client])
 | |
| 		SetEntDataFloat(client, g_hVelocityModifier, flVelocityModifier, true);
 | |
| 
 | |
| 	if(g_bRestoreHP[client])
 | |
| 		SetEntityHealth(client, iClientHealth);
 | |
| 
 | |
| 	return Plugin_Continue;
 | |
| }
 | |
| 
 | |
| stock int GetClientActiveWeapon(int client)
 | |
| {
 | |
| 	return GetEntDataEnt2(client, g_hActiveWeapon);
 | |
| }
 |