Update bin/includes

This commit is contained in:
Jackz 2023-05-07 08:44:32 -05:00
parent 8c2ca6453c
commit d23503099b
No known key found for this signature in database
GPG key ID: E0BBD94CF657F603
19 changed files with 1115 additions and 431 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -363,7 +363,8 @@ public Action Timer_UpdateGrab(Handle timer, DataPack pack) {
// *** Runs whether in rotation mode or not // *** Runs whether in rotation mode or not
float entNewPos[3]; float entNewPos[3];
GetEntNewPosition(client, entNewPos); int buttons = GetClientButtons(client);
GetEntNewPosition(client, entNewPos, buttons & IN_SPEED == 0);
entNewPos[0] += g_fGrabOffset[client][0]; entNewPos[0] += g_fGrabOffset[client][0];
entNewPos[1] += g_fGrabOffset[client][1]; entNewPos[1] += g_fGrabOffset[client][1];
entNewPos[2] += g_fGrabOffset[client][2]; entNewPos[2] += g_fGrabOffset[client][2];
@ -426,14 +427,14 @@ int GetLookingEntity(int client, TraceEntityFilter filter) {
static float pos[3], ang[3]; static float pos[3], ang[3];
GetClientEyePosition(client, pos); GetClientEyePosition(client, pos);
GetClientEyeAngles(client, ang); GetClientEyeAngles(client, ang);
TR_TraceRayFilter(pos, ang, MASK_OPAQUE, RayType_Infinite, filter, client); TR_TraceRayFilter(pos, ang, MASK_ALL, RayType_Infinite, filter, client);
if(TR_DidHit()) { if(TR_DidHit()) {
return TR_GetEntityIndex(); return TR_GetEntityIndex();
} }
return -1; return -1;
} }
stock bool GetEntNewPosition(int client, float endPos[3]) stock bool GetEntNewPosition(int client, float endPos[3], bool doCollision = true)
{ {
if (client > 0 && client <= MaxClients && IsClientInGame(client)) { if (client > 0 && client <= MaxClients && IsClientInGame(client)) {
float clientEye[3], clientAngle[3], direction[3]; float clientEye[3], clientAngle[3], direction[3];
@ -444,10 +445,12 @@ stock bool GetEntNewPosition(int client, float endPos[3])
ScaleVector(direction, g_fGrabDistance[client]); ScaleVector(direction, g_fGrabDistance[client]);
AddVectors(clientEye, direction, endPos); AddVectors(clientEye, direction, endPos);
if(doCollision) {
TR_TraceRayFilter(clientEye, endPos, MASK_OPAQUE, RayType_EndPoint, TraceRayFilterEnt, client); TR_TraceRayFilter(clientEye, endPos, MASK_OPAQUE, RayType_EndPoint, TraceRayFilterEnt, client);
if (TR_DidHit(INVALID_HANDLE)) { if (TR_DidHit(INVALID_HANDLE)) {
TR_GetEndPosition(endPos); TR_GetEndPosition(endPos);
} }
}
return true; return true;
} }

View file

@ -52,9 +52,9 @@ void SetupsTrollCombos() {
#if defined _behavior_included #if defined _behavior_included
combo.AddTroll("Witch Magnet"); combo.AddTroll("Witch Magnet");
#endif #endif
combo.AddTroll("No Button Touchie", TrollMod_Constant, 17); combo.AddTroll("No Button Touchie", 17, TrollMod_Constant);
combo.AddTroll("Slow Speed", TrollMod_Constant, 2); combo.AddTroll("Slow Speed", 2, TrollMod_Constant);
combo.AddTroll("Instant Commons", TrollMod_Instant, 1); combo.AddTroll("Instant Commons", 1, TrollMod_Instant);
// combo.AddTroll("Swarm", TrollMod_Instant); // combo.AddTroll("Swarm", TrollMod_Instant);
combo.AddTroll("Vomit Player"); combo.AddTroll("Vomit Player");
combo.AddTroll("Dull Melee", .flags=2); combo.AddTroll("Dull Melee", .flags=2);

View file

@ -218,6 +218,35 @@ public Action Event_WeaponReload(int weapon) {
} }
return Plugin_Continue; return Plugin_Continue;
} }
public Action L4D2_CGasCan_EventKilled(int gascan, int &inflictor, int &attacker) {
static int noButtonPressIndex;
if(!noButtonPressIndex) noButtonPressIndex = GetTrollID("No Button Touchie");
if(attacker > 0 && attacker <= MaxClients) {
if(Trolls[noButtonPressIndex].IsActive(attacker)) {
if(Trolls[noButtonPressIndex].activeFlagClients[attacker] & 1) {
return Plugin_Handled;
}
if(Trolls[noButtonPressIndex].activeFlagClients[attacker] & 2) {
L4D_CTerrorPlayer_OnVomitedUpon(attacker, attacker);
}
if(Trolls[noButtonPressIndex].activeFlagClients[attacker] & 4) {
L4D_SetPlayerIncapacitatedState(attacker, true);
}
if(Trolls[noButtonPressIndex].activeFlagClients[attacker] & 8) {
ServerCommand("sm_slay #%d", GetClientUserId(attacker));
}
if(Trolls[noButtonPressIndex].activeFlagClients[attacker] & 16) {
float speed = GetEntPropFloat(attacker, Prop_Send, "m_flLaggedMovementValue");
if(speed > 0.9) speed = 0.80;
speed -= 5.0;
SetEntPropFloat(attacker, Prop_Send, "m_flLaggedMovementValue", speed);
PrintToConsoleAdmins("[FTT] NoButtonTouchie: %N speed is now %f", speed);
}
}
lastButtonUser = attacker;
}
return Plugin_Continue;
}
public Action Event_ButtonPress(const char[] output, int entity, int client, float delay) { public Action Event_ButtonPress(const char[] output, int entity, int client, float delay) {
static int noButtonPressIndex; static int noButtonPressIndex;
if(!noButtonPressIndex) noButtonPressIndex = GetTrollID("No Button Touchie"); if(!noButtonPressIndex) noButtonPressIndex = GetTrollID("No Button Touchie");

View file

@ -740,6 +740,23 @@ stock void HSVToRGBInt(const float vec[3], int out[3]) {
out[2] = RoundToFloor(view_as<float>(out[2])); out[2] = RoundToFloor(view_as<float>(out[2]));
} }
stock bool GetCursorLocation(int client, float outPos[3]) {
float angle[3];
GetClientEyePosition(client, outPos);
GetClientEyeAngles(client, angle);
TR_TraceRayFilter(outPos, angle, MASK_SOLID, RayType_Infinite, Filter_IgnorePlayer, client);
if(TR_DidHit()) {
TR_GetEndPosition(outPos);
return true;
} else {
return false;
}
}
bool Filter_IgnorePlayer(int entity, int mask, int data) {
return entity > 0 && entity != data;
}
// Gets a position from where the cursor is upto distance away (basically <= distance, going against walls) // Gets a position from where the cursor is upto distance away (basically <= distance, going against walls)
stock bool GetCursorLimited(int client, float distance, float endPos[3], TraceEntityFilter filter) stock bool GetCursorLimited(int client, float distance, float endPos[3], TraceEntityFilter filter)
{ {

View file

@ -18,7 +18,7 @@
* Copyright (C) 2017 "Accelerator74" * Copyright (C) 2017 "Accelerator74"
* *
* Left 4 DHooks Direct SourceMod plugin * Left 4 DHooks Direct SourceMod plugin
* Copyright (C) 2022 "SilverShot" / "Silvers" * Copyright (C) 2023 "SilverShot" / "Silvers"
* *
* ============================================================================= * =============================================================================
* *
@ -58,13 +58,13 @@
// Natives: 252 (including 3 for L4D1 only) // Natives: 253 (including 3 for L4D1 only)
// L4D1 = 31 [left4downtown] + 47 [l4d_direct] + 16 [l4d2addresses] + 56 [silvers - mine!] + 4 [anim] = 154 // L4D1 = 31 [left4downtown] + 47 [l4d_direct] + 16 [l4d2addresses] + 58 [silvers - mine!] + 4 [anim] = 156
// L4D2 = 61 [left4downtown] + 59 [l4d_direct] + 31 [l4d2addresses] + 94 [silvers - mine!] + 4 [anim] = 249 // L4D2 = 61 [left4downtown] + 59 [l4d_direct] + 31 [l4d2addresses] + 95 [silvers - mine!] + 4 [anim] = 250
// Forwards: 183 (including 2 for L4D1 only) // Forwards: 188 (including 2 for L4D1 only)
// L4D1 = 129 // L4D1 = 132
// L4D2 = 181 // L4D2 = 186
// Stocks: 168 (L4D1 = 111, L4D2 = 164) // Stocks: 168 (L4D1 = 111, L4D2 = 164)
// left4dhooks_silver 45 stocks (L4D1 = 38, L4D2 = 52) // left4dhooks_silver 45 stocks (L4D1 = 38, L4D2 = 52)
@ -1058,6 +1058,40 @@ forward void L4D_OnEnterGhostState(int client);
*/ */
forward void L4D_OnEnterGhostState_PostHandled(int client); forward void L4D_OnEnterGhostState_PostHandled(int client);
/**
* @brief Called whenever CTerrorPlayer::TakeOverBot(CTerrorPlayer*) is invoked
* @remarks This happens when player is looking to take over a bot
*
* @param client the client that is looking to take over a bot
*
* @return Plugin_Handled to block, Plugin_Continue otherwise
*/
forward Action L4D_OnTakeOverBot(int client);
/**
* @brief Called whenever CTerrorPlayer::TakeOverBot(CTerrorPlayer*) is invoked
* @remarks This happens when player is looking to take over a bot
* @remarks This forward will not trigger if the relative pre-hook forward has been blocked with Plugin_Handled
*
* @param client the client that is looking to take over a bot
* @param success true if the takeover was successful, false otherwise
*
* @noreturn
*/
forward void L4D_OnTakeOverBot_Post(int client, bool success);
/**
* @brief Called whenever CTerrorPlayer::TakeOverBot(CTerrorPlayer*) is invoked
* @remarks This happens when player is looking to take over a bot
* @remarks This forward will ONLY trigger if the relative pre-hook forward has been blocked with Plugin_Handled
*
* @param client the client that is looking to take over a bot
* @param success true if the takeover was successful, false otherwise
*
* @noreturn
*/
forward void L4D_OnTakeOverBot_PostHandled(int client, bool success);
/** /**
* @brief Called whenever CTerrorPlayer::MaterializeFromGhost is invoked * @brief Called whenever CTerrorPlayer::MaterializeFromGhost is invoked
* @remarks Called when a Special Infected spawns out of ghost mode. * @remarks Called when a Special Infected spawns out of ghost mode.
@ -2471,7 +2505,7 @@ forward void L4D_OnGrabWithTongue_PostHandled(int victim, int attacker);
* *
* @return Plugin_Handled to block, Plugin_Continue otherwise * @return Plugin_Handled to block, Plugin_Continue otherwise
*/ */
// L4D2 only. // L4D2 only.
forward Action L4D2_OnJockeyRide(int victim, int attacker); forward Action L4D2_OnJockeyRide(int victim, int attacker);
/** /**
@ -2484,7 +2518,7 @@ forward Action L4D2_OnJockeyRide(int victim, int attacker);
* *
* @noreturn * @noreturn
*/ */
// L4D2 only. // L4D2 only.
forward void L4D2_OnJockeyRide_Post(int victim, int attacker); forward void L4D2_OnJockeyRide_Post(int victim, int attacker);
/** /**
@ -2497,7 +2531,7 @@ forward void L4D2_OnJockeyRide_Post(int victim, int attacker);
* *
* @noreturn * @noreturn
*/ */
// L4D2 only. // L4D2 only.
forward void L4D2_OnJockeyRide_PostHandled(int victim, int attacker); forward void L4D2_OnJockeyRide_PostHandled(int victim, int attacker);
/** /**
@ -2512,7 +2546,7 @@ forward void L4D2_OnJockeyRide_PostHandled(int victim, int attacker);
* *
* @return Plugin_Handled to block, Plugin_Changed to use overwritten values from plugin, Plugin_Continue otherwise * @return Plugin_Handled to block, Plugin_Changed to use overwritten values from plugin, Plugin_Continue otherwise
*/ */
// L4D2 only. // L4D2 only.
forward Action L4D2_OnSlammedSurvivor(int victim, int attacker, bool &bWallSlam, bool &bDeadlyCharge); forward Action L4D2_OnSlammedSurvivor(int victim, int attacker, bool &bWallSlam, bool &bDeadlyCharge);
/** /**
@ -2528,7 +2562,7 @@ forward Action L4D2_OnSlammedSurvivor(int victim, int attacker, bool &bWallSlam,
* *
* @noreturn * @noreturn
*/ */
// L4D2 only. // L4D2 only.
forward void L4D2_OnSlammedSurvivor_Post(int victim, int attacker, bool bWallSlam, bool bDeadlyCharge); forward void L4D2_OnSlammedSurvivor_Post(int victim, int attacker, bool bWallSlam, bool bDeadlyCharge);
/** /**
@ -2544,7 +2578,7 @@ forward void L4D2_OnSlammedSurvivor_Post(int victim, int attacker, bool bWallSla
* *
* @noreturn * @noreturn
*/ */
// L4D2 only. // L4D2 only.
forward void L4D2_OnSlammedSurvivor_PostHandled(int victim, int attacker, bool bWallSlam, bool bDeadlyCharge); forward void L4D2_OnSlammedSurvivor_PostHandled(int victim, int attacker, bool bWallSlam, bool bDeadlyCharge);
/** /**
@ -2556,7 +2590,7 @@ forward void L4D2_OnSlammedSurvivor_PostHandled(int victim, int attacker, bool b
* *
* @return Plugin_Handled to block, Plugin_Continue otherwise * @return Plugin_Handled to block, Plugin_Continue otherwise
*/ */
// L4D2 only. // L4D2 only.
forward Action L4D2_OnStartCarryingVictim(int victim, int attacker); forward Action L4D2_OnStartCarryingVictim(int victim, int attacker);
/** /**
@ -2569,7 +2603,7 @@ forward Action L4D2_OnStartCarryingVictim(int victim, int attacker);
* *
* @noreturn * @noreturn
*/ */
// L4D2 only. // L4D2 only.
forward void L4D2_OnStartCarryingVictim_Post(int victim, int attacker); forward void L4D2_OnStartCarryingVictim_Post(int victim, int attacker);
/** /**
@ -2582,7 +2616,7 @@ forward void L4D2_OnStartCarryingVictim_Post(int victim, int attacker);
* *
* @noreturn * @noreturn
*/ */
// L4D2 only. // L4D2 only.
forward void L4D2_OnStartCarryingVictim_PostHandled(int victim, int attacker); forward void L4D2_OnStartCarryingVictim_PostHandled(int victim, int attacker);
/** /**
@ -2862,7 +2896,7 @@ forward void L4D2_VomitJar_Detonate_PostHandled(int entity, int client);
* *
* @return Plugin_Handled to block allowing damage to an entity, Plugin_Continue otherwise * @return Plugin_Handled to block allowing damage to an entity, Plugin_Continue otherwise
*/ */
// L4D2 only. // L4D2 only.
forward Action L4D2_CInsectSwarm_CanHarm(int acid, int spitter, int entity); forward Action L4D2_CInsectSwarm_CanHarm(int acid, int spitter, int entity);
/** /**
@ -2876,7 +2910,7 @@ forward Action L4D2_CInsectSwarm_CanHarm(int acid, int spitter, int entity);
* *
* @noreturn * @noreturn
*/ */
// L4D2 only. // L4D2 only.
forward void L4D2_CInsectSwarm_CanHarm_Post(int acid, int spitter, int entity); forward void L4D2_CInsectSwarm_CanHarm_Post(int acid, int spitter, int entity);
/** /**
@ -2890,7 +2924,7 @@ forward void L4D2_CInsectSwarm_CanHarm_Post(int acid, int spitter, int entity);
* *
* @noreturn * @noreturn
*/ */
// L4D2 only. // L4D2 only.
forward void L4D2_CInsectSwarm_CanHarm_PostHandled(int acid, int spitter, int entity); forward void L4D2_CInsectSwarm_CanHarm_PostHandled(int acid, int spitter, int entity);
/** /**
@ -2905,6 +2939,19 @@ forward void L4D2_CInsectSwarm_CanHarm_PostHandled(int acid, int spitter, int en
*/ */
forward void L4D_CBreakableProp_Break(int prop, int entity); forward void L4D_CBreakableProp_Break(int prop, int entity);
/**
* @brief Called whenever CGasCan::Event_Killed() is invoked
* @remarks Called when a gascan is broken
*
* @param gascan the gascan entity index
* @param inflictor the inflictor entity index
* @param attacker the attacker entity index
*
* @return Plugin_Handled to block detonating, Plugin_Changed to change any values, Plugin_Continue otherwise
*/
// L4D2 only.
forward Action L4D2_CGasCan_EventKilled(int gascan, int &inflictor, int &attacker);
/** /**
* @brief Called whenever CGasCan::Event_Killed() is invoked * @brief Called whenever CGasCan::Event_Killed() is invoked
* @remarks Called when a gascan is broken * @remarks Called when a gascan is broken
@ -2915,8 +2962,21 @@ forward void L4D_CBreakableProp_Break(int prop, int entity);
* *
* @noreturn * @noreturn
*/ */
// L4D2 only. // L4D2 only.
forward void L4D2_CGasCan_EventKilled(int gascan, int inflictor, int attacker); forward void L4D2_CGasCan_EventKilled_Post(int gascan, int inflictor, int attacker);
/**
* @brief Called whenever CGasCan::Event_Killed() is invoked
* @remarks Called when a gascan is broken
*
* @param gascan the gascan entity index
* @param inflictor the inflictor entity index
* @param attacker the attacker entity index
*
* @noreturn
*/
// L4D2 only.
forward void L4D2_CGasCan_EventKilled_PostHandled(int gascan, int inflictor, int attacker);
/** /**
* @brief Called whenever CGasCan::ShouldStartAction() is invoked * @brief Called whenever CGasCan::ShouldStartAction() is invoked
@ -2928,7 +2988,7 @@ forward void L4D2_CGasCan_EventKilled(int gascan, int inflictor, int attacker);
* *
* @return Plugin_Handled to block adding to Scavenge score (see "Scavenge Score Fix" plugin for more details), Plugin_Continue otherwise * @return Plugin_Handled to block adding to Scavenge score (see "Scavenge Score Fix" plugin for more details), Plugin_Continue otherwise
*/ */
// L4D2 only. // L4D2 only.
forward Action L4D2_CGasCan_ShouldStartAction(int client, int gascan, int nozzle); forward Action L4D2_CGasCan_ShouldStartAction(int client, int gascan, int nozzle);
/** /**
@ -2942,7 +3002,7 @@ forward Action L4D2_CGasCan_ShouldStartAction(int client, int gascan, int nozzle
* *
* @noreturn * @noreturn
*/ */
// L4D2 only. // L4D2 only.
forward void L4D2_CGasCan_ShouldStartAction_Post(int client, int gascan, int nozzle); forward void L4D2_CGasCan_ShouldStartAction_Post(int client, int gascan, int nozzle);
/** /**
@ -2956,7 +3016,7 @@ forward void L4D2_CGasCan_ShouldStartAction_Post(int client, int gascan, int noz
* *
* @noreturn * @noreturn
*/ */
// L4D2 only. // L4D2 only.
forward void L4D2_CGasCan_ShouldStartAction_PostHandled(int client, int gascan, int nozzle); forward void L4D2_CGasCan_ShouldStartAction_PostHandled(int client, int gascan, int nozzle);
/** /**
@ -2969,7 +3029,7 @@ forward void L4D2_CGasCan_ShouldStartAction_PostHandled(int client, int gascan,
* *
* @return Plugin_Handled to block adding to Scavenge score (see "Scavenge Score Fix" plugin for more details), Plugin_Continue otherwise * @return Plugin_Handled to block adding to Scavenge score (see "Scavenge Score Fix" plugin for more details), Plugin_Continue otherwise
*/ */
// L4D2 only. // L4D2 only.
forward Action L4D2_CGasCan_ActionComplete(int client, int gascan, int nozzle); forward Action L4D2_CGasCan_ActionComplete(int client, int gascan, int nozzle);
/** /**
@ -2983,7 +3043,7 @@ forward Action L4D2_CGasCan_ActionComplete(int client, int gascan, int nozzle);
* *
* @noreturn * @noreturn
*/ */
// L4D2 only. // L4D2 only.
forward void L4D2_CGasCan_ActionComplete_Post(int client, int gascan, int nozzle); forward void L4D2_CGasCan_ActionComplete_Post(int client, int gascan, int nozzle);
/** /**
@ -2997,7 +3057,7 @@ forward void L4D2_CGasCan_ActionComplete_Post(int client, int gascan, int nozzle
* *
* @noreturn * @noreturn
*/ */
// L4D2 only. // L4D2 only.
forward void L4D2_CGasCan_ActionComplete_PostHandled(int client, int gascan, int nozzle); forward void L4D2_CGasCan_ActionComplete_PostHandled(int client, int gascan, int nozzle);
/** /**
@ -3028,7 +3088,7 @@ forward void L4D_OnServerHibernationUpdate(bool hibernating);
* *
* @return Plugin_Handled to let the client through with addons, Plugin_Continue otherwise. * @return Plugin_Handled to let the client through with addons, Plugin_Continue otherwise.
*/ */
// L4D2 only. // L4D2 only.
forward Action L4D2_OnClientDisableAddons(const char[] SteamID); forward Action L4D2_OnClientDisableAddons(const char[] SteamID);
/** /**
@ -3358,7 +3418,7 @@ native any L4D_GetNearestNavArea(const float vecPos[3], float maxDist = 300.0, b
* *
* @param client The client to check * @param client The client to check
* *
* @return The nav area adress or 0 on fail * @return The nav area address or 0 on fail
*/ */
native any L4D_GetLastKnownArea(int client); native any L4D_GetLastKnownArea(int client);
@ -3412,6 +3472,15 @@ native void L4D_FindRandomSpot(int NavArea, float vecPos[3]);
*/ */
native bool L4D2_IsVisibleToPlayer(int client, int team, int team_target, int NavArea, float vecPos[3]); native bool L4D2_IsVisibleToPlayer(int client, int team, int team_target, int NavArea, float vecPos[3]);
/**
* @brief Teleports a player to a valid position if they are stuck.
*
* @param client Client to perform the action
*
* @noreturn
*/
native void L4D_WarpToValidPositionIfStuck(int client);
/** /**
* @brief Returns true when any survivor has left the starting area and true in Versus when the saferoom door automatically opens. * @brief Returns true when any survivor has left the starting area and true in Versus when the saferoom door automatically opens.
* *
@ -3516,11 +3585,10 @@ native bool L4D2_IsReachable(int client, const float vecPos[3]);
* *
* @param startArea NavArea address * @param startArea NavArea address
* @param endArea NavArea address * @param endArea NavArea address
* @param ignoreNavBlockers Bool to ignore blocked areas while checking (does not seem to work as expected) * @param ignoreNavBlockers Bool to ignore blocked areas while checking (does not seem to work as expected) (ignored in L4D1)
* *
* @return Distance between the areas, 0.0 if the same area or -1.0 on failure. * @return Distance between the areas, 0.0 if the same area or -1.0 on failure.
*/ */
// L4D2 only.
native float L4D2_NavAreaTravelDistance(float startPos[3], float endPos[3], bool ignoreNavBlockers); native float L4D2_NavAreaTravelDistance(float startPos[3], float endPos[3], bool ignoreNavBlockers);
/** /**
@ -3738,7 +3806,7 @@ native bool L4D_GoAwayFromKeyboard(int client);
* *
* @return True or false * @return True or false
*/ */
// L4D2 only. // L4D2 only.
native bool L4D2_AreWanderersAllowed(); native bool L4D2_AreWanderersAllowed();
/** /**
@ -4330,6 +4398,7 @@ native int L4D_GetMaxChapters();
/** /**
* @brief Returns all TheNavAreas addresses * @brief Returns all TheNavAreas addresses
* @remarks Can only be called 1 frame after OnMapStart at the earliest otherwise the addresses are invalid
* *
*param aList The ArrayList to store all nav area addresses. *param aList The ArrayList to store all nav area addresses.
* *
@ -4339,6 +4408,7 @@ native void L4D_GetAllNavAreas(ArrayList aList);
/** /**
* @brief Returns a given NavArea's ID from it's address * @brief Returns a given NavArea's ID from it's address
* @remarks Can only be called 1 frame after OnMapStart at the earliest otherwise the addresses are invalid
* *
*param area The NavArea address *param area The NavArea address
* *
@ -4348,6 +4418,7 @@ native int L4D_GetNavAreaID(Address area);
/** /**
* @brief Returns a given NavArea address from it's ID * @brief Returns a given NavArea address from it's ID
* @remarks Can only be called 1 frame after OnMapStart at the earliest otherwise the addresses are invalid
* *
*param id The NavArea ID *param id The NavArea ID
* *
@ -4357,6 +4428,7 @@ native Address L4D_GetNavAreaByID(int id);
/** /**
* @brief Returns origin of a given NavArea * @brief Returns origin of a given NavArea
* @remarks Can only be called 1 frame after OnMapStart at the earliest otherwise the addresses are invalid
* *
*param area The address of the NavArea to read. *param area The address of the NavArea to read.
*param vecPos The vector to store the position read. *param vecPos The vector to store the position read.
@ -4367,6 +4439,7 @@ native void L4D_GetNavAreaPos(Address area, float vecPos[3]);
/** /**
* @brief Returns size of a given NavArea * @brief Returns size of a given NavArea
* @remarks Can only be called 1 frame after OnMapStart at the earliest otherwise the addresses are invalid
* *
*param area The address of the NavArea to read. *param area The address of the NavArea to read.
*param vecPos The vector to store the size read. *param vecPos The vector to store the size read.
@ -4378,6 +4451,7 @@ native void L4D_GetNavAreaSize(Address area, float vecSize[3]);
/** /**
* @brief Returns the nav area attribute flags * @brief Returns the nav area attribute flags
* @remarks See the "NAV_BASE_*" near the top of the include file * @remarks See the "NAV_BASE_*" near the top of the include file
* @remarks Can only be called 1 frame after OnMapStart at the earliest otherwise the addresses are invalid
* *
*param pTerrorNavArea Pointer to a NavArea *param pTerrorNavArea Pointer to a NavArea
* *
@ -4388,6 +4462,7 @@ native int L4D_GetNavArea_AttributeFlags(Address pTerrorNavArea);
/** /**
* @brief Sets the nav area attribute flags * @brief Sets the nav area attribute flags
* @remarks See the "NAV_BASE_*" near the top of the include file * @remarks See the "NAV_BASE_*" near the top of the include file
* @remarks Can only be called 1 frame after OnMapStart at the earliest otherwise the addresses are invalid
* *
*param pTerrorNavArea Pointer to a NavArea *param pTerrorNavArea Pointer to a NavArea
*param flags Attribute flags to set *param flags Attribute flags to set
@ -4399,6 +4474,7 @@ native void L4D_SetNavArea_AttributeFlags(Address pTerrorNavArea, int flags);
/** /**
* @brief Returns the terror nav area attribute flags * @brief Returns the terror nav area attribute flags
* @remarks See the "NAV_SPAWN_*" near the top of the include file * @remarks See the "NAV_SPAWN_*" near the top of the include file
* @remarks Can only be called 1 frame after OnMapStart at the earliest otherwise the addresses are invalid
* *
*param pTerrorNavArea Pointer to a TerrorNavArea *param pTerrorNavArea Pointer to a TerrorNavArea
* *
@ -4409,6 +4485,7 @@ native int L4D_GetNavArea_SpawnAttributes(Address pTerrorNavArea);
/** /**
* @brief Sets the terror nav area attribute flags * @brief Sets the terror nav area attribute flags
* @remarks See the "NAV_SPAWN_*" near the top of the include file * @remarks See the "NAV_SPAWN_*" near the top of the include file
* @remarks Can only be called 1 frame after OnMapStart at the earliest otherwise the addresses are invalid
* *
*param pTerrorNavArea Pointer to a TerrorNavArea *param pTerrorNavArea Pointer to a TerrorNavArea
*param flags Attribute flags to set *param flags Attribute flags to set

View file

@ -27,7 +27,7 @@
#tryinclude <left4dhooks_silver> #tryinclude <left4dhooks_silver>
#tryinclude <left4dhooks_stocks> #tryinclude <left4dhooks_stocks>
#define LUX_LIBRARY_VERSION "0.5.5" #define LUX_LIBRARY_VERSION "0.5.7"
#define GIMMEDATA "lux_library" #define GIMMEDATA "lux_library"
@ -395,16 +395,57 @@ stock float Terror_GetAdrenalineTime(int iClient)
} }
/** /**
* Create a physics explosion that does not affect players and don't check for line of sight and no force fall-off. * Calls CTerrorPlayer::OnRevivedByDefibrillator()
*
* @param iRevivee Client index be revived.
* @param iReviver Client index who revived can be same as revivee.
* @param iDeathModel Death model index, dead survivor model (survivor_death_model).
*
* @return True if revive was successful false otherwise.
* @error Invalid entity index or invalid attachment name,
* signature for function not found, or SDKCall failed.
**/
stock bool Terror_ReviveDeathModel(int iRevivee, int iReviver, int iDeathModel)
{
static Handle hSDKCall;
if(hSDKCall == null)
{
Handle hGamedata;
GetGameData(hGamedata);
StartPrepSDKCall(SDKCall_Player);
if(PrepSDKCall_SetFromConf(hGamedata, SDKConf_Signature, "CTerrorPlayer::OnRevivedByDefibrillator"))
{
PrepSDKCall_AddParameter(SDKType_CBasePlayer, SDKPass_Pointer);
PrepSDKCall_AddParameter(SDKType_CBaseEntity, SDKPass_Pointer);
hSDKCall = EndPrepSDKCall();
if(hSDKCall == null)
LogError("Unable to prep 'CTerrorPlayer::OnRevivedByDefibrillator'");
}
else
{
LogError("Error finding the 'CTerrorPlayer::OnRevivedByDefibrillator' signature.");
}
delete hGamedata;
if(hSDKCall == null)
return false;
}
SDKCall(hSDKCall, iRevivee, iReviver, iDeathModel);
return true;
}
/**
* Create a physics explosion that does not affect players.
* *
* @param vecOrigin Origin of the explosion. * @param vecOrigin Origin of the explosion.
* @param iMagnitude Magnitude of the explosion. * @param iMagnitude Magnitude of the explosion limit of 100, explode more than once for more power.
* @param flRadius Radius of the explosion. * @param flRadius Radius of the explosion.
* @param bDamage True to damage props, false otherwise. * @param bDamage True to damage props, false otherwise.
* @param flInnerRadius If not zero, the LOS is calculated from a point intersecting this sphere.
* *
* @error Failed to create explosion. * @error Failed to create explosion.
**/ **/
stock void PhysicsExplode(float vecOrigin[3], int iMagnitude, float flRadius, bool bDamage=false) stock void PhysicsExplode(float vecOrigin[3], int iMagnitude, float flRadius, bool bDamage=false, float flInnerRadius=0.0)
{ {
static int iBoom = INVALID_ENT_REFERENCE; static int iBoom = INVALID_ENT_REFERENCE;
@ -420,11 +461,11 @@ stock void PhysicsExplode(float vecOrigin[3], int iMagnitude, float flRadius, bo
if(bDamage) if(bDamage)
{ {
DispatchKeyValue(iBoom, "spawnflags", "0"); DispatchKeyValue(iBoom, "spawnflags", "8");
} }
else else
{ {
DispatchKeyValue(iBoom, "spawnflags", "1"); DispatchKeyValue(iBoom, "spawnflags", "9");
} }
char sBuf[32]; char sBuf[32];
@ -433,7 +474,7 @@ stock void PhysicsExplode(float vecOrigin[3], int iMagnitude, float flRadius, bo
IntToString(RoundFloat(flRadius), sBuf, sizeof(sBuf)); IntToString(RoundFloat(flRadius), sBuf, sizeof(sBuf));
DispatchKeyValue(iBoom, "radius", sBuf); DispatchKeyValue(iBoom, "radius", sBuf);
DispatchKeyValueFloat(iBoom, "inner_radius", flRadius); DispatchKeyValueFloat(iBoom, "inner_radius", flInnerRadius);
TeleportEntity(iBoom, vecOrigin, NULL_VECTOR, NULL_VECTOR); TeleportEntity(iBoom, vecOrigin, NULL_VECTOR, NULL_VECTOR);
@ -523,15 +564,8 @@ stock void TE_SetupPhysicsProp(float vecModelOrigin[3],
* *
* @return True on success, false on failure. * @return True on success, false on failure.
**/ **/
stock bool TE_SetupDynamicLight(float vecOrigin[3], int RGB[3], float flRadius, float flTime, float flDecay=0.0, int exponent=0) stock void TE_SetupDynamicLight(float vecOrigin[3], int RGB[3], float flRadius, float flTime, float flDecay=0.0, int exponent=0)
{ {
static int iLastTick;
int iCurrentTick = GetGameTickCount();
if(iLastTick == iCurrentTick)
return false;
iLastTick = iCurrentTick;
TE_Start("Dynamic Light"); TE_Start("Dynamic Light");
TE_WriteVector("m_vecOrigin", vecOrigin); TE_WriteVector("m_vecOrigin", vecOrigin);
@ -542,8 +576,6 @@ stock bool TE_SetupDynamicLight(float vecOrigin[3], int RGB[3], float flRadius,
TE_WriteFloat("m_fRadius", flRadius); TE_WriteFloat("m_fRadius", flRadius);
TE_WriteFloat("m_fTime", flTime); TE_WriteFloat("m_fTime", flTime);
TE_WriteFloat("m_fDecay", flDecay); TE_WriteFloat("m_fDecay", flDecay);
return true;
} }
/** /**
@ -1250,3 +1282,434 @@ stock void GetAbsOrigin(int iEntity, float vecOrigin[3], bool bCenter=false)
vecOrigin[2] += (vecMins[2] + vecMaxs[2]) * 0.5; vecOrigin[2] += (vecMins[2] + vecMaxs[2]) * 0.5;
} }
} }
///////////////////////////////////////Sound
/**
* level boost in some games maybe clamed, e.g. l4d max seems to be 150
* pitch is clamed between 1-200, this maybe different between games.
* sndChannel static sound wont be replaced, however if overflowed it wont play any sound until static sounds have finished.
*
* @param sample sound file path.
* @param origin origin to emit sound from.
* @param entity entity to emit sound from.
* @param level sound level attenuation, the wav sound it's self matters.
* @param pitch sound pitch.
* @param sndChannel sound channel.
* @param rangeMin players within the min range sound wont be mixed.
* @param rangeCurve range curve until max mix can be achieved.
* @param levelBoost add level boost to mixed sounds max rangeCurve will apply levelBoost, half will apply half levelBoost.
* @param exponent exponent value to multiply, logarithmic.
*
* @error Invalid client index.
**/
stock void EmitMixedAmbientSoundToAll(const char[] sample,
const float origin[3] = NULL_VECTOR,
int entity = SOUND_FROM_PLAYER,
int level = SNDLEVEL_NORMAL,
int pitch = SNDPITCH_NORMAL,
int sndChannel = SNDCHAN_AUTO,
float rangeMin,
float rangeCurve=1500.0,
int levelBoost=0,
float exponent=1.0)
{
for(int i = 1; i <= MaxClients; ++i)
{
if(!IsClientInGame(i) || IsFakeClient(i))
continue;
EmitMixedAmbientSound(i, sample, origin, entity, level, pitch, sndChannel, rangeMin, rangeCurve, levelBoost, exponent);
}
}
/**
* level boost in some games maybe clamed, e.g. l4d max seems to be 150
* pitch is clamed between 1-200, this maybe different between games.
* sndChannel static sound wont be replaced, however if overflowed it wont play any sound until static sounds have finished.
*
* @param sample sound file path.
* @param origin origin to emit sound from.
* @param entity entity to emit sound from.
* @param level sound level attenuation, the wav sound it's self matters.
* @param pitch sound pitch.
* @param sndChannel sound channel.
* @param rangeMin players within the min range sound wont be mixed.
* @param rangeCurve range curve until max mix can be achieved, sample2 is played when 2x this value.
* @param levelBoost add level boost to mixed sounds max rangeCurve will apply levelBoost, half will apply half levelBoost.
* @param exponent exponent value to multiply, logarithmic.
* @param sample2 sound file path.
* @param level2 sound level attenuation.
* @param pitch2 sound pitch.
*
* @error Invalid client index.
**/
stock void EmitMixedAmbientSoundToAll_FallBack(const char[] sample,
const float origin[3] = NULL_VECTOR,
int entity = SOUND_FROM_PLAYER,
int level = SNDLEVEL_NORMAL,
int pitch = SNDPITCH_NORMAL,
int sndChannel = SNDCHAN_AUTO,
float rangeMin,
float rangeCurve=1500.0,
int levelBoost=0,
float exponent=1.0,
const char[] sample2,
int level2 = SNDLEVEL_NORMAL,
int pitch2 = SNDPITCH_NORMAL)
{
for(int i = 1; i <= MaxClients; ++i)
{
if(!IsClientInGame(i) || IsFakeClient(i))
continue;
EmitMixedAmbientSound_FallBack(i, sample, origin, entity, level, pitch, sndChannel, rangeMin, rangeCurve, levelBoost, exponent, sample2, level2, pitch2);
}
}
/**
* level boost in some games maybe clamed, e.g. l4d max seems to be 150
* pitch is clamed between 1-200, this maybe different between games.
* sndChannel static sound wont be replaced, however if overflowed it wont play any sound until static sounds have finished.
*
* @param client client index.
* @param sample sound file path.
* @param origin origin to emit sound from.
* @param entity entity to emit sound from.
* @param level sound level attenuation, the wav sound it's self matters.
* @param pitch sound pitch.
* @param sndChannel sound channel.
* @param rangeMin players within the min range sound wont be mixed.
* @param rangeCurve range curve until max mix can be achieved.
* @param levelBoost add level boost to mixed sounds max rangeCurve will apply levelBoost, half will apply half levelBoost.
* @param exponent exponent value to multiply, logarithmic.
*
* @error Invalid client index.
**/
stock void EmitMixedAmbientSound(int client, const char[] sample,
const float origin[3] = NULL_VECTOR,
int entity = SOUND_FROM_PLAYER,
int level = SNDLEVEL_NORMAL,
int pitch = SNDPITCH_NORMAL,
int sndChannel = SNDCHAN_AUTO,
float rangeMin,
float rangeCurve=1500.0,
int levelBoost=0,
float exponent=1.0)
{
static float vecEyePos[3];
int newPitch;
float flDist;
float flDistPercent;
float flDistMulti;
int DistLevelBoost;
int viewEnt = -1;
viewEnt = GetEntPropEnt(client, Prop_Send, "m_hViewEntity");
if(viewEnt > 0)
{
GetAbsOrigin(viewEnt, vecEyePos);
}
else
{
GetClientEyePosition(client, vecEyePos);
}
flDist = GetVectorDistance(origin, vecEyePos);
if(rangeCurve == 0.0)
{
LogError("RangeCurve == 0.0");
return;
}
flDist = (flDist - rangeMin < 0.0 ? 0.0 : flDist - rangeMin);
flDistPercent = (flDist / rangeCurve);
flDistMulti = (flDistPercent * 2) * exponent;
newPitch = pitch;
if(flDistMulti > 1.0)
{
newPitch = FloatToInt(newPitch / (flDistMulti > 2.0 ? 2.0 : flDistMulti));
}
DistLevelBoost = FloatToInt((flDistPercent * exponent) * levelBoost);
if(DistLevelBoost > levelBoost)
DistLevelBoost = levelBoost;
EmitSoundToClient(client, sample, entity, sndChannel, level + DistLevelBoost, _, _, newPitch, _, origin);
}
/**
* level boost in some games maybe clamed, e.g. l4d max seems to be 150
* pitch is clamed between 1-200, this maybe different between games.
* sndChannel static sound wont be replaced, however if overflowed it wont play any sound until static sounds have finished.
*
* @param client client index.
* @param sample sound file path.
* @param origin origin to emit sound from.
* @param entity entity to emit sound from.
* @param level sound level attenuation, the wav sound it's self matters.
* @param pitch sound pitch.
* @param sndChannel sound channel.
* @param rangeMin players within the min range sound wont be mixed.
* @param rangeCurve range curve until max mix can be achieved, sample2 is played when 2x this value.
* @param levelBoost add level boost to mixed sounds max rangeCurve will apply levelBoost, half will apply half levelBoost.
* @param exponent exponent value to multiply, logarithmic.
* @param sample2 sound file path.
* @param level2 sound level attenuation.
* @param pitch2 sound pitch.
*
* @error Invalid client index.
**/
stock void EmitMixedAmbientSound_FallBack(int client, const char[] sample,
const float origin[3] = NULL_VECTOR,
int entity = SOUND_FROM_PLAYER,
int level = SNDLEVEL_NORMAL,
int pitch = SNDPITCH_NORMAL,
int sndChannel = SNDCHAN_AUTO,
float rangeMin,
float rangeCurve=1500.0,
int levelBoost=0,
float exponent=1.0,
const char[] sample2,
int level2 = SNDLEVEL_NORMAL,
int pitch2 = SNDPITCH_NORMAL)
{
static float vecEyePos[3];
int newPitch;
float flDist;
float flDistPercent;
float flDistMulti;
int DistLevelBoost;
int viewEnt = -1;
viewEnt = GetEntPropEnt(client, Prop_Send, "m_hViewEntity");
if(viewEnt > 0)
{
GetAbsOrigin(viewEnt, vecEyePos);
}
else
{
GetClientEyePosition(client, vecEyePos);
}
flDist = GetVectorDistance(origin, vecEyePos);
if(rangeCurve == 0.0)
{
LogError("RangeCurve == 0.0");
return;
}
flDist = (flDist - rangeMin < 0.0 ? 0.0 : flDist - rangeMin);
flDistPercent = (flDist / rangeCurve);
flDistMulti = (flDistPercent * 2) * exponent;
if(flDistMulti >= 2.0)
{
EmitSoundToClient(client, sample2, entity, sndChannel, level2, _, _, pitch2, _, origin);
return;
}
newPitch = pitch;
if(flDistMulti > 1.0)
{
newPitch = FloatToInt(newPitch / (flDistMulti > 2.0 ? 2.0 : flDistMulti));
}
DistLevelBoost = FloatToInt((flDistPercent * exponent) * levelBoost);
if(DistLevelBoost > levelBoost)
DistLevelBoost = levelBoost;
EmitSoundToClient(client, sample, entity, sndChannel, level + DistLevelBoost, _, _, newPitch, _, origin);
}
int FloatToInt(float val)
{
return RoundFloat(val);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////Legacy Particle Stock///////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Creates a tempent particle.
*
* @param iParticleIndex Particle index location in the "ParticleEffectNames" stringtable.
* @param iEntIndex Entity index to attach particle to.
* @param fDelay Delay for the TE_SendToAll function.
* @param SendToAll True to send to all clients, false otherwise. You must call the send function yourself if sending to specific clients.
* @param sParticleName Name of the particle to find the index with. Only used if the particle index is invalid.
* @param iAttachmentIndex Attachment index of the particle. Decompile the model to retrieve this value.
* @param fParticleAngles Angles of the particle. Usually effects particles that have no gravity.
* @param iFlags Flags of the particle. Note: A value of "1" is required for attachment points and damage types.
* @param iDamageType The damage type of the particle. (Used in impact effect dispatches and attachment points need to be set to use.)
* @param fMagnitude The magnitude of the particle. (Needs testing; used in pipe bomb blast.)
* @param fScale The scale of the particle (doesn't apply to most particles). (Needs testing.)
*
* @return True on success, false on failure.
* @error Invalid effect index or invalid particle stringtable index.
**/
#pragma deprecated Used for backwards compatibility.
stock bool L4D_TE_Create_Particle(float fParticleStartPos[3]={0.0, 0.0, 0.0},
float fParticleEndPos[3]={0.0, 0.0, 0.0},
int iParticleIndex=-1,
int iEntIndex=0,
float fDelay=0.0,
bool SendToAll=true,
char sParticleName[64]="",
int iAttachmentIndex=0,
float fParticleAngles[3]={0.0, 0.0, 0.0},
int iFlags=0,
int iDamageType=0,
float fMagnitude=0.0,
float fScale=1.0,
float fRadius=0.0)
{
TE_Start("EffectDispatch");
static EngineVersion IsEngine;
if(IsEngine == Engine_Unknown)
IsEngine = GetEngineVersion();
TE_WriteFloat(IsEngine == Engine_Left4Dead2 ? "m_vOrigin.x" :"m_vOrigin[0]", fParticleStartPos[0]);
TE_WriteFloat(IsEngine == Engine_Left4Dead2 ? "m_vOrigin.y" :"m_vOrigin[1]", fParticleStartPos[1]);
TE_WriteFloat(IsEngine == Engine_Left4Dead2 ? "m_vOrigin.z" :"m_vOrigin[2]", fParticleStartPos[2]);
TE_WriteFloat(IsEngine == Engine_Left4Dead2 ? "m_vStart.x" :"m_vStart[0]", fParticleEndPos[0]);//end point usually for bulletparticles or ropes
TE_WriteFloat(IsEngine == Engine_Left4Dead2 ? "m_vStart.y" :"m_vStart[1]", fParticleEndPos[1]);
TE_WriteFloat(IsEngine == Engine_Left4Dead2 ? "m_vStart.z" :"m_vStart[2]", fParticleEndPos[2]);
static int iEffectIndex = INVALID_STRING_INDEX;
if(iEffectIndex < 0)
{
iEffectIndex = __FindStringIndex2(FindStringTable("EffectDispatch"), "ParticleEffect");
if(iEffectIndex == INVALID_STRING_INDEX)
SetFailState("Unable to find EffectDispatch/ParticleEffect indexes");
}
TE_WriteNum("m_iEffectName", iEffectIndex);
if(iParticleIndex < 0)
{
static int iParticleStringIndex = INVALID_STRING_INDEX;
iParticleStringIndex = __FindStringIndex2(FindStringTable("ParticleEffectNames"), sParticleName);
if(iParticleStringIndex == INVALID_STRING_INDEX)
return false;
TE_WriteNum("m_nHitBox", iParticleStringIndex);
}
else
TE_WriteNum("m_nHitBox", iParticleIndex);
TE_WriteNum("entindex", iEntIndex);
TE_WriteNum("m_nAttachmentIndex", iAttachmentIndex);
TE_WriteVector("m_vAngles", fParticleAngles);
TE_WriteNum("m_fFlags", iFlags);
TE_WriteFloat("m_flMagnitude", fMagnitude);// saw this being used in pipebomb needs testing what it does probs shaking screen?
TE_WriteFloat("m_flScale", fScale);
TE_WriteFloat("m_flRadius", fRadius);// saw this being used in pipebomb needs testing what it does probs shaking screen?
TE_WriteNum("m_nDamageType", iDamageType);// this shit is required dunno why for attachpoint emitting valve probs named it wrong
if(SendToAll)
TE_SendToAll(fDelay);
return true;
}
/**
* Stops a tempent particle.
*
* @param fParticleStartPos Starting position of the particle.
* @param fParticleEndPos Ending position of the particle.
* @param iParticleIndex Particle index location in the "ParticleEffectNames" stringtable.
* @param iEntIndex Entity index to attach particle to.
* @param fDelay Delay for the TE_SendToAll function.
* @param SendToAll True to send to all clients, false otherwise. You must call the send function yourself if sending to specific clients.
* @param sParticleName Name of the particle to find the index with. Only used if the particle index is invalid.
* @param iAttachmentIndex Attachment index of the particle. Decompile the model to retrieve this value.
* @param fParticleAngles Angles of the particle. Usually effects particles that have no gravity.
* @param iFlags Flags of the particle.
* @param iDamageType The damage type of the particle. (Used in impact effect dispatches and attachment points need to be set to use.)
* @param fMagnitude The magnitude of the particle. (Needs testing; used in pipe bomb blast.)
* @param fScale The scale of the particle (doesn't apply to most particles). (Needs testing.)
* @param fRadius The radius of the particle.
*
* @return True on success, false on failure.
* @error Invalid effect index or invalid particle stringtable index.
**/
#pragma deprecated Used only for backwards compatibility.
stock bool L4D_TE_Stop_Particle(float fParticleStartPos[3]={0.0, 0.0, 0.0},
float fParticleEndPos[3]={0.0, 0.0, 0.0},
int iParticleIndex=-1,
int iEntIndex=0,
float fDelay=0.0,
bool SendToAll=true,
char sParticleName[64]="",
int iAttachmentIndex=0,
float fParticleAngles[3]={0.0, 0.0, 0.0},
int iFlags=0,
int iDamageType=0,
float fMagnitude=0.0,
float fScale=1.0,
float fRadius=0.0)
{
TE_Start("EffectDispatch");
static EngineVersion IsEngine;
if(IsEngine == Engine_Unknown)
IsEngine = GetEngineVersion();
TE_WriteFloat(IsEngine == Engine_Left4Dead2 ? "m_vOrigin.x" :"m_vStart[0]", fParticleStartPos[0]);
TE_WriteFloat(IsEngine == Engine_Left4Dead2 ? "m_vOrigin.y" :"m_vStart[1]", fParticleStartPos[1]);
TE_WriteFloat(IsEngine == Engine_Left4Dead2 ? "m_vOrigin.z" :"m_vStart[2]", fParticleStartPos[2]);
TE_WriteFloat(IsEngine == Engine_Left4Dead2 ? "m_vStart.x" :"m_vOrigin[0]", fParticleEndPos[0]);//end point usually for bulletparticles or ropes
TE_WriteFloat(IsEngine == Engine_Left4Dead2 ? "m_vStart.y" :"m_vOrigin[1]", fParticleEndPos[1]);
TE_WriteFloat(IsEngine == Engine_Left4Dead2 ? "m_vStart.z" :"m_vOrigin[2]", fParticleEndPos[2]);
static int iEffectIndex = INVALID_STRING_INDEX;
if(iEffectIndex < 0)
{
iEffectIndex = __FindStringIndex2(FindStringTable("EffectDispatch"), "ParticleEffectStop");
if(iEffectIndex == INVALID_STRING_INDEX)
SetFailState("Unable to find EffectDispatch/ParticleEffectStop indexes");
}
TE_WriteNum("m_iEffectName", iEffectIndex);
if(iParticleIndex < 0)
{
static int iParticleStringIndex = INVALID_STRING_INDEX;
iParticleStringIndex = __FindStringIndex2(FindStringTable("ParticleEffectNames"), sParticleName);
if(iParticleStringIndex == INVALID_STRING_INDEX)
return false;
TE_WriteNum("m_nHitBox", iParticleStringIndex);
}
else
TE_WriteNum("m_nHitBox", iParticleIndex);
TE_WriteNum("entindex", iEntIndex);
TE_WriteNum("m_nAttachmentIndex", iAttachmentIndex);
TE_WriteVector("m_vAngles", fParticleAngles);
TE_WriteNum("m_fFlags", iFlags);
TE_WriteFloat("m_flMagnitude", fMagnitude);// saw this being used in pipebomb needs testing what it does probs shaking screen?
TE_WriteFloat("m_flScale", fScale);
TE_WriteFloat("m_flRadius", fRadius);// saw this being used in pipebomb needs testing what it does probs shaking screen?
TE_WriteNum("m_nDamageType", iDamageType);// this shit is required dunno why for attachpoint emitting valve probs named it wrong
if(SendToAll)
TE_SendToAll(fDelay);
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

View file

@ -1,6 +1,6 @@
/* /*
* Left 4 DHooks Direct - Stock Functions * Left 4 DHooks Direct - Stock Functions
* Copyright (C) 2022 Silvers * Copyright (C) 2023 Silvers
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by

View file

@ -202,7 +202,7 @@ bool ComputeGroups(Group groups[MAX_GROUPS], GroupResult result, float activateF
inGroup[j] = true; inGroup[j] = true;
members.Push(GetClientUserId(j)); members.Push(GetClientUserId(j));
} else { } else {
PrintDebug("not adding member to group %d: %N (dist = %.4f) (fldiff = %.1f) (l:%N)", groupIndex + 1, j, dist, flowDiff, i); // PrintDebug("not adding member to group %d: %N (dist = %.4f) (fldiff = %.1f) (l:%N)", groupIndex + 1, j, dist, flowDiff, i);
} }
} }
} }
@ -294,6 +294,39 @@ bool ComputeGroups(Group groups[MAX_GROUPS], GroupResult result, float activateF
return groupIndex > 0; return groupIndex > 0;
} }
public Action L4D2_CGasCan_EventKilled(int gascan, int &inflictor, int &attacker) {
if(hEnabled.IntValue > 0 && attacker > 0 && attacker <= MaxClients) {
float activatorFlow = L4D2Direct_GetFlowDistance(attacker);
Group groups[MAX_GROUPS];
GroupResult result;
ComputeGroups(groups, result, activatorFlow);
AdminId admin = GetUserAdmin(attacker);
if(admin != INVALID_ADMIN_ID && admin.HasFlag(Admin_Custom1)) {
lastButtonPressTime = GetGameTime();
return Plugin_Continue;
} else if(result.groupCount > 0 && result.ungroupedCount > 0) {
lastButtonPressTime = GetGameTime();
return Plugin_Continue;
}
if(panicStarted) {
panicStarted = false;
return Plugin_Continue;
}
PrintToConsoleAll("[CC] Gascan Light by %N", attacker);
if(hEnabled.IntValue == 2 || !IsActivationAllowed(activatorFlow, 1500.0)) {
ClientCommand(attacker, "play ui/menu_invalid.wav");
PrintToChat(attacker, "Please wait for players to catch up.");
return Plugin_Handled;
}
lastButtonPressTime = GetGameTime();
}
return Plugin_Continue;
}
// 5 far/8 total // 5 far/8 total
// [Debug] average 4222.518066 - difference 2262.424316 // [Debug] average 4222.518066 - difference 2262.424316

View file

@ -150,7 +150,7 @@ Restore from saved inventory
static StringMap weaponMaxClipSizes; static StringMap weaponMaxClipSizes;
static StringMap pInv; static StringMap pInv;
static char HUD_SCRIPT_DATA[] = "g_ModeScript._eph <- {Fields = {players = {slot = g_ModeScript.HUD_RIGHT_BOT, dataval = \"%s\", flags = g_ModeScript.HUD_FLAG_ALIGN_LEFT|g_ModeScript.HUD_FLAG_TEAM_SURVIVORS|g_ModeScript.HUD_FLAG_NOBG}}};HUDSetLayout(g_ModeScript._eph);HUDPlace(g_ModeScript.HUD_RIGHT_BOT, 0.72,0.78,0.3,0.3);g_ModeScript"; static char HUD_SCRIPT_DATA[] = "g_ModeScript._eph <- { Fields = { players = { slot = g_ModeScript.HUD_RIGHT_BOT, dataval = \"%s\", flags = g_ModeScript.HUD_FLAG_ALIGN_LEFT | g_ModeScript.HUD_FLAG_TEAM_SURVIVORS | g_ModeScript.HUD_FLAG_NOBG } } }; HUDSetLayout(g_ModeScript._eph); HUDPlace(g_ModeScript.HUD_RIGHT_BOT, 0.72,0.78,0.3,0.3); g_ModeScript";
static char HUD_SCRIPT_CLEAR[] = "g_ModeScript._eph <- { Fields = { players = { slot = g_ModeScript.HUD_RIGHT_BOT, dataval = \"\", flags = g_ModeScript.HUD_FLAG_ALIGN_LEFT|g_ModeScript.HUD_FLAG_TEAM_SURVIVORS|g_ModeScript.HUD_FLAG_NOBG } } };HUDSetLayout( g_ModeScript._eph );g_ModeScript"; static char HUD_SCRIPT_CLEAR[] = "g_ModeScript._eph <- { Fields = { players = { slot = g_ModeScript.HUD_RIGHT_BOT, dataval = \"\", flags = g_ModeScript.HUD_FLAG_ALIGN_LEFT|g_ModeScript.HUD_FLAG_TEAM_SURVIVORS|g_ModeScript.HUD_FLAG_NOBG } } };HUDSetLayout( g_ModeScript._eph );g_ModeScript";
@ -1271,7 +1271,6 @@ public Action Timer_UpdateHud(Handle h) {
Format(prefix, 13, "AFK %N", client); Format(prefix, 13, "AFK %N", client);
else else
Format(prefix, 8, "%N", i); Format(prefix, 8, "%N", i);
} else { } else {
Format(prefix, 8, "%N", i); Format(prefix, 8, "%N", i);
} }

View file

@ -3,7 +3,7 @@
#define PLUGIN_VERSION "1.0" #define PLUGIN_VERSION "1.0"
#define PLAYER_HAT_REQUEST_COOLDOWN 10 #define PLAYER_HAT_REQUEST_COOLDOWN 10
#define DEBUG_GLOW 1 // #define DEBUG_GLOW 1
static float EMPTY_ANG[3] = { 0.0, 0.0, 0.0 }; static float EMPTY_ANG[3] = { 0.0, 0.0, 0.0 };
#define DUMMY_MODEL "models/props/cs_office/vending_machine.mdl" #define DUMMY_MODEL "models/props/cs_office/vending_machine.mdl"
@ -114,7 +114,6 @@ enum struct WallBuilderData {
wallMode mode; wallMode mode;
int axis; int axis;
int snapAngle; int snapAngle;
int movetype;
int moveSpeed; int moveSpeed;
float moveDistance; float moveDistance;
int entity; int entity;
@ -123,10 +122,9 @@ enum struct WallBuilderData {
void Reset() { void Reset() {
this.size[0] = this.size[1] = this.size[2] = 5.0; this.size[0] = this.size[1] = this.size[2] = 5.0;
this.angles[0] = this.angles[1] = this.angles[2] = 0.0; this.angles[0] = this.angles[1] = this.angles[2] = 0.0;
this.axis = 0; this.axis = 1;
this.movetype = 0;
this.canScale = true; this.canScale = true;
this.moveDistance = 100.0; this.moveDistance = 200.0;
this.moveSpeed = 1; this.moveSpeed = 1;
this.snapAngle = 30; this.snapAngle = 30;
this.entity = INVALID_ENT_REFERENCE; this.entity = INVALID_ENT_REFERENCE;
@ -190,19 +188,22 @@ enum struct WallBuilderData {
} }
void CycleAxis(int client, float tick) { void CycleAxis(int client, float tick) {
if(tick - cmdThrottle[client] <= 0.20) return; if(tick - cmdThrottle[client] <= 0.15) return;
if(this.axis == 0) { if(this.axis == 0) {
this.axis = 1; this.axis = 1;
PrintToChat(client, "\x04[Walls]\x01 Rotate Axis: \x05HEADING (Y)\x01"); PrintToChat(client, "\x04[Walls]\x01 Rotate Axis: \x05HEADING (Y)\x01");
} else if(this.axis == 1) {
this.axis = 2;
PrintToChat(client, "\x04[Walls]\x01 Rotate Axis: \x05PITCH (X)\x01");
} else { } else {
this.axis = 0; this.axis = 0;
PrintToChat(client, "\x04[Walls]\x01 Rotate Axis: \x05PITCH (X)\x01"); PrintToChat(client, "\x04[Walls]\x01 Rotate Axis: \x05ROLL (Z)\x01");
} }
cmdThrottle[client] = tick; cmdThrottle[client] = tick;
} }
void CycleSnapAngle(int client, float tick) { void CycleSnapAngle(int client, float tick) {
if(tick - cmdThrottle[client] <= 0.20) return; if(tick - cmdThrottle[client] <= 0.15) return;
switch(this.snapAngle) { switch(this.snapAngle) {
case 1: this.snapAngle = 15; case 1: this.snapAngle = 15;
case 15: this.snapAngle = 30; case 15: this.snapAngle = 30;
@ -237,21 +238,6 @@ enum struct WallBuilderData {
cmdThrottle[client] = tick; cmdThrottle[client] = tick;
} }
void CycleMoveMode(int client, float tick) {
if(tick - cmdThrottle[client] <= 0.25) return;
this.movetype++;
PrintToChat(client, "\x04[Walls]\x01 Move Type: \x05%d\x01", this.movetype);
// if(this.movetype == 0) {
// this.movetype = 1;
// PrintToChat(client, "\x04[SM]\x01 Move Type: \x05HEADING (Y)\x01");
// } else {
// this.movetype = 0;
// PrintToChat(client, "\x04[SM]\x01 Rotate Axis: \x05PITCH (X)\x01");
// }
if(this.movetype == 3) this.movetype = 0;
cmdThrottle[client] = tick;
}
int Build() { int Build() {
if(!this.canScale) { if(!this.canScale) {
this.Reset(); this.Reset();
@ -259,9 +245,11 @@ enum struct WallBuilderData {
} }
// Don't need to build a new one if we editing: // Don't need to build a new one if we editing:
int blocker = this.entity; int blocker = this.entity;
bool isEdit = true; bool isEdit = false;
if(blocker == INVALID_ENT_REFERENCE) { if(blocker != INVALID_ENT_REFERENCE) {
isEdit = false; AcceptEntityInput(this.entity, "Kill");
isEdit = true;
}
blocker = CreateEntityByName("func_brush"); blocker = CreateEntityByName("func_brush");
if(blocker == -1) return -1; if(blocker == -1) return -1;
DispatchKeyValueVector(blocker, "mins", this.mins); DispatchKeyValueVector(blocker, "mins", this.mins);
@ -287,11 +275,6 @@ enum struct WallBuilderData {
enteffects |= 32; //EF_NODRAW enteffects |= 32; //EF_NODRAW
SetEntProp(blocker, Prop_Send, "m_fEffects", enteffects); SetEntProp(blocker, Prop_Send, "m_fEffects", enteffects);
AcceptEntityInput(blocker, "Enable"); AcceptEntityInput(blocker, "Enable");
} else {
TeleportEntity(this.entity, this.origin, this.angles, NULL_VECTOR);
SetEntPropVector(this.entity, Prop_Send, "m_vecMins", this.mins);
SetEntPropVector(this.entity, Prop_Send, "m_vecMaxs", this.size);
}
this.Draw(WALL_COLOR, 5.0, 1.0); this.Draw(WALL_COLOR, 5.0, 1.0);
this.Reset(); this.Reset();
@ -346,7 +329,7 @@ public void OnPluginStart() {
cvar_sm_hats_blacklist_enabled = CreateConVar("sm_hats_blacklist_enabled", "1", "Is the prop blacklist enabled", FCVAR_NONE, true, 0.0, true, 1.0); cvar_sm_hats_blacklist_enabled = CreateConVar("sm_hats_blacklist_enabled", "1", "Is the prop blacklist enabled", FCVAR_NONE, true, 0.0, true, 1.0);
cvar_sm_hats_enabled = CreateConVar("sm_hats_enabled", "1.0", "Enable hats.\n0=OFF, 1=Admins Only, 2=Any", FCVAR_NONE, true, 0.0, true, 2.0); cvar_sm_hats_enabled = CreateConVar("sm_hats_enabled", "1.0", "Enable hats.\n0=OFF, 1=Admins Only, 2=Any", FCVAR_NONE, true, 0.0, true, 2.0);
cvar_sm_hats_enabled.AddChangeHook(Event_HatsEnableChanged); cvar_sm_hats_enabled.AddChangeHook(Event_HatsEnableChanged);
cvar_sm_hats_flags = CreateConVar("sm_hats_features", "153", "Toggle certain features. Add bits together\n1 = Player Hats\n2 = Respect Admin Immunity\n4 = Create a fake hat for hat wearer to view instead, and for yeeting\n8 = No saferoom hats\n16 = Player hatting requires victim consent\n32 = Infected Hats\n64 = Reverse hats", FCVAR_CHEAT, true, 0.0); cvar_sm_hats_flags = CreateConVar("sm_hats_features", "27", "Toggle certain features. Add bits together\n1 = Player Hats\n2 = Respect Admin Immunity\n4 = Create a fake hat for hat wearer to view instead, and for yeeting\n8 = No saferoom hats\n16 = Player hatting requires victim consent\n32 = Infected Hats\n64 = Reverse hats", FCVAR_CHEAT, true, 0.0);
cvar_sm_hat_rainbow_speed = CreateConVar("sm_hats_rainbow_speed", "1", "Speed of rainbow", FCVAR_NONE, true, 0.0); cvar_sm_hat_rainbow_speed = CreateConVar("sm_hats_rainbow_speed", "1", "Speed of rainbow", FCVAR_NONE, true, 0.0);
noHatVictimCookie = new Cookie("hats_no_target", "Disables other players from making you their hat", CookieAccess_Public); noHatVictimCookie = new Cookie("hats_no_target", "Disables other players from making you their hat", CookieAccess_Public);
@ -410,25 +393,30 @@ public Action Command_DoAHat(int client, int args) {
char sizeStr[4]; char sizeStr[4];
GetCmdArg(2, sizeStr, sizeof(sizeStr)); GetCmdArg(2, sizeStr, sizeof(sizeStr));
float size = StringToFloat(sizeStr); float size = StringToFloat(sizeStr);
if(size == 0.0) {
ReplyToCommand(client, "[Hats] Invalid size");
return Plugin_Handled;
}
if(HasEntProp(entity, Prop_Send, "m_flModelScale")) if(HasEntProp(entity, Prop_Send, "m_flModelScale"))
SetEntPropFloat(entity, Prop_Send, "m_flModelScale", size); SetEntPropFloat(entity, Prop_Send, "m_flModelScale", size);
else else
PrintHintText(client, "Hat does not support scaling"); PrintHintText(client, "Hat does not support scaling");
// Change the size of it's parent instead
int child = -1; int child = -1;
while((child = FindEntityByClassname(child, "*")) != INVALID_ENT_REFERENCE ) while((child = FindEntityByClassname(child, "*")) != INVALID_ENT_REFERENCE )
{ {
int parent = GetEntPropEnt(child, Prop_Data, "m_pParent"); int parent = GetEntPropEnt(child, Prop_Data, "m_pParent");
if(parent == entity && HasEntProp(child, Prop_Send, "m_flModelScale")) { if(parent == entity) {
if(HasEntProp(child, Prop_Send, "m_flModelScale")) { if(HasEntProp(child, Prop_Send, "m_flModelScale")) {
PrintToConsole(client, "found child %d for %d", child, entity); PrintToConsole(client, "found child %d for %d", child, entity);
SetEntPropFloat(child, Prop_Send, "m_flModelScale", size); SetEntPropFloat(child, Prop_Send, "m_flModelScale", size);
} else { } else {
PrintToConsole(client, "Child %d for %d cannot be scaled", child, entity); PrintToChat(client, "Child %d for %d cannot be scaled", child, entity);
} }
break;
} }
} }
// Reattach entity:
EquipHat(client, entity); EquipHat(client, entity);
return Plugin_Handled; return Plugin_Handled;
} else if(arg[0] == 'r' && arg[1] == 'a') { } else if(arg[0] == 'r' && arg[1] == 'a') {
@ -443,13 +431,16 @@ public Action Command_DoAHat(int client, int args) {
return Plugin_Handled; return Plugin_Handled;
} }
// Re-enable physics and restore collision/solidity
AcceptEntityInput(entity, "EnableMotion"); AcceptEntityInput(entity, "EnableMotion");
SetEntProp(entity, Prop_Send, "m_CollisionGroup", hatData[client].collisionGroup); SetEntProp(entity, Prop_Send, "m_CollisionGroup", hatData[client].collisionGroup);
SetEntProp(entity, Prop_Send, "m_nSolidType", hatData[client].solidType); SetEntProp(entity, Prop_Send, "m_nSolidType", hatData[client].solidType);
int flags = ~GetEntityFlags(entity) & FL_FROZEN; // Remove frozen flag (only "infected" and "witch" are frozen, but just incase:)
int flags = GetEntityFlags(entity) & ~FL_FROZEN;
SetEntityFlags(entity, flags); SetEntityFlags(entity, flags);
// Clear visible hats (HatConfig_FakeHat is enabled)
int visibleEntity = EntRefToEntIndex(hatData[client].visibleEntity); int visibleEntity = EntRefToEntIndex(hatData[client].visibleEntity);
SDKUnhook(entity, SDKHook_SetTransmit, OnRealTransmit); SDKUnhook(entity, SDKHook_SetTransmit, OnRealTransmit);
if(visibleEntity > 0) { if(visibleEntity > 0) {
@ -457,33 +448,39 @@ public Action Command_DoAHat(int client, int args) {
AcceptEntityInput(visibleEntity, "Kill"); AcceptEntityInput(visibleEntity, "Kill");
hatData[client].visibleEntity = INVALID_ENT_REFERENCE; hatData[client].visibleEntity = INVALID_ENT_REFERENCE;
} }
// Grant temp god & remove after time
tempGod[client] = true; tempGod[client] = true;
if(client <= MaxClients) CreateTimer(2.0, Timer_RemoveGod, GetClientUserId(client));
if(entity <= MaxClients) CreateTimer(2.0, Timer_RemoveGod, GetClientUserId(entity));
CreateTimer(2.0, Timer_RemoveGod, GetClientUserId(client)); // Restore movement:
if(entity <= MaxClients) { if(entity <= MaxClients) {
tempGod[entity] = true; // If player, remove roll & and just default to WALK movetype
hatData[client].orgAng[2] = 0.0; hatData[client].orgAng[2] = 0.0;
CreateTimer(2.5, Timer_RemoveGod, GetClientUserId(entity));
SetEntityMoveType(entity, MOVETYPE_WALK); SetEntityMoveType(entity, MOVETYPE_WALK);
} else { } else {
// If not player, then just use whatever they were pre-hat
SetEntProp(entity, Prop_Send, "movetype", hatData[client].moveType); SetEntProp(entity, Prop_Send, "movetype", hatData[client].moveType);
} }
if(arg[0] == 'y') { if(arg[0] == 'y') { // Hat yeeting:
GetClientEyeAngles(client, hatData[client].orgAng); GetClientEyeAngles(client, hatData[client].orgAng);
GetClientAbsOrigin(client, hatData[client].orgPos); GetClientAbsOrigin(client, hatData[client].orgPos);
hatData[client].orgPos[2] += 45.0; hatData[client].orgPos[2] += 45.0;
float ang[3], vel[3]; float ang[3], vel[3];
// Calculate the angle to throw at
GetClientEyeAngles(client, ang); GetClientEyeAngles(client, ang);
ang[2] = 0.0; ang[2] = 0.0;
if(ang[0] > 0.0) ang[0] = -ang[0]; if(ang[0] > 0.0) ang[0] = -ang[0];
// ang[0] = -45.0; // ang[0] = -45.0;
// Calculate velocity to throw based on direction
vel[0] = Cosine(DegToRad(ang[1])) * 1500.0; vel[0] = Cosine(DegToRad(ang[1])) * 1500.0;
vel[1] = Sine(DegToRad(ang[1])) * 1500.0; vel[1] = Sine(DegToRad(ang[1])) * 1500.0;
vel[2] = 700.0; vel[2] = 700.0;
if(entity <= MaxClients) { if(entity <= MaxClients) {
// For players, use the built in fling function
TeleportEntity(entity, hatData[client].orgPos, hatData[client].orgAng, NULL_VECTOR); TeleportEntity(entity, hatData[client].orgPos, hatData[client].orgAng, NULL_VECTOR);
L4D2_CTerrorPlayer_Fling(entity, client, vel); L4D2_CTerrorPlayer_Fling(entity, client, vel);
} /*else if(visibleEntity > 0) { } /*else if(visibleEntity > 0) {
@ -506,49 +503,60 @@ public Action Command_DoAHat(int client, int args) {
hatData[client].visibleEntity = INVALID_ENT_REFERENCE; hatData[client].visibleEntity = INVALID_ENT_REFERENCE;
hatData[client].entity = INVALID_ENT_REFERENCE; hatData[client].entity = INVALID_ENT_REFERENCE;
} */ else { } */ else {
// For actual props, offset it 35 units above and 80 units infront to reduce collision-incaps and then throw
GetHorizontalPositionFromClient(client, 80.0, hatData[client].orgPos); GetHorizontalPositionFromClient(client, 80.0, hatData[client].orgPos);
hatData[client].orgPos[2] += 35.0; hatData[client].orgPos[2] += 35.0;
TeleportEntity(entity, hatData[client].orgPos, hatData[client].orgAng, vel); TeleportEntity(entity, hatData[client].orgPos, hatData[client].orgAng, vel);
CreateTimer(6.0, Timer_PropSleep, hatData[client].entity); // Sleep the physics after enoug time for it to most likely have landed
CreateTimer(5.7, Timer_PropSleep, hatData[client].entity);
} }
PrintToChat(client, "[Hats] Yeeted hat"); PrintToChat(client, "[Hats] Yeeted hat");
hatData[client].entity = INVALID_ENT_REFERENCE; hatData[client].entity = INVALID_ENT_REFERENCE;
return Plugin_Handled; return Plugin_Handled;
} else if(arg[0] == 'c') { } else if(arg[0] == 'c') {
if(GetCursorLocation(client, hatData[client].orgPos)) { float pos[3];
GlowPoint(hatData[client].orgPos); // Grabs a cursor position with some checks to prevent placing into (in)visible walls
if(GetSmartCursorLocation(client, pos)) {
if(CanHatBePlaced(client, pos)) {
if(entity <= MaxClients)
L4D_WarpToValidPositionIfStuck(entity);
hatData[client].orgPos = pos;
TeleportEntity(entity, hatData[client].orgPos, hatData[client].orgAng, NULL_VECTOR); TeleportEntity(entity, hatData[client].orgPos, hatData[client].orgAng, NULL_VECTOR);
PrintToChat(client, "[Hats] Placed hat on cursor."); PrintToChat(client, "[Hats] Placed hat on cursor.");
}
} else { } else {
PrintToChat(client, "[Hats] Could not find cursor position."); PrintToChat(client, "[Hats] Could not find cursor position.");
} }
} else if(arg[0] == 'p' || (entity <= MaxClients && arg[0] != 'r')) { } else if(arg[0] == 'p' || (entity <= MaxClients && arg[0] != 'r')) {
if(!HasFlag(client, HAT_REVERSED)) { // Place the hat down on the cursor if specified OR if entity is hat
float pos[3], ang[3];
GetClientEyePosition(client, hatData[client].orgPos); if(HasFlag(client, HAT_REVERSED)) {
GetClientEyeAngles(client, hatData[client].orgAng); // If we are reversed, then place ourselves where our "hatter" is
GetHorizontalPositionFromOrigin(hatData[client].orgPos, hatData[client].orgAng, 80.0, hatData[client].orgPos); GetClientEyePosition(entity, hatData[client].orgPos);
hatData[client].orgAng[0] = 0.0; GetClientEyeAngles(entity, hatData[client].orgAng);
// GlowPoint(hatData[client].orgPos, 2.0); TeleportEntity(client, hatData[client].orgPos, hatData[client].orgAng, NULL_VECTOR);
PrintToChat(entity, "[Hats] Placed hat in front of you.");
} else {
// If we are normal, then get position infront of us, offset by model size
GetClientEyePosition(client, pos);
GetClientEyeAngles(client, ang);
GetHorizontalPositionFromOrigin(pos, ang, 80.0, pos);
ang[0] = 0.0;
float mins[3]; float mins[3];
GetEntPropVector(entity, Prop_Data, "m_vecMins", mins); GetEntPropVector(entity, Prop_Data, "m_vecMins", mins);
// GlowPoint(hatData[client].orgPos, 3.0); pos[2] += mins[2];
hatData[client].orgPos[2] += mins[2]; // Find the nearest ground (raytrace bottom->up)
FindGround(hatData[client].orgPos, hatData[client].orgPos); FindGround(pos, pos);
// GlowPoint(hatData[client].orgPos); // Check if the destination is acceptable (not saferooms if enabled)
} if(CanHatBePlaced(client, pos)) {
hatData[client].orgPos = pos;
hatData[client].orgAng = ang;
/*GetGroundTopDown(client, hatData[client].orgPos, hatData[client].orgAng);
// GetGround(client, hatData[client].orgPos, hatData[client].orgAng);
float mins[3];
GetEntPropVector(entity, Prop_Data, "m_vecMins", mins);
hatData[client].orgPos[2] -= mins[2];
GetHorizontalPositionFromOrigin(hatData[client].orgPos, hatData[client].orgAng, 80.0, hatData[client].orgPos);*/
TeleportEntity(entity, hatData[client].orgPos, hatData[client].orgAng, NULL_VECTOR); TeleportEntity(entity, hatData[client].orgPos, hatData[client].orgAng, NULL_VECTOR);
// hatData[client].orgPos[2] = mins[2];
PrintToChat(client, "[Hats] Placed hat in front of you."); PrintToChat(client, "[Hats] Placed hat in front of you.");
}
}
} else if(arg[0] == 'd') { } else if(arg[0] == 'd') {
// Use the new wall editor
WallBuilder[client].Reset(); WallBuilder[client].Reset();
WallBuilder[client].entity = EntIndexToEntRef(entity); WallBuilder[client].entity = EntIndexToEntRef(entity);
WallBuilder[client].canScale = false; WallBuilder[client].canScale = false;
@ -558,18 +566,27 @@ public Action Command_DoAHat(int client, int args) {
PrintToChat(client, "[Hats] Restored hat to its original position."); PrintToChat(client, "[Hats] Restored hat to its original position.");
} }
// Restore the scale pre-hat
if(hatData[client].scale > 0 && HasEntProp(entity, Prop_Send, "m_flModelScale")) if(hatData[client].scale > 0 && HasEntProp(entity, Prop_Send, "m_flModelScale"))
SetEntPropFloat(entity, Prop_Send, "m_flModelScale", hatData[client].scale); SetEntPropFloat(entity, Prop_Send, "m_flModelScale", hatData[client].scale);
// If no other options performed, then restore to original position and remove our reference
AcceptEntityInput(entity, "Sleep"); AcceptEntityInput(entity, "Sleep");
TeleportEntity(entity, hatData[client].orgPos, hatData[client].orgAng, NULL_VECTOR); TeleportEntity(entity, hatData[client].orgPos, hatData[client].orgAng, NULL_VECTOR);
hatData[client].entity = INVALID_ENT_REFERENCE; hatData[client].entity = INVALID_ENT_REFERENCE;
} else { } else {
// Find a new hatable entity
int flags = 0; int flags = 0;
entity = GetLookingEntity(client, Filter_ValidHats); //GetClientAimTarget(client, false); entity = GetLookingEntity(client, Filter_ValidHats);
if(entity <= 0) { if(entity <= 0) {
PrintCenterText(client, "[Hats] No entity found"); PrintCenterText(client, "[Hats] No entity found");
} else { return Plugin_Handled;
} else if(entity == EntRefToEntIndex(WallBuilder[client].entity)) {
// Prevent making an entity you editing a hat
return Plugin_Handled;
}
// Make hat reversed if 'r' passed in
if(args > 0) { if(args > 0) {
char arg[4]; char arg[4];
GetCmdArg(1, arg, sizeof(arg)); GetCmdArg(1, arg, sizeof(arg));
@ -577,22 +594,24 @@ public Action Command_DoAHat(int client, int args) {
flags |= view_as<int>(HAT_REVERSED); flags |= view_as<int>(HAT_REVERSED);
} }
} }
int parent = GetEntPropEnt(entity, Prop_Data, "m_hParent"); int parent = GetEntPropEnt(entity, Prop_Data, "m_hParent");
if(parent > 0 && entity > MaxClients) { if(parent > 0 && entity > MaxClients) {
PrintToConsole(client, "[Hats] Selected a child entity, selecting parent (child %d -> parent %d)", entity, parent); PrintToConsole(client, "[Hats] Selected a child entity, selecting parent (child %d -> parent %d)", entity, parent);
entity = parent; entity = parent;
} else if(entity <= MaxClients) { } else if(entity <= MaxClients) { // Checks for hatting a player entity
if(GetClientTeam(entity) != 2 && ~cvar_sm_hats_flags.IntValue & view_as<int>(HatConfig_InfectedHats)) { if(GetClientTeam(entity) != 2 && ~cvar_sm_hats_flags.IntValue & view_as<int>(HatConfig_InfectedHats)) {
PrintToChat(client, "[Hats] Cannot make enemy a hat... it's dangerous"); PrintToChat(client, "[Hats] Cannot make enemy a hat... it's dangerous");
return Plugin_Handled; return Plugin_Handled;
} else if(entity == EntRefToEntIndex(WallBuilder[client].entity)) { } else if(entity == EntRefToEntIndex(WallBuilder[client].entity)) {
// Old check left in in case you hatting child entity
PrintToChat(client, "[Hats] You are currently editing this entity"); PrintToChat(client, "[Hats] You are currently editing this entity");
return Plugin_Handled; return Plugin_Handled;
} else if(inSaferoom[client] && cvar_sm_hats_flags.IntValue & view_as<int>(HatConfig_NoSaferoomHats)) { } else if(inSaferoom[client] && cvar_sm_hats_flags.IntValue & view_as<int>(HatConfig_NoSaferoomHats)) {
PrintToChat(client, "[Hats] Hats are not allowed in the saferoom"); PrintToChat(client, "[Hats] Hats are not allowed in the saferoom");
return Plugin_Handled; return Plugin_Handled;
} else if(!IsPlayerAlive(entity) || GetEntProp(entity, Prop_Send, "m_isHangingFromLedge") || L4D_IsPlayerCapped(entity)) { } else if(!IsPlayerAlive(entity) || GetEntProp(entity, Prop_Send, "m_isHangingFromLedge") || L4D_IsPlayerCapped(entity)) {
PrintToChat(client, "[Hats] Player is either dead, hanging or in the process of dying."); PrintToChat(client, "[Hats] Player is either dead, hanging, or in the process of dying.");
return Plugin_Handled; return Plugin_Handled;
} else if(EntRefToEntIndex(hatData[entity].entity) == client) { } else if(EntRefToEntIndex(hatData[entity].entity) == client) {
PrintToChat(client, "[Hats] Woah you can't be making a black hole, jesus be careful."); PrintToChat(client, "[Hats] Woah you can't be making a black hole, jesus be careful.");
@ -641,6 +660,7 @@ public Action Command_DoAHat(int client, int args) {
char classname[64]; char classname[64];
GetEntityClassname(entity, classname, sizeof(classname)); GetEntityClassname(entity, classname, sizeof(classname));
// Check is pretty redudant as the traceray itself shouldn't grab it, but just in case:
if(cvar_sm_hats_blacklist_enabled.BoolValue) { if(cvar_sm_hats_blacklist_enabled.BoolValue) {
for(int i = 0; i < MAX_FORBIDDEN_CLASSNAMES; i++) { for(int i = 0; i < MAX_FORBIDDEN_CLASSNAMES; i++) {
if(StrEqual(FORBIDDEN_CLASSNAMES[i], classname)) { if(StrEqual(FORBIDDEN_CLASSNAMES[i], classname)) {
@ -650,6 +670,7 @@ public Action Command_DoAHat(int client, int args) {
} }
} }
// Check for any class that should always be reversed
if(~flags & view_as<int>(HAT_REVERSED)) { if(~flags & view_as<int>(HAT_REVERSED)) {
for(int i = 0; i < MAX_REVERSE_CLASSNAMES; i++) { for(int i = 0; i < MAX_REVERSE_CLASSNAMES; i++) {
if(StrEqual(REVERSE_CLASSNAMES[i], classname)) { if(StrEqual(REVERSE_CLASSNAMES[i], classname)) {
@ -658,52 +679,19 @@ public Action Command_DoAHat(int client, int args) {
} }
} }
} }
EquipHat(client, entity, classname, flags); EquipHat(client, entity, classname, flags);
} }
}
return Plugin_Handled; return Plugin_Handled;
} }
public Action Command_MakeWall(int client, int args) { public Action Command_MakeWall(int client, int args) {
if(WallBuilder[client].IsActive()) { if(WallBuilder[client].IsActive()) {
if(args == 1) { ReplyToCommand(client, "\x04[Walls]\x01 You are already editing/building, either finish with \x05/wall build\x01 or cancel with \x04/wall cancel\x01");
char arg1[16];
GetCmdArg(1, arg1, sizeof(arg1));
if(StrEqual(arg1, "build", false)) {
int flags = GetEntityFlags(client) & ~FL_FROZEN;
SetEntityFlags(client, flags);
int id = WallBuilder[client].Build();
if(id == -1) {
PrintToChat(client, "\x04[Walls]\x01 Wall Creation: \x04Error\x01");
} else if(id == -2) {
PrintToChat(client, "\x04[Walls]\x01 Wall Edit: \x04Complete\x01");
} else if(id == -3) {
PrintToChat(client, "\x04[Walls]\x01 Entity Edit: \x04Complete\x01");
} else {
PrintToChat(client, "\x04[Walls]\x01 Wall Creation: \x05Wall #%d Created\x01", id + 1);
}
return Plugin_Handled;
} else if(StrEqual(arg1, "export")) {
PrintToChat(client, "{");
PrintToChat(client, "\t\"origin\" \"%.2f %.2f %.2f\"", WallBuilder[client].origin[0], WallBuilder[client].origin[1], WallBuilder[client].origin[2]);
PrintToChat(client, "\t\"angles\" \"%.2f %.2f %.2f\"", WallBuilder[client].angles[0], WallBuilder[client].angles[1], WallBuilder[client].angles[2]);
PrintToChat(client, "\t\"size\" \"%.2f %.2f %.2f\"", WallBuilder[client].size[0], WallBuilder[client].size[1], WallBuilder[client].size[2]);
PrintToChat(client, "}");
} else if(StrEqual(arg1, "cancel")) {
int flags = GetEntityFlags(client) & ~FL_FROZEN;
SetEntityFlags(client, flags);
WallBuilder[client].SetMode(INACTIVE);
PrintToChat(client, "\x04[Walls]\x01 Wall Creation: \x04Cancelled\x01");
}
} else {
ReplyToCommand(client, "\x04[Walls]\x01 Unknown option, try \x05/mkwall build\x01 to finish or \x04/mkwall cancel\x01 to cancel");
}
} else { } else {
WallBuilder[client].Reset(); WallBuilder[client].Reset();
if(args > 0) { if(args > 0) {
// TODO: Determine axis // Get values for X, Y and Z axis (defaulting to 1.0):
char arg2[8]; char arg2[8];
for(int i = 0; i < 3; i++) { for(int i = 0; i < 3; i++) {
GetCmdArg(i + 1, arg2, sizeof(arg2)); GetCmdArg(i + 1, arg2, sizeof(arg2));
@ -716,6 +704,8 @@ public Action Command_MakeWall(int client, int args) {
float rot[3]; float rot[3];
GetClientEyeAngles(client, rot); GetClientEyeAngles(client, rot);
// Flip X and Y depending on rotation
// TODO: Validate
if(rot[2] > 45 && rot[2] < 135 || rot[2] > -135 && rot[2] < -45) { if(rot[2] > 45 && rot[2] < 135 || rot[2] > -135 && rot[2] < -45) {
float temp = WallBuilder[client].size[0]; float temp = WallBuilder[client].size[0];
WallBuilder[client].size[0] = WallBuilder[client].size[1]; WallBuilder[client].size[0] = WallBuilder[client].size[1];
@ -724,9 +714,10 @@ public Action Command_MakeWall(int client, int args) {
WallBuilder[client].CalculateMins(); WallBuilder[client].CalculateMins();
} }
WallBuilder[client].SetMode(SCALE); WallBuilder[client].SetMode(SCALE);
GetCursorLimited(client, 100.0, WallBuilder[client].origin, Filter_IgnorePlayer); GetCursorLimited(client, 100.0, WallBuilder[client].origin, Filter_IgnorePlayer);
PrintToChat(client, "\x04[Walls]\x01 New Wall Started. End with \x05/mkwall build\x01 or \x04/mkwall cancel\x01"); PrintToChat(client, "\x04[Walls]\x01 New Wall Started. End with \x05/wall build\x01 or \x04/wall cancel\x01");
PrintToChat(client, "\x04[Walls]\x01 Mode: \x05Scale\x01"); PrintToChat(client, "\x04[Walls]\x01 Mode: \x05Scale\x01");
} }
return Plugin_Handled; return Plugin_Handled;
@ -743,7 +734,35 @@ public Action Command_ManageWalls(int client, int args) {
char arg1[16], arg2[16]; char arg1[16], arg2[16];
GetCmdArg(1, arg1, sizeof(arg1)); GetCmdArg(1, arg1, sizeof(arg1));
GetCmdArg(2, arg2, sizeof(arg2)); GetCmdArg(2, arg2, sizeof(arg2));
if(StrEqual(arg1, "delete")) { if(StrEqual(arg1, "build") || StrEqual(arg1, "done")) {
// Remove frozen flag from user, as some modes use this
int flags = GetEntityFlags(client) & ~FL_FROZEN;
SetEntityFlags(client, flags);
int id = WallBuilder[client].Build();
if(id == -1) {
PrintToChat(client, "\x04[Walls]\x01 Wall Creation: \x04Error\x01");
} else if(id == -2) {
PrintToChat(client, "\x04[Walls]\x01 Wall Edit: \x04Complete\x01");
} else if(id == -3) {
PrintToChat(client, "\x04[Walls]\x01 Entity Edit: \x04Complete\x01");
} else {
PrintToChat(client, "\x04[Walls]\x01 Wall Creation: \x05Wall #%d Created\x01", id + 1);
}
} else if(StrEqual(arg1, "cancel")) {
int flags = GetEntityFlags(client) & ~FL_FROZEN;
SetEntityFlags(client, flags);
WallBuilder[client].SetMode(INACTIVE);
PrintToChat(client, "\x04[Walls]\x01 Wall Creation: \x04Cancelled\x01");
} else if(StrEqual(arg1, "export")) {
// TODO: support exp #id
// int wallId = GetwallId(client, arg2, false);
PrintToChat(client, "{");
PrintToChat(client, "\t\"origin\" \"%.2f %.2f %.2f\"", WallBuilder[client].origin[0], WallBuilder[client].origin[1], WallBuilder[client].origin[2]);
PrintToChat(client, "\t\"angles\" \"%.2f %.2f %.2f\"", WallBuilder[client].angles[0], WallBuilder[client].angles[1], WallBuilder[client].angles[2]);
PrintToChat(client, "\t\"size\" \"%.2f %.2f %.2f\"", WallBuilder[client].size[0], WallBuilder[client].size[1], WallBuilder[client].size[2]);
PrintToChat(client, "}");
} else if(StrEqual(arg1, "delete")) {
if(StrEqual(arg2, "all")) { if(StrEqual(arg2, "all")) {
int walls = createdWalls.Length; int walls = createdWalls.Length;
for(int i = 1; i <= createdWalls.Length; i++) { for(int i = 1; i <= createdWalls.Length; i++) {
@ -808,17 +827,17 @@ public Action Command_ManageWalls(int client, int args) {
if(id > -1) { if(id > -1) {
int entity = GetWallEntity(id); int entity = GetWallEntity(id);
WallBuilder[client].Import(entity); WallBuilder[client].Import(entity);
PrintToChat(client, "\x04[Walls]\x01 Editing wall \x05%d\x01. End with \x05/mkwall build\x01 or \x04/mkwall cancel\x01", id); PrintToChat(client, "\x04[Walls]\x01 Editing wall \x05%d\x01. End with \x05/wall done\x01 or \x04/wall cancel\x01", id);
PrintToChat(client, "\x04[Walls]\x01 Mode: \x05Scale\x01"); PrintToChat(client, "\x04[Walls]\x01 Mode: \x05Scale\x01");
} }
} else if(StrEqual(arg1, "edite")) { } else if(StrEqual(arg1, "edite")) {
int index = StringToInt(arg2); int index = GetLookingEntity(client, Filter_ValidHats); //GetClientAimTarget(client, false);
if(index > 0 && IsValidEntity(index)) { if(index > 0) {
WallBuilder[client].Reset(); WallBuilder[client].Reset();
WallBuilder[client].entity = EntIndexToEntRef(index); WallBuilder[client].entity = EntIndexToEntRef(index);
WallBuilder[client].canScale = false; WallBuilder[client].canScale = false;
WallBuilder[client].SetMode(MOVE_ORIGIN); WallBuilder[client].SetMode(MOVE_ORIGIN);
PrintToChat(client, "\x04[Walls]\x01 Editing wall \x05%d\x01. End with \x05/mkwall build\x01 or \x04/mkwall cancel\x01", index); PrintToChat(client, "\x04[Walls]\x01 Editing entity \x05%d\x01. End with \x05/wall done\x01", index);
PrintToChat(client, "\x04[Walls]\x01 Mode: \x05Move & Rotate\x01"); PrintToChat(client, "\x04[Walls]\x01 Mode: \x05Move & Rotate\x01");
} else { } else {
ReplyToCommand(client, "\x04[Walls]\x01 Invalid or non existent entity"); ReplyToCommand(client, "\x04[Walls]\x01 Invalid or non existent entity");
@ -829,7 +848,7 @@ public Action Command_ManageWalls(int client, int args) {
int entity = GetWallEntity(id); int entity = GetWallEntity(id);
WallBuilder[client].Import(entity, true); WallBuilder[client].Import(entity, true);
GetCursorLimited(client, 100.0, WallBuilder[client].origin, Filter_IgnorePlayer); GetCursorLimited(client, 100.0, WallBuilder[client].origin, Filter_IgnorePlayer);
PrintToChat(client, "\x04[Walls]\x01 Editing copy of wall \x05%d\x01. End with \x05/mkwall build\x01 or \x04/mkwall cancel\x01", id); PrintToChat(client, "\x04[Walls]\x01 Editing copy of wall \x05%d\x01. End with \x05/wall build\x01 or \x04/wall cancel\x01", id);
PrintToChat(client, "\x04[Walls]\x01 Mode: \x05Scale\x01"); PrintToChat(client, "\x04[Walls]\x01 Mode: \x05Scale\x01");
} }
} else if(StrEqual(arg1, "list")) { } else if(StrEqual(arg1, "list")) {
@ -864,6 +883,7 @@ int GetWallEntity(int id) {
return createdWalls.Get(id - 1); return createdWalls.Get(id - 1);
} }
// Handles consent that a person to be hatted by another player
public int HatConsentHandler(Menu menu, MenuAction action, int target, int param2) { public int HatConsentHandler(Menu menu, MenuAction action, int target, int param2) {
if (action == MenuAction_Select) { if (action == MenuAction_Select) {
static char info[8]; static char info[8];
@ -873,7 +893,7 @@ public int HatConsentHandler(Menu menu, MenuAction action, int target, int param
int activator = GetClientOfUserId(StringToInt(str[0])); int activator = GetClientOfUserId(StringToInt(str[0]));
int hatAction = StringToInt(str[1]); int hatAction = StringToInt(str[1]);
if(activator == 0) { if(activator == 0) {
ReplyToCommand(target, "Player has gone idle or left"); ReplyToCommand(target, "Player has disconnected");
return 0; return 0;
} else if(hatAction == 1) { } else if(hatAction == 1) {
EquipHat(activator, target, "player", 0); EquipHat(activator, target, "player", 0);
@ -891,22 +911,21 @@ public int HatConsentHandler(Menu menu, MenuAction action, int target, int param
public void Event_ItemPickup(Event event, const char[] name, bool dontBroadcast) { public void Event_ItemPickup(Event event, const char[] name, bool dontBroadcast) {
int client = GetClientOfUserId(event.GetInt("userid")); int client = GetClientOfUserId(event.GetInt("userid"));
for(int slot = 0; slot <= 5; slot++) { // Check if an item picked up a user's hat and do nothing...
int wpn = GetPlayerWeaponSlot(client, slot); // for(int slot = 0; slot <= 5; slot++) {
for(int i = 1; i <= MaxClients; i++) { // int wpn = GetPlayerWeaponSlot(client, slot);
if(i != client && IsClientConnected(i) && IsClientInGame(i) && !IsFakeClient(i)) { // for(int i = 1; i <= MaxClients; i++) {
int hat = GetHat(i); // if(i != client && IsClientConnected(i) && IsClientInGame(i) && !IsFakeClient(i)) {
if(hat == wpn) { // int hat = GetHat(i);
// if(hat == wpn) {
break; // break;
} // }
} // }
} // }
} // }
} }
// TODO: Possibly detect instead the hat itself entering saferoom
public void OnEnterSaferoom(Event event, const char[] name, bool dontBroadcast) { public void OnEnterSaferoom(Event event, const char[] name, bool dontBroadcast) {
int userid = event.GetInt("userid"); int userid = event.GetInt("userid");
int client = GetClientOfUserId(userid); int client = GetClientOfUserId(userid);
@ -919,9 +938,6 @@ public void OnEnterSaferoom(Event event, const char[] name, bool dontBroadcast)
ClearHat(client, true); ClearHat(client, true);
} else { } else {
CreateTimer(2.0, Timer_PlaceHat, userid); CreateTimer(2.0, Timer_PlaceHat, userid);
// float maxflow = L4D2Direct_GetMapMaxFlowDistance()
// L4D_GetNavArea_SpawnAttributes
// L4D_GetNavAreaPos
} }
} }
} }
@ -936,28 +952,12 @@ public void OnLeaveSaferoom(Event event, const char[] name, bool dontBroadcast)
} }
} }
public void EntityOutput_OnStartTouchSaferoom(const char[] output, int caller, int client, float time) {
if(cvar_sm_hats_flags.IntValue & view_as<int>(HatConfig_NoSaferoomHats) && client > 0 && client <= MaxClients && IsValidClient(client) && GetClientTeam(client) == 2) {
if(HasHat(client)) {
if(!IsHatAllowed(client)) {
PrintToChat(client, "[Hats] Hat is not allowed in the saferoom and has been returned");
ClearHat(client, true);
} else {
CreateTimer(2.0, Timer_PlaceHat, GetClientUserId(client));
// float maxflow = L4D2Direct_GetMapMaxFlowDistance()
// L4D_GetNavArea_SpawnAttributes
// L4D_GetNavAreaPos
}
}
}
}
Action Timer_PlaceHat(Handle h, int userid) { Action Timer_PlaceHat(Handle h, int userid) {
int client = GetClientOfUserId(userid); int client = GetClientOfUserId(userid);
if(client > 0 && HasHat(client)) { if(client > 0 && HasHat(client)) {
GetClientEyePosition(client, hatData[client].orgPos); GetClientAbsOrigin(client, hatData[client].orgPos);
GetClientEyeAngles(client, hatData[client].orgAng); GetClientEyeAngles(client, hatData[client].orgAng);
GetHorizontalPositionFromOrigin(hatData[client].orgPos, hatData[client].orgAng, 80.0, hatData[client].orgPos); GetHorizontalPositionFromOrigin(hatData[client].orgPos, hatData[client].orgAng, 40.0, hatData[client].orgPos);
hatData[client].orgAng[0] = 0.0; hatData[client].orgAng[0] = 0.0;
PrintToChat(client, "[Hats] Hat has been placed down"); PrintToChat(client, "[Hats] Hat has been placed down");
ClearHat(client, true); ClearHat(client, true);
@ -982,13 +982,16 @@ Action Timer_Kill(Handle h, int entity) {
return Plugin_Handled; return Plugin_Handled;
} }
stock bool GetCursorLocation(int client, float outPos[3]) { // Tries to find a valid location at user's cursor, avoiding placing into solid walls (such as invisible walls) or objects
stock bool GetSmartCursorLocation(int client, float outPos[3]) {
float start[3], angle[3], ceilPos[3], wallPos[3], normal[3]; float start[3], angle[3], ceilPos[3], wallPos[3], normal[3];
// Get the cursor location
GetClientEyePosition(client, start); GetClientEyePosition(client, start);
GetClientEyeAngles(client, angle); GetClientEyeAngles(client, angle);
TR_TraceRayFilter(start, angle, MASK_SOLID, RayType_Infinite, Filter_NoPlayers, client); TR_TraceRayFilter(start, angle, MASK_SOLID, RayType_Infinite, Filter_NoPlayers, client);
if(TR_DidHit()) { if(TR_DidHit()) {
TR_GetEndPosition(outPos); TR_GetEndPosition(outPos);
// Check if the position is a wall
TR_GetPlaneNormal(null, normal); TR_GetPlaneNormal(null, normal);
if(normal[2] < 0.1) { if(normal[2] < 0.1) {
@ -1001,6 +1004,7 @@ stock bool GetCursorLocation(int client, float outPos[3]) {
bool ceilOK = !TR_AllSolid(); bool ceilOK = !TR_AllSolid();
TR_GetEndPosition(ceilPos); TR_GetEndPosition(ceilPos);
float distCeil = GetVectorDistance(outPos, ceilPos, true); float distCeil = GetVectorDistance(outPos, ceilPos, true);
// Find a suitable position backwards // Find a suitable position backwards
angle[0] = 70.0; angle[0] = 70.0;
angle[1] += 180.0; angle[1] += 180.0;
@ -1024,6 +1028,7 @@ stock bool GetCursorLocation(int client, float outPos[3]) {
} }
} }
// Periodically fixes hat offsets, as some events/actions/anything can cause entities to offset from their parent
Action Timer_RemountHats(Handle h) { Action Timer_RemountHats(Handle h) {
float p1[3], p2[3]; float p1[3], p2[3];
for(int i = 1; i <= MaxClients; i++) { for(int i = 1; i <= MaxClients; i++) {
@ -1049,6 +1054,7 @@ Action Timer_RemountHats(Handle h) {
return Plugin_Handled; return Plugin_Handled;
} }
// Remounts entity in a new frame to ensure their parent was properly cleared
void Frame_Remount(int i) { void Frame_Remount(int i) {
int entity = GetHat(i); int entity = GetHat(i);
if(entity == -1) return; if(entity == -1) return;
@ -1064,10 +1070,11 @@ void Frame_Remount(int i) {
} }
} }
// TODO: toggle highlight
// pick a diff model // Handles making a prop sleep after a set amount of time (called after hat yeet)
Action Timer_PropSleep(Handle h, int ref) { Action Timer_PropSleep(Handle h, int ref) {
if(IsValidEntity(ref)) { if(IsValidEntity(ref)) {
// Check if we should delete thrown hat objects, such as physic props
if(cvar_sm_hats_flags.IntValue & view_as<int>(HatConfig_DeleteThrownHats)) { if(cvar_sm_hats_flags.IntValue & view_as<int>(HatConfig_DeleteThrownHats)) {
// Don't delete if someone has hatted it (including ourself): // Don't delete if someone has hatted it (including ourself):
for(int i = 1; i <= MaxClients; i++) { for(int i = 1; i <= MaxClients; i++) {
@ -1075,6 +1082,8 @@ Action Timer_PropSleep(Handle h, int ref) {
return Plugin_Handled; return Plugin_Handled;
} }
} }
// Check for prop_ class, only yeetable non-player entity we care as they may be large/collidabl
// Things like weapons aren't a problem as you can't "collide" and get thrown
char classname[64]; char classname[64];
GetEntityClassname(ref, classname, sizeof(classname)); GetEntityClassname(ref, classname, sizeof(classname));
if(StrContains(classname, "prop_") > -1) { if(StrContains(classname, "prop_") > -1) {
@ -1119,7 +1128,6 @@ Action Timer_RemoveGod(Handle h, int userid) {
return Plugin_Handled; return Plugin_Handled;
} }
public void Event_PlayerOutOfIdle(Event event, const char[] name, bool dontBroadcast) { public void Event_PlayerOutOfIdle(Event event, const char[] name, bool dontBroadcast) {
int bot = GetClientOfUserId(event.GetInt("bot")); int bot = GetClientOfUserId(event.GetInt("bot"));
int client = GetClientOfUserId(event.GetInt("player")); int client = GetClientOfUserId(event.GetInt("player"));
@ -1303,9 +1311,9 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3
return Plugin_Handled; return Plugin_Handled;
} else if(tick - cmdThrottle[client] > 0.25) { } else if(tick - cmdThrottle[client] > 0.25) {
if(buttons & IN_ATTACK) { if(buttons & IN_ATTACK) {
ClientCommand(client, "sm_hat %s", 'y'); ClientCommand(client, "sm_hat y");
} else if(buttons & IN_DUCK) { } else if(buttons & IN_DUCK) {
ClientCommand(client, "sm_hat %s", 'p'); ClientCommand(client, "sm_hat p");
} }
} }
} else if(tick - cmdThrottle[client] > 0.25 && L4D2_GetPlayerUseAction(client) == L4D2UseAction_None) { } else if(tick - cmdThrottle[client] > 0.25 && L4D2_GetPlayerUseAction(client) == L4D2UseAction_None) {
@ -1319,15 +1327,15 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3
///#WALL BUILDER PROCESS ///#WALL BUILDER PROCESS
if(WallBuilder[client].IsActive()) { if(WallBuilder[client].IsActive()) {
if(buttons & IN_USE && buttons & IN_RELOAD) {
ClientCommand(client, "sm_wall done");
return Plugin_Handled;
}
bool allowMove = true; bool allowMove = true;
switch(WallBuilder[client].mode) { switch(WallBuilder[client].mode) {
case MOVE_ORIGIN: { case MOVE_ORIGIN: {
SetWeaponDelay(client, 0.5); SetWeaponDelay(client, 0.5);
// switch(buttons) {
// case IN_: WallBuilder[client].CycleSpeed(client, tick);
// }
if(WallBuilder[client].movetype == 0) {
bool isRotate; bool isRotate;
int flags = GetEntityFlags(client); int flags = GetEntityFlags(client);
if(buttons & IN_USE) { if(buttons & IN_USE) {
@ -1337,43 +1345,32 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3
if(buttons & IN_ATTACK) WallBuilder[client].CycleAxis(client, tick); if(buttons & IN_ATTACK) WallBuilder[client].CycleAxis(client, tick);
else if(buttons & IN_ATTACK2) WallBuilder[client].CycleSnapAngle(client, tick); else if(buttons & IN_ATTACK2) WallBuilder[client].CycleSnapAngle(client, tick);
if(tick - cmdThrottle[client] > 0.25) { if(tick - cmdThrottle[client] > 0.20) {
if(WallBuilder[client].axis == 0) { if(WallBuilder[client].axis == 0) {
if(mouse[1] > 10) WallBuilder[client].angles[0] += WallBuilder[client].snapAngle; if(mouse[1] > 10) WallBuilder[client].angles[0] += WallBuilder[client].snapAngle;
else if(mouse[1] < -10) WallBuilder[client].angles[0] -= WallBuilder[client].snapAngle; else if(mouse[1] < -10) WallBuilder[client].angles[0] -= WallBuilder[client].snapAngle;
} else if(WallBuilder[client].axis == 1) { } else if(WallBuilder[client].axis == 1) {
if(mouse[0] > 10) WallBuilder[client].angles[1] += WallBuilder[client].snapAngle; if(mouse[0] > 10) WallBuilder[client].angles[1] += WallBuilder[client].snapAngle;
else if(mouse[0] < -10) WallBuilder[client].angles[1] -= WallBuilder[client].snapAngle; else if(mouse[0] < -10) WallBuilder[client].angles[1] -= WallBuilder[client].snapAngle;
} else {
if(mouse[1] > 10) WallBuilder[client].angles[2] += WallBuilder[client].snapAngle;
else if(mouse[1] < -10) WallBuilder[client].angles[2] -= WallBuilder[client].snapAngle;
} }
cmdThrottle[client] = tick; cmdThrottle[client] = tick;
} }
} else { } else {
switch(buttons) { // Move position
case IN_ATTACK: WallBuilder[client].moveDistance++; if(buttons & IN_ATTACK) WallBuilder[client].moveDistance++;
case IN_ATTACK2: WallBuilder[client].moveDistance--; else if(buttons & IN_ATTACK2) WallBuilder[client].moveDistance--;
case IN_WALK: WallBuilder[client].CycleMoveMode(client, tick);
}
} }
// Clear IN_FROZEN when no longer rotate
if(!isRotate && flags & FL_FROZEN) { if(!isRotate && flags & FL_FROZEN) {
flags = ~flags & FL_FROZEN; flags = flags & ~FL_FROZEN;
SetEntityFlags(client, flags); SetEntityFlags(client, flags);
} }
GetCursorLimited(client, WallBuilder[client].moveDistance, WallBuilder[client].origin, Filter_IgnorePlayerAndWall);
// GetCursorLocationLimited(client, WallBuilder[client].moveDistance, WallBuilder[client].origin); GetCursorLimited2(client, WallBuilder[client].moveDistance, WallBuilder[client].origin, Filter_IgnorePlayerAndWall, buttons & IN_SPEED == 0);
} else if(WallBuilder[client].movetype == 1) {
switch(buttons) {
case IN_FORWARD: WallBuilder[client].origin[0] += WallBuilder[client].moveSpeed;
case IN_BACK: WallBuilder[client].origin[0] -= WallBuilder[client].moveSpeed;
case IN_MOVELEFT: WallBuilder[client].origin[1] += WallBuilder[client].moveSpeed;
case IN_MOVERIGHT: WallBuilder[client].origin[1] -= WallBuilder[client].moveSpeed;
case IN_JUMP: WallBuilder[client].origin[2] += WallBuilder[client].moveSpeed;
case IN_DUCK: WallBuilder[client].origin[2] -= WallBuilder[client].moveSpeed;
}
allowMove = false;
} else {
GetCursorLocation(client, WallBuilder[client].origin);
}
} }
case SCALE: { case SCALE: {
SetWeaponDelay(client, 0.5); SetWeaponDelay(client, 0.5);
@ -1418,6 +1415,7 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3
return allowMove ? Plugin_Continue : Plugin_Handled; return allowMove ? Plugin_Continue : Plugin_Handled;
} }
return Plugin_Continue; return Plugin_Continue;
} }
@ -1465,9 +1463,9 @@ public void OnClientDisconnect(int client) {
public void OnEntityDestroyed(int entity) { public void OnEntityDestroyed(int entity) {
for(int i = 1; i <= MaxClients; i++) { for(int i = 1; i <= MaxClients; i++) {
if(IsClientConnected(i) && IsClientInGame(i) && !IsFakeClient(i)) { if(IsClientConnected(i) && IsClientInGame(i) && !IsFakeClient(i)) {
if(EntRefToEntIndex(hatData[i].entity) == entity) { if(hatData[i].entity != INVALID_ENT_REFERENCE && EntRefToEntIndex(hatData[i].entity) == entity) {
ClearHat(i); ClearHat(i);
PrintHintText(i, "Hat entity has vanished"); PrintHintText(i, "Hat has vanished");
ClientCommand(i, "play ui/menu_back.wav"); ClientCommand(i, "play ui/menu_back.wav");
break; break;
} }
@ -1484,8 +1482,6 @@ public void OnMapStart() {
} }
NavAreas = GetSpawnLocations(); NavAreas = GetSpawnLocations();
HookEntityOutput("info_changelevel", "OnStartTouch", EntityOutput_OnStartTouchSaferoom);
HookEntityOutput("trigger_changelevel", "OnStartTouch", EntityOutput_OnStartTouchSaferoom);
} }
public void OnMapEnd() { public void OnMapEnd() {
@ -1540,9 +1536,7 @@ stock bool Filter_IgnorePlayerAndWall(int entity, int mask, int data) {
return entity > 0 && entity != data && EntRefToEntIndex(WallBuilder[data].entity) != entity; return entity > 0 && entity != data && EntRefToEntIndex(WallBuilder[data].entity) != entity;
} }
bool Filter_IgnorePlayer(int entity, int mask, int data) {
return entity > 0 && entity != data;
}
bool Filter_ValidHats(int entity, int mask, int data) { bool Filter_ValidHats(int entity, int mask, int data) {
if(entity == data) return false; if(entity == data) return false;
if(entity <= MaxClients) { if(entity <= MaxClients) {
@ -1588,7 +1582,7 @@ void ClearHat(int i, bool restore = false) {
return; return;
} }
int flags = ~GetEntityFlags(entity) & FL_FROZEN; int flags = GetEntityFlags(entity) & ~FL_FROZEN;
SetEntityFlags(entity, flags); SetEntityFlags(entity, flags);
// if(HasEntProp(entity, Prop_Send, "m_flModelScale")) // if(HasEntProp(entity, Prop_Send, "m_flModelScale"))
// SetEntPropFloat(entity, Prop_Send, "m_flModelScale", 1.0); // SetEntPropFloat(entity, Prop_Send, "m_flModelScale", 1.0);
@ -1634,9 +1628,8 @@ int GetHat(int client) {
} }
int GetHatter(int client) { int GetHatter(int client) {
int myRef = EntIndexToEntRef(client);
for(int i = 1; i <= MaxClients; i++) { for(int i = 1; i <= MaxClients; i++) {
if(hatData[client].entity == myRef) { if(EntRefToEntIndex(hatData[client].entity) == client) {
return i; return i;
} }
} }
@ -1668,6 +1661,20 @@ bool IsHatAllowed(int client) {
return false; return false;
} }
bool CanHatBePlaced(int client, const float pos[3]) {
if(cvar_sm_hats_flags.IntValue & view_as<int>(HatConfig_NoSaferoomHats)) {
Address nav = L4D_GetNearestNavArea(pos, 200.0);
if(nav != Address_Null) {
int spawnFlags = L4D_GetNavArea_SpawnAttributes(nav) ;
if(spawnFlags & NAV_SPAWN_CHECKPOINT) {
PrintToServer("\"%L\" tried to place hat in saferoom, denied.", client);
PrintToChat(client, "[Hats] Hats are not allowed in saferoom and has been returned.");
return false;
}
}
}
return true;
}
void SetFlag(int client, hatFlags flag) { void SetFlag(int client, hatFlags flag) {
hatData[client].flags |= view_as<int>(flag); hatData[client].flags |= view_as<int>(flag);
@ -1694,9 +1701,11 @@ void EquipHat(int client, int entity, const char[] classname = "", int flags = H
hatData[client].solidType = GetEntProp(modifyEntity, Prop_Send, "m_nSolidType"); hatData[client].solidType = GetEntProp(modifyEntity, Prop_Send, "m_nSolidType");
hatData[client].moveType = GetEntProp(modifyEntity, Prop_Send, "movetype"); hatData[client].moveType = GetEntProp(modifyEntity, Prop_Send, "movetype");
if(client <= MaxClients) SDKHook(client, SDKHook_OnTakeDamageAlive, OnTakeDamageAlive);
if(entity <= MaxClients) SDKHook(entity, SDKHook_OnTakeDamageAlive, OnTakeDamageAlive);
if(modifyEntity <= MaxClients) { if(modifyEntity <= MaxClients) {
SDKHook(modifyEntity, SDKHook_OnTakeDamageAlive, OnTakeDamageAlive);
AcceptEntityInput(modifyEntity, "DisableLedgeHang"); AcceptEntityInput(modifyEntity, "DisableLedgeHang");
} else if(cvar_sm_hats_flags.IntValue & view_as<int>(HatConfig_FakeHat)) { } else if(cvar_sm_hats_flags.IntValue & view_as<int>(HatConfig_FakeHat)) {
return; return;
@ -1753,7 +1762,7 @@ void EquipHat(int client, int entity, const char[] classname = "", int flags = H
PrintToChat(entity, "[Hats] %N has set themselves as your hat", client); PrintToChat(entity, "[Hats] %N has set themselves as your hat", client);
} }
} else { } else {
if(StrEqual(classname, "infected")) { if(StrEqual(classname, "infected") || StrEqual(classname, "witch")) {
int eflags = GetEntityFlags(entity) | FL_FROZEN; int eflags = GetEntityFlags(entity) | FL_FROZEN;
SetEntityFlags(entity, eflags); SetEntityFlags(entity, eflags);
hatData[client].offset[2] = 36.0; hatData[client].offset[2] = 36.0;
@ -1781,11 +1790,6 @@ void EquipHat(int client, int entity, const char[] classname = "", int flags = H
hatData[client].moveType = GetEntProp(modifyEntity, Prop_Send, "movetype"); hatData[client].moveType = GetEntProp(modifyEntity, Prop_Send, "movetype");
if(StrEqual(classname, "witch", false)) {
TeleportEntity(entity, EMPTY_ANG, NULL_VECTOR, NULL_VECTOR);
SetFlag(client, HAT_POCKET);
}
if(!HasFlag(client, HAT_POCKET)) { if(!HasFlag(client, HAT_POCKET)) {
// TeleportEntity(entity, EMPTY_ANG, EMPTY_ANG, NULL_VECTOR); // TeleportEntity(entity, EMPTY_ANG, EMPTY_ANG, NULL_VECTOR);
if(HasFlag(client, HAT_REVERSED)) { if(HasFlag(client, HAT_REVERSED)) {
@ -1842,6 +1846,7 @@ void GlowWall(int id, float lifetime = 5.0) {
GetEntPropVector(ref, Prop_Send, "m_vecMaxs", maxs); GetEntPropVector(ref, Prop_Send, "m_vecMaxs", maxs);
Effect_DrawBeamBoxRotatableToAll(pos, mins, maxs, angles, g_iLaserIndex, 0, 0, 30, lifetime, 0.4, 0.4, 0, 1.0, WALL_COLOR, 0); Effect_DrawBeamBoxRotatableToAll(pos, mins, maxs, angles, g_iLaserIndex, 0, 0, 30, lifetime, 0.4, 0.4, 0, 1.0, WALL_COLOR, 0);
} }
void DeleteWall(int id) { void DeleteWall(int id) {
GlowWall(id); GlowWall(id);
int ref = GetWallEntity(id); int ref = GetWallEntity(id);
@ -1884,7 +1889,7 @@ stock void LookAtPoint(int entity, const float destination[3]){
if(angles[0] >= 270){ if(angles[0] >= 270){
angles[0] -= 270; angles[0] -= 270;
angles[0] = (90-angles[0]); angles[0] = (90-angles[0]);
}else{ } else {
if(angles[0] <= 90){ if(angles[0] <= 90){
angles[0] *= -1; angles[0] *= -1;
} }
@ -1896,3 +1901,26 @@ stock void LookAtPoint(int entity, const float destination[3]){
stock float SnapTo(const float value, const float degree) { stock float SnapTo(const float value, const float degree) {
return float(RoundFloat(value / degree)) * degree; return float(RoundFloat(value / degree)) * degree;
} }
// Gets a position from where the cursor is upto distance away (basically <= distance, going against walls)
stock bool GetCursorLimited2(int client, float distance, float endPos[3], TraceEntityFilter filter, bool doCollide = true) {
if (client > 0 && client <= MaxClients && IsClientInGame(client)) {
float clientEye[3], clientAngle[3], direction[3];
GetClientEyePosition(client, clientEye);
GetClientEyeAngles(client, clientAngle);
GetAngleVectors(clientAngle, direction, NULL_VECTOR, NULL_VECTOR);
ScaleVector(direction, distance);
AddVectors(clientEye, direction, endPos);
if(doCollide) {
TR_TraceRayFilter(clientEye, endPos, MASK_OPAQUE, RayType_EndPoint, filter, client);
if (TR_DidHit(INVALID_HANDLE)) {
TR_GetEndPosition(endPos);
}
}
return true;
}
return false;
}

View file

@ -1,6 +1,6 @@
/* /*
* Anti Rush * Anti Rush
* Copyright (C) 2021 Silvers * Copyright (C) 2023 Silvers
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -18,7 +18,7 @@
#define PLUGIN_VERSION "1.17" #define PLUGIN_VERSION "1.19"
#define DEBUG_BENCHMARK 0 // 0=Off. 1=Benchmark logic function. #define DEBUG_BENCHMARK 0 // 0=Off. 1=Benchmark logic function.
/*====================================================================================== /*======================================================================================
@ -33,6 +33,15 @@
======================================================================================== ========================================================================================
Change Log: Change Log:
1.19 (10-Mar-2023)
- Added cvar "l4d_anti_rush_health" to hurt players who are rushing. Requested by "Voevoda".
- Changed cvar "l4d_anti_rush_type" to allow disabling teleport or slowdown, to only enable health drain.
- Translation phrases updated to support the health drain only type. Thanks to "Voevoda" and "HarryPotter" for updating Russian and Chinese translations.
1.18 (01-Jun-2022)
- L4D1: Fixed throwing errors.
- L4D2: Added map "c5m5_bridge" to the data config.
1.17 (04-Dec-2021) 1.17 (04-Dec-2021)
- Changes to fix warnings when compiling on SourceMod 1.11. - Changes to fix warnings when compiling on SourceMod 1.11.
@ -136,8 +145,8 @@ float g_iBenchTicks;
#define EVENTS_CONFIG "data/l4d_anti_rush.cfg" #define EVENTS_CONFIG "data/l4d_anti_rush.cfg"
ConVar g_hCvarAllow, g_hCvarMPGameMode, g_hCvarModes, g_hCvarModesOff, g_hCvarModesTog, g_hCvarFinale, g_hCvarFlags, g_hCvarIgnore, g_hCvarIncap, g_hCvarPlayers, g_hCvarRangeLast, g_hCvarRangeLead, g_hCvarSlow, g_hCvarTank, g_hCvarText, g_hCvarTime, g_hCvarType, g_hCvarWarnLast, g_hCvarWarnLead, g_hCvarWarnTime; ConVar g_hCvarAllow, g_hCvarMPGameMode, g_hCvarModes, g_hCvarModesOff, g_hCvarModesTog, g_hCvarFinale, g_hCvarFlags, g_hCvarIgnore, g_hCvarHealth, g_hCvarIncap, g_hCvarPlayers, g_hCvarRangeLast, g_hCvarRangeLead, g_hCvarSlow, g_hCvarTank, g_hCvarText, g_hCvarTime, g_hCvarType, g_hCvarWarnLast, g_hCvarWarnLead, g_hCvarWarnTime;
float g_fCvarRangeLast, g_fCvarRangeLead, g_fCvarSlow, g_fCvarTime, g_fCvarWarnLast, g_fCvarWarnLead, g_fCvarWarnTime; float g_fCvarHealth, g_fCvarRangeLast, g_fCvarRangeLead, g_fCvarSlow, g_fCvarTime, g_fCvarWarnLast, g_fCvarWarnLead, g_fCvarWarnTime;
int g_iCvarFinale, g_iCvarFlags, g_iCvarIgnore, g_iCvarIncap, g_iCvarPlayers, g_iCvarTank, g_iCvarText, g_iCvarType; int g_iCvarFinale, g_iCvarFlags, g_iCvarIgnore, g_iCvarIncap, g_iCvarPlayers, g_iCvarTank, g_iCvarText, g_iCvarType;
bool g_bCvarAllow, g_bMapStarted, g_bLeft4Dead2; bool g_bCvarAllow, g_bMapStarted, g_bLeft4Dead2;
@ -145,6 +154,7 @@ bool g_bInhibit[MAXPLAYERS+1];
float g_fHintLast[MAXPLAYERS+1]; float g_fHintLast[MAXPLAYERS+1];
float g_fHintWarn[MAXPLAYERS+1]; float g_fHintWarn[MAXPLAYERS+1];
float g_fLastFlow[MAXPLAYERS+1]; float g_fLastFlow[MAXPLAYERS+1];
float g_fHighestFlow[MAXPLAYERS+1];
Handle g_hTimer; Handle g_hTimer;
char g_sMap[PLATFORM_MAX_PATH]; char g_sMap[PLATFORM_MAX_PATH];
@ -152,6 +162,7 @@ bool g_bFoundMap;
bool g_bEventStarted; bool g_bEventStarted;
float g_fEventExtended; float g_fEventExtended;
ArrayList g_hElevators; ArrayList g_hElevators;
GlobalForward g_OnRushForward; GlobalForward g_OnRushForward;
@ -193,16 +204,17 @@ public void OnPluginStart()
g_hCvarModesTog = CreateConVar( "l4d_anti_rush_modes_tog", "0", "Turn on the plugin in these game modes. 0=All, 1=Coop, 2=Survival, 4=Versus, 8=Scavenge. Add numbers together.", CVAR_FLAGS ); g_hCvarModesTog = CreateConVar( "l4d_anti_rush_modes_tog", "0", "Turn on the plugin in these game modes. 0=All, 1=Coop, 2=Survival, 4=Versus, 8=Scavenge. Add numbers together.", CVAR_FLAGS );
g_hCvarFinale = CreateConVar( "l4d_anti_rush_finale", g_bLeft4Dead2 ? "2" : "0", "Should the plugin activate in finales. 0=Off. 1=All finales. 2=Gauntlet type finales (L4D2 only).", CVAR_FLAGS ); g_hCvarFinale = CreateConVar( "l4d_anti_rush_finale", g_bLeft4Dead2 ? "2" : "0", "Should the plugin activate in finales. 0=Off. 1=All finales. 2=Gauntlet type finales (L4D2 only).", CVAR_FLAGS );
g_hCvarFlags = CreateConVar( "l4d_anti_rush_flags", "", "Players with these flags will be immune from teleporting forward when behind or slowing down when ahead.", CVAR_FLAGS ); g_hCvarFlags = CreateConVar( "l4d_anti_rush_flags", "", "Players with these flags will be immune from teleporting forward when behind or slowing down when ahead.", CVAR_FLAGS );
g_hCvarHealth = CreateConVar( "l4d_anti_rush_health", "0", "0=Off. Amount of health to remove every second when someone is rushing.", CVAR_FLAGS);
g_hCvarIgnore = CreateConVar( "l4d_anti_rush_ignore", "0", "Should players with the immune flags be counted toward total flow distance. 0=Ignore them. 1=Count them.", CVAR_FLAGS ); g_hCvarIgnore = CreateConVar( "l4d_anti_rush_ignore", "0", "Should players with the immune flags be counted toward total flow distance. 0=Ignore them. 1=Count them.", CVAR_FLAGS );
g_hCvarIncap = CreateConVar( "l4d_anti_rush_incapped", "0", "0=Off. How many survivors must be incapped before ignoring them in calculating rushers and slackers.", CVAR_FLAGS ); g_hCvarIncap = CreateConVar( "l4d_anti_rush_incapped", "0", "0=Off. How many survivors must be incapped before ignoring them in calculating rushers and slackers.", CVAR_FLAGS );
g_hCvarPlayers = CreateConVar( "l4d_anti_rush_players", "3", "Minimum number of alive survivors before the function kicks in. Must be 3 or greater otherwise the lead/last and average cannot be detected.", CVAR_FLAGS, true, 3.0 ); g_hCvarPlayers = CreateConVar( "l4d_anti_rush_players", "3", "Minimum number of alive survivors before the function kicks in. Must be 3 or greater otherwise the lead/last and average cannot be detected.", CVAR_FLAGS, true, 3.0 );
g_hCvarRangeLast = CreateConVar( "l4d_anti_rush_range_last", "3000.0", "How far behind someone can travel from the average Survivor distance before being teleported forward.", CVAR_FLAGS, true, MINIMUM_RANGE ); g_hCvarRangeLast = CreateConVar( "l4d_anti_rush_range_last", "3000.0", "0.0=Off. How far behind someone can travel from the average Survivor distance before being teleported forward.", CVAR_FLAGS );
g_hCvarRangeLead = CreateConVar( "l4d_anti_rush_range_lead", "3000.0", "How far forward someone can travel from the average Survivor distance before being teleported or slowed down.", CVAR_FLAGS, true, MINIMUM_RANGE ); g_hCvarRangeLead = CreateConVar( "l4d_anti_rush_range_lead", "3000.0", "How far forward someone can travel from the average Survivor distance before being teleported, slowed down or health drained.", CVAR_FLAGS, true, MINIMUM_RANGE );
g_hCvarSlow = CreateConVar( "l4d_anti_rush_slow", "75.0", "Maximum speed someone can travel when being slowed down.", CVAR_FLAGS, true, 20.0 ); g_hCvarSlow = CreateConVar( "l4d_anti_rush_slow", "75.0", "Maximum speed someone can travel when being slowed down.", CVAR_FLAGS, true, 20.0 );
g_hCvarTank = CreateConVar( "l4d_anti_rush_tanks", "1", "0=Off. 1=On. Should Anti-Rush be enabled when there are active Tanks.", CVAR_FLAGS ); g_hCvarTank = CreateConVar( "l4d_anti_rush_tanks", "1", "0=Off. 1=On. Should Anti-Rush be enabled when there are active Tanks.", CVAR_FLAGS );
g_hCvarText = CreateConVar( "l4d_anti_rush_text", "1", "0=Off. 1=Print To Chat. 2=Hint Text. Display a message to someone rushing, or falling behind.", CVAR_FLAGS ); g_hCvarText = CreateConVar( "l4d_anti_rush_text", "1", "0=Off. 1=Print To Chat. 2=Hint Text. Display a message to someone rushing, or falling behind.", CVAR_FLAGS );
g_hCvarTime = CreateConVar( "l4d_anti_rush_time", "10", "How often to print the message to someone if slowdown is enabled and affecting them.", CVAR_FLAGS ); g_hCvarTime = CreateConVar( "l4d_anti_rush_time", "10", "How often to print the message to someone if slowdown is enabled and affecting them.", CVAR_FLAGS );
g_hCvarType = CreateConVar( "l4d_anti_rush_type", "1", "What to do with rushers. 1=Slowdown player speed when moving forward. 2=Teleport back to group. 3=Spawn Special On Them", CVAR_FLAGS ); g_hCvarType = CreateConVar( "l4d_anti_rush_type", "1", "What to do with rushers. 0=Ignore (used for health drain / forward only). 1=Slowdown player speed when moving forward. 2=Teleport back to group.", CVAR_FLAGS );
g_hCvarWarnLast = CreateConVar( "l4d_anti_rush_warn_last", "2500.0", "How far behind someone can travel from the average Survivor distance before being warned about being teleported.", CVAR_FLAGS, true, MINIMUM_RANGE ); g_hCvarWarnLast = CreateConVar( "l4d_anti_rush_warn_last", "2500.0", "How far behind someone can travel from the average Survivor distance before being warned about being teleported.", CVAR_FLAGS, true, MINIMUM_RANGE );
g_hCvarWarnLead = CreateConVar( "l4d_anti_rush_warn_lead", "2500.0", "How far forward someone can travel from the average Survivor distance before being warned about being teleported or slowed down.", CVAR_FLAGS, true, MINIMUM_RANGE ); g_hCvarWarnLead = CreateConVar( "l4d_anti_rush_warn_lead", "2500.0", "How far forward someone can travel from the average Survivor distance before being warned about being teleported or slowed down.", CVAR_FLAGS, true, MINIMUM_RANGE );
g_hCvarWarnTime = CreateConVar( "l4d_anti_rush_warn_time", "15.0", "0.0=Off. How often to print a message to someone warning them they are ahead or behind and will be teleported or slowed down.", CVAR_FLAGS ); g_hCvarWarnTime = CreateConVar( "l4d_anti_rush_warn_time", "15.0", "0.0=Off. How often to print a message to someone warning them they are ahead or behind and will be teleported or slowed down.", CVAR_FLAGS );
@ -217,6 +229,7 @@ public void OnPluginStart()
g_hCvarAllow.AddChangeHook(ConVarChanged_Allow); g_hCvarAllow.AddChangeHook(ConVarChanged_Allow);
g_hCvarFinale.AddChangeHook(ConVarChanged_Cvars); g_hCvarFinale.AddChangeHook(ConVarChanged_Cvars);
g_hCvarFlags.AddChangeHook(ConVarChanged_Cvars); g_hCvarFlags.AddChangeHook(ConVarChanged_Cvars);
g_hCvarHealth.AddChangeHook(ConVarChanged_Cvars);
g_hCvarIgnore.AddChangeHook(ConVarChanged_Cvars); g_hCvarIgnore.AddChangeHook(ConVarChanged_Cvars);
g_hCvarIncap.AddChangeHook(ConVarChanged_Cvars); g_hCvarIncap.AddChangeHook(ConVarChanged_Cvars);
g_hCvarPlayers.AddChangeHook(ConVarChanged_Cvars); g_hCvarPlayers.AddChangeHook(ConVarChanged_Cvars);
@ -250,12 +263,12 @@ public void OnConfigsExecuted()
IsAllowed(); IsAllowed();
} }
public void ConVarChanged_Allow(Handle convar, const char[] oldValue, const char[] newValue) void ConVarChanged_Allow(Handle convar, const char[] oldValue, const char[] newValue)
{ {
IsAllowed(); IsAllowed();
} }
public void ConVarChanged_Cvars(Handle convar, const char[] oldValue, const char[] newValue) void ConVarChanged_Cvars(Handle convar, const char[] oldValue, const char[] newValue)
{ {
GetCvars(); GetCvars();
} }
@ -268,6 +281,7 @@ void GetCvars()
g_iCvarIgnore = g_hCvarIgnore.IntValue; g_iCvarIgnore = g_hCvarIgnore.IntValue;
g_iCvarFinale = g_hCvarFinale.IntValue; g_iCvarFinale = g_hCvarFinale.IntValue;
g_fCvarHealth = g_hCvarHealth.FloatValue;
g_iCvarIncap = g_hCvarIncap.IntValue; g_iCvarIncap = g_hCvarIncap.IntValue;
g_iCvarPlayers = g_hCvarPlayers.IntValue; g_iCvarPlayers = g_hCvarPlayers.IntValue;
g_fCvarTime = g_hCvarTime.FloatValue; g_fCvarTime = g_hCvarTime.FloatValue;
@ -377,7 +391,7 @@ bool IsAllowedGameMode()
return true; return true;
} }
public void OnGamemode(const char[] output, int caller, int activator, float delay) void OnGamemode(const char[] output, int caller, int activator, float delay)
{ {
if( strcmp(output, "OnCoop") == 0 ) if( strcmp(output, "OnCoop") == 0 )
g_iCurrentMode = 1; g_iCurrentMode = 1;
@ -428,7 +442,7 @@ bool ParseConfigFile(const char[] file)
return (result == SMCError_Okay); return (result == SMCError_Okay);
} }
public SMCResult ColorConfig_NewSection(Handle parser, const char[] section, bool quotes) SMCResult ColorConfig_NewSection(Handle parser, const char[] section, bool quotes)
{ {
g_iSectionLevel++; g_iSectionLevel++;
@ -443,7 +457,7 @@ public SMCResult ColorConfig_NewSection(Handle parser, const char[] section, boo
return SMCParse_Continue; return SMCParse_Continue;
} }
public SMCResult ColorConfig_KeyValue(Handle parser, const char[] key, const char[] value, bool key_quotes, bool value_quotes) SMCResult ColorConfig_KeyValue(Handle parser, const char[] key, const char[] value, bool key_quotes, bool value_quotes)
{ {
// On / Off // On / Off
if( g_iSectionLevel == 2 && g_bFoundMap ) if( g_iSectionLevel == 2 && g_bFoundMap )
@ -478,24 +492,24 @@ public SMCResult ColorConfig_KeyValue(Handle parser, const char[] key, const cha
return SMCParse_Continue; return SMCParse_Continue;
} }
public void OutputStart(const char[] output, int caller, int activator, float delay) void OutputStart(const char[] output, int caller, int activator, float delay)
{ {
g_bEventStarted = true; g_bEventStarted = true;
} }
public void OutputStop(const char[] output, int caller, int activator, float delay) void OutputStop(const char[] output, int caller, int activator, float delay)
{ {
g_bEventStarted = false; g_bEventStarted = false;
} }
public SMCResult ColorConfig_EndSection(Handle parser) SMCResult ColorConfig_EndSection(Handle parser)
{ {
g_iSectionLevel--; g_iSectionLevel--;
return SMCParse_Continue; return SMCParse_Continue;
} }
public void ColorConfig_End(Handle parser, bool halted, bool failed) void ColorConfig_End(Handle parser, bool halted, bool failed)
{ {
if( failed ) if( failed )
SetFailState("Error: Cannot load the config file: \"%s\"", EVENTS_CONFIG); SetFailState("Error: Cannot load the config file: \"%s\"", EVENTS_CONFIG);
@ -536,7 +550,7 @@ int FindByClassTargetName(const char[] sClass, const char[] sTarget)
// ==================================================================================================== // ====================================================================================================
// EVENTS // EVENTS
// ==================================================================================================== // ====================================================================================================
public void Event_RoundStart(Event event, const char[] name, bool dontBroadcast) void Event_RoundStart(Event event, const char[] name, bool dontBroadcast)
{ {
delete g_hTimer; delete g_hTimer;
@ -568,13 +582,13 @@ public void Event_RoundStart(Event event, const char[] name, bool dontBroadcast)
} }
} }
public void Event_RoundEnd(Event event, const char[] name, bool dontBroadcast) void Event_RoundEnd(Event event, const char[] name, bool dontBroadcast)
{ {
ResetSlowdown(); ResetSlowdown();
ResetPlugin(); ResetPlugin();
} }
public void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast) void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast)
{ {
int client = GetClientOfUserId(event.GetInt("userid")); int client = GetClientOfUserId(event.GetInt("userid"));
if( client ) if( client )
@ -583,7 +597,7 @@ public void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast
} }
} }
public void Event_PlayerTeam(Event event, const char[] name, bool dontBroadcast) void Event_PlayerTeam(Event event, const char[] name, bool dontBroadcast)
{ {
int client = GetClientOfUserId(event.GetInt("userid")); int client = GetClientOfUserId(event.GetInt("userid"));
if( client ) if( client )
@ -624,6 +638,7 @@ void ResetClient(int i)
g_fHintLast[i] = 0.0; g_fHintLast[i] = 0.0;
g_fHintWarn[i] = 0.0; g_fHintWarn[i] = 0.0;
g_fLastFlow[i] = 0.0; g_fLastFlow[i] = 0.0;
g_fHighestFlow[i] = 0.0;
SDKUnhook(i, SDKHook_PreThinkPost, PreThinkPost); SDKUnhook(i, SDKHook_PreThinkPost, PreThinkPost);
} }
@ -646,7 +661,7 @@ void ResetSlowdown()
// ==================================================================================================== // ====================================================================================================
// LOGIC // LOGIC
// ==================================================================================================== // ====================================================================================================
public Action TimerTest(Handle timer) Action TimerTest(Handle timer)
{ {
if( !g_bMapStarted ) return Plugin_Continue; if( !g_bMapStarted ) return Plugin_Continue;
@ -712,7 +727,7 @@ public Action TimerTest(Handle timer)
continue; continue;
// Ignore healing / using stuff // Ignore healing / using stuff
if( GetEntPropEnt(client, Prop_Send, "m_useActionTarget") > 0 ) if( g_bLeft4Dead2 && GetEntPropEnt(client, Prop_Send, "m_useActionTarget") > 0 )
continue; continue;
// Ignore reviving // Ignore reviving
@ -734,6 +749,13 @@ public Action TimerTest(Handle timer)
// Get flow // Get flow
flow = L4D2Direct_GetFlowDistance(client); flow = L4D2Direct_GetFlowDistance(client);
// Only get the highest flow
if(flow > g_fHighestFlow[client]) {
g_fHighestFlow[client] = flow;
} else {
flow = g_fHighestFlow[client];
}
if( flow && flow != -9999.0 ) // Invalid flows if( flow && flow != -9999.0 ) // Invalid flows
{ {
countflow++; countflow++;
@ -792,17 +814,17 @@ public Action TimerTest(Handle timer)
{ {
g_fHintWarn[client] = GetGameTime() + g_fCvarWarnTime; g_fHintWarn[client] = GetGameTime() + g_fCvarWarnTime;
if( g_iCvarType == 1 ) switch( g_iCvarType )
ClientHintMessage(client, "Warn_Slowdown"); {
else case 0: ClientHintMessage(client, "Warn_Health");
ClientHintMessage(client, "Warn_Ahead"); case 1: ClientHintMessage(client, "Warn_Slowdown");
case 2: ClientHintMessage(client, "Warn_Ahead");
}
} }
// Compare higher flow with next survivor, they're rushing // Compare higher flow with next survivor, they're rushing
if( distance > g_fCvarRangeLead ) if( distance > g_fCvarRangeLead )
{ {
// PrintToServer("RUSH: %N %f", client, distance);
flowBack = false;
int punishType = g_iCvarType, result; int punishType = g_iCvarType, result;
Call_StartForward(g_OnRushForward); Call_StartForward(g_OnRushForward);
@ -812,8 +834,11 @@ public Action TimerTest(Handle timer)
if(Call_Finish(result) == SP_ERROR_NONE && result > 0) break; if(Call_Finish(result) == SP_ERROR_NONE && result > 0) break;
// PrintToServer("RUSH: %N %f", client, distance);
flowBack = false;
// Slowdown enabled? // Slowdown enabled?
if( punishType == 1 ) if( g_fCvarHealth || punishType == 1 )
{ {
// Inhibit moving forward // Inhibit moving forward
// Only check > or < because when == the same flow distance, they're either already being slowed or running back, so we don't want to change/affect them within the same flow NavMesh. // Only check > or < because when == the same flow distance, they're either already being slowed or running back, so we don't want to change/affect them within the same flow NavMesh.
@ -821,7 +846,7 @@ public Action TimerTest(Handle timer)
{ {
g_fLastFlow[client] = flow; g_fLastFlow[client] = flow;
if( g_bInhibit[client] == false ) if( g_iCvarType == 1 && g_bInhibit[client] == false )
{ {
g_bInhibit[client] = true; g_bInhibit[client] = true;
SDKHook(client, SDKHook_PreThinkPost, PreThinkPost); SDKHook(client, SDKHook_PreThinkPost, PreThinkPost);
@ -832,7 +857,17 @@ public Action TimerTest(Handle timer)
{ {
g_fHintLast[client] = GetGameTime() + g_fCvarTime; g_fHintLast[client] = GetGameTime() + g_fCvarTime;
ClientHintMessage(client, "Rush_Slowdown"); switch( g_iCvarType )
{
case 0: ClientHintMessage(client, "Rush_Health");
case 1: ClientHintMessage(client, "Rush_Slowdown");
}
}
// Hurt for rushing?
if( g_fCvarHealth )
{
SDKHooks_TakeDamage(client, 0, 0, g_fCvarHealth);
} }
} }
else if( flow < g_fLastFlow[client] ) else if( flow < g_fLastFlow[client] )
@ -973,7 +1008,7 @@ public Action L4D_OnGetWalkTopSpeed(int target, float &retVal)
} }
// */ // */
public void PreThinkPost(int client) void PreThinkPost(int client)
{ {
SetEntPropFloat(client, Prop_Send, "m_flMaxspeed", g_fCvarSlow); SetEntPropFloat(client, Prop_Send, "m_flMaxspeed", g_fCvarSlow);
} }

View file

@ -80,7 +80,7 @@ public void OnPluginStart() {
void ShowRepMenu(int client, int targetUserid) { void ShowRepMenu(int client, int targetUserid) {
Menu menu = new Menu(RepFinalHandler); Menu menu = new Menu(RepFinalHandler);
menu.SetTitle("Choose a rating"); menu.SetTitle("Choose a rating");
char id[8]; char id[16];
Format(id, sizeof(id), "%d|1", targetUserid); Format(id, sizeof(id), "%d|1", targetUserid);
menu.AddItem(id, "+Rep"); menu.AddItem(id, "+Rep");
Format(id, sizeof(id), "%d|1", targetUserid); Format(id, sizeof(id), "%d|1", targetUserid);
@ -91,7 +91,7 @@ void ShowRepMenu(int client, int targetUserid) {
public int RepPlayerHandler(Menu menu, MenuAction action, int param1, int param2) { public int RepPlayerHandler(Menu menu, MenuAction action, int param1, int param2) {
/* If an option was selected, tell the client about the item. */ /* If an option was selected, tell the client about the item. */
if (action == MenuAction_Select) { if (action == MenuAction_Select) {
static char info[4]; static char info[8];
menu.GetItem(param2, info, sizeof(info)); menu.GetItem(param2, info, sizeof(info));
int targetUserid = StringToInt(info); int targetUserid = StringToInt(info);
int target = GetClientOfUserId(targetUserid); int target = GetClientOfUserId(targetUserid);
@ -111,7 +111,7 @@ public int RepPlayerHandler(Menu menu, MenuAction action, int param1, int param2
public int RepFinalHandler(Menu menu, MenuAction action, int param1, int param2) { public int RepFinalHandler(Menu menu, MenuAction action, int param1, int param2) {
/* If an option was selected, tell the client about the item. */ /* If an option was selected, tell the client about the item. */
if (action == MenuAction_Select) { if (action == MenuAction_Select) {
char info[8]; char info[16];
menu.GetItem(param2, info, sizeof(info)); menu.GetItem(param2, info, sizeof(info));
char str[2][8]; char str[2][8];
ExplodeString(info, "|", str, 2, 8, false); ExplodeString(info, "|", str, 2, 8, false);
@ -443,7 +443,7 @@ public void DB_FindNotes(Database db, DBResultSet results, const char[] error, a
CPrintChatToAdmins(" > {olive}%d Auto Actions Applied", actions); CPrintChatToAdmins(" > {olive}%d Auto Actions Applied", actions);
} }
if(repP > 0 || repN > 0) { if(repP > 0 || repN > 0) {
CPrintChatToAdmins(" > {olive}%d +rep\t{yellow}-rep", repP, repN); CPrintChatToAdmins(" > {olive}%d +rep\t{yellow}%d-rep", repP, repN);
} }
} }
} }
@ -615,6 +615,6 @@ any Native_AddNoteIdentity(Handle plugin, int numParams) {
void AddNoteIdentity(const char noteCreator[32], const char noteTarget[32], const char[] message) { void AddNoteIdentity(const char noteCreator[32], const char noteTarget[32], const char[] message) {
// messaege length + steamids (32 + 32 + null term) // messaege length + steamids (32 + 32 + null term)
// char[] query = new char[strlen(message) + 65]; // char[] query = new char[strlen(message) + 65];
DB.Format(query, sizeof(query), "INSERT INTO `notes` (steamid, markedBy, content) VALUES ('%s', '%s', '%s')", noteCreator, noteTarget, message); DB.Format(query, sizeof(query), "INSERT INTO `notes` (steamid, markedBy, content) VALUES ('%s', '%s', '%s')", noteTarget, noteCreator, message);
DB.Query(DB_AddNote, query); DB.Query(DB_AddNote, query);
} }