PHP Code:
#pragma semicolon 1
#include <sourcemod>
#include <sdktools>
#include <sdkhooks>
#define PLUGIN_VERSION "1.2"
#define TEAM_INFECTED 3
#define CLASS_CHARGER 6
#define PROP_CAR (1<<0)
#define PROP_CAR_ALARM (1<<1)
#define PROP_CONTAINER (1<<2)
#define PROP_TRUCK (1<<3)
#define PUSH_COUNT "m_iHealth"
new Handle:g_h_CvarChargerPower, Handle:g_h_CvarChargerCarry, Handle:g_h_CvarMessageType, Handle:g_h_CvarObjects, Handle:g_h_CvarPushLimit, Handle:g_h_CvarRemoveObject,
Handle:g_h_CvarChargerDamage,
i_Owner[2048+1]; // to store the entities' owner
public Plugin:myinfo =
{
name = "[L4D2] Super Charge",
author = "DJ_WEST, cravenge",
description = "Provides Chargers To Move Cars With Ability.",
version = PLUGIN_VERSION,
url = "http://amx-x.ru"
};
public OnPluginStart()
{
decl String:s_Game[12], Handle:h_Version;
GetGameFolderName(s_Game, sizeof(s_Game));
if (!StrEqual(s_Game, "left4dead2"))
{
SetFailState("[SM] Plugin Supports L4D2 Only!");
}
LoadTranslations("super_charge-l4d2.phrases");
h_Version = CreateConVar("super_charge-l4d2_version", PLUGIN_VERSION, "Super Charge Version", FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY);
g_h_CvarChargerPower = CreateConVar("super_charge-l4d2", "750.0", "Power Applied To Super Charge", FCVAR_NOTIFY, true, 0.0, true, 5000.0);
g_h_CvarChargerCarry = CreateConVar("super_charge-l4d2_carry", "1", "Enable/Disable Super Charge If Carrying", FCVAR_NOTIFY, true, 0.0, true, 1.0);
g_h_CvarMessageType = CreateConVar("super_charge-l4d2_message_type", "3", "Notification Type: 0=Disable, 1=Chat Text, 2=Hint Box, 3=Instructor Hint", FCVAR_NOTIFY, true, 0.0, true, 3.0);
g_h_CvarObjects = CreateConVar("super_charge-l4d2_objects", "7", "Objects: 1=Cars, 2=Car Alarms, 4=Containers, 8=Truck", FCVAR_NOTIFY, true, 1.0, true, 15.0);
g_h_CvarPushLimit = CreateConVar("super_charge-l4d2_push_limit", "10", "Super Charge Push Limit", FCVAR_NOTIFY, true, 1.0, true, 100.0);
g_h_CvarRemoveObject = CreateConVar("super_charge-l4d2_remove", "30", "Delay Before Charged Objects Disappear", FCVAR_NOTIFY, true, 0.0, true, 100.0);
g_h_CvarChargerDamage = CreateConVar("super_charge-l4d2_damage", "10", "Damage Applied To Charger", FCVAR_NOTIFY, true, 0.0, true, 100.0);
HookEvent("charger_charge_end", OnChargerChargeEnd);
HookEvent("player_spawn", OnPlayerSpawn);
SetConVarString(h_Version, PLUGIN_VERSION);
}
public OnClientPostAdminCheck(client)
{
SDKHook(client, SDKHook_OnTakeDamage, OnTakeDamage); // Gotcha, damage!!
}
public Action:OnTakeDamage(victim, &attacker, &inflictor, &Float:damage, &damageType)
{
if (victim <= 0 || victim > MaxClients || !IsClientInGame(victim) || GetClientTeam(victim) != 2 || !IsPlayerAlive(victim))
{
return Plugin_Continue;
}
if (inflictor <= 0 || !IsValidEntity(inflictor) || !IsValidEdict(inflictor))
{
return Plugin_Continue;
}
decl String:sClass[64];
GetEdictClassname(inflictor, sClass, sizeof(sClass));
if (StrEqual(sClass, "prop_physics") || StrEqual(sClass, "prop_car_alarm"))
{
new charger = CheckForChargers();
if (i_Owner[inflictor] == charger) // since the game never makes the charger the attacker, we force it to do so.
{
new realDmg = RoundToZero(damage); // we convert the damage from float to int.
new Handle:OnPlayerHurt = CreateEvent("player_hurt", true); // because the event we created is not being hooked so it's true, otherwise it's false
SetEventInt(OnPlayerHurt, "userid", GetClientUserId(victim));
SetEventInt(OnPlayerHurt, "attacker", GetClientUserId(charger));
SetEventInt(OnPlayerHurt, "dmg_health", realDmg); // record the damage
SetEventString(OnPlayerHurt, "weapon", inflictor); // make the entities the weapon that the charger used to attack
FireEvent(OnPlayerHurt, false);
new Handle:incapFix = CreateDataPack();
WritePackCell(incapFix, GetClientUserId(victim));
WritePackCell(incapFix, GetClientUserId(charger));
CreateTimer(1.0, CheckForIncaps, incapFix, TIMER_FLAG_NO_MAPCHANGE|TIMER_REPEAT|TIMER_DATA_HNDL_CLOSE); // we create a repeating timer to check the victim's state, if this returns errors, just use CreateDataTimer instead.
return Plugin_Changed;
}
}
return Plugin_Continue;
}
public Action:CheckForIncaps(Handle:timer, Handle:incapFix)
{
ResetPack(incapFix);
new client = GetClientOfUserId(ReadPackCell(incapFix));
if (client <= 0 || !IsClientInGame(client) || GetClientTeam(client) != 2 || !IsPlayerAlive(client) || !IsPlayerIncapped(client))
{
return Plugin_Continue;
}
new attacker = GetClientOfUserId(ReadPackCell(incapFix));
if (attacker <= 0 || !IsClientInGame(attacker) || GetClientTeam(attacker) != TEAM_INFECTED || GetEntProp(attacker, Prop_Send, "m_zombieClass") != CLASS_CHARGER)
{
return Plugin_Continue;
}
new Handle:OnPlayerIncapacitated = CreateEvent("player_incapacitated", true);
SetEventInt(OnPlayerIncapacitated, "userid", GetClientUserId(client));
SetEventInt(OnPlayerIncapacitated, "attacker", GetClientUserId(attacker));
FireEvent(OnPlayerIncapacitated, false);
return Plugin_Stop;
}
CheckForChargers()
{
new count = 0;
for (new i = 1; i <= MaxClients; i++)
{
if (IsClientInGame(i) && GetClientTeam(i) == TEAM_INFECTED && GetEntProp(i, Prop_Send, "m_zombieClass") == CLASS_CHARGER)
{
count++;
}
}
return count;
}
stock bool:IsPlayerIncapped(client)
{
if (GetEntProp(client, Prop_Send, "m_isIncapacitated", 1))
{
return true;
}
return false;
}
public Action:OnChargerChargeEnd(Handle:h_Event, const String:s_Name[], bool:b_DontBroadcast)
{
decl i_UserID, i_Client, Float:f_Origin[3], Float:f_Angles[3], Float:f_EndOrigin[3], Float:f_Velocity[3],
Handle:h_Trace, Handle:h_Pack, i_Target, String:s_ClassName[16], Float:f_Power, String:s_ModelName[64],
i_Type, i_PushCount, i_Health, i_Damage, i_RemoveTime;
i_UserID = GetEventInt(h_Event, "userid");
i_Client = GetClientOfUserId(i_UserID);
if (!i_Client || !IsClientInGame(i_Client) || GetClientTeam(i_Client) != 3 || GetInfectedClass(i_Client) != CLASS_CHARGER)
{
return Plugin_Continue;
}
if (!GetConVarInt(g_h_CvarChargerCarry) && GetEntProp(i_Client, Prop_Send, "m_carryVictim") > 0)
{
return Plugin_Continue;
}
GetClientAbsOrigin(i_Client, f_Origin);
GetClientAbsAngles(i_Client, f_Angles);
f_Origin[2] += 20.0;
h_Trace = TR_TraceRayFilterEx(f_Origin, f_Angles, MASK_ALL, RayType_Infinite, TraceFilterClients, i_Client);
if (TR_DidHit(h_Trace))
{
i_Target = TR_GetEntityIndex(h_Trace);
TR_GetEndPosition(f_EndOrigin, h_Trace);
if (i_Target && IsValidEdict(i_Target) && GetVectorDistance(f_Origin, f_EndOrigin) <= 100.0)
{
if (GetEntityMoveType(i_Target) != MOVETYPE_VPHYSICS)
{
return Plugin_Continue;
}
i_PushCount = GetEntProp(i_Target, Prop_Data, PUSH_COUNT);
if (i_PushCount >= GetConVarInt(g_h_CvarPushLimit))
{
return Plugin_Continue;
}
i_Type = GetConVarInt(g_h_CvarObjects);
GetEdictClassname(i_Target, s_ClassName, sizeof(s_ClassName));
GetEntPropString(i_Target, Prop_Data, "m_ModelName", s_ModelName, sizeof(s_ModelName));
if(StrEqual(s_ClassName, "prop_car_alarm") && !(i_Type & PROP_CAR_ALARM))
{
return Plugin_Continue;
}
else if (StrEqual(s_ClassName, "prop_physics"))
{
if ((StrContains(s_ModelName, "car") != -1 && !(i_Type & PROP_CAR) && !(i_Type & PROP_CAR_ALARM)) || (StrContains(s_ModelName, "dumpster") != -1 && !(i_Type & PROP_CONTAINER)) || (StrContains(s_ModelName, "forklift") != -1 && !(i_Type & PROP_TRUCK)))
{
return Plugin_Continue;
}
}
i_PushCount++;
SetEntProp(i_Target, Prop_Data, PUSH_COUNT, i_PushCount);
GetAngleVectors(f_Angles, f_Velocity, NULL_VECTOR, NULL_VECTOR);
f_Power = GetConVarFloat(g_h_CvarChargerPower);
f_Velocity[0] *= f_Power;
f_Velocity[1] *= f_Power;
f_Velocity[2] *= f_Power;
TeleportEntity(i_Target, NULL_VECTOR, NULL_VECTOR, f_Velocity);
i_Owner[i_Target] = i_Client; // make the charger the owner of the entity.
CreateTimer(3.0, ResetOwner, i_Target); // reset the entity's owner.
h_Pack = CreateDataPack();
WritePackCell(h_Pack, i_Target);
WritePackFloat(h_Pack, f_EndOrigin[0]);
CreateTimer(0.5, CheckEntity, h_Pack);
i_Damage = GetConVarInt(g_h_CvarChargerDamage);
if (i_Damage)
{
i_Health = GetClientHealth(i_Client);
i_Health -= i_Damage;
if (i_Health > 0)
{
SetEntityHealth(i_Client, i_Health);
}
else
{
ForcePlayerSuicide(i_Client);
}
}
i_RemoveTime = GetConVarInt(g_h_CvarRemoveObject);
if (i_RemoveTime)
{
CreateTimer(float(i_RemoveTime), RemoveEntity, i_Target, TIMER_FLAG_NO_MAPCHANGE);
}
}
}
return Plugin_Continue;
}
public Action:ResetOwner(Handle:timer, any:ent)
{
i_Owner[ent] = 0; // no matter if said entity was removed, just reset its owner to prevent bugs.
return Plugin_Stop;
}
public Action:RemoveEntity(Handle:h_Timer, any:i_Ent)
{
if (i_Ent > 0 && IsValidEnt(i_Ent))
{
RemoveEdict(i_Ent);
}
return Plugin_Stop;
}
public bool:TraceFilterClients(i_Entity, i_Mask, any:i_Data)
{
if (i_Entity == i_Data)
{
return false;
}
if (1 <= i_Entity <= MaxClients)
{
return false;
}
return true;
}
public Action:CheckEntity(Handle:h_Timer, Handle:h_Pack)
{
decl i_Ent, Float:f_Origin[3], Float:f_LastOrigin, Handle:h_NewPack;
ResetPack(h_Pack, false);
i_Ent = ReadPackCell(h_Pack);
f_LastOrigin = ReadPackFloat(h_Pack);
CloseHandle(h_Pack);
if (i_Ent > 0 && IsValidEnt(i_Ent))
{
GetEntPropVector(i_Ent, Prop_Data, "m_vecOrigin", f_Origin);
if (f_Origin[0] != f_LastOrigin)
{
h_NewPack = CreateDataPack();
WritePackCell(h_NewPack, i_Ent);
WritePackFloat(h_NewPack, f_Origin[0]);
CreateTimer(0.1, CheckEntity, h_NewPack);
}
else
{
TeleportEntity(i_Ent, NULL_VECTOR, NULL_VECTOR, Float:{0.0, 0.0, 0.0});
}
}
return Plugin_Stop;
}
public Action:OnPlayerSpawn(Handle:h_Event, const String:s_Name[], bool:b_DontBroadcast)
{
decl i_UserID, i_Client, Handle:h_Pack;
i_UserID = GetEventInt(h_Event, "userid");
i_Client = GetClientOfUserId(i_UserID);
if (i_Client && IsClientInGame(i_Client) && GetClientTeam(i_Client) == TEAM_INFECTED && GetInfectedClass(i_Client) == CLASS_CHARGER && !IsFakeClient(i_Client))
{
h_Pack = CreateDataPack();
WritePackCell(h_Pack, i_Client);
WritePackString(h_Pack, "Move objects");
WritePackString(h_Pack, "+attack");
CreateTimer(0.1, DisplayHint, h_Pack);
}
}
public Action:DisplayHint(Handle:h_Timer, Handle:h_Pack)
{
decl i_Client;
ResetPack(h_Pack, false);
i_Client = ReadPackCell(h_Pack);
if (GetConVarInt(g_h_CvarMessageType) == 3 && IsClientInGame(i_Client))
{
ClientCommand(i_Client, "gameinstructor_enable 1");
}
CreateTimer(0.3, DelayDisplayHint, h_Pack);
return Plugin_Stop;
}
public Action:DelayDisplayHint(Handle:h_Timer, Handle:h_Pack)
{
decl i_Client, String:s_LanguageKey[16], String:s_Message[256], String:s_Bind[10];
ResetPack(h_Pack, false);
i_Client = ReadPackCell(h_Pack);
ReadPackString(h_Pack, s_LanguageKey, sizeof(s_LanguageKey));
ReadPackString(h_Pack, s_Bind, sizeof(s_Bind));
CloseHandle(h_Pack);
switch (GetConVarInt(g_h_CvarMessageType))
{
case 1:
{
FormatEx(s_Message, sizeof(s_Message), "\x03[%t]\x01 %t.", "Information", s_LanguageKey);
ReplaceString(s_Message, sizeof(s_Message), "\n", " ");
PrintToChat(i_Client, s_Message);
}
case 2: PrintHintText(i_Client, "%t", s_LanguageKey);
case 3:
{
FormatEx(s_Message, sizeof(s_Message), "%t", s_LanguageKey);
DisplayInstructorHint(i_Client, s_Message, s_Bind);
}
}
return Plugin_Stop;
}
public DisplayInstructorHint(i_Client, String:s_Message[256], String:s_Bind[])
{
decl i_Ent, String:s_TargetName[32], Handle:h_RemovePack;
i_Ent = CreateEntityByName("env_instructor_hint");
FormatEx(s_TargetName, sizeof(s_TargetName), "hint%d", i_Client);
ReplaceString(s_Message, sizeof(s_Message), "\n", " ");
DispatchKeyValue(i_Client, "targetname", s_TargetName);
DispatchKeyValue(i_Ent, "hint_target", s_TargetName);
DispatchKeyValue(i_Ent, "hint_timeout", "5");
DispatchKeyValue(i_Ent, "hint_range", "0.01");
DispatchKeyValue(i_Ent, "hint_color", "255 255 255");
DispatchKeyValue(i_Ent, "hint_icon_onscreen", "use_binding");
DispatchKeyValue(i_Ent, "hint_caption", s_Message);
DispatchKeyValue(i_Ent, "hint_binding", s_Bind);
DispatchSpawn(i_Ent);
AcceptEntityInput(i_Ent, "ShowHint");
h_RemovePack = CreateDataPack();
WritePackCell(h_RemovePack, i_Client);
WritePackCell(h_RemovePack, i_Ent);
CreateTimer(5.0, RemoveInstructorHint, h_RemovePack);
}
public Action:RemoveInstructorHint(Handle:h_Timer, Handle:h_Pack)
{
decl i_Ent, i_Client;
ResetPack(h_Pack, false);
i_Client = ReadPackCell(h_Pack);
i_Ent = ReadPackCell(h_Pack);
CloseHandle(h_Pack);
if (!i_Client || !IsClientInGame(i_Client) || GetClientTeam(i_Client) != TEAM_INFECTED || GetInfectedClass(i_Client) != CLASS_CHARGER)
{
return Plugin_Stop;
}
if (i_Ent > 0 && IsValidEnt(i_Ent))
{
RemoveEdict(i_Ent);
}
ClientCommand(i_Client, "gameinstructor_enable 0");
DispatchKeyValue(i_Client, "targetname", "");
return Plugin_Stop;
}
stock GetInfectedClass(i_Client)
{
return GetEntProp(i_Client, Prop_Send, "m_zombieClass");
}
stock IsValidEnt(i_Ent)
{
return (IsValidEdict(i_Ent) && IsValidEntity(i_Ent));
}