I never spent enough time to figure out a proper way. This is the last I worked on this.
PHP Code:
#include <sourcemod>
#include <sdktools>
#include <sdkhooks>
public Plugin:myinfo =
{
name = "BuyShield",
author = "backwards",
description = "Allows players to buy shields by typing !buyshield.",
version = "1.3",
url = "http://www.steamcommunity.com/id/mypassword"
}
new ShieldCost = 5000
new bool:RequireBuyZone = true;
new bool:ResetShieldHealthPerRound = true;
new bool:PunishTeammatesAttackingShield = true;
new bool:RegiveNewShieldsOnSpawnToPreventEquipBug = true;
Handle BuyStartRoundTimer;
#define MAX_SHIELDS_ALLOWED 128
#define ShieldsGodModeHealth -999999.0
new Shields[MAX_SHIELDS_ALLOWED];
new Float:ShieldsDamage[MAX_SHIELDS_ALLOWED];
new Float:ShieldsDamageOld[MAX_SHIELDS_ALLOWED] = {ShieldsGodModeHealth, ...};
public OnPluginStart()
{
RegConsoleCmd("sm_buyshield", BuyShieldCMD);
HookEvent("round_prestart", Event_RoundPreStart);
HookEvent("weapon_fire", Event_WeaponFire);
ClearAllShieldSlots();
}
/////////////////////////////////////////////////////////
/////Credits to SztangaBiceps for DealDamage function.///
/////////////////////////////////////////////////////////
void DealDamage(nClientVictim, nDamage, nClientAttacker = 0, nDamageType = 0, String:sWeapon[] = "")
{
if(nClientVictim > 0 && IsValidEdict(nClientVictim) && IsClientInGame(nClientVictim) && IsPlayerAlive(nClientVictim) && nDamage > 0)
{
new EntityPointHurt = CreateEntityByName("point_hurt");
if(EntityPointHurt != 0)
{
new String:sDamage[16];
IntToString(nDamage, sDamage, sizeof(sDamage));
new String:sDamageType[32];
IntToString(nDamageType, sDamageType, sizeof(sDamageType));
DispatchKeyValue(nClientVictim, "targetname", "hurtme");
DispatchKeyValue(EntityPointHurt, "DamageTarget", "hurtme");
DispatchKeyValue(EntityPointHurt, "Damage", sDamage);
DispatchKeyValue(EntityPointHurt, "DamageType", sDamageType);
if(!StrEqual(sWeapon, ""))
DispatchKeyValue(EntityPointHurt, "classname", sWeapon);
DispatchSpawn(EntityPointHurt);
AcceptEntityInput(EntityPointHurt, "Hurt", (nClientAttacker != 0) ? nClientAttacker : -1);
DispatchKeyValue(EntityPointHurt, "classname", "point_hurt");
DispatchKeyValue(nClientVictim, "targetname", "donthurtme");
RemoveEdict(EntityPointHurt);
}
}
}
public Event_WeaponFire(Handle:event, const String:name[], bool:dontBroadcast)
{
new Shooter = GetClientOfUserId(GetEventInt(event, "userid"));
for(int i = 0;i<MAX_SHIELDS_ALLOWED;i++)
{
if(Shields[i] != -1 && GetShieldHealth(Shields[i]) > ShieldsDamageOld[i])
{
new Float:diff = FloatAbs(GetShieldHealth(Shields[i])) - FloatAbs(ShieldsDamageOld[i]);
ShieldsDamageOld[i] = GetShieldHealth(Shields[i]);
if(GetClientTeam(Shooter) == GetClientTeam(GetShieldOwner(Shields[i])))
{
PrintToChat(Shooter, "You can't damage your teammates shield.");
if(PunishTeammatesAttackingShield)
DealDamage(Shooter, 10, Shooter, 0, "weapon_shield");
continue;
}
else
{
ShieldsDamage[i] += FloatAbs(diff);
}
if(ShieldsDamage[i] >= GetShieldMaxAllowedHealth())
{
BreakShield(Shields[i]);
FindAndRemoveFromShieldSlot(Shields[i]);
}
}
}
}
int GetShieldMaxAllowedHealth()
{
return FindConVar("sv_shield_hitpoints").IntValue;
}
float GetShieldMaxAllowedHealthf()
{
return float(FindConVar("sv_shield_hitpoints").IntValue);
}
float GetShieldHealth(ent)
{
return GetEntDataFloat(ent, 2832);
}
int GetShieldOwner(ent)
{
return GetEntPropEnt(ent, Prop_Data, "m_hOwner");
}
void MakeShieldUnBreakable(ent)
{
SetEntDataFloat(ent, 2832, ShieldsGodModeHealth, true);
}
void BreakShield(ent)
{
SetEntDataFloat(ent, 2832, GetShieldMaxAllowedHealthf(), true);
}
void GivePlayersNewShields()
{
for(int i = 0;i<MAX_SHIELDS_ALLOWED;i++)
{
if(Shields[i] != -1)
{
new client = GetShieldOwner(Shields[i]);
if(client == -1)
continue;
if(!IsValidEntity(Shields[i]))
continue;
RemovePlayerItem(client, Shields[i]);
RemoveEdict(Shields[i]);
new Shield = GivePlayerItem(client, "weapon_shield");
Shields[i] = Shield;
}
}
}
void ResetAllShieldsHealth()
{
for(int i = 0;i<MAX_SHIELDS_ALLOWED;i++)
{
if(Shields[i] != -1)
{
ShieldsDamage[i] = 0.0;
ShieldsDamageOld[i] = ShieldsGodModeHealth;
}
}
}
void ClearAllShieldSlotsWithNoOwner()
{
for(int i = 0;i<MAX_SHIELDS_ALLOWED;i++)
{
if(Shields[i] != -1)
{
if(GetShieldOwner(Shields[i]) != -1)
continue;
Shields[i] = -1;
ShieldsDamage[i] = 0.0;
ShieldsDamageOld[i] = ShieldsGodModeHealth;
}
}
}
void ClearAllShieldSlots()
{
for(int i = 0;i<MAX_SHIELDS_ALLOWED;i++)
{
Shields[i] = -1;
ShieldsDamage[i] = 0.0;
ShieldsDamageOld[i] = ShieldsGodModeHealth;
}
}
void FindAndRemoveFromShieldSlot(entity)
{
for(int i = 0;i<MAX_SHIELDS_ALLOWED;i++)
{
if(Shields[i] == entity)
{
Shields[i] = -1;
ShieldsDamage[i] = 0.0;
ShieldsDamageOld[i] = ShieldsGodModeHealth;
return;
}
}
}
void FindAndInsertIntoShieldSlot(entity)
{
for(int i = 0;i<MAX_SHIELDS_ALLOWED;i++)
{
if(Shields[i] == -1)
{
Shields[i] = entity;
ShieldsDamage[i] = 0.0;
ShieldsDamageOld[i] = ShieldsGodModeHealth;
return;
}
}
}
public Action BuyShieldCMD(int client, int args)
{
if(RequireBuyZone)
{
new bool:InBuyZone = view_as<bool>(GetEntProp(client, Prop_Send, "m_bInBuyZone"));
if(!InBuyZone)
{
PrintToChat(client, "Sorry You're Not In a Buy Zone.");
return Plugin_Handled;
}
if (BuyStartRoundTimer == null)
{
PrintToChat(client, "The Buy Time Has Expired For This Round.")
return Plugin_Handled;
}
}
new account = GetEntProp(client, Prop_Send, "m_iAccount");
if(account < ShieldCost)
{
PrintToChat(client, "Sorry you don't have $%i to buy the shield.", ShieldCost);
return Plugin_Handled;
}
new weaponIdx = GetPlayerWeaponSlot(client, 11);
if(weaponIdx != -1)
{
if(IsValidEdict(weaponIdx) && IsValidEntity(weaponIdx))
{
decl String:className[128];
GetEntityClassname(weaponIdx, className, sizeof(className));
if(StrEqual("weapon_shield", className))
{
PrintToChat(client, "You are already carrying a shield.");
return Plugin_Handled;
}
}
}
SetEntProp(client, Prop_Send, "m_iAccount", account - ShieldCost);
new Shield = GivePlayerItem(client, "weapon_shield");
PrintToChat(client, "You've bought a shield.");
FindAndInsertIntoShieldSlot(Shield);
MakeShieldUnBreakable(Shield);
new Address:ShieldAddress = GetEntityAddress(Shield);
PrintToChat(client, "Address = 0x%X offset = 0x%X", ShieldAddress, ShieldAddress + Address:0xB10);
//SDKHook(Shield, SDKHook_OnTakeDamage, OnTakeDamage);
//SDKHook(Shield, SDKHook_TraceAttackPost, Event_TrackAttack);
return Plugin_Handled;
}
public Action:Event_TrackAttack(victim, &attacker, &inflictor, &Float:damage, &damagetype, &ammotype, hitbox, hitgroup)
{
PrintToChatAll("%i %i TraceAttack", attacker, inflictor);
}
public Action:OnTakeDamage(victim, &attacker, &inflictor, &Float:damage, &damagetype)
{
PrintToChat(attacker, "leave this alone");
damage = 0.0;
return Plugin_Changed;
}
public Event_RoundPreStart(Handle:event, const String:name[], bool:dontBroadcast)
{
new Float:BuyTime = 45.0;
ConVar cvarBuyTime = FindConVar("mp_buytime");
if(cvarBuyTime != null)
BuyTime = float(cvarBuyTime.IntValue);
if (BuyStartRoundTimer != null)
{
KillTimer(BuyStartRoundTimer);
BuyStartRoundTimer = null;
}
BuyStartRoundTimer = CreateTimer(BuyTime, StopBuying);
ClearAllShieldSlotsWithNoOwner();
if(ResetShieldHealthPerRound)
ResetAllShieldsHealth();
if(RegiveNewShieldsOnSpawnToPreventEquipBug)
GivePlayersNewShields();
}
public Action StopBuying(Handle timer, any client)
{
BuyStartRoundTimer = null;
return Plugin_Stop;
}
The concept is to track all shield entitys that are created. Everytime a player shoots (you would hook pre and post) and check if any of the shield entitys have taken more damage. You make the shield have a really high "damage to break" value by setting it to start with -999999. Then if the last person to shoot was a teammate with the owner of the shield it wouldn't count. You manually add up non teammate's causing damage this way until sv_shield_hitpoints is reached. Then you force the damage to the value of sv_shield_hitpoints to break the shield like normal. This method should work pretty well as i couldn't get any of the entity hooks on damage to work. The Event_WeaponFire I use in this example is 1 bullet too late.