mirror of
https://github.com/Jackzmc/sourcemod-plugins.git
synced 2025-05-06 16:43:21 +00:00
update
This commit is contained in:
parent
078b7c4bf6
commit
cb66da2ca2
50 changed files with 3176 additions and 383 deletions
533
scripting/GrabEnt.sp
Normal file
533
scripting/GrabEnt.sp
Normal file
|
@ -0,0 +1,533 @@
|
|||
#define DEBUG
|
||||
|
||||
#define PLUGIN_AUTHOR "Stugger"
|
||||
#define PLUGIN_VERSION "2.2"
|
||||
|
||||
#include <sourcemod>
|
||||
#include <sdktools>
|
||||
#include <sdkhooks>
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "GrabEnt",
|
||||
author = PLUGIN_AUTHOR,
|
||||
description = "Grab then Move, Push/Pull or Rotate the entity you're looking at until released",
|
||||
version = PLUGIN_VERSION,
|
||||
url = ""
|
||||
};
|
||||
|
||||
int g_pGrabbedEnt[MAXPLAYERS + 1];
|
||||
int g_eRotationAxis[MAXPLAYERS + 1] = { -1, ... };
|
||||
int g_eOriginalColor[MAXPLAYERS + 1][4];
|
||||
|
||||
float g_pLastButtonPress[MAXPLAYERS + 1];
|
||||
float g_fGrabOffset[MAXPLAYERS + 1][3];
|
||||
float g_fGrabDistance[MAXPLAYERS + 1];
|
||||
|
||||
MoveType g_pLastMoveType[MAXPLAYERS + 1];
|
||||
bool g_pInRotationMode[MAXPLAYERS + 1];
|
||||
bool g_eReleaseFreeze[MAXPLAYERS + 1] = { true, ... };
|
||||
|
||||
Handle g_eGrabTimer[MAXPLAYERS+1];
|
||||
|
||||
int g_BeamSprite;
|
||||
int g_HaloSprite;
|
||||
|
||||
#define MAX_FORBIDDEN_CLASSNAMES 10
|
||||
static char FORBIDDEN_CLASSNAMES[MAX_FORBIDDEN_CLASSNAMES][] = {
|
||||
"env_physics_blocker",
|
||||
"env_player_blocker",
|
||||
"func_brush",
|
||||
"func_simpleladder",
|
||||
"func_button",
|
||||
"func_elevator",
|
||||
"func_button_timed",
|
||||
// "func_movelinear",
|
||||
// "infected",
|
||||
"func_lod",
|
||||
"func_door",
|
||||
"prop_ragdoll"
|
||||
};
|
||||
|
||||
public void OnPluginStart()
|
||||
{
|
||||
RegAdminCmd("sm_grabent_freeze", Cmd_ReleaseFreeze, ADMFLAG_CHEATS, "<0/1> - Toggle entity freeze/unfreeze on release.");
|
||||
RegAdminCmd("sm_grab", Cmd_Grab, ADMFLAG_CHEATS, "Toggle Grab the entity in your crosshair.");
|
||||
RegAdminCmd("+grabent", Cmd_Grab, ADMFLAG_CHEATS, "Grab the entity in your crosshair.");
|
||||
RegAdminCmd("-grabent", Cmd_Release, ADMFLAG_CHEATS, "Release the grabbed entity.");
|
||||
}
|
||||
|
||||
public void OnMapStart()
|
||||
{
|
||||
g_BeamSprite = PrecacheModel("materials/sprites/laser.vmt", true);
|
||||
g_HaloSprite = PrecacheModel("materials/sprites/halo01.vmt", true);
|
||||
|
||||
for (int i = 0; i < MAXPLAYERS; i++) {
|
||||
g_pGrabbedEnt[i] = -1;
|
||||
g_eRotationAxis[i] = -1;
|
||||
g_pLastButtonPress[i] = 0.0;
|
||||
|
||||
g_pInRotationMode[i] = false;
|
||||
g_eReleaseFreeze[i] = true;
|
||||
|
||||
g_eGrabTimer[i] = null;
|
||||
}
|
||||
}
|
||||
public void OnClientDisconnect(client)
|
||||
{
|
||||
if (g_pGrabbedEnt[client] != -1 && IsValidEntity(g_pGrabbedEnt[client]))
|
||||
Cmd_Release(client, 0);
|
||||
|
||||
g_eRotationAxis[client] = -1;
|
||||
|
||||
g_pLastButtonPress[client] = 0.0;
|
||||
|
||||
g_pInRotationMode[client] = false;
|
||||
g_eReleaseFreeze[client] = true;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// FREEZE SETTING COMMAND //
|
||||
//============================================================================
|
||||
public Action Cmd_ReleaseFreeze(client, args)
|
||||
{
|
||||
if (args < 1) {
|
||||
ReplyToCommand(client, "\x04[SM]\x01 \x05sm_grabent_freeze <0/1>\x01 -- \x050\x01: Entity unfreeze on release, \x051\x01: Entity freeze on release");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
char sArg[16];
|
||||
GetCmdArg(1, sArg, sizeof(sArg)); TrimString(sArg);
|
||||
|
||||
if (!StrEqual(sArg, "0") && !StrEqual(sArg, "1")) {
|
||||
ReplyToCommand(client, "\x04[SM]\x01 ERROR: Value can only be either 0 or 1");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
g_eReleaseFreeze[client] = StrEqual(sArg, "1") ? true : false;
|
||||
|
||||
PrintToChat(client, "\x04[SM]\x01 Entities will now be \x05%s\x01 on Release!", g_eReleaseFreeze[client] == true ? "Frozen" : "Unfrozen");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// GRAB ENTITY COMMAND //
|
||||
//============================================================================
|
||||
public Action Cmd_Grab(client, args) {
|
||||
if (client < 1 || client > MaxClients || !IsClientInGame(client))
|
||||
return Plugin_Handled;
|
||||
|
||||
if (g_pGrabbedEnt[client] > 0 && IsValidEntity(g_pGrabbedEnt[client])) {
|
||||
Cmd_Release(client, 0);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
// int ent = GetClientAimTarget(client, false);
|
||||
int ent = GetLookingEntity(client, Filter_IgnoreForbidden);
|
||||
|
||||
if (ent == -1 || !IsValidEntity(ent))
|
||||
return Plugin_Handled; //<-- timer to allow search for entity??
|
||||
|
||||
float entOrigin[3], playerGrabOrigin[3];
|
||||
GetEntPropVector(ent, Prop_Send, "m_vecOrigin", entOrigin);
|
||||
GetClientEyePosition(client, playerGrabOrigin);
|
||||
|
||||
g_pGrabbedEnt[client] = ent;
|
||||
|
||||
// Get the point at which the ray first hit the entity
|
||||
float initialRay[3];
|
||||
initialRay[0] = GetInitialRayPosition(client, 'x');
|
||||
initialRay[1] = GetInitialRayPosition(client, 'y');
|
||||
initialRay[2] = GetInitialRayPosition(client, 'z');
|
||||
|
||||
// Calculate the offset between intitial ray hit and the entities origin
|
||||
g_fGrabOffset[client][0] = entOrigin[0] - initialRay[0];
|
||||
g_fGrabOffset[client][1] = entOrigin[1] - initialRay[1];
|
||||
g_fGrabOffset[client][2] = entOrigin[2] - initialRay[2];
|
||||
|
||||
// Calculate the distance between ent and player
|
||||
float xDis = Pow(initialRay[0]-(playerGrabOrigin[0]), 2.0);
|
||||
float yDis = Pow(initialRay[1]-(playerGrabOrigin[1]), 2.0);
|
||||
float zDis = Pow(initialRay[2]-(playerGrabOrigin[2]), 2.0);
|
||||
g_fGrabDistance[client] = SquareRoot((xDis)+(yDis)+(zDis));
|
||||
|
||||
// Get and Store entities original color (useful if colored)
|
||||
int entColor[4];
|
||||
int colorOffset = GetEntSendPropOffs(ent, "m_clrRender");
|
||||
|
||||
if (colorOffset > 0)
|
||||
{
|
||||
entColor[0] = GetEntData(ent, colorOffset, 1);
|
||||
entColor[1] = GetEntData(ent, colorOffset + 1, 1);
|
||||
entColor[2] = GetEntData(ent, colorOffset + 2, 1);
|
||||
entColor[3] = GetEntData(ent, colorOffset + 3, 1);
|
||||
}
|
||||
|
||||
g_eOriginalColor[client][0] = entColor[0];
|
||||
g_eOriginalColor[client][1] = entColor[1];
|
||||
g_eOriginalColor[client][2] = entColor[2];
|
||||
g_eOriginalColor[client][3] = entColor[3];
|
||||
|
||||
// Set entities color to grab color (green and semi-transparent)
|
||||
SetEntityRenderMode(ent, RENDER_TRANSALPHA);
|
||||
SetEntityRenderColor(ent, 0, 255, 0, 235);
|
||||
|
||||
// Freeze entity
|
||||
char sClass[64];
|
||||
GetEntityClassname(ent, sClass, sizeof(sClass)); TrimString(sClass);
|
||||
|
||||
if (StrEqual(sClass, "player", false)) {
|
||||
g_pLastMoveType[ent] = GetEntityMoveType(ent);
|
||||
SetEntityMoveType(ent, MOVETYPE_NONE);
|
||||
} else
|
||||
AcceptEntityInput(ent, "DisableMotion");
|
||||
|
||||
|
||||
g_pLastMoveType[client] = GetEntityMoveType(client);
|
||||
// Disable weapon prior to timer
|
||||
SetWeaponDelay(client, 1.0);
|
||||
|
||||
// Make sure rotation mode can immediately be entered
|
||||
g_pLastButtonPress[client] = GetGameTime() - 2.0;
|
||||
g_pInRotationMode[client] = false;
|
||||
|
||||
DataPack pack;
|
||||
g_eGrabTimer[client] = CreateDataTimer(0.1, Timer_UpdateGrab, pack, TIMER_REPEAT);
|
||||
pack.WriteCell(client);
|
||||
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// TIMER FOR GRAB ENTITY //
|
||||
//============================================================================
|
||||
public Action Timer_UpdateGrab(Handle timer, DataPack pack) {
|
||||
int client;
|
||||
pack.Reset();
|
||||
client = pack.ReadCell();
|
||||
|
||||
if (!IsValidEntity(client) || client < 1 || client > MaxClients || !IsClientInGame(client))
|
||||
return Plugin_Stop;
|
||||
|
||||
if (g_pGrabbedEnt[client] == -1 || !IsValidEntity(g_pGrabbedEnt[client]))
|
||||
return Plugin_Stop;
|
||||
|
||||
// Continuously delay use of weapon, as to not fire any bullets when pushing/pulling/rotating
|
||||
SetWeaponDelay(client, 1.0);
|
||||
|
||||
// *** Enable/Disable Rotation Mode
|
||||
if (GetClientButtons(client) & IN_RELOAD) {
|
||||
// Avoid instant enable/disable of rotation mode by requiring a one second buffer
|
||||
if (GetGameTime() - g_pLastButtonPress[client] >= 1.0) {
|
||||
g_pLastButtonPress[client] = GetGameTime();
|
||||
g_pInRotationMode[client] = g_pInRotationMode[client] == true ? false : true;
|
||||
PrintToChat(client, "\x04[SM]\x01 Rotation Mode \x05%s\x01", g_pInRotationMode[client] == true ? "Enabled" : "Disabled");
|
||||
|
||||
// Restore the entities color and alpha if enabling
|
||||
if(g_pInRotationMode[client]) {
|
||||
SetEntityRenderColor(g_pGrabbedEnt[client], 255, 255, 255, 255);
|
||||
PrintToChat(client, "\x05[A]\x01 RED \x05[S]\x01 GREEN \x05[D]\x01 BLUE \x05[W]\x01 SHOW RINGS");
|
||||
}
|
||||
// Change back to grabbed color if disabling
|
||||
else
|
||||
SetEntityRenderColor(g_pGrabbedEnt[client], 0, 255, 0, 235);
|
||||
}
|
||||
}
|
||||
// ***In Rotation Mode
|
||||
if (g_pInRotationMode[client]) {
|
||||
SetEntityMoveType(client, MOVETYPE_NONE);
|
||||
|
||||
float ang[3], pos[3], mins[3], maxs[3];
|
||||
GetEntPropVector(g_pGrabbedEnt[client], Prop_Send, "m_angRotation", ang);
|
||||
GetEntPropVector(g_pGrabbedEnt[client], Prop_Send, "m_vecOrigin", pos);
|
||||
GetEntPropVector(g_pGrabbedEnt[client], Prop_Send, "m_vecMins", mins);
|
||||
GetEntPropVector(g_pGrabbedEnt[client], Prop_Send, "m_vecMaxs", maxs);
|
||||
|
||||
// If the entity is a child, it will have a null position, so we'll hesitantly use the parents position
|
||||
int parent = GetEntPropEnt(g_pGrabbedEnt[client], Prop_Data, "m_hMoveParent");
|
||||
if (parent > 0 && IsValidEntity(parent))
|
||||
GetEntPropVector(parent, Prop_Send, "m_vecOrigin", pos);
|
||||
|
||||
// Get rotation axis from button press
|
||||
int buttonPress = GetClientButtons(client);
|
||||
switch(buttonPress) {
|
||||
case IN_FORWARD: {
|
||||
g_eRotationAxis[client] = -1; // [W] = Show Rings
|
||||
PrintToChat(client, "\x04[SM]\x01 Show Rings \x05On\x01");
|
||||
}
|
||||
case IN_MOVELEFT: {
|
||||
g_eRotationAxis[client] = 0; // [A] = x axis
|
||||
PrintToChat(client, "\x04[SM]\x01 Rotation Axis \x05X\x01");
|
||||
}
|
||||
case IN_BACK: {
|
||||
g_eRotationAxis[client] = 1; // [S] = y axis
|
||||
PrintToChat(client, "\x04[SM]\x01 Rotation Axis \x05Y\x01");
|
||||
}
|
||||
case IN_MOVERIGHT: {
|
||||
g_eRotationAxis[client] = 2; // [D] = z axis
|
||||
PrintToChat(client, "\x04[SM]\x01 Rotation Axis \x05Z\x01");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Reset angles when A+S+D is pressed
|
||||
if((buttonPress & IN_MOVELEFT) && (buttonPress & IN_BACK) && (buttonPress & IN_MOVERIGHT)) {
|
||||
ang[0] = 0.0; ang[1] = 0.0; ang[2] = 0.0;
|
||||
g_eRotationAxis[client] = -1;
|
||||
}
|
||||
|
||||
// Largest side should dictate the diameter of the rings
|
||||
float diameter, sendAng[3];
|
||||
diameter = (maxs[0] > maxs[1]) ? (maxs[0] + 10.0) : (maxs[1] + 10.0);
|
||||
diameter = ((maxs[2] + 10.0) > diameter) ? (maxs[2] + 10.0) : diameter;
|
||||
|
||||
// Sending original ang will cause non-stop rotation issue
|
||||
sendAng = ang;
|
||||
|
||||
// Draw rotation rings
|
||||
switch(g_eRotationAxis[client]) {
|
||||
case -1: CreateRing(client, sendAng, pos, diameter, 0, true); // all 3 rings
|
||||
case 0: CreateRing(client, sendAng, pos, diameter, 0, false); // red (x)
|
||||
case 1: CreateRing(client, sendAng, pos, diameter, 1, false); // green (y)
|
||||
case 2: CreateRing(client, sendAng, pos, diameter, 2, false); // blue (z)
|
||||
}
|
||||
|
||||
// Rotate with mouse if on a rotation axis (A,S,D)
|
||||
if (g_eRotationAxis[client] != -1) {
|
||||
// + Rotate
|
||||
if (GetClientButtons(client) & IN_ATTACK)
|
||||
ang[g_eRotationAxis[client]] += 10.0;
|
||||
// - Rotate
|
||||
else if (GetClientButtons(client) & IN_ATTACK2)
|
||||
ang[g_eRotationAxis[client]] -= 10.0;
|
||||
}
|
||||
|
||||
TeleportEntity(g_pGrabbedEnt[client], NULL_VECTOR, ang, NULL_VECTOR);
|
||||
}
|
||||
// ***Not in Rotation Mode
|
||||
if (!g_pInRotationMode[client] || g_eRotationAxis[client] == -1) {
|
||||
// Keep track of player noclip as to avoid forced enable/disable
|
||||
if(!g_pInRotationMode[client]) {
|
||||
SetEntityMoveType(client, g_pLastMoveType[client])
|
||||
}
|
||||
// Push entity (Allowed if we're in rotation mode, not on a rotation axis (-1))
|
||||
if (GetClientButtons(client) & IN_ATTACK)
|
||||
{
|
||||
if (g_fGrabDistance[client] < 80)
|
||||
g_fGrabDistance[client] += 10;
|
||||
else
|
||||
g_fGrabDistance[client] += g_fGrabDistance[client] / 25;
|
||||
}
|
||||
// Pull entity (Allowed if we're in rotation mode, not on a rotation axis (-1))
|
||||
else if (GetClientButtons(client) & IN_ATTACK2 && g_fGrabDistance[client] > 25)
|
||||
{
|
||||
if (g_fGrabDistance[client] < 80)
|
||||
g_fGrabDistance[client] -= 10;
|
||||
else
|
||||
g_fGrabDistance[client] -= g_fGrabDistance[client] / 25;
|
||||
}
|
||||
|
||||
g_eRotationAxis[client] = -1;
|
||||
}
|
||||
|
||||
// *** Runs whether in rotation mode or not
|
||||
float entNewPos[3];
|
||||
entNewPos[0] = GetEntNewPosition(client, 'x') + g_fGrabOffset[client][0];
|
||||
entNewPos[1] = GetEntNewPosition(client, 'y') + g_fGrabOffset[client][1];
|
||||
entNewPos[2] = GetEntNewPosition(client, 'z') + g_fGrabOffset[client][2];
|
||||
|
||||
float mins[3];
|
||||
GetEntPropVector(g_pGrabbedEnt[client], Prop_Data, "m_vecMins", mins);
|
||||
entNewPos[2] += mins[2];
|
||||
|
||||
TeleportEntity(g_pGrabbedEnt[client], entNewPos, NULL_VECTOR, NULL_VECTOR);
|
||||
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// RELEASE ENTITY COMMAND //
|
||||
//============================================================================
|
||||
public Action Cmd_Release(client, args) {
|
||||
if (!IsValidEntity(client) || client < 1 || client > MaxClients || !IsClientInGame(client))
|
||||
return Plugin_Handled;
|
||||
|
||||
if (g_pGrabbedEnt[client] == -1 || !IsValidEntity(g_pGrabbedEnt[client]))
|
||||
return Plugin_Handled;
|
||||
|
||||
// Allow near-immediate use of weapon
|
||||
SetWeaponDelay(client, 0.2);
|
||||
|
||||
SetEntityMoveType(client, g_pLastMoveType[client]);
|
||||
|
||||
|
||||
// Unfreeze if target was a player and unfreeze if setting is set to 0
|
||||
char sClass[64];
|
||||
GetEntityClassname(g_pGrabbedEnt[client], sClass, sizeof(sClass)); TrimString(sClass);
|
||||
|
||||
if (StrEqual(sClass, "player", false))
|
||||
SetEntityMoveType(g_pGrabbedEnt[client], g_pLastMoveType[g_pGrabbedEnt[client]]);
|
||||
else if (g_eReleaseFreeze[client] == false)
|
||||
AcceptEntityInput(g_pGrabbedEnt[client], "EnableMotion");
|
||||
|
||||
// Restore color and alpha to original prior to grab
|
||||
SetEntityRenderColor(g_pGrabbedEnt[client], g_eOriginalColor[client][0], g_eOriginalColor[client][1], g_eOriginalColor[client][2], g_eOriginalColor[client][3]);
|
||||
|
||||
// Kill the grab timer and reset control values
|
||||
if (g_eGrabTimer[client] != null) {
|
||||
KillTimer(g_eGrabTimer[client]);
|
||||
g_eGrabTimer[client] = null;
|
||||
}
|
||||
|
||||
g_pGrabbedEnt[client] = -1;
|
||||
g_eRotationAxis[client] = -1;
|
||||
g_pInRotationMode[client] = false;
|
||||
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// *** UTILITIES *** //
|
||||
//============================================================================
|
||||
int GetLookingEntity(int client, TraceEntityFilter filter) {
|
||||
static float pos[3], ang[3];
|
||||
GetClientEyePosition(client, pos);
|
||||
GetClientEyeAngles(client, ang);
|
||||
TR_TraceRayFilter(pos, ang, MASK_ALL, RayType_Infinite, filter, client);
|
||||
if(TR_DidHit()) {
|
||||
return TR_GetEntityIndex();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
stock float GetEntNewPosition(int client, char axis)
|
||||
{
|
||||
if (client > 0 && client <= MaxClients && IsClientInGame(client)) {
|
||||
float endPos[3], clientEye[3], clientAngle[3], direction[3];
|
||||
GetClientEyePosition(client, clientEye);
|
||||
GetClientEyeAngles(client, clientAngle);
|
||||
|
||||
GetAngleVectors(clientAngle, direction, NULL_VECTOR, NULL_VECTOR);
|
||||
ScaleVector(direction, g_fGrabDistance[client]);
|
||||
AddVectors(clientEye, direction, endPos);
|
||||
|
||||
TR_TraceRayFilter(clientEye, endPos, MASK_SOLID, RayType_EndPoint, TraceRayFilterEnt, client);
|
||||
if (TR_DidHit(INVALID_HANDLE)) {
|
||||
TR_GetEndPosition(endPos);
|
||||
}
|
||||
|
||||
if (axis == 'x') return endPos[0];
|
||||
else if (axis == 'y') return endPos[1];
|
||||
else if (axis == 'z') return endPos[2];
|
||||
}
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
/////
|
||||
stock float GetInitialRayPosition(int client, char axis)
|
||||
{
|
||||
if (client > 0 && client <= MaxClients && IsClientInGame(client)) {
|
||||
float endPos[3], clientEye[3], clientAngle[3];
|
||||
GetClientEyePosition(client, clientEye);
|
||||
GetClientEyeAngles(client, clientAngle);
|
||||
|
||||
TR_TraceRayFilter(clientEye, clientAngle, MASK_SOLID, RayType_Infinite, TraceRayFilterActivator, client);
|
||||
if (TR_DidHit(INVALID_HANDLE))
|
||||
TR_GetEndPosition(endPos);
|
||||
|
||||
if (axis == 'x') return endPos[0];
|
||||
else if (axis == 'y') return endPos[1];
|
||||
else if (axis == 'z') return endPos[2];
|
||||
}
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
/////
|
||||
stock void SetWeaponDelay(int client, float delay)
|
||||
{
|
||||
if (IsValidEntity(client) && client > 0 && client <= MaxClients && IsClientInGame(client)) {
|
||||
int pWeapon = GetEntPropEnt(client, Prop_Send, "m_hActiveWeapon");
|
||||
|
||||
if (IsValidEntity(pWeapon) && pWeapon != -1) {
|
||||
SetEntPropFloat(pWeapon, Prop_Send, "m_flNextPrimaryAttack", GetGameTime() + delay);
|
||||
SetEntPropFloat(pWeapon, Prop_Send, "m_flNextSecondaryAttack", GetGameTime() + delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
/////
|
||||
stock void CreateRing(int client, float ang[3], float pos[3], float diameter, int axis, bool trio)
|
||||
{
|
||||
if (!IsValidEntity(client) || client < 1 || client > MaxClients || !IsClientInGame(client))
|
||||
return;
|
||||
|
||||
float ringVecs[26][3];
|
||||
int ringColor[3][4];
|
||||
|
||||
ringColor[0] = { 255, 0, 0, 255 };
|
||||
ringColor[1] = { 0, 255, 0, 255 };
|
||||
ringColor[2] = { 0, 0, 255, 255 };
|
||||
|
||||
int numSides = (!trio) ? 26 : 17;
|
||||
float angIncrement = (!trio) ? 15.0 : 24.0;
|
||||
|
||||
for (int i = 1; i < numSides; i++) {
|
||||
float direction[3], endPos[3];
|
||||
switch(axis) {
|
||||
case 0: GetAngleVectors(ang, direction, NULL_VECTOR, NULL_VECTOR);
|
||||
case 1:
|
||||
{
|
||||
ang[2] = 0.0;
|
||||
GetAngleVectors(ang, NULL_VECTOR, direction, NULL_VECTOR);
|
||||
}
|
||||
case 2: GetAngleVectors(ang, NULL_VECTOR, NULL_VECTOR, direction);
|
||||
}
|
||||
|
||||
ScaleVector(direction, diameter);
|
||||
AddVectors(pos, direction, endPos);
|
||||
|
||||
if (i == 1) ringVecs[0] = endPos;
|
||||
|
||||
ringVecs[i] = endPos;
|
||||
ang[axis] += angIncrement;
|
||||
|
||||
TE_SetupBeamPoints(ringVecs[i-1], ringVecs[i], g_BeamSprite, g_HaloSprite, 0, 15, 0.2, 2.5, 2.5, 1, 0.0, ringColor[axis], 10);
|
||||
TE_SendToClient(client, 0.0);
|
||||
|
||||
if(trio && i == numSides-1 && axis < 2) {
|
||||
i = 0;
|
||||
ang[axis] -= angIncrement * (numSides-1);
|
||||
axis += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// *** FILTERS *** //
|
||||
//============================================================================
|
||||
|
||||
public bool TraceRayFilterEnt(int entity, int mask, any:client)
|
||||
{
|
||||
if (entity == client || entity == g_pGrabbedEnt[client])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
/////
|
||||
public bool TraceRayFilterActivator(int entity, int mask, any:activator)
|
||||
{
|
||||
if (entity == activator)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Filter_IgnoreForbidden(int entity, int mask, int data) {
|
||||
if(entity == data || entity == 0) return false;
|
||||
if(entity <= MaxClients) return true;
|
||||
static char classname[32];
|
||||
GetEntityClassname(entity, classname, sizeof(classname));
|
||||
for(int i = 0; i < MAX_FORBIDDEN_CLASSNAMES; i++) {
|
||||
if(StrEqual(FORBIDDEN_CLASSNAMES[i], classname)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
|
@ -185,7 +185,7 @@ int GetAllowedPlayerIndex(const char[] authid2) {
|
|||
public void OnClientPostAdminCheck(int client) {
|
||||
if(!IsFakeClient(client)) {
|
||||
if(reserveMode == Reserve_AdminOnly && GetUserAdmin(client) == INVALID_ADMIN_ID) {
|
||||
static char auth[32];
|
||||
char auth[32];
|
||||
GetClientAuthId(client, AuthId_Steam2, auth, sizeof(auth));
|
||||
if(GetAllowedPlayerIndex(auth) == -1) {
|
||||
KickClient(client, "Sorry, server is reserved");
|
||||
|
@ -454,7 +454,7 @@ public Action Command_SetClientModel(int client, int args) {
|
|||
if(args < 1) {
|
||||
ReplyToCommand(client, "Usage: sm_model <model> [player] ['keep']");
|
||||
} else {
|
||||
static char arg1[2], arg2[16], arg3[8];
|
||||
char arg1[2], arg2[16], arg3[8];
|
||||
GetCmdArg(1, arg1, sizeof(arg1));
|
||||
GetCmdArg(2, arg2, sizeof(arg2));
|
||||
GetCmdArg(3, arg3, sizeof(arg3));
|
||||
|
@ -644,6 +644,8 @@ public Action VGUIMenu(UserMsg msg_id, Handle bf, const int[] players, int playe
|
|||
public void OnClientPutInServer(int client) {
|
||||
if(!IsFakeClient(client))
|
||||
SDKHook(client, SDKHook_WeaponEquip, Event_OnWeaponEquip);
|
||||
else
|
||||
SDKHook(client, SDKHook_OnTakeDamage, Event_OnTakeDamage);
|
||||
}
|
||||
|
||||
public void OnClientDisconnect(int client) {
|
||||
|
@ -656,14 +658,16 @@ public void OnClientDisconnect(int client) {
|
|||
botDropMeleeWeapon[client] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
public void Event_PlayerDisconnect(Event event, const char[] name, bool dontBroadcast) {
|
||||
int client = GetClientOfUserId(event.GetInt("userid"));
|
||||
if(client && !IsFakeClient(client)) {
|
||||
static char auth[32];
|
||||
char auth[32];
|
||||
GetClientAuthId(client, AuthId_Steam2, auth, sizeof(auth));
|
||||
SteamIDs.Remove(auth);
|
||||
}
|
||||
}
|
||||
|
||||
int disabledItem[2048];
|
||||
//Can also probably prevent kit drop to pick them up
|
||||
public void Event_WeaponDrop(Event event, const char[] name, bool dontBroadcast) {
|
||||
|
@ -744,7 +748,7 @@ public void Event_BotPlayerSwap(Event event, const char[] name, bool dontBroadca
|
|||
public Action Event_OnWeaponDrop(int client, int weapon) {
|
||||
if(!IsValidEntity(weapon) || !IsFakeClient(client)) return Plugin_Continue;
|
||||
if(GetEntProp(client, Prop_Send, "m_humanSpectatorUserID") > 0) {
|
||||
static char wpn[32];
|
||||
char wpn[32];
|
||||
GetEdictClassname(weapon, wpn, sizeof(wpn));
|
||||
if(StrEqual(wpn, "weapon_melee") || StrEqual(wpn, "weapon_pistol_magnum")) {
|
||||
#if defined DEBUG
|
||||
|
@ -757,7 +761,8 @@ public Action Event_OnWeaponDrop(int client, int weapon) {
|
|||
return Plugin_Continue;
|
||||
}
|
||||
public void Frame_HideEntity(int entity) {
|
||||
TeleportEntity(entity, OUT_OF_BOUNDS, NULL_VECTOR, NULL_VECTOR);
|
||||
if(IsValidEntity(entity))
|
||||
TeleportEntity(entity, OUT_OF_BOUNDS, NULL_VECTOR, NULL_VECTOR);
|
||||
}
|
||||
//STUCK BOTS WITH ZOMBIES FIX
|
||||
public Action Event_OnTakeDamage(int victim, int& attacker, int& inflictor, float& damage, int& damagetype) {
|
||||
|
@ -768,11 +773,11 @@ public Action Event_OnTakeDamage(int victim, int& attacker, int& inflictor, floa
|
|||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
bool attackerVisible = IsEntityInSightRange(victim, attacker, 130.0, 100.0);
|
||||
bool attackerVisible = IsEntityInSightRange(victim, attacker, 130.0, 10000.0);
|
||||
if(!attackerVisible) {
|
||||
//Zombie is behind the bot, reduce damage taken and slowly kill zombie (1/10 of default hp per hit)
|
||||
damage /= 2.0;
|
||||
SDKHooks_TakeDamage(attacker, victim, victim, 10.0);
|
||||
SDKHooks_TakeDamage(attacker, victim, victim, 30.0);
|
||||
return Plugin_Changed;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -101,6 +101,10 @@ public void OnPluginEnd() {
|
|||
TriggerTimer(pushTimer, true);
|
||||
}
|
||||
|
||||
public void OnMapEnd() {
|
||||
TriggerTimer(pushTimer, true);
|
||||
}
|
||||
|
||||
public void OnMapStart() {
|
||||
static char curMap[64];
|
||||
GetCurrentMap(curMap, sizeof(curMap));
|
||||
|
|
|
@ -220,11 +220,11 @@ public void DB_OnConnectCheck(Database db, DBResultSet results, const char[] err
|
|||
} else {
|
||||
//No failure, check the data.
|
||||
while(client > 0 && results.FetchRow()) { //Is there a ban found?
|
||||
static char reason[128], steamid[64], public_message[255];
|
||||
static char reason[255], steamid[64], public_message[255];
|
||||
DBResult colResult;
|
||||
|
||||
results.FetchString(1, steamid, sizeof(steamid));
|
||||
results.FetchString(0, reason, sizeof(reason), colResult);
|
||||
results.FetchString(1, steamid, sizeof(steamid));
|
||||
if(colResult == DBVal_Null) {
|
||||
reason[0] = '\0';
|
||||
}
|
||||
|
@ -259,7 +259,7 @@ public void DB_OnConnectCheck(Database db, DBResultSet results, const char[] err
|
|||
g_db.Format(query, sizeof(query), "UPDATE bans SET times_tried=times_tried+1 WHERE steamid = '%s'", steamid);
|
||||
g_db.Query(DB_GenericCallback, query);
|
||||
} else {
|
||||
LogAction(-1, client, "%N (%s) was previously banned from server: \"%s\"", client, steamid, reason);
|
||||
LogAction(-1, client, "\"%L\" was previously banned from server: \"%s\"", client, reason);
|
||||
// User was previously banned
|
||||
PrintChatToAdmins("%N (%s) has a previous suspended/expired ban of reason \"%s\"", client, steamid, reason);
|
||||
}
|
||||
|
|
|
@ -240,6 +240,7 @@ void ResetClient(int victim, bool wipe = true) {
|
|||
Trolls[i].activeFlagClients[victim] = -1;
|
||||
}
|
||||
}
|
||||
noRushingUsSpeed[victim] = 1.0;
|
||||
BaseComm_SetClientMute(victim, false);
|
||||
SetEntityGravity(victim, 1.0);
|
||||
SetEntPropFloat(victim, Prop_Send, "m_flLaggedMovementValue", 1.0);
|
||||
|
@ -368,8 +369,8 @@ void ApplyTroll(int victim, const char[] name, int activator, trollModifier modi
|
|||
// Log all actions, indicating if constant or single-fire, and if any flags
|
||||
if(!silent) {
|
||||
if(isActive) {
|
||||
CShowActivityEx(activator, "[FTT] ", "deactivated {yellow}\"%s\"{default} on %N. ", troll.name, victim);
|
||||
LogAction(activator, victim, "\"%L\" deactivated {yellow}\"%s\"{default} on \"%L\"", activator, troll.name, victim);
|
||||
CShowActivityEx(activator, "[FTT] ", "deactivated {yellow}%s{default} on %N. ", troll.name, victim);
|
||||
LogAction(activator, victim, "\"%L\" deactivated \"%s\" on \"%L\"", activator, troll.name, victim);
|
||||
} else {
|
||||
static char flagName[MAX_TROLL_FLAG_LENGTH];
|
||||
// strcopy(flagName, sizeof(flagName), troll.name)
|
||||
|
|
|
@ -46,6 +46,19 @@ void SetupsTrollCombos() {
|
|||
combo.AddTroll("Witch Magnet");
|
||||
#endif
|
||||
|
||||
SetupCombo(combo, "Rush Stopper");
|
||||
combo.AddTroll("Special Magnet");
|
||||
combo.AddTroll("Tank Magnet");
|
||||
#if defined _behavior_included
|
||||
combo.AddTroll("Witch Magnet");
|
||||
#endif
|
||||
combo.AddTroll("No Button Touchie", TrollMod_Constant, 17);
|
||||
combo.AddTroll("Slow Speed", TrollMod_Constant, 2);
|
||||
combo.AddTroll("Instant Commons", TrollMod_Instant, 1);
|
||||
// combo.AddTroll("Swarm", TrollMod_Instant);
|
||||
combo.AddTroll("Vomit Player");
|
||||
combo.AddTroll("Dull Melee", .flags=2);
|
||||
|
||||
SetupCombo(combo, "Tank Run Noob");
|
||||
combo.AddTroll("Slow Speed");
|
||||
combo.AddTroll("Tank Magnet");
|
||||
|
|
|
@ -57,7 +57,7 @@ public Action Command_InstaSpecial(int client, int args) {
|
|||
}
|
||||
}
|
||||
if(successes > 0)
|
||||
ShowActivityEx(client, "[FTT] ", "spawned Insta-%s™ near %s", arg2, target_name);
|
||||
CShowActivityEx(client, "[FTT] ", "spawned {green}Insta-%s™{default} near {green}%s", arg2, target_name);
|
||||
}
|
||||
|
||||
|
||||
|
@ -123,7 +123,7 @@ public Action Command_InstaSpecialFace(int client, int args) {
|
|||
}
|
||||
}
|
||||
if(successes > 0)
|
||||
ShowActivityEx(client, "[FTT] ", "spawned Insta-%s™ on %s", arg2, target_name);
|
||||
CShowActivityEx(client, "[FTT] ", "spawned {green}Insta-%s™{default} on {green}%s", arg2, target_name);
|
||||
}
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
|
|
@ -223,9 +223,27 @@ public Action Event_ButtonPress(const char[] output, int entity, int client, flo
|
|||
if(!noButtonPressIndex) noButtonPressIndex = GetTrollID("No Button Touchie");
|
||||
if(client > 0 && client <= MaxClients) {
|
||||
if(Trolls[noButtonPressIndex].IsActive(client)) {
|
||||
AcceptEntityInput(entity, "Lock");
|
||||
RequestFrame(Frame_ResetButton, entity);
|
||||
return Plugin_Handled;
|
||||
if(Trolls[noButtonPressIndex].activeFlagClients[client] & 1) {
|
||||
AcceptEntityInput(entity, "Lock");
|
||||
RequestFrame(Frame_ResetButton, entity);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
if(Trolls[noButtonPressIndex].activeFlagClients[client] & 2) {
|
||||
L4D_CTerrorPlayer_OnVomitedUpon(client, client);
|
||||
}
|
||||
if(Trolls[noButtonPressIndex].activeFlagClients[client] & 4) {
|
||||
L4D_SetPlayerIncapacitatedState(client, true);
|
||||
}
|
||||
if(Trolls[noButtonPressIndex].activeFlagClients[client] & 8) {
|
||||
ServerCommand("sm_slay #%d", GetClientUserId(client));
|
||||
}
|
||||
if(Trolls[noButtonPressIndex].activeFlagClients[client] & 16) {
|
||||
float speed = GetEntPropFloat(client, Prop_Send, "m_flLaggedMovementValue");
|
||||
if(speed > 0.9) speed = 0.80;
|
||||
speed -= 5.0;
|
||||
SetEntPropFloat(client, Prop_Send, "m_flLaggedMovementValue", speed);
|
||||
PrintToConsoleAdmins("[FTT] NoButtonTouchie: %N speed is now %f", speed);
|
||||
}
|
||||
}
|
||||
lastButtonUser = client;
|
||||
}
|
||||
|
@ -776,12 +794,12 @@ public Action OnVocalizeCommand(int client, const char[] vocalize, int initiator
|
|||
if(vocalGagID == 0) vocalGagID = GetTrollID("Vocalize Gag");
|
||||
if(noRushingUsID == 0) noRushingUsID = GetTrollID("No Rushing Us");
|
||||
if(Trolls[noRushingUsID].IsActive(client) && (StrEqual(vocalize, "PlayerHurryUp") || StrEqual(vocalize, "PlayerYellRun") || StrEqual(vocalize, "PlayerMoveOn") || StrEqual(vocalize, "PlayerLeadOn"))) {
|
||||
float speed = GetEntPropFloat(client, Prop_Send, "m_flLaggedMovementValue");
|
||||
speed -= 0.01;
|
||||
if(speed < 0.0) SetEntPropFloat(client, Prop_Send, "m_flLaggedMovementValue", 0.0);
|
||||
else if(speed > 0.05)
|
||||
SetEntPropFloat(client, Prop_Send, "m_flLaggedMovementValue", speed);
|
||||
PrintToConsoleAdmins("[FTT] NoRushingUs: Dropping speed for %N (now %.1f%)", client, speed * 100.0);
|
||||
noRushingUsSpeed[client] -= 0.01;
|
||||
if(noRushingUsSpeed[client] < 0.05) {
|
||||
noRushingUsSpeed[client] = 0.05;
|
||||
}
|
||||
SetEntPropFloat(client, Prop_Send, "m_flLaggedMovementValue", noRushingUsSpeed[client]);
|
||||
PrintToConsoleAdmins("[FTT] NoRushingUs: Dropping speed for %N (now %.1f%)", client, noRushingUsSpeed[client] * 100.0);
|
||||
}
|
||||
if(Trolls[vocalGagID].IsActive(client)) {
|
||||
return Plugin_Handled;
|
||||
|
|
|
@ -90,8 +90,9 @@ public int ChoosePlayerHandler(Menu menu, MenuAction action, int param1, int par
|
|||
}
|
||||
|
||||
SetupCategoryMenu(param1, userid);
|
||||
} else if (action == MenuAction_End)
|
||||
} else if (action == MenuAction_End) {
|
||||
delete menu;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -208,7 +209,7 @@ public int ChooseModeMenuHandler(Menu menu, MenuAction action, int param1, int p
|
|||
|
||||
modiferMenu.ExitButton = true;
|
||||
modiferMenu.Display(param1, 0);
|
||||
} else if(troll.HasFlags() && !troll.IsActive(client)) {
|
||||
} else if(!troll.IsActive(client) && troll.HasFlags()) {
|
||||
ShowSelectFlagMenu(param1, userid, -1, troll);
|
||||
} else {
|
||||
troll.Activate(client, param1);
|
||||
|
@ -241,8 +242,10 @@ public int ChooseClumsySlotHandler(Menu menu, MenuAction action, int param1, int
|
|||
} else {
|
||||
ThrowItemToPlayer(client, param1, slot);
|
||||
}
|
||||
LogAction(param1, client, "\"%L\" activated troll \"Throw It all\" slot=%d for \"%L\"", param1, slot, client);
|
||||
ShowActivityEx(param1, "[FTT] ", "activated troll \"Throw It All\" for %N. ", client);
|
||||
if(slot != -2) {
|
||||
LogAction(param1, client, "\"%L\" activated troll \"Throw It all\" slot=%d for \"%L\"", param1, slot, client);
|
||||
ShowActivityEx(param1, "[FTT] ", "activated troll \"Throw It All\" for %N. ", client);
|
||||
}
|
||||
|
||||
ShowThrowItAllMenu(param1, userid);
|
||||
} else if (action == MenuAction_End)
|
||||
|
@ -395,17 +398,19 @@ void ShowTrollMenu(int client, bool isComboList) {
|
|||
for(int i = 1; i <= MaxClients; i++) {
|
||||
if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2) {
|
||||
IntToString(GetClientUserId(i), userid, sizeof(userid));
|
||||
int specClient = GetSpectatorClient(i);
|
||||
int realPlayer = L4D_GetBotOfIdlePlayer(i);
|
||||
PrintToServer("%d/%d", i, realPlayer);
|
||||
// Incase player is idle, grab their bot instead of them
|
||||
if(specClient > 0) {
|
||||
if(IsPlayerAlive(specClient))
|
||||
Format(display, sizeof(display), "%N (AFK)", specClient);
|
||||
if(realPlayer > 0 && IsClientConnected(realPlayer)) {
|
||||
if(IsPlayerAlive(i))
|
||||
Format(display, sizeof(display), "%N (AFK)", realPlayer);
|
||||
else
|
||||
Format(display, sizeof(display), "%N (AFK/Dead)", specClient);
|
||||
Format(display, sizeof(display), "%N (AFK/Dead)", realPlayer);
|
||||
} else if(!IsPlayerAlive(i))
|
||||
Format(display, sizeof(display), "%N (Dead)", i);
|
||||
|
||||
GetClientName(i, display, sizeof(display));
|
||||
else {
|
||||
GetClientName(i, display, sizeof(display));
|
||||
}
|
||||
menu.AddItem(userid, display);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -101,14 +101,21 @@ bool ProcessSpecialQueue() {
|
|||
CheatCommand(target, "z_spawn_old", SPECIAL_NAMES[view_as<int>(spActiveRequest.type) - 1], "auto");
|
||||
} else if(spActiveRequest.type == Special_Witch) {
|
||||
int witch = L4D2_SpawnWitch(spActiveRequest.position, spActiveRequest.angle);
|
||||
DataPack pack;
|
||||
CreateDataTimer(0.2, Timer_SetWitchTarget, pack);
|
||||
pack.WriteCell(witch);
|
||||
pack.WriteCell(GetClientUserId(target));
|
||||
if(witch != -1)
|
||||
SetWitchTarget(witch, target);
|
||||
return ProcessSpecialQueue();
|
||||
} else if(spActiveRequest.type == Special_Tank) {
|
||||
// BypassLimit();
|
||||
int tank = L4D2_SpawnTank(spActiveRequest.position, spActiveRequest.angle);
|
||||
if(tank > 0 && IsClientConnected(tank))
|
||||
if(tank > 0 && IsClientConnected(tank)) {
|
||||
PrintToConsoleAll("[ftt/debug] requested tank spawned %d -> %N", tank, target)
|
||||
pdata[tank].attackerTargetUid = spActiveRequest.targetUserId;
|
||||
pdata[tank].specialAttackFlags = view_as<int>(SPI_AlwaysTarget);
|
||||
}
|
||||
return ProcessSpecialQueue();
|
||||
}
|
||||
return true;
|
||||
|
@ -117,6 +124,16 @@ bool ProcessSpecialQueue() {
|
|||
return false;
|
||||
}
|
||||
|
||||
Action Timer_SetWitchTarget(Handle h, DataPack pack) {
|
||||
pack.Reset();
|
||||
int witch = pack.ReadCell();
|
||||
int target = GetClientOfUserId(pack.ReadCell());
|
||||
if(IsValidEntity(witch) && target > 0) {
|
||||
SetWitchTarget(witch, target);
|
||||
}
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
stock SpecialType GetSpecialType(const char[] input) {
|
||||
for(int i = 0; i < 8; i++) {
|
||||
if(strcmp(SPECIAL_NAMES[i], input, false) == 0) return view_as<SpecialType>(i + 1);
|
||||
|
|
|
@ -192,7 +192,14 @@ void SetupTrolls() {
|
|||
SetCategory("Misc");
|
||||
SetupTroll("Gun Jam", "On reload, small chance their gun gets jammed - Can't reload.", TrollMod_Constant);
|
||||
SetupTroll("No Shove", "Prevents a player from shoving", TrollMod_Constant);
|
||||
SetupTroll("No Button Touchie", "Stops people from pressing buttons", TrollMod_Constant);
|
||||
index = SetupTroll("No Button Touchie", "Stops people from pressing buttons", TrollMod_Constant);
|
||||
Trolls[index].AddFlagPrompt(true);
|
||||
Trolls[index].AddFlag("Prevent Use", true);
|
||||
Trolls[index].AddFlag("Vomit On Touch", false);
|
||||
Trolls[index].AddFlag("Incap On Touch", false);
|
||||
Trolls[index].AddFlag("Slay On Touch", false);
|
||||
Trolls[index].AddFlag("0.8x Speed", false);
|
||||
// add flag: vomit on touch
|
||||
index = SetupTroll("Meta: Inverse", "Uhm you are not supposed to see this...", TrollMod_Instant);
|
||||
Trolls[index].hidden = true;
|
||||
Trolls[index].AddFlagPrompt(false);
|
||||
|
@ -223,11 +230,12 @@ bool ApplyAffect(int victim, const Troll troll, int activator, trollModifier mod
|
|||
if(StrEqual(troll.name, "Reset User")) {
|
||||
LogAction(activator, victim, "\"%L\" reset all effects for \"%L\"", activator, victim);
|
||||
ShowActivityEx(activator, "[FTT] ", "reset effects for %N. ", victim);
|
||||
for(int i = 0; i <= MAX_TROLLS; i++) {
|
||||
Trolls[i].activeFlagClients[victim] = -1;
|
||||
}
|
||||
SetEntityGravity(victim, 1.0);
|
||||
SetEntPropFloat(victim, Prop_Send, "m_flLaggedMovementValue", 1.0);
|
||||
// for(int i = 0; i <= MAX_TROLLS; i++) {
|
||||
// Trolls[i].activeFlagClients[victim] = -1;
|
||||
// }
|
||||
// SetEntityGravity(victim, 1.0);
|
||||
// SetEntPropFloat(victim, Prop_Send, "m_flLaggedMovementValue", 1.0);
|
||||
ResetClient(victim, true);
|
||||
return false;
|
||||
} else if(StrEqual(troll.name, "Slow Speed")) {
|
||||
if(toActive) {
|
||||
|
@ -280,7 +288,6 @@ bool ApplyAffect(int victim, const Troll troll, int activator, trollModifier mod
|
|||
if(modifier & TrollMod_Instant) {
|
||||
L4D2_RunScript("RushVictim(GetPlayerFromUserID(%d), %d)", victim, 15000);
|
||||
}
|
||||
return true;
|
||||
} else if(StrEqual(troll.name, "Gun Jam")) {
|
||||
int wpn = GetClientWeaponEntIndex(victim, 0);
|
||||
if(wpn > -1)
|
||||
|
|
|
@ -98,6 +98,9 @@ float entLastHeight[2048];
|
|||
float fLastAntiRushEvent[MAXPLAYERS+1];
|
||||
float fAntiRushFrequencyCounter[MAXPLAYERS+1];
|
||||
|
||||
float noRushingUsSpeed[MAXPLAYERS+1];
|
||||
|
||||
|
||||
|
||||
#define MODEL_CAR "models/props_vehicles/cara_95sedan.mdl"
|
||||
|
||||
|
|
|
@ -485,14 +485,16 @@ stock bool IsClientInSightRange(int client, int target, float angle = 90.0, floa
|
|||
else return false;
|
||||
}
|
||||
|
||||
/// Checks if entity is in sight of client. Angle is the FOV, distance to be squared
|
||||
stock bool IsEntityInSightRange(int client, int target, float angle = 90.0, float distance = 0.0, bool heightcheck = true, bool negativeangle = false) {
|
||||
if(angle > 360.0 || angle < 0.0)
|
||||
ThrowError("Angle Max : 360 & Min : 0. %d isn't proper angle.", angle);
|
||||
else if(!IsValidClient(client))
|
||||
else if(!IsClientConnected(client) || !IsClientInGame(client))
|
||||
ThrowError("Client is not Alive.");
|
||||
else if(target <= MaxClients || !IsValidEntity(target))
|
||||
ThrowError("Target is not valid entity.");
|
||||
|
||||
|
||||
|
||||
float clientPos[3], targetPos[3], angleVector[3], targetVector[3], resultAngle, resultDistance;
|
||||
|
||||
GetClientEyeAngles(client, angleVector);
|
||||
|
@ -505,7 +507,7 @@ stock bool IsEntityInSightRange(int client, int target, float angle = 90.0, floa
|
|||
GetClientAbsOrigin(client, clientPos);
|
||||
GetEntPropVector(target, Prop_Send, "m_vecOrigin", targetPos);
|
||||
if(heightcheck && distance > 0)
|
||||
resultDistance = GetVectorDistance(clientPos, targetPos);
|
||||
resultDistance = GetVectorDistance(clientPos, targetPos, true);
|
||||
clientPos[2] = targetPos[2] = 0.0;
|
||||
MakeVectorFromPoints(clientPos, targetPos, targetVector);
|
||||
NormalizeVector(targetVector, targetVector);
|
||||
|
@ -517,7 +519,7 @@ stock bool IsEntityInSightRange(int client, int target, float angle = 90.0, floa
|
|||
if(distance > 0)
|
||||
{
|
||||
if(!heightcheck)
|
||||
resultDistance = GetVectorDistance(clientPos, targetPos);
|
||||
resultDistance = GetVectorDistance(clientPos, targetPos, true);
|
||||
|
||||
return distance >= resultDistance;
|
||||
}
|
||||
|
@ -665,10 +667,10 @@ stock void ClearParent(int child) {
|
|||
}
|
||||
|
||||
stock void GetForwardVector(float vPos[3], float vAng[3], float vReturn[3], float fDistance) {
|
||||
float vDir[3];
|
||||
GetAngleVectors(vAng, vDir, NULL_VECTOR, NULL_VECTOR);
|
||||
ScaleVector(vDir, fDistance);
|
||||
AddVectors(vPos, vDir, vReturn);
|
||||
float vDir[3];
|
||||
GetAngleVectors(vAng, vDir, NULL_VECTOR, NULL_VECTOR);
|
||||
ScaleVector(vDir, fDistance);
|
||||
AddVectors(vPos, vDir, vReturn);
|
||||
}
|
||||
|
||||
stock void GetDirectionVector(float pos1[3], float angle[3], float rVec[3], float distance, float force) {
|
||||
|
@ -680,3 +682,60 @@ stock void GetDirectionVector(float pos1[3], float angle[3], float rVec[3], floa
|
|||
|
||||
ScaleVector(rVec, force);
|
||||
}
|
||||
|
||||
// Taken from https://gist.github.com/Aeldrion/48c82912f632eec4c8b9da7394b89c5d
|
||||
stock void HSVToRGB(const float vec[3], float out[3]) {
|
||||
// Translates HSV color to RGB color
|
||||
// H: 0.0 - 360.0, S: 0.0 - 100.0, V: 0.0 - 100.0
|
||||
// R, G, B: 0.0 - 1.0
|
||||
|
||||
float hue = vec[0];
|
||||
float saturation = vec[1];
|
||||
float value = vec[2];
|
||||
|
||||
float c = (value / 100.0) * (saturation / 100.0);
|
||||
float x = c * (1.0 - FloatAbs(float(RoundToFloor(hue / 60) % 2) - 1));
|
||||
float m = (value / 100.0) - c;
|
||||
|
||||
if (hue >= 0 && hue < 60.0) {
|
||||
out[0] = c;
|
||||
out[1] = x;
|
||||
out[2] = 0.0;
|
||||
} else if (hue >= 60.0 && hue < 120.0) {
|
||||
out[0] = x;
|
||||
out[1] = c;
|
||||
out[2] = 0.0;
|
||||
} else if (hue >= 120.0 && hue < 180.0) {
|
||||
out[0] = 0.0;
|
||||
out[1] = c;
|
||||
out[2] = x;
|
||||
} else if (hue >= 180.0 && hue < 240.0) {
|
||||
out[0] = 0.0;
|
||||
out[1] = x;
|
||||
out[2] = c;
|
||||
} else if (hue >= 240.0 && hue < 300.0) {
|
||||
out[0] = x;
|
||||
out[1] = 0.0;
|
||||
out[2] = c;
|
||||
} else if (hue >= 300.0 && hue < 360.0) {
|
||||
out[0] = c;
|
||||
out[1] = 0.0;
|
||||
out[2] = x;
|
||||
}
|
||||
|
||||
out[0] += m;
|
||||
out[1] += m;
|
||||
out[2] += m;
|
||||
|
||||
out[0] * 255.0;
|
||||
out[1] * 255.0;
|
||||
out[2] * 255.0;
|
||||
}
|
||||
stock void HSVToRGBInt(const float vec[3], int out[3]) {
|
||||
// Don't initialize memory, just use the existing memory as int out[3], just tell it that is a float
|
||||
HSVToRGB(vec, view_as<float>(out));
|
||||
// Convert float to int:
|
||||
out[0] = RoundToFloor(view_as<float>(out[0]));
|
||||
out[1] = RoundToFloor(view_as<float>(out[1]));
|
||||
out[2] = RoundToFloor(view_as<float>(out[2]));
|
||||
}
|
|
@ -58,16 +58,16 @@
|
|||
|
||||
|
||||
|
||||
// Natives: 246 (including 3 for L4D1 only)
|
||||
// L4D1 = 31 [left4downtown] + 47 [l4d_direct] + 16 [l4d2addresses] + 51 [silvers - mine!] + 4 [anim] = 149
|
||||
// L4D2 = 61 [left4downtown] + 59 [l4d_direct] + 32 [l4d2addresses] + 87 [silvers - mine!] + 4 [anim] = 243
|
||||
// Natives: 252 (including 3 for L4D1 only)
|
||||
// L4D1 = 31 [left4downtown] + 47 [l4d_direct] + 16 [l4d2addresses] + 56 [silvers - mine!] + 4 [anim] = 154
|
||||
// L4D2 = 61 [left4downtown] + 59 [l4d_direct] + 31 [l4d2addresses] + 94 [silvers - mine!] + 4 [anim] = 249
|
||||
|
||||
// Forwards: 172 (including 2 for L4D1 only)
|
||||
// L4D1 = 126;
|
||||
// L4D2 = 170;
|
||||
// Forwards: 183 (including 2 for L4D1 only)
|
||||
// L4D1 = 129
|
||||
// L4D2 = 181
|
||||
|
||||
// Stocks: 163 (L4D1 = 109, L4D2 = 159)
|
||||
// left4dhooks_silver 43 stocks (L4D1 = 36, L4D2 = 47)
|
||||
// Stocks: 168 (L4D1 = 111, L4D2 = 164)
|
||||
// left4dhooks_silver 45 stocks (L4D1 = 38, L4D2 = 52)
|
||||
// left4dhooks_stocks 83 stocks (L4D1 = 44, L4D2 = 79)
|
||||
// left4dhooks_lux_library 34 stocks (L4D1 = 30, L4D2 = 34)
|
||||
|
||||
|
@ -114,6 +114,8 @@ public void __pl_l4dh_SetNTVOptional()
|
|||
MarkNativeAsOptional("L4D_GetNearestNavArea");
|
||||
MarkNativeAsOptional("L4D_GetLastKnownArea");
|
||||
MarkNativeAsOptional("L4D2_GetFurthestSurvivorFlow");
|
||||
MarkNativeAsOptional("L4D2_GetFirstSpawnClass");
|
||||
MarkNativeAsOptional("L4D2_SetFirstSpawnClass");
|
||||
MarkNativeAsOptional("L4D_FindRandomSpot");
|
||||
MarkNativeAsOptional("L4D2_IsVisibleToPlayer");
|
||||
MarkNativeAsOptional("L4D_HasAnySurvivorLeftSafeArea");
|
||||
|
@ -208,6 +210,11 @@ public void __pl_l4dh_SetNTVOptional()
|
|||
MarkNativeAsOptional("L4D2_GetWitchCount");
|
||||
MarkNativeAsOptional("L4D_GetCurrentChapter");
|
||||
MarkNativeAsOptional("L4D_GetMaxChapters");
|
||||
MarkNativeAsOptional("L4D_GetAllNavAreas");
|
||||
MarkNativeAsOptional("L4D_GetNavAreaID");
|
||||
MarkNativeAsOptional("L4D_GetNavAreaByID");
|
||||
MarkNativeAsOptional("L4D_GetNavAreaPos");
|
||||
MarkNativeAsOptional("L4D_GetNavAreaSize");
|
||||
MarkNativeAsOptional("L4D_GetNavArea_SpawnAttributes");
|
||||
MarkNativeAsOptional("L4D_SetNavArea_SpawnAttributes");
|
||||
MarkNativeAsOptional("L4D_GetNavArea_AttributeFlags");
|
||||
|
@ -410,7 +417,8 @@ enum PointerType
|
|||
POINTER_EVENTMANAGER = 8, // pScriptedEventManager (L4D2 Only)
|
||||
POINTER_SCAVENGEMODE = 9, // pScavengeMode (L4D2 Only)
|
||||
POINTER_VERSUSMODE = 10, // pVersusMode
|
||||
POINTER_SCRIPTVM = 11 // @g_pScriptVM (L4D2 Only)
|
||||
POINTER_SCRIPTVM = 11, // @g_pScriptVM (L4D2 Only)
|
||||
POINTER_THENAVAREAS = 12 // @TheNavAreas
|
||||
};
|
||||
|
||||
// Provided by "BHaType":
|
||||
|
@ -463,65 +471,192 @@ enum
|
|||
};
|
||||
|
||||
// From: https://developer.valvesoftware.com/wiki/List_of_L4D_Series_Nav_Mesh_Attributes
|
||||
// Use by "L4D_GetNavArea_AttributeFlags" and "L4D_SetNavArea_AttributeFlags" natives.
|
||||
// NavArea Base Attributes:
|
||||
enum
|
||||
{
|
||||
NAV_BASE_CROUCH = 1,
|
||||
NAV_BASE_JUMP = 2,
|
||||
NAV_BASE_PRECISE = 4,
|
||||
NAV_BASE_NO_JUMP = 8,
|
||||
NAV_BASE_STOP = 16,
|
||||
NAV_BASE_RUN = 32,
|
||||
NAV_BASE_WALK = 64,
|
||||
NAV_BASE_AVOID = 128,
|
||||
NAV_BASE_TRANSIENT = 256,
|
||||
NAV_BASE_DONT_HIDE = 512,
|
||||
NAV_BASE_STAND = 1024,
|
||||
NAV_BASE_NO_HOSTAGES = 2048,
|
||||
NAV_BASE_STAIRS = 4096,
|
||||
NAV_BASE_NO_MERGE = 8192,
|
||||
NAV_BASE_OBSTACLE_TOP = 16384,
|
||||
NAV_BASE_CLIFF = 32768,
|
||||
NAV_BASE_TANK_ONLY = 65536,
|
||||
NAV_BASE_MOB_ONLY = 131072,
|
||||
NAV_BASE_PLAYERCLIP = 262144,
|
||||
NAV_BASE_BREAKABLEWALL = 524288,
|
||||
NAV_BASE_FLOW_BLOCKED = 134217728,
|
||||
NAV_BASE_OUTSIDE_WORLD = 268435456,
|
||||
NAV_BASE_MOSTLY_FLAT = 536870912,
|
||||
NAV_BASE_HAS_ELEVATOR = 1073741824,
|
||||
NAV_BASE_NAV_BLOCKER = -2147483648
|
||||
NAV_BASE_CROUCH = 1, // (1<<0)
|
||||
NAV_BASE_JUMP = 2, // (1<<1)
|
||||
NAV_BASE_PRECISE = 4, // (1<<2)
|
||||
NAV_BASE_NO_JUMP = 8, // (1<<3)
|
||||
NAV_BASE_STOP = 16, // (1<<4)
|
||||
NAV_BASE_RUN = 32, // (1<<5)
|
||||
NAV_BASE_WALK = 64, // (1<<6)
|
||||
NAV_BASE_AVOID = 128, // (1<<7)
|
||||
NAV_BASE_TRANSIENT = 256, // (1<<8)
|
||||
NAV_BASE_DONT_HIDE = 512, // (1<<9)
|
||||
NAV_BASE_STAND = 1024, // (1<<10)
|
||||
NAV_BASE_NO_HOSTAGES = 2048, // (1<<11)
|
||||
NAV_BASE_STAIRS = 4096, // (1<<12)
|
||||
NAV_BASE_NO_MERGE = 8192, // (1<<13)
|
||||
NAV_BASE_OBSTACLE_TOP = 16384, // (1<<14)
|
||||
NAV_BASE_CLIFF = 32768, // (1<<15)
|
||||
NAV_BASE_TANK_ONLY = 65536, // (1<<16)
|
||||
NAV_BASE_MOB_ONLY = 131072, // (1<<17)
|
||||
NAV_BASE_PLAYERCLIP = 262144, // (1<<18)
|
||||
NAV_BASE_BREAKABLEWALL = 524288, // (1<<19)
|
||||
NAV_BASE_FLOW_BLOCKED = 134217728, // (1<<27)
|
||||
NAV_BASE_OUTSIDE_WORLD = 268435456, // (1<<28)
|
||||
NAV_BASE_MOSTLY_FLAT = 536870912, // (1<<29)
|
||||
NAV_BASE_HAS_ELEVATOR = 1073741824, // (1<<30)
|
||||
NAV_BASE_NAV_BLOCKER = -2147483648 // (1<<31)
|
||||
};
|
||||
|
||||
// Use by "L4D_GetNavArea_SpawnAttributes" and "L4D_SetNavArea_SpawnAttributes" natives.
|
||||
// NavArea Spawn Attributes:
|
||||
enum
|
||||
{
|
||||
NAV_SPAWN_EMPTY = 2,
|
||||
NAV_SPAWN_STOP_SCAN = 4,
|
||||
NAV_SPAWN_BATTLESTATION = 32,
|
||||
NAV_SPAWN_FINALE = 64,
|
||||
NAV_SPAWN_PLAYER_START = 128,
|
||||
NAV_SPAWN_BATTLEFIELD = 256,
|
||||
NAV_SPAWN_IGNORE_VISIBILITY = 512,
|
||||
NAV_SPAWN_NOT_CLEARABLE = 1024,
|
||||
NAV_SPAWN_CHECKPOINT = 2048,
|
||||
NAV_SPAWN_OBSCURED = 4096,
|
||||
NAV_SPAWN_NO_MOBS = 8192,
|
||||
NAV_SPAWN_THREAT = 16384,
|
||||
NAV_SPAWN_RESCUE_VEHICLE = 32768,
|
||||
NAV_SPAWN_RESCUE_CLOSET = 65536,
|
||||
NAV_SPAWN_ESCAPE_ROUTE = 131072,
|
||||
NAV_SPAWN_DESTROYED_DOOR = 262144,
|
||||
NAV_SPAWN_NOTHREAT = 524288,
|
||||
NAV_SPAWN_LYINGDOWN = 1048576,
|
||||
NAV_SPAWN_COMPASS_NORTH = 16777216,
|
||||
NAV_SPAWN_COMPASS_NORTHEAST = 33554432,
|
||||
NAV_SPAWN_COMPASS_EAST = 67108864,
|
||||
NAV_SPAWN_COMPASS_EASTSOUTH = 134217728,
|
||||
NAV_SPAWN_COMPASS_SOUTH = 268435456,
|
||||
NAV_SPAWN_COMPASS_SOUTHWEST = 536870912,
|
||||
NAV_SPAWN_COMPASS_WEST = 1073741824,
|
||||
NAV_SPAWN_COMPASS_WESTNORTH = -2147483648
|
||||
NAV_SPAWN_EMPTY = 2, // (1<<0)
|
||||
NAV_SPAWN_STOP_SCAN = 4, // (1<<1)
|
||||
NAV_SPAWN_BATTLESTATION = 32, // (1<<5)
|
||||
NAV_SPAWN_FINALE = 64, // (1<<6)
|
||||
NAV_SPAWN_PLAYER_START = 128, // (1<<7)
|
||||
NAV_SPAWN_BATTLEFIELD = 256, // (1<<8)
|
||||
NAV_SPAWN_IGNORE_VISIBILITY = 512, // (1<<9)
|
||||
NAV_SPAWN_NOT_CLEARABLE = 1024, // (1<<10)
|
||||
NAV_SPAWN_CHECKPOINT = 2048, // (1<<11)
|
||||
NAV_SPAWN_OBSCURED = 4096, // (1<<12)
|
||||
NAV_SPAWN_NO_MOBS = 8192, // (1<<13)
|
||||
NAV_SPAWN_THREAT = 16384, // (1<<14)
|
||||
NAV_SPAWN_RESCUE_VEHICLE = 32768, // (1<<15)
|
||||
NAV_SPAWN_RESCUE_CLOSET = 65536, // (1<<16)
|
||||
NAV_SPAWN_ESCAPE_ROUTE = 131072, // (1<<17)
|
||||
NAV_SPAWN_DESTROYED_DOOR = 262144, // (1<<18)
|
||||
NAV_SPAWN_NOTHREAT = 524288, // (1<<19)
|
||||
NAV_SPAWN_LYINGDOWN = 1048576, // (1<<20)
|
||||
NAV_SPAWN_COMPASS_NORTH = 16777216, // (1<<24)
|
||||
NAV_SPAWN_COMPASS_NORTHEAST = 33554432, // (1<<25)
|
||||
NAV_SPAWN_COMPASS_EAST = 67108864, // (1<<26)
|
||||
NAV_SPAWN_COMPASS_EASTSOUTH = 134217728, // (1<<27)
|
||||
NAV_SPAWN_COMPASS_SOUTH = 268435456, // (1<<28)
|
||||
NAV_SPAWN_COMPASS_SOUTHWEST = 536870912, // (1<<29)
|
||||
NAV_SPAWN_COMPASS_WEST = 1073741824, // (1<<30)
|
||||
NAV_SPAWN_COMPASS_WESTNORTH = -2147483648 // (1<<31)
|
||||
};
|
||||
|
||||
// List provided by "A1m`" taken from: https://github.com/A1mDev/l4d2_structs/blob/master/terror_player_animstate.h
|
||||
// There are constants that are not used, these constants were already inside the engine, the developers added their own over the existing code.
|
||||
// Some constants from 'l4d2util_contants.inc'.
|
||||
// These are used by the "L4D2Direct_DoAnimationEvent" native and "L4D_OnDoAnimationEvent*" forwards.
|
||||
// L4D1 seems to only have 35 animation events, the names may not be relative to those listed here.
|
||||
enum PlayerAnimEvent_t
|
||||
{
|
||||
// Made by A1m`.
|
||||
|
||||
PLAYERANIMEVENT_ATTACK_PRIMARY = 1, // CMultiPlayerAnimState::DoAnimationEvent
|
||||
PLAYERANIMEVENT_ATTACK_SECONDARY = 2, // CMultiPlayerAnimState::DoAnimationEvent
|
||||
PLAYERANIMEVENT_ATTACK_GRENADE = 3, // CMultiPlayerAnimState::DoAnimationEvent
|
||||
PLAYERANIMEVENT_RELOAD = 4, // CMultiPlayerAnimState::DoAnimationEvent, CTerrorGun::SendWeaponAnim
|
||||
PLAYERANIMEVENT_RELOAD_LOOP = 5, // CMultiPlayerAnimState::DoAnimationEvent, CBaseShotgun::CheckReload->PlayReloadAnim
|
||||
PLAYERANIMEVENT_RELOAD_END = 6, //CMultiPlayerAnimState::DoAnimationEvent, CBaseShotgun::CheckReload->PlayReloadAnim, CTerrorGun::AbortReload
|
||||
PLAYERANIMEVENT_JUMP = 7, // CMultiPlayerAnimState::DoAnimationEvent, CTerrorGameMovement::DoJump, CCSGameMovement::CheckJumpButton
|
||||
PLAYERANIMEVENT_LAND = 8, // CTerrorGameMovement::PlayerRoughLandingEffects
|
||||
|
||||
PLAYERANIMEVENT_SWIM = 9, // Not sure, not used in the game anyway
|
||||
|
||||
PLAYERANIMEVENT_DIE = 10, // CMultiPlayerAnimState::DoAnimationEvent, CTerrorPlayer::StartSurvivorDeathAnim, CTerrorPlayer::OnIncapacitatedAsTank
|
||||
PLAYERANIMEVENT_FLINCH_CHEST = 11, // CMultiPlayerAnimState::DoAnimationEvent
|
||||
PLAYERANIMEVENT_FLINCH_HEAD = 12, // CMultiPlayerAnimState::DoAnimationEvent
|
||||
PLAYERANIMEVENT_FLINCH_LEFTARM = 13, // CMultiPlayerAnimState::DoAnimationEvent
|
||||
PLAYERANIMEVENT_FLINCH_RIGHTARM = 14, // CMultiPlayerAnimState::DoAnimationEvent
|
||||
PLAYERANIMEVENT_FLINCH_LEFTLEG = 15, // CMultiPlayerAnimState::DoAnimationEvent
|
||||
PLAYERANIMEVENT_FLINCH_RIGHTLEG = 16, // CMultiPlayerAnimState::DoAnimationEvent
|
||||
|
||||
PLAYERANIMEVENT_DOUBLEJUMP = 17, // Not sure, not used in the game anyway
|
||||
|
||||
PLAYERANIMEVENT_CANCEL_GESTURE_ATTACK_AND_RELOAD = 18, // CTerrorPlayer::OnShovedByPounceLanding, CTerrorPlayer::OnShovedBySurvivor, CTerrorPlayer::OnRideEnded, CTerrorPlayer::OnPounceEnded
|
||||
|
||||
PLAYERANIMEVENT_CANCEL = 19, // Not sure, not used in the game anyway
|
||||
|
||||
PLAYERANIMEVENT_SPAWN = 20, // CMultiPlayerAnimState::DoAnimationEvent
|
||||
PLAYERANIMEVENT_SNAP_YAW = 21, // CMultiPlayerAnimState::DoAnimationEvent
|
||||
PLAYERANIMEVENT_CUSTOM = 22, // CMultiPlayerAnimState::DoAnimationEvent
|
||||
PLAYERANIMEVENT_CUSTOM_GESTURE = 23, // CMultiPlayerAnimState::DoAnimationEvent
|
||||
PLAYERANIMEVENT_CUSTOM_SEQUENCE = 24, // CMultiPlayerAnimState::DoAnimationEvent
|
||||
|
||||
PLAYERANIMEVENT_CUSTOM_GESTURE_SEQUENCE = 25, // Not sure, not used in the game anyway
|
||||
|
||||
// TF Specific. Here until there's a derived game solution to this.
|
||||
PLAYERANIMEVENT_ATTACK_PRE = 26, // Not sure, not used in the game anyway
|
||||
PLAYERANIMEVENT_ATTACK_POST = 27, // Not sure, not used in the game anyway
|
||||
PLAYERANIMEVENT_GRENADE1_DRAW = 28, // Not sure, not used in the game anyway
|
||||
PLAYERANIMEVENT_GRENADE2_DRAW = 29, // Not sure, not used in the game anyway
|
||||
PLAYERANIMEVENT_GRENADE1_THROW = 30, // Not sure, not used in the game anyway
|
||||
PLAYERANIMEVENT_GRENADE2_THROW = 31, // Not sure, not used in the game anyway
|
||||
PLAYERANIMEVENT_VOICE_COMMAND_GESTURE = 32, // Not sure, not used in the game?. CTerrorPlayerAnimState::DoAnimationEvent
|
||||
|
||||
PLAYERANIMEVENT_HAND_ATTACK = 33, // CClaw::OnSwingStart, CTerrorPlayer::UpdateTankEffects, CTankClaw::OnSwingStart
|
||||
PLAYERANIMEVENT_HAND_LOW_ATTACK = 34, // CTankClaw::OnSwingStart, CTerrorWeapon::OnSwingStart
|
||||
PLAYERANIMEVENT_SHOVE_COMMON = 35, // CTerrorWeapon::OnSwingStart
|
||||
PLAYERANIMEVENT_SHOVE = 36, // CTerrorWeapon::OnSwingStart
|
||||
PLAYERANIMEVENT_SHOVE_ZOMBIE_STOMP = 37, //CTerrorWeapon::OnSwingStart
|
||||
PLAYERANIMEVENT_START_RELOADING_SHOTGUN = 38, // CBaseShotgun::Reload->PlayReloadAnim
|
||||
PLAYERANIMEVENT_START_CHAINSAW = 39, // CChainsaw::Deploy
|
||||
PLAYERANIMEVENT_PRIMARY_ATTACK = 40, // CTerrorMeleeWeapon::StartMeleeSwing, CBaseBeltItem::PrimaryAttack, FireTerrorBullets, CGrenadeLauncher::PrimaryAttack
|
||||
PLAYERANIMEVENT_SECONDARY_ATTACK = 41, // CTerrorMeleeWeapon::StartMeleeSwing, CVomit::ActivateAbility, FireTerrorBullets
|
||||
PLAYERANIMEVENT_HEAL_SELF = 42,
|
||||
PLAYERANIMEVENT_HEAL_OTHER = 43,
|
||||
PLAYERANIMEVENT_CROUCH_HEAL_INCAP = 44, // CTerrorPlayer::StartReviving
|
||||
PLAYERANIMEVENT_CROUCH_HEAL_INCAPACITATED_ABOVE = 45, // CTerrorPlayer::StartReviving
|
||||
PLAYERANIMEVENT_STOP_USE_ACTION = 46, // CTerrorPlayer::StopRevivingSomeone, CTerrorPlayer::StopBeingRevived, CFirstAidKit::OnStopAction, CItemAmmoPack::OnStopAction, CItemBaseUpgradePack::OnStopAction, CItemDefibrillator::OnStopAction
|
||||
PLAYERANIMEVENT_PICKUP_START_SUBJECT = 47, // CTerrorPlayer::StartReviving
|
||||
PLAYERANIMEVENT_PICKUP_STOP_SUBJECT = 48, // CTerrorPlayer::CleanupPlayerState, CTerrorPlayer::StopBeingRevived, CTerrorPlayer::StopRevivingSomeone
|
||||
PLAYERANIMEVENT_PICKUP_SUCCESS_SUBJECT = 49, // CTerrorPlayer::OnRevived
|
||||
PLAYERANIMEVENT_DEFIB_START = 50,
|
||||
PLAYERANIMEVENT_DEFIB_END = 51,
|
||||
PLAYERANIMEVENT_DEPLOY_AMMO = 52,
|
||||
PLAYERANIMEVENT_USE_GASCAN_START = 53,
|
||||
PLAYERANIMEVENT_USE_GASCAN_END = 54, // CGasCan::OnStopAction
|
||||
PLAYERANIMEVENT_USE_COLA_START = 55,
|
||||
PLAYERANIMEVENT_USE_COLA_END = 56, // CColaBottles::OnStopAction
|
||||
PLAYERANIMEVENT_FLINCH_EVENT_SHOVED_BY_TEAMMATE = 57, // CTerrorPlayer::OnTakeDamageInternal->GetFlinchEvent, CTerrorPlayer::OnTakeDamage_Alive->GetFlinchEvent, CTerrorWeapon::OnHit->GetFlinchEvent
|
||||
PLAYERANIMEVENT_FLINCH_EVENT_TAKE_DAMAGE = 58, // CTerrorPlayer::GetFlinchEvent
|
||||
PLAYERANIMEVENT_THROW_ITEM_START = 59, // CBaseCSGrenade::PrimaryAttack
|
||||
|
||||
PLAYERANIMEVENT_ROLL_GRENADE = 60, // Not sure, not used in the game anyway
|
||||
|
||||
PLAYERANIMEVENT_THROW_ITEM_FINISH = 61, // CBaseCSGrenade::ItemPostFrame
|
||||
PLAYERANIMEVENT_THROW_GRENADE = 62, // CCSPlayer::DoAnimationEvent
|
||||
PLAYERANIMEVENT_THROW_ITEM_HOLSTER = 63, // CBaseCSGrenade::Holster
|
||||
PLAYERANIMEVENT_PLAYER_USE = 64, // CTerrorPlayer::OnUseEntity
|
||||
PLAYERANIMEVENT_CHANGE_SLOT = 65, // CWeaponCSBase::DefaultDeploy
|
||||
|
||||
PLAYERANIMEVENT_UNKNOWN_START_GESTURE = 66, // Don't know. Not used in the game? Something like option 32? CTerrorPlayerAnimState::DoAnimationEvent
|
||||
|
||||
PLAYERANIMEVENT_TUG_HANGING_PLAYER = 67, // CTerrorPlayer::StartTug
|
||||
PLAYERANIMEVENT_STUMBLE = 68, // CTerrorPlayer::UpdateStagger, CTerrorPlayer::OnShovedByPounceLanding, CTerrorPlayer::OnStaggered, CTerrorPlayer::UpdateStagger, CTerrorPlayer::OnShovedBySurvivor
|
||||
PLAYERANIMEVENT_POUNCE_VICTIM_END = 69,
|
||||
PLAYERANIMEVENT_SPIT_SPITTING = 70, // CSpitAbility::ActivateAbility
|
||||
PLAYERANIMEVENT_CHARGER_START_CHARGE = 71, // CCharge::BeginCharge
|
||||
PLAYERANIMEVENT_CHARGER_END_CHARGE = 72, // CCharge::EndCharge
|
||||
PLAYERANIMEVENT_CHARGER_PUMMELING_START = 73,
|
||||
PLAYERANIMEVENT_CHARGER_PUMMELING_END = 74, // ZombieReplacement::Restore, CTerrorPlayer::UpdatePound, ZombieReplacement::Restore
|
||||
PLAYERANIMEVENT_CHARGER_SLAM_INTO_GROUND = 75, // CTerrorPlayer::OnSlammedSurvivor
|
||||
PLAYERANIMEVENT_IMPACT_BY_CHARGER = 76,
|
||||
PLAYERANIMEVENT_CHARGER_PUMMELED = 77, // ThrowImpactedSurvivor->CTerrorPlayer::Fling; CTerrorPlayerAnimState::HandleActivity_Pummeling
|
||||
PLAYERANIMEVENT_POUNDED_BY_CHARGER = 78, // ZombieReplacement::Restore, CTerrorPlayer::UpdatePound, CTerrorPlayerAnimState::HandleActivity_Pummeling
|
||||
PLAYERANIMEVENT_CARRIED_BY_CHARGER = 79, // ZombieReplacement::Restore, CTerrorPlayer::OnStartBeingCarried
|
||||
PLAYERANIMEVENT_STAGGERING = 80, // CTerrorPlayer::OnSlammedSurvivor
|
||||
PLAYERANIMEVENT_VICTIM_SLAMMED_INTO_GROUND = 81, // CTerrorPlayer::OnSlammedSurvivor
|
||||
PLAYERANIMEVENT_HUNTER_POUNCING = 82, // ZombieReplacement::Restore, CTerrorPlayer::OnPouncedUpon, ZombieReplacement::Restore
|
||||
PLAYERANIMEVENT_HUNTER_POUNCE_ON_VICTIM = 83, // CTerrorPlayer::OnPouncedOnSurvivor
|
||||
PLAYERANIMEVENT_JOCKEY_RIDING = 84,
|
||||
PLAYERANIMEVENT_JOCKEY_RIDDEN = 85, // ZombieReplacement::Restore
|
||||
PLAYERANIMEVENT_HUNTER_GETUP = 86, // CTerrorPlayer::OnPouncedUpon, ZombieReplacement::Restore
|
||||
PLAYERANIMEVENT_TONGUE_LAUNCH_START = 87, // SmokerTongueVictim::OnStart
|
||||
PLAYERANIMEVENT_TONGUE_LAUNCH_END = 88, // CTongue::OnEnterExtendingState
|
||||
PLAYERANIMEVENT_TONGUE_REELING_IN = 89, // CTongue::OnEnterAttachedToTargetState
|
||||
PLAYERANIMEVENT_TONGUE_ATTACKING_START = 90, // CTongue::OnTouch
|
||||
PLAYERANIMEVENT_TONGUE_ATTACKING_END = 91, // CTerrorPlayer::OnReleasingWithTongue
|
||||
PLAYERANIMEVENT_VICTIM_PULLED = 92, // ZombieReplacement::Restore, CTerrorPlayer::OnGrabbedByTongue
|
||||
PLAYERANIMEVENT_ROCK_THROW = 93, // CThrow::ActivateAbility
|
||||
PLAYERANIMEVENT_TANK_CLIMB = 94, // TankLocomotion::ClimbUpToLedge
|
||||
PLAYERANIMEVENT_TANK_RAGE = 95, // CTerrorPlayer::OnAttackSuccess, CTerrorPlayer::OnMissionLost, CTerrorPlayer::ClientCommand (dance)
|
||||
PLAYERANIMEVENT_PLAYERHIT_BY_TANK = 96, // CTankClaw::OnPlayerHit, CTerrorPlayer::OnTakeDamage->Fling, CTerrorPlayer::OnKnockedDown
|
||||
PLAYERANIMEVENT_PUSH_ENTITY = 97, // CTerrorPlayer::PlayerUse
|
||||
PLAYERANIMEVENT_FIDGET = 98, // CTerrorPlayerAnimState::UpdateFidgeting
|
||||
|
||||
PLAYERANIMEVENT_COUNT // Total size 99. Function 'CTerrorPlayer::DoAnimationEvent'.
|
||||
};
|
||||
|
||||
|
||||
|
@ -747,7 +882,7 @@ forward void L4D2_OnSpawnWitchBride_Post(int entity, const float vecPos[3], cons
|
|||
/**
|
||||
* @brief Called whenever ZombieManager::SpawnWitchBride(Vector&,QAngle&) is invoked
|
||||
* @brief Called when a Witch Bride spawns
|
||||
* @remarks This forward will not trigger if the relative pre-hook forward has been blocked with Plugin_Handled
|
||||
* @remarks This forward will ONLY trigger if the relative pre-hook forward has been blocked with Plugin_Handled
|
||||
*
|
||||
* @param entity Entity index that spawned (can be -1 if blocked)
|
||||
* @param vecPos Vector coordinate where witch is spawned
|
||||
|
@ -763,7 +898,6 @@ forward void L4D2_OnSpawnWitchBride_PostHandled(int entity, const float vecPos[3
|
|||
* @remarks called on random hordes, mini and finale hordes, and boomer hordes, causes Zombies to attack
|
||||
* Not called on "z_spawn mob", hook the console command and check arguments to catch plugin mobs
|
||||
* This function is used to reset the Director's natural horde timer.
|
||||
* @remarks This forward will not trigger if the relative pre-hook forward has been blocked with Plugin_Handled
|
||||
*
|
||||
* @return Plugin_Handled to block, Plugin_Continue otherwise
|
||||
*/
|
||||
|
@ -794,7 +928,6 @@ forward void L4D_OnMobRushStart_PostHandled();
|
|||
/**
|
||||
* @brief Called whenever ZombieManager::SpawnITMob(int) is invoked
|
||||
* @remarks called on boomer hordes, increases Zombie Spawn Queue
|
||||
* @remarks This forward will not trigger if the relative pre-hook forward has been blocked with Plugin_Handled
|
||||
*
|
||||
* @param amount Amount of Zombies to add to Queue
|
||||
*
|
||||
|
@ -1401,7 +1534,6 @@ forward void L4D_TankClaw_GroundPound_Pre(int tank, int claw);
|
|||
* @remarks When hitting the ground (maybe only when hitting an incapped player)
|
||||
* @remarks The forwards "L4D_TankClaw_OnPlayerHit_Pre" and "L4D_TankClaw_OnPlayerHit_Post" trigger before this
|
||||
* @remarks The forwards "L4D_TankClaw_DoSwing_Pre" and "L4D_TankClaw_DoSwing_Post" can trigger after this
|
||||
* @remarks This forward will not trigger if the relative pre-hook forward has been blocked with Plugin_Handled
|
||||
*
|
||||
* @param tank tank client index
|
||||
* @param claw the claw entity index
|
||||
|
@ -1624,6 +1756,17 @@ forward Action L4D_OnStartMeleeSwing(int client, bool boolean);
|
|||
// L4D2 only.
|
||||
forward void L4D_OnStartMeleeSwing_Post(int client, bool boolean);
|
||||
|
||||
/**
|
||||
* @brief Called whenever CTerrorMeleeWeapon::StartMeleeSwing(CTerrorPlayer *, bool) is invoked
|
||||
* @remarks Called when a player uses his melee weapons primary attack. This is before the game
|
||||
* reads the melee weapon data (model etc) and decides if he CAN attack at all.
|
||||
* @remarks This forward will ONLY trigger if the relative pre-hook forward has been blocked with Plugin_Handled
|
||||
*
|
||||
* @noreturn
|
||||
*/
|
||||
// L4D2 only.
|
||||
forward void L4D_OnStartMeleeSwing_PostHandled(int client, bool boolean);
|
||||
|
||||
/**
|
||||
* @brief Called whenever CTerrorMeleeWeapon::GetDamageForVictim() is invoked
|
||||
* @remarks Called to calculate the damage when a melee weapon hits something
|
||||
|
@ -1640,6 +1783,35 @@ forward void L4D_OnStartMeleeSwing_Post(int client, bool boolean);
|
|||
// L4D2 only.
|
||||
forward Action L4D2_MeleeGetDamageForVictim(int client, int weapon, int victim, float &damage);
|
||||
|
||||
/**
|
||||
* @brief Called whenever CTerrorPlayer::DoAnimationEvent is invoked
|
||||
* @note The event argument is NOT the same as the sequence numbers found in the model viewer
|
||||
* @note You can get the number for your animation by looking at the disasm for virtual calls to DoAnimationEvent
|
||||
*
|
||||
* @return Plugin_Handled to block, Plugin_Changed to modify value, Plugin_Continue otherwise
|
||||
*/
|
||||
forward Action L4D_OnDoAnimationEvent(int client, int &event, int &variant_param);
|
||||
|
||||
/**
|
||||
* @brief Called whenever CTerrorPlayer::DoAnimationEvent is invoked
|
||||
* @note The event argument is NOT the same as the sequence numbers found in the model viewer
|
||||
* @note You can get the number for your animation by looking at the disasm for virtual calls to DoAnimationEvent
|
||||
* @remarks This forward will not trigger if the relative pre-hook forward has been blocked with Plugin_Handled
|
||||
*
|
||||
* @noreturn
|
||||
*/
|
||||
forward void L4D_OnDoAnimationEvent_Post(int client, int event, int variant_param);
|
||||
|
||||
/**
|
||||
* @brief Called whenever CTerrorPlayer::DoAnimationEvent is invoked
|
||||
* @note The event argument is NOT the same as the sequence numbers found in the model viewer
|
||||
* @note You can get the number for your animation by looking at the disasm for virtual calls to DoAnimationEvent
|
||||
* @remarks This forward will ONLY trigger if the relative pre-hook forward has been blocked with Plugin_Handled
|
||||
*
|
||||
* @noreturn
|
||||
*/
|
||||
forward void L4D_OnDoAnimationEvent_PostHandled(int client, int event, int variant_param);
|
||||
|
||||
/**
|
||||
* @brief Called whenever CDirectorScriptedEventManager::SendInRescueVehicle(void) is invoked
|
||||
* @remarks Called when the last Finale stage is reached and the Rescue means becomes 'available'.
|
||||
|
@ -1679,6 +1851,21 @@ forward Action L4D2_OnChangeFinaleStage(int &finaleType, const char[] arg);
|
|||
// L4D2 only.
|
||||
forward void L4D2_OnChangeFinaleStage_Post(int finaleType, const char[] arg);
|
||||
|
||||
/**
|
||||
* @brief Called whenever CDirectorScriptedEventManager::ChangeFinaleStage is invoked
|
||||
* @remarks Called when the director stage changes
|
||||
* @remarks some values for FinaleStageType: 1 - Finale Started; 6 - Rescue Vehicle Ready; 7 - Zombie Hordes; 8 - Tank; 10 - Combat Respite (nothing spawns)
|
||||
* @remarks SendInRescueVehicle does not depend on Finale Stage being 6, that only signals endless Hordes/Tanks
|
||||
* @remarks Can use the "FINALE_*" enums (search for them above) for the finaleType value.
|
||||
* @remarks This forward will ONLY trigger if the relative pre-hook forward has been blocked with Plugin_Handled
|
||||
*
|
||||
* @param FinaleStageType integer value
|
||||
*
|
||||
* @noreturn
|
||||
*/
|
||||
// L4D2 only.
|
||||
forward void L4D2_OnChangeFinaleStage_PostHandled(int finaleType, const char[] arg);
|
||||
|
||||
/**
|
||||
* @brief Called whenever CDirectorVersusMode::EndVersusModeRound(bool) is invoked
|
||||
* @remarks Called before score calculations and the scoreboard display
|
||||
|
@ -1981,7 +2168,6 @@ forward Action L4D_OnMotionControlledXY(int client, int activity);
|
|||
|
||||
/**
|
||||
* @brief Called whenever CTerrorPlayer::OnShovedByPounceLanding(CTerrorPlayer*) is invoked
|
||||
* @remarks This forward will not trigger if the relative pre-hook forward has been blocked with Plugin_Handled
|
||||
*
|
||||
* @param victim the survivor that is about to get stumbled as a result of "attacker" capping someone in close proximity
|
||||
* @param attacker the SI that is about to cause a stumble as a result of capping someone in close proximity to a survivor
|
||||
|
@ -2097,7 +2283,7 @@ forward void L4D2_OnThrowImpactedSurvivor_PostHandled(int attacker, int victim);
|
|||
* @remarks Does not trigger for all cases when someone is fatally falling.
|
||||
* @remarks Use this forward to check if the current map has death fall cameras (fatal falls).
|
||||
*
|
||||
* @param client Client index of the player.
|
||||
* @param client Client index of the player. Can be 0.
|
||||
* @param camera Death fall camera index.
|
||||
*
|
||||
* @return Plugin_Handled to block the death fall camera, Plugin_Continue to allow.
|
||||
|
@ -2279,7 +2465,6 @@ forward void L4D_OnGrabWithTongue_PostHandled(int victim, int attacker);
|
|||
/**
|
||||
* @brief Called whenever CTerrorPlayer::OnLeptOnSurvivor() is invoked
|
||||
* @remarks Called when a Survivor player is about to be ridden by a Jockey
|
||||
* @remarks This forward will not trigger if the relative pre-hook forward has been blocked with Plugin_Handled
|
||||
*
|
||||
* @param victim the client who's being grabbed
|
||||
* @param attacker the Jockey grabbing someone
|
||||
|
@ -2302,17 +2487,30 @@ forward Action L4D2_OnJockeyRide(int victim, int attacker);
|
|||
// L4D2 only.
|
||||
forward void L4D2_OnJockeyRide_Post(int victim, int attacker);
|
||||
|
||||
/**
|
||||
* @brief Called whenever CTerrorPlayer::OnLeptOnSurvivor() is invoked
|
||||
* @remarks Called when a Survivor player is starting to be ridden by a Jockey
|
||||
* @remarks This forward will ONLY trigger if the relative pre-hook forward has been blocked with Plugin_Handled
|
||||
*
|
||||
* @param victim the client who's being grabbed
|
||||
* @param attacker the Jockey grabbing someone
|
||||
*
|
||||
* @noreturn
|
||||
*/
|
||||
// L4D2 only.
|
||||
forward void L4D2_OnJockeyRide_PostHandled(int victim, int attacker);
|
||||
|
||||
/**
|
||||
* @brief Called whenever CTerrorPlayer::OnSlammedSurvivor() is invoked
|
||||
* @remarks Called when a Survivor is slammed into a wall by a Charger, or on the first pummel if bWallSlam is 0
|
||||
* @bDeadlyCharge seems to always return 1 on Windows
|
||||
* @remarks bDeadlyCharge seems to always return 1 on Windows
|
||||
*
|
||||
* @param victim the client who's being slammed
|
||||
* @param attacker the Charger slamming someone
|
||||
* @param bWallSlam when slammed into a wall. Changing this can play a different animation
|
||||
* @param bDeadlyCharge indicates the carry ends at a height down 360.0 units from the carry start, and adds DMG_PARALYZE to the damage flags to incap the victim. Changing this can incap the victim.
|
||||
*
|
||||
* @return 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.
|
||||
forward Action L4D2_OnSlammedSurvivor(int victim, int attacker, bool &bWallSlam, bool &bDeadlyCharge);
|
||||
|
@ -2321,6 +2519,7 @@ forward Action L4D2_OnSlammedSurvivor(int victim, int attacker, bool &bWallSlam,
|
|||
* @brief Called whenever CTerrorPlayer::OnSlammedSurvivor() is invoked
|
||||
* @remarks Called when a Survivor is slammed into a wall by a Charger, or on the first pummel if bWallSlam is 0
|
||||
* @bDeadlyCharge seems to always return 1 on Windows
|
||||
* @remarks This forward will not trigger if the relative pre-hook forward has been blocked with Plugin_Handled
|
||||
*
|
||||
* @param victim the client who's being slammed
|
||||
* @param attacker the Charger slamming someone
|
||||
|
@ -2332,10 +2531,25 @@ forward Action L4D2_OnSlammedSurvivor(int victim, int attacker, bool &bWallSlam,
|
|||
// L4D2 only.
|
||||
forward void L4D2_OnSlammedSurvivor_Post(int victim, int attacker, bool bWallSlam, bool bDeadlyCharge);
|
||||
|
||||
/**
|
||||
* @brief Called whenever CTerrorPlayer::OnSlammedSurvivor() is invoked
|
||||
* @remarks Called when a Survivor is slammed into a wall by a Charger, or on the first pummel if bWallSlam is 0
|
||||
* @bDeadlyCharge seems to always return 1 on Windows
|
||||
* @remarks This forward will ONLY trigger if the relative pre-hook forward has been blocked with Plugin_Handled
|
||||
*
|
||||
* @param victim the client who's being slammed
|
||||
* @param attacker the Charger slamming someone
|
||||
* @param bWallSlam when slammed into a wall. Changing this can play a different animation
|
||||
* @param bDeadlyCharge indicates the carry ends at a height down 360.0 units from the carry start, and adds DMG_PARALYZE to the damage flags to incap the victim. Changing this can incap the victim.
|
||||
*
|
||||
* @noreturn
|
||||
*/
|
||||
// L4D2 only.
|
||||
forward void L4D2_OnSlammedSurvivor_PostHandled(int victim, int attacker, bool bWallSlam, bool bDeadlyCharge);
|
||||
|
||||
/**
|
||||
* @brief Called whenever CTerrorPlayer::OnStartCarryingVictim() is invoked
|
||||
* @remarks Called when a Survivor player is about to be carried by a Charger
|
||||
* @remarks This forward will not trigger if there's no room to charge when grabbing a survivor, but "L4D2_OnPummelVictim" will trigger
|
||||
*
|
||||
* @param victim the client who's being grabbed
|
||||
* @param attacker the Charger picking up someone
|
||||
|
@ -2348,7 +2562,6 @@ forward Action L4D2_OnStartCarryingVictim(int victim, int attacker);
|
|||
/**
|
||||
* @brief Called whenever CTerrorPlayer::OnStartCarryingVictim() is invoked
|
||||
* @remarks Called when a Survivor player is about to be carried by a Charger
|
||||
* @remarks This forward will not trigger if there's no room to charge when grabbing a survivor, but "L4D2_OnPummelVictim" will trigger
|
||||
* @remarks This forward will not trigger if the relative pre-hook forward has been blocked with Plugin_Handled
|
||||
*
|
||||
* @param victim the client who's being grabbed
|
||||
|
@ -2359,6 +2572,19 @@ forward Action L4D2_OnStartCarryingVictim(int victim, int attacker);
|
|||
// L4D2 only.
|
||||
forward void L4D2_OnStartCarryingVictim_Post(int victim, int attacker);
|
||||
|
||||
/**
|
||||
* @brief Called whenever CTerrorPlayer::OnStartCarryingVictim() is invoked
|
||||
* @remarks Called when a Survivor player is about to be carried by a Charger
|
||||
* @remarks This forward will ONLY trigger if the relative pre-hook forward has been blocked with Plugin_Handled
|
||||
*
|
||||
* @param victim the client who's being grabbed
|
||||
* @param attacker the Charger picking up someone
|
||||
*
|
||||
* @noreturn
|
||||
*/
|
||||
// L4D2 only.
|
||||
forward void L4D2_OnStartCarryingVictim_PostHandled(int victim, int attacker);
|
||||
|
||||
/**
|
||||
* @brief Called when CTerrorPlayer::QueuePummelVictim is invoked.
|
||||
* @remarks Called when a player is about to be pummelled by a Charger.
|
||||
|
@ -2440,7 +2666,6 @@ forward void L4D_OnVomitedUpon_PostHandled(int victim, int attacker, bool boomer
|
|||
/**
|
||||
* @brief Called whenever CTerrorPlayer::OnHitByVomitJar is invoked
|
||||
* @remarks Called when a Special Infected is about to be hit from a Bilejar explosion
|
||||
* @remarks This forward will not trigger if the relative pre-hook forward has been blocked with Plugin_Handled
|
||||
*
|
||||
* @param victim the client who's now it
|
||||
* @param attacker the attacker who caused the vomit (can be 0)
|
||||
|
@ -2461,10 +2686,21 @@ forward Action L4D2_OnHitByVomitJar(int victim, int &attacker);
|
|||
*/
|
||||
forward void L4D2_OnHitByVomitJar_Post(int victim, int attacker);
|
||||
|
||||
/**
|
||||
* @brief Called whenever CTerrorPlayer::OnHitByVomitJar is invoked
|
||||
* @remarks Called when a Special Infected is hit from a Bilejar explosion
|
||||
* @remarks This forward will ONLY trigger if the relative pre-hook forward has been blocked with Plugin_Handled
|
||||
*
|
||||
* @param victim the client who's now it
|
||||
* @param attacker the attacker who caused the vomit (can be 0)
|
||||
*
|
||||
* @noreturn
|
||||
*/
|
||||
forward void L4D2_OnHitByVomitJar_PostHandled(int victim, int attacker);
|
||||
|
||||
/**
|
||||
* @brief Called whenever CPipeBombProjectile::Create is invoked
|
||||
* @remarks Called when a PipeBomb projectile is being created
|
||||
* @remarks This forward will not trigger if the relative pre-hook forward has been blocked with Plugin_Handled
|
||||
*
|
||||
* @param client the client who is throwing the grenade (can be 0)
|
||||
* @param vecPos the position vector of the projectile
|
||||
|
@ -2619,7 +2855,6 @@ forward void L4D2_VomitJar_Detonate_PostHandled(int entity, int client);
|
|||
/**
|
||||
* @brief Called whenever CInsectSwarm::CanHarm() is invoked
|
||||
* @remarks Called when Spitter Acid is determining if a client or entity can be damaged
|
||||
* @remarks This forward will not trigger if the relative pre-hook forward has been blocked with Plugin_Handled
|
||||
*
|
||||
* @param acid the acid entity index causing the damage
|
||||
* @param spitter the Spitter or client who created the acid (can be 0 or -1)
|
||||
|
@ -2710,10 +2945,23 @@ forward Action L4D2_CGasCan_ShouldStartAction(int client, int gascan, int nozzle
|
|||
// L4D2 only.
|
||||
forward void L4D2_CGasCan_ShouldStartAction_Post(int client, int gascan, int nozzle);
|
||||
|
||||
/**
|
||||
* @brief Called whenever CGasCan::ShouldStartAction() is invoked
|
||||
* @remarks Called when someone has started to pour a gascan into a nozzle
|
||||
* @remarks This forward will ONLY trigger if the relative pre-hook forward has been blocked with Plugin_Handled
|
||||
*
|
||||
* @param client the client pouring
|
||||
* @param gascan the gascan entity index that is being consumed
|
||||
* @param nozzle the nozzle being poured into
|
||||
*
|
||||
* @noreturn
|
||||
*/
|
||||
// L4D2 only.
|
||||
forward void L4D2_CGasCan_ShouldStartAction_PostHandled(int client, int gascan, int nozzle);
|
||||
|
||||
/**
|
||||
* @brief Called whenever CGasCan::OnActionComplete() is invoked
|
||||
* @remarks Called when someone is about to complete pouring a gascan into a nozzle
|
||||
* @remarks This forward will not trigger if the relative pre-hook forward has been blocked with Plugin_Handled
|
||||
*
|
||||
* @param client the client pouring
|
||||
* @param gascan the gascan entity index that is being consumed
|
||||
|
@ -2739,7 +2987,21 @@ forward Action L4D2_CGasCan_ActionComplete(int client, int gascan, int nozzle);
|
|||
forward void L4D2_CGasCan_ActionComplete_Post(int client, int gascan, int nozzle);
|
||||
|
||||
/**
|
||||
* @brief Returns the current game mode type when it changes. 0=Unknown or error. 1=Coop. 2=Survival. 4=Versus. 8=Scavenge (L4D2).
|
||||
* @brief Called whenever CGasCan::OnActionComplete() is invoked
|
||||
* @remarks Called when someone completes pouring a gascan into a nozzle
|
||||
* @remarks This forward will ONLY trigger if the relative pre-hook forward has been blocked with Plugin_Handled
|
||||
*
|
||||
* @param client the client pouring
|
||||
* @param gascan the gascan entity index that is being consumed
|
||||
* @param nozzle the nozzle being poured into
|
||||
*
|
||||
* @noreturn
|
||||
*/
|
||||
// L4D2 only.
|
||||
forward void L4D2_CGasCan_ActionComplete_PostHandled(int client, int gascan, int nozzle);
|
||||
|
||||
/**
|
||||
* @brief Returns the current game mode type when it changes. 0=Unknown or error. 1=Coop. 2=Versus. 4=Survival. 8=Scavenge (L4D2).
|
||||
* @remarks You can use the "GAMEMODE_*" enums provided above to match the mode.
|
||||
* @remarks Only triggers when the server starts and after when the game mode changes.
|
||||
*
|
||||
|
@ -2922,7 +3184,7 @@ native bool L4D2_ExecVScriptCode(char[] code);
|
|||
native bool L4D2_GetVScriptOutput(char[] code, char[] buffer, int maxlength);
|
||||
|
||||
/**
|
||||
* @brief Returns the current game mode type. 0=Unknown or error. 1=Coop. 2=Survival. 4=Versus. 8=Scavenge (L4D2).
|
||||
* @brief Returns the current game mode type. 0=Unknown or error. 1=Coop. 2=Versus. 4=Survival. 8=Scavenge (L4D2).
|
||||
* @remarks You can use the "GAMEMODE_*" enums provided above to match the mode.
|
||||
*
|
||||
* @return Current game mode.
|
||||
|
@ -3100,6 +3362,24 @@ native any L4D_GetNearestNavArea(const float vecPos[3], float maxDist = 300.0, b
|
|||
*/
|
||||
native any L4D_GetLastKnownArea(int client);
|
||||
|
||||
/**
|
||||
* @brief Gets the first Special Infected type the Director will spawn. Value set on map start.
|
||||
* @remarks zombieClass: 1=Smoker, 2=Boomer, 3=Hunter, 4=Spitter, 5=Jockey, 6=Charger
|
||||
*
|
||||
* @return zombieClass of the Special Infected first spawning.
|
||||
*/
|
||||
// L4D2 only.
|
||||
native int L4D2_GetFirstSpawnClass();
|
||||
|
||||
/**
|
||||
* @brief Sets the first Special Infected type the Director will spawn.
|
||||
* @remarks zombieClass: 1=Smoker, 2=Boomer, 3=Hunter, 4=Spitter, 5=Jockey, 6=Charger
|
||||
*
|
||||
* @noreturn
|
||||
*/
|
||||
// L4D2 only.
|
||||
native void L4D2_SetFirstSpawnClass(int zombieClass);
|
||||
|
||||
/**
|
||||
* @brief Gets the maximum flow distance any survivor has achieved.
|
||||
*
|
||||
|
@ -3162,6 +3442,9 @@ native bool L4D_AreAllSurvivorsInFinaleArea();
|
|||
|
||||
/**
|
||||
* @brief Returns true when the specified Survivor or Special Infected is in the starting checkpoint area.
|
||||
* @remarks This might return true on certain maps, maybe in Survival/Scavenge start areas if they are close enough to the saferoom.
|
||||
* @remarks You could use the "L4D_IsPositionInFirstCheckpoint" native instead to accurately determine if someone is in the starting area.
|
||||
* @remarks This will always returns false when the "Unlock Finales" plugin by "Marttt" is installed: https://forums.alliedmods.net/showthread.php?t=333274
|
||||
*
|
||||
* @param client Client id to check their checkpoint.
|
||||
*
|
||||
|
@ -4045,8 +4328,56 @@ native int L4D_GetCurrentChapter();
|
|||
*/
|
||||
native int L4D_GetMaxChapters();
|
||||
|
||||
/**
|
||||
* @brief Returns all TheNavAreas addresses
|
||||
*
|
||||
*param aList The ArrayList to store all nav area addresses.
|
||||
*
|
||||
* @noreturn
|
||||
*/
|
||||
native void L4D_GetAllNavAreas(ArrayList aList);
|
||||
|
||||
/**
|
||||
* @brief Returns a given NavArea's ID from it's address
|
||||
*
|
||||
*param area The NavArea address
|
||||
*
|
||||
* @return NavArea ID
|
||||
*/
|
||||
native int L4D_GetNavAreaID(Address area);
|
||||
|
||||
/**
|
||||
* @brief Returns a given NavArea address from it's ID
|
||||
*
|
||||
*param id The NavArea ID
|
||||
*
|
||||
* @return NavArea address or Address_Null if invalid ID
|
||||
*/
|
||||
native Address L4D_GetNavAreaByID(int id);
|
||||
|
||||
/**
|
||||
* @brief Returns origin of a given NavArea
|
||||
*
|
||||
*param area The address of the NavArea to read.
|
||||
*param vecPos The vector to store the position read.
|
||||
*
|
||||
* @noreturn
|
||||
*/
|
||||
native void L4D_GetNavAreaPos(Address area, float vecPos[3]);
|
||||
|
||||
/**
|
||||
* @brief Returns size of a given NavArea
|
||||
*
|
||||
*param area The address of the NavArea to read.
|
||||
*param vecPos The vector to store the size read.
|
||||
*
|
||||
* @noreturn
|
||||
*/
|
||||
native void L4D_GetNavAreaSize(Address area, float vecSize[3]);
|
||||
|
||||
/**
|
||||
* @brief Returns the nav area attribute flags
|
||||
* @remarks See the "NAV_BASE_*" near the top of the include file
|
||||
*
|
||||
*param pTerrorNavArea Pointer to a NavArea
|
||||
*
|
||||
|
@ -4056,6 +4387,7 @@ native int L4D_GetNavArea_AttributeFlags(Address pTerrorNavArea);
|
|||
|
||||
/**
|
||||
* @brief Sets the nav area attribute flags
|
||||
* @remarks See the "NAV_BASE_*" near the top of the include file
|
||||
*
|
||||
*param pTerrorNavArea Pointer to a NavArea
|
||||
*param flags Attribute flags to set
|
||||
|
@ -4066,6 +4398,7 @@ native void L4D_SetNavArea_AttributeFlags(Address pTerrorNavArea, int flags);
|
|||
|
||||
/**
|
||||
* @brief Returns the terror nav area attribute flags
|
||||
* @remarks See the "NAV_SPAWN_*" near the top of the include file
|
||||
*
|
||||
*param pTerrorNavArea Pointer to a TerrorNavArea
|
||||
*
|
||||
|
@ -4075,6 +4408,7 @@ native int L4D_GetNavArea_SpawnAttributes(Address pTerrorNavArea);
|
|||
|
||||
/**
|
||||
* @brief Sets the terror nav area attribute flags
|
||||
* @remarks See the "NAV_SPAWN_*" near the top of the include file
|
||||
*
|
||||
*param pTerrorNavArea Pointer to a TerrorNavArea
|
||||
*param flags Attribute flags to set
|
||||
|
@ -5068,7 +5402,7 @@ native float L4D2Direct_GetFlowDistance(int client);
|
|||
*
|
||||
* @noreturn
|
||||
*/
|
||||
native void L4D2Direct_DoAnimationEvent(int client, int event);
|
||||
native void L4D2Direct_DoAnimationEvent(int client, int event, int variant_param = 0);
|
||||
|
||||
/**
|
||||
* Get the clients health bonus.
|
||||
|
@ -5555,6 +5889,7 @@ native void L4D2_SwapTeams();
|
|||
* @return 0=Not flipped. 1=Flipped
|
||||
*/
|
||||
// L4D2 only.
|
||||
#pragma deprecated Use this instead: GameRules_GetProp("m_bAreTeamsFlipped");
|
||||
native bool L4D2_AreTeamsFlipped();
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Left 4 DHooks Direct
|
||||
* Copyright (C) 2022 Silvers
|
||||
* Copyright (C) 2023 Silvers
|
||||
*
|
||||
* 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
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* Copyright (C) 2022 LuxLuma
|
||||
* Copyright (C) 2023 LuxLuma
|
||||
*
|
||||
* 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
|
||||
|
|
|
@ -280,6 +280,32 @@ stock void StopUsingMinigun(int client)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns if a player is on fire
|
||||
*
|
||||
* @param client Client index to check
|
||||
*
|
||||
* @return true on fire, false otherwise
|
||||
*/
|
||||
stock bool L4D_IsPlayerOnFire(int client)
|
||||
{
|
||||
if( GetEntProp(client, Prop_Data, "m_fFlags") & FL_ONFIRE ) return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns if a player is burning
|
||||
*
|
||||
* @param client Client index to check
|
||||
*
|
||||
* @return true on burning, false otherwise
|
||||
*/
|
||||
stock bool L4D_IsPlayerBurning(int client)
|
||||
{
|
||||
float fBurning = GetEntPropFloat(client, Prop_Send, "m_burnPercent");
|
||||
return (fBurning > 0.0) ? true : false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ==================================================
|
||||
|
@ -713,48 +739,65 @@ stock bool L4D_HasReachedSmoker(int client)
|
|||
// ==================================================
|
||||
// CHARGER STOCKS - Written by "Forgetest"
|
||||
// ==================================================
|
||||
#define QueuedPummel_Victim 0
|
||||
#define QueuedPummel_StartTime 4
|
||||
#define QueuedPummel_Attacker 8
|
||||
|
||||
/**
|
||||
* @brief Internally used to get offset to the start of queued pummel field.
|
||||
*
|
||||
* @return Offset into CTerrorPlayer to the start of queued pummel props
|
||||
*/
|
||||
static int L4D2_OffsQueuedPummelInfo()
|
||||
static stock int L4D2_OffsQueuedPummelInfo()
|
||||
{
|
||||
static int m_hQueuedPummelVictim = -1;
|
||||
if ( m_hQueuedPummelVictim == -1 )
|
||||
if( m_hQueuedPummelVictim == -1 )
|
||||
m_hQueuedPummelVictim = FindSendPropInfo("CTerrorPlayer", "m_pummelAttacker") + 4;
|
||||
|
||||
|
||||
return m_hQueuedPummelVictim;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the timestamp when the queued pummel begins.
|
||||
*
|
||||
* @param client Client ID of the player to check
|
||||
* @param client Client ID of the charger to check
|
||||
*
|
||||
* @return timestamp or -1.0 if no queued pummel
|
||||
*/
|
||||
stock float L4D2_GetQueuedPummelStartTime(int client)
|
||||
stock float L4D2_GetQueuedPummelStartTime(int charger)
|
||||
{
|
||||
return GetEntDataFloat(client, L4D2_OffsQueuedPummelInfo() + 4);
|
||||
return GetEntDataFloat(charger, L4D2_OffsQueuedPummelInfo() + QueuedPummel_StartTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the timestamp when the queued pummel begins.
|
||||
*
|
||||
* @param client Client ID of the charger to check
|
||||
* @param timestamp Timestamp to set
|
||||
*
|
||||
* @noreturn
|
||||
*/
|
||||
stock void L4D2_SetQueuedPummelStartTime(int charger, float timestamp)
|
||||
{
|
||||
SetEntDataFloat(charger, L4D2_OffsQueuedPummelInfo() + QueuedPummel_StartTime, timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns if a Charger is in a queued pummel.
|
||||
*
|
||||
* @param client Client ID of the player to check
|
||||
* @param charger Client ID of the charger to check
|
||||
*
|
||||
* @return true if in queued pummel, false otherwise
|
||||
*/
|
||||
stock bool L4D2_IsInQueuedPummel(int client)
|
||||
stock bool L4D2_IsInQueuedPummel(int charger)
|
||||
{
|
||||
float flTimestamp = L4D2_GetQueuedPummelStartTime(client);
|
||||
|
||||
float flTimestamp = L4D2_GetQueuedPummelStartTime(charger);
|
||||
|
||||
return flTimestamp != -1.0 && flTimestamp > GetGameTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the victim when a Charger is in a queued pummel.
|
||||
* @brief Returns the victim of a Charger in a queued pummel.
|
||||
*
|
||||
* @param client Client ID of the player to check
|
||||
*
|
||||
|
@ -762,11 +805,24 @@ stock bool L4D2_IsInQueuedPummel(int client)
|
|||
*/
|
||||
stock int L4D2_GetQueuedPummelVictim(int client)
|
||||
{
|
||||
return GetEntDataEnt2(client, L4D2_OffsQueuedPummelInfo());
|
||||
return GetEntDataEnt2(client, L4D2_OffsQueuedPummelInfo() + QueuedPummel_Victim);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the attacker when a Survivor is in a queued pummel.
|
||||
* @brief Sets the victim of a Charger in a queued pummel.
|
||||
*
|
||||
* @param client Client ID of the player to set
|
||||
* @param target Client ID of the target to set
|
||||
*
|
||||
* @noreturn
|
||||
*/
|
||||
stock void L4D2_SetQueuedPummelVictim(int client, int target)
|
||||
{
|
||||
SetEntDataEnt2(client, L4D2_OffsQueuedPummelInfo() + QueuedPummel_Victim, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the attacker of a Survivor in a queued pummel.
|
||||
*
|
||||
* @param client Client ID of the player to check
|
||||
*
|
||||
|
@ -774,7 +830,20 @@ stock int L4D2_GetQueuedPummelVictim(int client)
|
|||
*/
|
||||
stock int L4D2_GetQueuedPummelAttacker(int client)
|
||||
{
|
||||
return GetEntDataEnt2(client, L4D2_OffsQueuedPummelInfo() + 8);
|
||||
return GetEntDataEnt2(client, L4D2_OffsQueuedPummelInfo() + QueuedPummel_Attacker);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the attacker of a Survivor in a queued pummel.
|
||||
*
|
||||
* @param client Client ID of the player to set
|
||||
* @param target Client ID of the target to set
|
||||
*
|
||||
* @noreturn
|
||||
*/
|
||||
stock void L4D2_SetQueuedPummelAttacker(int client, int target)
|
||||
{
|
||||
SetEntDataEnt2(client, L4D2_OffsQueuedPummelInfo() + QueuedPummel_Attacker, target);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* =============================================================================
|
||||
* Left 4 Dead Stocks Library (C)2011-2012 Buster "Mr. Zero" Nielsen
|
||||
* Syntax Update and merge into "Left 4 DHooks Direct" (C) 2022 "SilverShot"
|
||||
* Syntax Update and merge into "Left 4 DHooks Direct" (C) 2023 "SilverShot"
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
|
@ -134,13 +134,17 @@ enum L4D2ZombieClassType
|
|||
|
||||
enum L4D2UseAction
|
||||
{
|
||||
L4D2UseAction_None = 0, // No use action active
|
||||
L4D2UseAction_Healing = 1, // Includes healing yourself or a teammate.
|
||||
L4D2UseAction_Defibing = 4, // When defib'ing a dead body.
|
||||
L4D2UseAction_GettingDefibed = 5, // When comming back to life from a dead body.
|
||||
L4D2UseAction_PouringGas = 8, // Pouring gas into a generator
|
||||
L4D2UseAction_Cola = 9, // For Dead Center map 2 cola event, when handing over the cola to whitalker.
|
||||
L4D2UseAction_Button = 10 // Such as buttons, timed buttons, generators, etc.
|
||||
L4D2UseAction_None = 0, // No use action active
|
||||
L4D2UseAction_Healing = 1, // Includes healing yourself or a teammate.
|
||||
L4D2UseAction_AmmoPack = 2, // When deploying the ammo pack that was never added into the game
|
||||
L4D2UseAction_Defibing = 4, // When defib'ing a dead body.
|
||||
L4D2UseAction_GettingDefibed = 5, // When comming back to life from a dead body.
|
||||
L4D2UseAction_DeployIncendiary = 6, // When deploying Incendiary ammo
|
||||
L4D2UseAction_DeployExplosive = 7, // When deploying Explosive ammo
|
||||
L4D2UseAction_PouringGas = 8, // Pouring gas into a generator
|
||||
L4D2UseAction_Cola = 9, // For Dead Center map 2 cola event, when handing over the cola to whitalker.
|
||||
L4D2UseAction_Button = 10, // Such as buttons, timed buttons, generators, etc.
|
||||
L4D2UseAction_UsePointScript = 11 // When using a "point_script_use_target" entity
|
||||
/* List is not fully done, these are just the ones I have found so far */
|
||||
}
|
||||
|
||||
|
|
|
@ -46,6 +46,8 @@ enum struct PlayerData {
|
|||
int immunityFlags;
|
||||
|
||||
bool pendingAction;
|
||||
|
||||
bool joined;
|
||||
}
|
||||
|
||||
PlayerData pData[MAXPLAYERS+1];
|
||||
|
@ -96,7 +98,7 @@ public void OnPluginStart() {
|
|||
hFFAutoScaleAmount = CreateConVar("l4d2_tk_auto_ff_rate", "0.02", "The rate at which auto reverse-ff is scaled by.", FCVAR_NONE, true, 0.0);
|
||||
hFFAutoScaleMaxRatio = CreateConVar("l4d2_tk_auto_ff_max_ratio", "5.0", "The maximum amount that the reverse ff can go. 0.0 for unlimited", FCVAR_NONE, true, 0.0);
|
||||
hFFAutoScaleForgivenessAmount = CreateConVar("l4d2_tk_auto_ff_forgive_rate", "0.05", "This amount times amount of minutes since last ff is removed from ff rate", FCVAR_NONE, true, 0.0);
|
||||
hFFAutoScaleActivateTypes = CreateConVar("l4d2_tk_auto_ff_activate_types", "7", "The types of damages to ignore. Add bits together.\n0 = Just direct fire\n1 = Damage from admins\n2 = Blast damage (pipes, grenade launchers)\n4 = Molotov/gascan/firework damage\n8 = Killing black and white players", FCVAR_NONE, true, 0.0, true, 15.0);
|
||||
hFFAutoScaleActivateTypes = CreateConVar("l4d2_tk_auto_ff_activate_types", "6", "The types of damages to ignore. Add bits together.\n0 = Just direct fire\n1 = Damage from admins\n2 = Blast damage (pipes, grenade launchers)\n4 = Molotov/gascan/firework damage\n8 = Killing black and white players", FCVAR_NONE, true, 0.0, true, 15.0);
|
||||
|
||||
ConVar hGamemode = FindConVar("mp_gamemode");
|
||||
hGamemode.AddChangeHook(Event_GamemodeChange);
|
||||
|
@ -238,6 +240,7 @@ public void Event_FinaleVehicleReady(Event event, const char[] name, bool dontBr
|
|||
PrintChatToAdmins("Note: %N is still marked as troll and will be banned after this game. Use \"/ignore <player> tk\" to ignore them.", i);
|
||||
}
|
||||
}
|
||||
PrintToServer("[TKStopper] Escape vehicle active, 2x rff in effect");
|
||||
}
|
||||
|
||||
public void OnMapEnd() {
|
||||
|
@ -250,7 +253,8 @@ public void OnClientPutInServer(int client) {
|
|||
}
|
||||
|
||||
public void OnClientPostAdminCheck(int client) {
|
||||
if(GetUserAdmin(client) != INVALID_ADMIN_ID) {
|
||||
if(GetUserAdmin(client) != INVALID_ADMIN_ID && !pData[client].joined) {
|
||||
pData[client].joined = true;
|
||||
pData[client].immunityFlags = Immune_TK;
|
||||
// If no admins can do ff and they
|
||||
if(~hFFAutoScaleActivateTypes.IntValue & view_as<int>(RffActType_AdminDamage)) {
|
||||
|
@ -298,6 +302,7 @@ public void Event_PlayerDisconnect(Event event, const char[] name, bool dontBroa
|
|||
pData[client].ffCount = 0;
|
||||
pData[client].immunityFlags = 0;
|
||||
pData[client].totalFFCount = 0;
|
||||
pData[client].joined = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -314,7 +319,7 @@ public Action Event_OnTakeDamage(int victim, int& attacker, int& inflictor, flo
|
|||
return Plugin_Changed;
|
||||
}
|
||||
// Otherwise if attacker was ignored or is a bot, stop here and let vanilla handle it
|
||||
else if(pData[attacker].immunityFlags & Immune_RFF || IsFakeClient(attacker)) return Plugin_Continue;
|
||||
else if(pData[attacker].immunityFlags & Immune_RFF || IsFakeClient(attacker) || IsFakeClient(victim)) return Plugin_Continue;
|
||||
// If victim is black and white and rff damage isnt turned on for it, allow it:
|
||||
else if(damagetype & DMG_DIRECT && GetEntProp(victim, Prop_Send, "m_isGoingToDie") && ~hFFAutoScaleActivateTypes.IntValue & view_as<int>(RffActType_BlackAndWhiteDamage)) {
|
||||
return Plugin_Continue;
|
||||
|
@ -462,7 +467,7 @@ public Action Event_OnTakeDamage(int victim, int& attacker, int& inflictor, flo
|
|||
|
||||
SDKHooks_TakeDamage(attacker, attacker, attacker, pData[attacker].autoRFFScaleFactor * damage);
|
||||
if(pData[attacker].autoRFFScaleFactor > 1.0)
|
||||
damage /= pData[attacker].autoRFFScaleFactor;
|
||||
damage = 0.0;
|
||||
else
|
||||
damage /= 2.0;
|
||||
return Plugin_Changed;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
//#define DEBUG
|
||||
|
||||
#define ALLOW_HEALING_MIN_IDLE_TIME 180
|
||||
#define PLUGIN_VERSION "1.0"
|
||||
|
||||
#include <sourcemod>
|
||||
|
@ -10,6 +11,8 @@
|
|||
#include <actions>
|
||||
//#include <sdkhooks>
|
||||
|
||||
int idleTimeStart[MAXPLAYERS+1];
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "L4D2 AI Tweaks",
|
||||
|
@ -24,8 +27,16 @@ public void OnPluginStart() {
|
|||
if(g_Game != Engine_Left4Dead2) {
|
||||
SetFailState("This plugin is for L4D2 only.");
|
||||
}
|
||||
// HookEvent("player_bot_replace", Event_PlayerOutOfIdle );
|
||||
HookEvent("bot_player_replace", Event_PlayerToIdle);
|
||||
}
|
||||
|
||||
public Action Event_PlayerToIdle(Event event, const char[] name, bool dontBroadcast) {
|
||||
int client = GetClientOfUserId(event.GetInt("userid"));
|
||||
if(client > 0) {
|
||||
idleTimeStart[client] = GetTime();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnActionCreated( BehaviorAction action, int actor, const char[] name ) {
|
||||
/* Hooking friend healing action (when bot wants to heal someone) */
|
||||
|
@ -37,7 +48,8 @@ public Action OnFriendAction( BehaviorAction action, int actor, BehaviorAction p
|
|||
// Do not allow idle bots to heal another player, unless they are black and white.
|
||||
// Do not let idle bots heal non-idle bots
|
||||
int target = action.Get(0x34) & 0xFFF;
|
||||
if(GetEntProp(actor, Prop_Send, "m_humanSpectatorUserID") > 0) { // If idle bot
|
||||
int realPlayer = GetClientOfUserId(GetEntProp(actor, Prop_Send, "m_humanSpectatorUserID"));
|
||||
if(realPlayer > 0) { // If idle bot
|
||||
if(IsFakeClient(target)) {
|
||||
// If target is a bot, not idle player, ignore
|
||||
if(GetEntProp(target, Prop_Send, "m_humanSpectatorUserID") == 0) {
|
||||
|
@ -46,7 +58,7 @@ public Action OnFriendAction( BehaviorAction action, int actor, BehaviorAction p
|
|||
}
|
||||
}
|
||||
// If they are not black and white, also stop
|
||||
if(!GetEntProp(target, Prop_Send, "m_bIsOnThirdStrike")) { //If real player and not black and white, stop
|
||||
if(!GetEntProp(target, Prop_Send, "m_bIsOnThirdStrike") && idleTimeStart[realPlayer] < ALLOW_HEALING_MIN_IDLE_TIME) { //If real player and not black and white, stop
|
||||
result.type = DONE;
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
|
|
@ -54,10 +54,12 @@ public void OnPluginStart()
|
|||
if(IsValidEntity(i)) {
|
||||
GetEntityClassname(i, classname, sizeof(classname));
|
||||
if(StrEqual(classname, "witch", false)) {
|
||||
WitchList.Push(i);
|
||||
#if defined DEBUG
|
||||
PrintToServer("Found pre-existing witch %d", i);
|
||||
#endif
|
||||
if(HasEntProp(i, Prop_Send, "m_rage")) {
|
||||
WitchList.Push(EntIndexToEntRef(i));
|
||||
#if defined DEBUG
|
||||
PrintToServer("Found pre-existing witch %d", i);
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -136,29 +138,28 @@ public void Change_Gamemode(ConVar convar, const char[] oldValue, const char[] n
|
|||
|
||||
}
|
||||
|
||||
public Action Event_WitchSpawn(Event event, const char[] name, bool dontBroadcast) {
|
||||
public void Event_WitchSpawn(Event event, const char[] name, bool dontBroadcast) {
|
||||
int witchID = event.GetInt("witchid");
|
||||
WitchList.Push(witchID);
|
||||
#if defined DEBUG
|
||||
PrintToServer("Witch spawned: %d", witchID);
|
||||
#endif
|
||||
//If not currently scanning, begin scanning ONLY if not active
|
||||
if(timer == INVALID_HANDLE && AutoCrownBot == -1) {
|
||||
timer = CreateTimer(SCAN_INTERVAL, Timer_Scan, _, TIMER_REPEAT);
|
||||
if(HasEntProp(witchID, Prop_Send, "m_rage")) {
|
||||
WitchList.Push(EntIndexToEntRef(witchID));
|
||||
#if defined DEBUG
|
||||
PrintToServer("Witch spawned: %d", witchID);
|
||||
#endif
|
||||
//If not currently scanning, begin scanning ONLY if not active
|
||||
if(timer == INVALID_HANDLE && AutoCrownBot == -1) {
|
||||
timer = CreateTimer(SCAN_INTERVAL, Timer_Scan, _, TIMER_REPEAT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Action Event_WitchKilled(Event event, const char[] name, bool dontBroadcast) {
|
||||
int witchID = event.GetInt("witchid");
|
||||
int index = FindValueInArray(WitchList, witchID);
|
||||
#if defined DEBUG
|
||||
PrintToServer("Witched killed: %d", witchID);
|
||||
#endif
|
||||
public void Event_WitchKilled(Event event, const char[] name, bool dontBroadcast) {
|
||||
int witchRef = EntIndexToEntRef(event.GetInt("witchid"));
|
||||
int index = WitchList.FindValue(witchRef);
|
||||
if(index > -1) {
|
||||
RemoveFromArray(WitchList, index);
|
||||
WitchList.Erase(index);
|
||||
}
|
||||
//If witch that was killed, terminate active loop
|
||||
if(AutoCrownTarget == witchID) {
|
||||
if(AutoCrownTarget == witchRef) {
|
||||
ResetAutoCrown();
|
||||
#if defined DEBUG
|
||||
PrintToServer("AutoCrownTarget has died");
|
||||
|
@ -174,65 +175,67 @@ public Action Timer_Active(Handle hdl) {
|
|||
return Plugin_Stop;
|
||||
}
|
||||
//TODO: Also check if startled and cancel it immediately.
|
||||
if(AutoCrownBot > -1) {
|
||||
int client = GetClientOfUserId(AutoCrownBot);
|
||||
if(!IsValidEntity(AutoCrownTarget) || IsPlayerIncapped(client)) {
|
||||
ResetAutoCrown();
|
||||
|
||||
#if defined DEBUG
|
||||
PrintToServer("Could not find valid AutoCrownTarget");
|
||||
#endif
|
||||
return Plugin_Stop;
|
||||
}else if(client <= 0 || !IsClientConnected(client) || !IsClientInGame(client) || !IsPlayerAlive(client)) {
|
||||
ResetAutoCrown();
|
||||
#if defined DEBUG
|
||||
PrintToServer("Could not find valid AutoCrownBot");
|
||||
#endif
|
||||
return Plugin_Stop;
|
||||
}
|
||||
|
||||
char wpn[32];
|
||||
if(!GetClientWeapon(client, wpn, sizeof(wpn)) || !StrEqual(wpn, "weapon_autoshotgun") && !StrEqual(wpn, "weapon_shotgun_spas")) {
|
||||
ResetAutoCrown();
|
||||
#if defined DEBUG
|
||||
PrintToServer("AutoCrownBot does not have a valid weapon (%s)", wpn);
|
||||
#endif
|
||||
return Plugin_Stop;
|
||||
}
|
||||
|
||||
GetEntPropVector(AutoCrownTarget, Prop_Send, "m_vecOrigin", witchPos);
|
||||
GetClientAbsOrigin(client, botPosition);
|
||||
|
||||
float distance = GetVectorDistance(botPosition, witchPos);
|
||||
if(distance <= 60) {
|
||||
float botAngles[3];
|
||||
GetClientAbsAngles(client, botAngles);
|
||||
botAngles[0] = 60.0;
|
||||
botAngles[1] = RadToDeg(ArcTangent2( botPosition[1] - witchPos[1], botPosition[0] - witchPos[0])) + 180.0;
|
||||
//Is In Position
|
||||
|
||||
ClientCommand(client, "slot0");
|
||||
TeleportEntity(client, NULL_VECTOR, botAngles, NULL_VECTOR);
|
||||
AutoCrownInPosition = true;
|
||||
}else{
|
||||
L4D2_RunScript("CommandABot({cmd=1,bot=GetPlayerFromUserID(%i),pos=Vector(%f,%f,%f)})", AutoCrownBot, witchPos[0], witchPos[1], witchPos[2]);
|
||||
PathfindTries++;
|
||||
}
|
||||
if(PathfindTries > 30) {
|
||||
ResetAutoCrown();
|
||||
int index = FindValueInArray(WitchList, AutoCrownTarget);
|
||||
if(index > -1)
|
||||
RemoveFromArray(WitchList, index);
|
||||
//remove witch
|
||||
#if defined DEBUG
|
||||
PrintToServer("Could not pathfind to witch in time.");
|
||||
#endif
|
||||
}
|
||||
return Plugin_Continue;
|
||||
}else{
|
||||
if(AutoCrownBot == -1) {
|
||||
timer = CreateTimer(SCAN_INTERVAL, Timer_Scan, _, TIMER_REPEAT);
|
||||
return Plugin_Stop;
|
||||
}
|
||||
|
||||
int client = GetClientOfUserId(AutoCrownBot);
|
||||
int crownTarget = EntRefToEntIndex(AutoCrownTarget);
|
||||
if(crownTarget == INVALID_ENT_REFERENCE) {
|
||||
ResetAutoCrown();
|
||||
|
||||
#if defined DEBUG
|
||||
PrintToServer("Could not find valid AutoCrownTarget");
|
||||
#endif
|
||||
return Plugin_Stop;
|
||||
}else if(client <= 0 || !IsPlayerAlive(client)) {
|
||||
ResetAutoCrown();
|
||||
#if defined DEBUG
|
||||
PrintToServer("Could not find valid AutoCrownBot");
|
||||
#endif
|
||||
return Plugin_Stop;
|
||||
}
|
||||
|
||||
char wpn[32];
|
||||
if(!GetClientWeapon(client, wpn, sizeof(wpn)) || !StrEqual(wpn, "weapon_autoshotgun") && !StrEqual(wpn, "weapon_shotgun_spas")) {
|
||||
ResetAutoCrown();
|
||||
#if defined DEBUG
|
||||
PrintToServer("AutoCrownBot does not have a valid weapon (%s)", wpn);
|
||||
#endif
|
||||
return Plugin_Stop;
|
||||
}
|
||||
|
||||
GetEntPropVector(crownTarget, Prop_Send, "m_vecOrigin", witchPos);
|
||||
GetClientAbsOrigin(client, botPosition);
|
||||
|
||||
float distance = GetVectorDistance(botPosition, witchPos, true);
|
||||
if(distance <= 3600) {
|
||||
float botAngles[3];
|
||||
GetClientAbsAngles(client, botAngles);
|
||||
botAngles[0] = 60.0;
|
||||
botAngles[1] = RadToDeg(ArcTangent2(botPosition[1] - witchPos[1], botPosition[0] - witchPos[0])) + 180.0;
|
||||
//Is In Position
|
||||
|
||||
ClientCommand(client, "slot0");
|
||||
TeleportEntity(client, NULL_VECTOR, botAngles, NULL_VECTOR);
|
||||
AutoCrownInPosition = true;
|
||||
} else {
|
||||
L4D2_RunScript("CommandABot({cmd=1,bot=GetPlayerFromUserID(%i),pos=Vector(%f,%f,%f)})", AutoCrownBot, witchPos[0], witchPos[1], witchPos[2]);
|
||||
PathfindTries++;
|
||||
}
|
||||
|
||||
if(PathfindTries > 40) {
|
||||
ResetAutoCrown();
|
||||
int index = WitchList.FindValue(AutoCrownTarget);
|
||||
if(index > -1)
|
||||
WitchList.Erase(index);
|
||||
//remove witch
|
||||
#if defined DEBUG
|
||||
PrintToServer("Could not pathfind to witch in time.");
|
||||
#endif
|
||||
}
|
||||
return Plugin_Continue;
|
||||
}
|
||||
public Action Timer_Scan(Handle hdl) {
|
||||
float botPosition[3], witchPos[3];
|
||||
|
@ -253,8 +256,13 @@ public Action Timer_Scan(Handle hdl) {
|
|||
|
||||
//Loop all witches, find any valid nearby witches:
|
||||
for(int i = 0; i < WitchList.Length; i++) {
|
||||
int witchID = WitchList.Get(i);
|
||||
if(IsValidEntity(witchID) && HasEntProp(witchID, Prop_Send, "m_rage") && GetEntPropFloat(witchID, Prop_Send, "m_rage") <= 0.4) {
|
||||
int witchRef = WitchList.Get(i);
|
||||
int witchID = EntRefToEntIndex(witchRef);
|
||||
if(witchID == INVALID_ENT_REFERENCE) {
|
||||
WitchList.Erase(i);
|
||||
continue;
|
||||
}
|
||||
if(GetEntPropFloat(witchID, Prop_Send, "m_rage") <= 0.4) {
|
||||
GetEntPropVector(witchID, Prop_Send, "m_vecOrigin", witchPos);
|
||||
if(GetVectorDistance(botPosition, witchPos) <= SCAN_RANGE) {
|
||||
//GetEntPropVector(witchID, Prop_Send, "m_angRotation", witchAng);
|
||||
|
@ -264,7 +272,7 @@ public Action Timer_Scan(Handle hdl) {
|
|||
#endif
|
||||
|
||||
L4D2_RunScript("CommandABot({cmd=1,bot=GetPlayerFromUserID(%i),pos=Vector(%f,%f,%f)})", GetClientUserId(bot), witchPos[0], witchPos[1], witchPos[2]);
|
||||
AutoCrownTarget = witchID;
|
||||
AutoCrownTarget = witchRef;
|
||||
AutoCrownBot = GetClientUserId(bot);
|
||||
AutoCrownInPosition = false;
|
||||
CreateTimer(ACTIVE_INTERVAL, Timer_Active, _, TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE);
|
||||
|
@ -282,19 +290,19 @@ public Action Timer_Scan(Handle hdl) {
|
|||
|
||||
public Action Timer_StopFiring(Handle hdl) {
|
||||
ResetAutoCrown();
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3], float angles[3], int& weapon, int& subtype, int& cmdnum, int& tickcount, int& seed, int mouse[2]) {
|
||||
if(AutoCrownInPosition && GetClientOfUserId(AutoCrownBot) == client && !(buttons & IN_ATTACK)) {
|
||||
buttons |= IN_ATTACK;
|
||||
//CreateTimer(0.4, Timer_StopFiring);
|
||||
return Plugin_Changed;
|
||||
}
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
public void ResetAutoCrown() {
|
||||
AutoCrownTarget = -1;
|
||||
AutoCrownTarget = INVALID_ENT_REFERENCE;
|
||||
AutoCrownInPosition = false;
|
||||
if(AutoCrownBot > -1)
|
||||
L4D2_RunScript("CommandABot({cmd=3,bot=GetPlayerFromUserID(%i)})", AutoCrownBot);
|
||||
|
|
|
@ -4,12 +4,14 @@
|
|||
//#define DEBUG
|
||||
|
||||
#define PLUGIN_VERSION "1.0"
|
||||
#define MAX_TIME_ONLINE_MS 604800
|
||||
#define MAX_TIME_ONLINE_SECONDS 172800
|
||||
//604800
|
||||
|
||||
#include <sourcemod>
|
||||
#include <sdktools>
|
||||
//#include <sdkhooks>
|
||||
int startupTime, triesBots, triesEmpty;
|
||||
bool pendingRestart;
|
||||
|
||||
public Plugin myinfo = {
|
||||
name = "L4D2 Autorestart",
|
||||
|
@ -41,7 +43,7 @@ public Action Command_RequestRestart(int client, int args) {
|
|||
ReplyToCommand(client, "Restarting...");
|
||||
LogAction(client, -1, "requested to restart server if empty.");
|
||||
ServerCommand("quit");
|
||||
}else{
|
||||
} else {
|
||||
ReplyToCommand(client, "Players are online.");
|
||||
}
|
||||
return Plugin_Handled;
|
||||
|
@ -55,8 +57,9 @@ public Action Timer_Check(Handle h) {
|
|||
ServerCommand("quit");
|
||||
}
|
||||
return Plugin_Continue;
|
||||
} else if(GetTime() - startupTime > MAX_TIME_ONLINE_MS) {
|
||||
} else if(GetTime() - startupTime > MAX_TIME_ONLINE_SECONDS) {
|
||||
LogAction(0, -1, "Server has passed max online time threshold, will restart if remains empty");
|
||||
pendingRestart = true;
|
||||
noHibernate.BoolValue = true;
|
||||
if(IsServerEmpty()) {
|
||||
if(++triesEmpty > 4) {
|
||||
|
@ -65,12 +68,20 @@ public Action Timer_Check(Handle h) {
|
|||
}
|
||||
return Plugin_Continue;
|
||||
}
|
||||
// If server is occupied, falls down below and resets:
|
||||
}
|
||||
triesBots = 0;
|
||||
triesEmpty = 0;
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
public void OnConfigsExecuted() {
|
||||
// Reset no hibernate setting when level changes:
|
||||
if(pendingRestart) {
|
||||
noHibernate.BoolValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if server is empty, and there is only bots. No players
|
||||
bool IsServerEmptyWithOnlyBots() {
|
||||
bool hasBot;
|
||||
|
|
|
@ -2,17 +2,29 @@
|
|||
#pragma newdecls required
|
||||
|
||||
#define DEBUG 0
|
||||
|
||||
#define PLUGIN_VERSION "1.0"
|
||||
|
||||
#define PANIC_DETECT_THRESHOLD 50.0
|
||||
|
||||
#include <sourcemod>
|
||||
#include <sdktools>
|
||||
#include <left4dhooks>
|
||||
//#include <sdkhooks>
|
||||
|
||||
static ConVar hPercent, hRange, hEnabled;
|
||||
#define PANIC_DETECT_THRESHOLD 50.0
|
||||
#define MAX_GROUPS 4
|
||||
|
||||
enum struct Group {
|
||||
float pos[3];
|
||||
ArrayList members;
|
||||
}
|
||||
|
||||
enum struct GroupResult {
|
||||
int groupCount;
|
||||
int ungroupedCount;
|
||||
float ungroupedRatio;
|
||||
}
|
||||
|
||||
|
||||
static ConVar hPercent, hRange, hEnabled, hGroupTeamDist;
|
||||
static char gamemode[32];
|
||||
static bool panicStarted;
|
||||
static float lastButtonPressTime;
|
||||
|
@ -38,15 +50,27 @@ public void OnPluginStart()
|
|||
hEnabled = CreateConVar("l4d2_crescendo_control", "1", "Should plugin be active?\n 1 = Enabled normally\n2 = Admins with bypass allowed only", FCVAR_NONE, true, 0.0, true, 1.0);
|
||||
hPercent = CreateConVar("l4d2_crescendo_percent", "0.5", "The percent of players needed to be in range for crescendo to start", FCVAR_NONE);
|
||||
hRange = CreateConVar("l4d2_crescendo_range", "250.0", "How many units away something range brain no work", FCVAR_NONE);
|
||||
hGroupTeamDist = CreateConVar("l4d2_cc_team_maxdist", "320.0", "The maximum distance another player can be away from someone to form a group", FCVAR_NONE, true, 10.0);
|
||||
|
||||
ConVar hGamemode = FindConVar("mp_gamemode");
|
||||
hGamemode.GetString(gamemode, sizeof(gamemode));
|
||||
hGamemode.AddChangeHook(Event_GamemodeChange);
|
||||
|
||||
AddNormalSoundHook(SoundHook);
|
||||
|
||||
RegAdminCmd("sm_dgroup", Command_DebugGroups, ADMFLAG_GENERIC);
|
||||
//dhook setup
|
||||
}
|
||||
|
||||
Action Command_DebugGroups(int client, int args) {
|
||||
PrintDebug("Running manual compute of groups");
|
||||
float activatorFlow = L4D2Direct_GetFlowDistance(client);
|
||||
Group groups[MAX_GROUPS];
|
||||
GroupResult result;
|
||||
ComputeGroups(groups, result, activatorFlow);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public void Event_GamemodeChange(ConVar cvar, const char[] oldValue, const char[] newValue) {
|
||||
cvar.GetString(gamemode, sizeof(gamemode));
|
||||
}
|
||||
|
@ -83,19 +107,37 @@ public Action Timer_GetFlows(Handle h) {
|
|||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
public float GetFlowAtPosition(const float pos[3]) {
|
||||
Address area = L4D_GetNearestNavArea(pos, 50.0, false, false, false, 2);
|
||||
if(area == Address_Null) return -1.0;
|
||||
return L4D2Direct_GetTerrorNavAreaFlow(area);
|
||||
}
|
||||
|
||||
public Action Event_ButtonPress(const char[] output, int entity, int client, float delay) {
|
||||
if(hEnabled.IntValue > 0 && client > 0 && client <= MaxClients) {
|
||||
float activatorFlow = L4D2Direct_GetFlowDistance(client);
|
||||
Group groups[MAX_GROUPS];
|
||||
GroupResult result;
|
||||
ComputeGroups(groups, result, activatorFlow);
|
||||
|
||||
AdminId admin = GetUserAdmin(client);
|
||||
if(admin != INVALID_ADMIN_ID && admin.HasFlag(Admin_Custom1)) return Plugin_Continue;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
static float pos[3];
|
||||
GetEntPropVector(entity, Prop_Send, "m_vecOrigin", pos);
|
||||
float activatorFlow = L4D2Direct_GetFlowDistance(client);
|
||||
|
||||
|
||||
PrintToConsoleAll("[CC] Button Press by %N", client);
|
||||
if(hEnabled.IntValue == 2 || !IsActivationAllowed(activatorFlow, 1500.0)) {
|
||||
|
@ -123,6 +165,134 @@ public Action SoundHook(int clients[MAXPLAYERS], int& numClients, char sample[PL
|
|||
public void Frame_ResetButton(int entity) {
|
||||
AcceptEntityInput(entity, "Unlock");
|
||||
}
|
||||
bool ComputeGroups(Group groups[MAX_GROUPS], GroupResult result, float activateFlow) {
|
||||
float prevPos[3], pos[3];
|
||||
// int prevMember = -1;
|
||||
// ArrayList groupMembers = new ArrayList();
|
||||
int groupIndex = 0;
|
||||
// ArrayList groups = new ArrayList();
|
||||
|
||||
// Group group;
|
||||
// // Create the first group
|
||||
// group.pos = pos;
|
||||
// group.members = new ArrayList();
|
||||
// PrintToServer("[cc] Creating first group");
|
||||
|
||||
bool inGroup[MAXPLAYERS+1];
|
||||
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
if(!inGroup[i] && IsClientConnected(i) && IsClientInGame(i) && IsPlayerAlive(i) && GetClientTeam(i) == 2) {
|
||||
float prevFlow = L4D2Direct_GetFlowDistance(i);
|
||||
GetClientAbsOrigin(i, prevPos);
|
||||
ArrayList members = new ArrayList();
|
||||
|
||||
for(int j = 1; j <= MaxClients; j++) {
|
||||
if(j != i && IsClientConnected(j) && IsClientInGame(j) && IsPlayerAlive(j) && GetClientTeam(j) == 2) {
|
||||
// TODO: MERGE groups
|
||||
GetClientAbsOrigin(j, pos);
|
||||
float flow = L4D2Direct_GetFlowDistance(j);
|
||||
float dist = FloatAbs(GetVectorDistance(prevPos, pos));
|
||||
float flowDiff = FloatAbs(prevFlow - flow);
|
||||
if(dist <= hGroupTeamDist.FloatValue) {
|
||||
if(members.Length == 0) {
|
||||
members.Push(GetClientUserId(i));
|
||||
PrintDebug("add leader to group %d: %N", groupIndex + 1, i);
|
||||
}
|
||||
PrintDebug("add member to group %d: %N (dist = %.4f) (fldiff = %.1f)", groupIndex + 1, j, dist, flowDiff);
|
||||
inGroup[j] = true;
|
||||
members.Push(GetClientUserId(j));
|
||||
} else {
|
||||
PrintDebug("not adding member to group %d: %N (dist = %.4f) (fldiff = %.1f) (l:%N)", groupIndex + 1, j, dist, flowDiff, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(members.Length > 1) {
|
||||
groups[groupIndex].pos = prevPos;
|
||||
groups[groupIndex].members = members;
|
||||
groupIndex++;
|
||||
PrintDebug("created group #%d with %d members", groupIndex, members.Length);
|
||||
if(groupIndex == MAX_GROUPS) {
|
||||
PrintDebug("maximum amount of groups reached (%d)", MAX_GROUPS);
|
||||
}
|
||||
} else {
|
||||
delete members;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int totalGrouped = 0;
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
if(IsClientConnected(i) && IsClientInGame(i) && IsPlayerAlive(i) && GetClientTeam(i) == 2) {
|
||||
if(inGroup[i])
|
||||
totalGrouped++;
|
||||
else
|
||||
result.ungroupedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
result.ungroupedRatio = float(result.ungroupedCount) / float(totalGrouped);
|
||||
|
||||
PrintDebug("total grouped: %d | total ungrouped: %d | ratio: %f", totalGrouped, result.ungroupedCount, result.ungroupedRatio);
|
||||
|
||||
PrintDebug("total groups created: %d", groupIndex);
|
||||
|
||||
// for(int i = 1; i <= MaxClients; i++) {
|
||||
// if(IsClientConnected(i) && IsClientInGame(i) && IsPlayerAlive(i) && GetClientTeam(i) == 2) {
|
||||
// GetClientAbsOrigin(i, pos);
|
||||
// // Skip the first member, as they will start the group
|
||||
// if(prevMember == -1) {
|
||||
// prevMember = i;
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// // Check if player is in a radius of the group source
|
||||
// float dist = GetVectorDistance(group.pos, pos);
|
||||
// if(dist < TEAM_GROUP_DIST) {
|
||||
// // TODO: not just join last group
|
||||
// if(group.members.Length == 0) {
|
||||
// PrintToServer("[cc] add leader to group %d: %N", groupIndex + 1, prevMember);
|
||||
// // groupMembers.Push(GetClientUserId(prevMember));
|
||||
// group.members.Push(GetClientUserId(prevMember));
|
||||
// }
|
||||
// // groupMembers.Push(GetClientUserId(i));
|
||||
// group.members.Push(GetClientUserId(i));
|
||||
// PrintToServer("[cc] add member to group %d: %N (dist = %.2f)", groupIndex + 1, i, dist);
|
||||
// } else {
|
||||
// // Player is not, create a new group.
|
||||
// if(group.members.Length > 0) {
|
||||
// groups.PushArray(group);
|
||||
// }
|
||||
// groupIndex++;
|
||||
// group.pos = pos;
|
||||
// group.members = new ArrayList();
|
||||
// PrintToServer("[cc] Creating group %d", groupIndex + 1);
|
||||
// }
|
||||
// prevPos = pos;
|
||||
// prevMember = i;
|
||||
// }
|
||||
// }
|
||||
|
||||
PrintDebug("===GROUP SUMMARY===");
|
||||
for(int i = 0; i < MAX_GROUPS; i++) {
|
||||
if(groups[i].members != null) {
|
||||
PrintDebug("---Group %d---", i + 1);
|
||||
PrintDebug("Origin: %.1f %.1f %.1f", groups[i].pos[0], groups[i].pos[1], groups[i].pos[2]);
|
||||
float groupFlow = GetFlowAtPosition(groups[i].pos);
|
||||
PrintDebug("Flow Diff: %.2f (g:%.1f) (a:%.1f) (gdt:%.f)", FloatAbs(activateFlow - groupFlow), activateFlow, groupFlow, hGroupTeamDist.FloatValue);
|
||||
PrintDebug("Leader: %N (uid#%d)", GetClientOfUserId(groups[i].members.Get(0)), groups[i].members.Get(0));
|
||||
for(int j = 1; j < groups[i].members.Length; j++) {
|
||||
int userid = groups[i].members.Get(j);
|
||||
PrintDebug("Member: %N (uid#%d)", GetClientOfUserId(userid), userid);
|
||||
}
|
||||
delete groups[i].members;
|
||||
}
|
||||
}
|
||||
PrintDebug("===END GROUP SUMMARY===");
|
||||
// delete groupMembers;
|
||||
|
||||
result.groupCount = groupIndex;
|
||||
return groupIndex > 0;
|
||||
}
|
||||
|
||||
|
||||
// 5 far/8 total
|
||||
|
@ -136,7 +306,7 @@ stock bool IsActivationAllowed(float flowmax, float threshold) {
|
|||
int farSurvivors, totalSurvivors;
|
||||
float totalFlow;
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i)) {
|
||||
if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i) && !IsFakeClient(i)) {
|
||||
if(flowRate[i] < flowmax - threshold) {
|
||||
PrintDebug("Adding %N with flow of %.2f to far survivors average", i, flowRate[i]);
|
||||
farSurvivors++;
|
||||
|
@ -145,7 +315,7 @@ stock bool IsActivationAllowed(float flowmax, float threshold) {
|
|||
totalSurvivors++;
|
||||
}
|
||||
}
|
||||
if(farSurvivors == 0) return true;
|
||||
if(farSurvivors == 0 || totalSurvivors == 1) return true;
|
||||
float average = totalFlow / farSurvivors;
|
||||
float percentFar = float(farSurvivors) / float(totalSurvivors);
|
||||
|
||||
|
@ -156,7 +326,7 @@ stock bool IsActivationAllowed(float flowmax, float threshold) {
|
|||
return true;
|
||||
}
|
||||
//If not, check the ratio of players
|
||||
bool isAllowed = percentFar <= 0.30;
|
||||
bool isAllowed = percentFar <= 0.40;
|
||||
PrintDebug("Activation is %s", isAllowed ? "allowed" : "blocked");
|
||||
return isAllowed;
|
||||
}
|
||||
|
@ -178,7 +348,8 @@ stock void PrintDebug(const char[] format, any ... ) {
|
|||
#if defined DEBUG
|
||||
char buffer[256];
|
||||
VFormat(buffer, sizeof(buffer), format, 2);
|
||||
PrintToServer("[Debug] %s", buffer);
|
||||
PrintToConsoleAll("[Debug] %s", buffer);
|
||||
// PrintToServer("[CrescendoControl:Debug] %s", buffer);
|
||||
PrintToConsoleAll("[CrescendoControl:Debug] %s", buffer);
|
||||
LogMessage("%s", buffer);
|
||||
#endif
|
||||
}
|
|
@ -16,6 +16,7 @@
|
|||
enum KitDetectionState {
|
||||
KDS_None,
|
||||
KDS_NoKitEnteringSaferoom,
|
||||
KDS_PickedUpKit,
|
||||
KDS_Healed
|
||||
}
|
||||
|
||||
|
@ -23,6 +24,7 @@ enum struct PlayerDetections {
|
|||
int kitPickupsSaferoom;
|
||||
int saferoomLastOpen;
|
||||
int saferoomOpenCount;
|
||||
bool hadKitBeforeHeal;
|
||||
// Do not reset normally; need to keep track during level transitions
|
||||
KitDetectionState saferoomKitState;
|
||||
|
||||
|
@ -149,8 +151,8 @@ public void Event_ItemPickup(Event event, const char[] name, bool dontBroadcast)
|
|||
if(StrEqual(itmName, "first_aid_kit")) {
|
||||
if(detections[client].saferoomKitState == KDS_NoKitEnteringSaferoom) {
|
||||
// Player had no kit entering saferoom and has healed
|
||||
detections[client].saferoomKitState = KDS_Healed;
|
||||
} else if(detections[client].saferoomKitState == KDS_Healed) {
|
||||
detections[client].saferoomKitState = KDS_PickedUpKit;
|
||||
} else if(detections[client].saferoomKitState == KDS_PickedUpKit) {
|
||||
// Player has healed. Double kit detected
|
||||
InternalDebugLog("DOUBLE_KIT", client);
|
||||
Call_StartForward(fwd_PlayerDoubleKit);
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
#define DEBUG_ANY 3
|
||||
|
||||
//Set the debug level
|
||||
#define DEBUG_LEVEL DEBUG_GENERIC
|
||||
#define DEBUG_LEVEL DEBUG_ANY
|
||||
#define EXTRA_PLAYER_HUD_UPDATE_INTERVAL 0.8
|
||||
//Sets abmExtraCount to this value if set
|
||||
// #define DEBUG_FORCE_PLAYERS 7
|
||||
|
@ -103,11 +103,19 @@ enum State {
|
|||
State_PendingEmpty,
|
||||
State_Active
|
||||
}
|
||||
#if defined DEBUG_LEVEL
|
||||
char StateNames[3][] = {
|
||||
"Empty",
|
||||
"PendingEmpty",
|
||||
"Actve"
|
||||
};
|
||||
#endif
|
||||
|
||||
enum struct PlayerData {
|
||||
bool itemGiven; //Is player being given an item (such that the next pickup event is ignored)
|
||||
bool isUnderAttack; //Is the player under attack (by any special)
|
||||
State state;
|
||||
bool hasJoined;
|
||||
}
|
||||
|
||||
enum struct PlayerInventory {
|
||||
|
@ -187,7 +195,7 @@ public void OnPluginStart() {
|
|||
HookEvent("tank_spawn", Event_TankSpawn);
|
||||
|
||||
//Special Event Tracking
|
||||
HookEvent("player_team", Event_PlayerTeam);
|
||||
HookEvent("player_disconnect", Event_PlayerDisconnect);
|
||||
|
||||
HookEvent("charger_carry_start", Event_ChargerCarry);
|
||||
HookEvent("charger_carry_end", Event_ChargerCarry);
|
||||
|
@ -212,7 +220,7 @@ public void OnPluginStart() {
|
|||
hSaferoomDoorAutoOpen = CreateConVar("l4d2_extraitems_doorunlock_open", "0", "Controls when the door automatically opens after unlocked. Add bits together.\n0 = Never, 1 = When timer expires, 2 = When all players loaded in", FCVAR_NONE, true, 0.0);
|
||||
hEPIHudState = CreateConVar("l4d2_extraitems_hudstate", "1", "Controls when the hud displays.\n0 -> OFF, 1 = When 5+ players, 2 = ALWAYS", FCVAR_NONE, true, 0.0, true, 3.0);
|
||||
hExtraFinaleTank = CreateConVar("l4d2_extraitems_extra_tanks", "3", "Add bits together. 0 = Normal tank spawning, 1 = 50% tank split on non-finale (half health), 2 = Tank split (full health) on finale ", FCVAR_NONE, true, 0.0, true, 3.0);
|
||||
hSplitTankChance = CreateConVar("l4d2_extraitems_splittank_chance", "0.5", "Add bits together. 0 = Normal tank spawning, 1 = 50% tank split on non-finale (half health), 2 = Tank split (full health) on finale ", FCVAR_NONE, true, 0.0, true, 1.0);
|
||||
hSplitTankChance = CreateConVar("l4d2_extraitems_splittank_chance", "0.75", "The % chance of a split tank occurring in non-finales", FCVAR_NONE, true, 0.0, true, 1.0);
|
||||
cvDropDisconnectTime = CreateConVar("l4d2_extraitems_disconnect_time", "120.0", "The amount of seconds after a player has actually disconnected, where their character slot will be void. 0 to disable", FCVAR_NONE, true, 0.0);
|
||||
cvFFDecreaseRate = CreateConVar("l4d2_extraitems_ff_decrease_rate", "0.3", "The friendly fire factor is subtracted from the formula (playerCount-4) * this rate. Effectively reduces ff penalty when more players. 0.0 to subtract none", FCVAR_NONE, true, 0.0);
|
||||
|
||||
|
@ -264,6 +272,7 @@ public void OnPluginStart() {
|
|||
RegAdminCmd("sm_epi_lock", Command_ToggleDoorLocks, ADMFLAG_CHEATS, "Toggle all toggle\'s lock state");
|
||||
RegAdminCmd("sm_epi_kits", Command_GetKitAmount, ADMFLAG_CHEATS);
|
||||
RegAdminCmd("sm_epi_items", Command_RunExtraItems, ADMFLAG_CHEATS);
|
||||
RegConsoleCmd("sm_epi_status", Command_DebugStatus);
|
||||
#endif
|
||||
|
||||
CreateTimer(10.0, Timer_ForceUpdateInventories, _, TIMER_REPEAT);
|
||||
|
@ -333,7 +342,10 @@ public void Cvar_HudStateChange(ConVar convar, const char[] oldValue, const char
|
|||
delete updateHudTimer;
|
||||
}else {
|
||||
int count = GetRealSurvivorsCount();
|
||||
int threshold = hEPIHudState.IntValue == 1 ? 4 : 0;
|
||||
int threshold = 0;
|
||||
if(hEPIHudState.IntValue == 1) {
|
||||
threshold = L4D2_GetSurvivorSetMap() == 2 ? 4 : 5;
|
||||
}
|
||||
if(convar.IntValue > 0 && count > threshold && updateHudTimer == null) {
|
||||
PrintToServer("[EPI] Creating new hud timer");
|
||||
updateHudTimer = CreateTimer(EXTRA_PLAYER_HUD_UPDATE_INTERVAL, Timer_UpdateHud, _, TIMER_REPEAT);
|
||||
|
@ -414,7 +426,7 @@ public Action Command_SetKitAmount(int client, int args) {
|
|||
extraKitsAmount = number;
|
||||
extraKitsStarted = extraKitsAmount;
|
||||
ReplyToCommand(client, "Set extra kits amount to %d", number);
|
||||
}else{
|
||||
} else {
|
||||
ReplyToCommand(client, "Must be a number greater than 0. -1 to disable");
|
||||
}
|
||||
return Plugin_Handled;
|
||||
|
@ -440,6 +452,15 @@ public Action Command_RunExtraItems(int client, int args) {
|
|||
PopulateItems();
|
||||
return Plugin_Handled;
|
||||
}
|
||||
public Action Command_DebugStatus(int client, int args) {
|
||||
ReplyToCommand(client, "Player Statuses:");
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
if(IsClientConnected(i) && !IsFakeClient(i)) {
|
||||
ReplyToCommand(i, "\t%d. %N: %s", i, i, StateNames[view_as<int>(playerData[i].state)]);
|
||||
}
|
||||
}
|
||||
return Plugin_Handled;
|
||||
}
|
||||
#endif
|
||||
/////////////////////////////////////
|
||||
/// EVENTS
|
||||
|
@ -482,7 +503,8 @@ public Action L4D2_OnChangeFinaleStage(int &finaleType, const char[] arg) {
|
|||
public void Event_TankSpawn(Event event, const char[] name, bool dontBroadcast) {
|
||||
int user = event.GetInt("userid");
|
||||
int tank = GetClientOfUserId(user);
|
||||
if(tank > 0 && IsFakeClient(tank) && abmExtraCount > 4 && hExtraFinaleTank.BoolValue) {
|
||||
if(tank > 0 && IsFakeClient(tank) && abmExtraCount > 4 && hExtraFinaleTank.IntValue > 0) {
|
||||
PrintToConsoleAll("[EPI] Split tank is enabled, checking new spawned tank");
|
||||
if(finaleStage == Stage_FinaleTank2 && allowTankSplit && hExtraFinaleTank.IntValue & 2) {
|
||||
PrintToConsoleAll("[EPI] Second tank spawned, setting health.");
|
||||
// Sets health in half, sets finaleStage to health
|
||||
|
@ -500,6 +522,8 @@ public void Event_TankSpawn(Event event, const char[] name, bool dontBroadcast)
|
|||
extraTankHP = hp;
|
||||
CreateTimer(0.2, Timer_SetHealth, user);
|
||||
CreateTimer(GetRandomFloat(10.0, 18.0), Timer_SpawnSplitTank, user);
|
||||
} else {
|
||||
PrintToConsoleAll("[EPI] Random chance for split tank failed");
|
||||
}
|
||||
// Then, summon the next tank
|
||||
} else if(finaleStage == Stage_TankSplit) {
|
||||
|
@ -548,7 +572,7 @@ public void OnGetWeaponsInfo(int pThis, const char[] classname) {
|
|||
///////////////////////////////////////////////////////
|
||||
|
||||
//Called on the first spawn in a mission.
|
||||
public Action Event_GameStart(Event event, const char[] name, bool dontBroadcast) {
|
||||
public void Event_GameStart(Event event, const char[] name, bool dontBroadcast) {
|
||||
firstGiven = false;
|
||||
extraKitsAmount = 0;
|
||||
extraKitsStarted = 0;
|
||||
|
@ -559,62 +583,71 @@ public Action Event_GameStart(Event event, const char[] name, bool dontBroadcast
|
|||
for(int i = 1; i <= MaxClients; i++) {
|
||||
playerData[i].state = State_Empty;
|
||||
}
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
public Action Event_PlayerFirstSpawn(Event event, const char[] name, bool dontBroadcast) {
|
||||
public void Event_PlayerFirstSpawn(Event event, const char[] name, bool dontBroadcast) {
|
||||
int userid = event.GetInt("userid");
|
||||
int client = GetClientOfUserId(userid);
|
||||
if(GetClientTeam(client) == 2) {
|
||||
CreateTimer(1.5, Timer_RemoveInvincibility, client);
|
||||
if(GetClientTeam(client) != 2) return;
|
||||
if(IsFakeClient(client)) {
|
||||
// Make the real player's bot invincible, ONLY for the first time it appears
|
||||
int player = L4D_GetIdlePlayerOfBot(client);
|
||||
if(player > 0 && !playerData[client].hasJoined) {
|
||||
playerData[client].hasJoined = true;
|
||||
// TODO: Confirm this fix works
|
||||
CreateTimer(1.5, Timer_RemoveInvincibility, userid);
|
||||
SDKHook(client, SDKHook_OnTakeDamage, OnInvincibleDamageTaken);
|
||||
}
|
||||
} else {
|
||||
// Make the (real) player invincible:
|
||||
CreateTimer(1.5, Timer_RemoveInvincibility, userid);
|
||||
SDKHook(client, SDKHook_OnTakeDamage, OnInvincibleDamageTaken);
|
||||
if(!IsFakeClient(client)) {
|
||||
playerData[client].state = State_Active;
|
||||
if(L4D_IsFirstMapInScenario() && !firstGiven) {
|
||||
//Check if all clients are ready, and survivor count is > 4.
|
||||
if(AreAllClientsReady()) {
|
||||
abmExtraCount = GetRealSurvivorsCount();
|
||||
if(abmExtraCount > 4) {
|
||||
firstGiven = true;
|
||||
//Set the initial value ofhMinPlayers
|
||||
if(hUpdateMinPlayers.BoolValue && hMinPlayers != null) {
|
||||
hMinPlayers.IntValue = abmExtraCount;
|
||||
}
|
||||
PopulateItems();
|
||||
CreateTimer(1.0, Timer_GiveKits);
|
||||
}
|
||||
if(firstSaferoomDoorEntity > 0 && IsValidEntity(firstSaferoomDoorEntity)) {
|
||||
UnlockDoor(firstSaferoomDoorEntity, 2);
|
||||
|
||||
playerData[client].state = State_Active;
|
||||
if(L4D_IsFirstMapInScenario() && !firstGiven) {
|
||||
//Check if all clients are ready, and survivor count is > 4.
|
||||
if(AreAllClientsReady()) {
|
||||
abmExtraCount = GetRealSurvivorsCount();
|
||||
if(abmExtraCount > 4) {
|
||||
PrintToServer("[EPI] First chapter kits given");
|
||||
firstGiven = true;
|
||||
//Set the initial value ofhMinPlayers
|
||||
if(hUpdateMinPlayers.BoolValue && hMinPlayers != null) {
|
||||
hMinPlayers.IntValue = abmExtraCount;
|
||||
}
|
||||
PopulateItems();
|
||||
CreateTimer(1.0, Timer_GiveKits);
|
||||
}
|
||||
} else {
|
||||
// New client has connected, not on first map.
|
||||
// TODO: Check if Timer_UpdateMinPlayers is needed, or if this works:
|
||||
// Never decrease abmExtraCount
|
||||
int newCount = GetRealSurvivorsCount();
|
||||
if(newCount > abmExtraCount && abmExtraCount > 4) {
|
||||
abmExtraCount = newCount;
|
||||
hMinPlayers.IntValue = abmExtraCount;
|
||||
|
||||
ConVar friendlyFireFactor = GetActiveFriendlyFireFactor();
|
||||
// TODO: Get previous default
|
||||
friendlyFireFactor.FloatValue = friendlyFireFactor.FloatValue - ((newCount - 4) * cvFFDecreaseRate.FloatValue);
|
||||
if(friendlyFireFactor.FloatValue < 0.0) {
|
||||
friendlyFireFactor.FloatValue = 0.01;
|
||||
}
|
||||
if(firstSaferoomDoorEntity > 0 && IsValidEntity(firstSaferoomDoorEntity)) {
|
||||
UnlockDoor(firstSaferoomDoorEntity, 2);
|
||||
}
|
||||
// If 5 survivors, then set them up, TP them.
|
||||
if(newCount > 4) {
|
||||
CreateTimer(0.1, Timer_SetupNewClient, userid);
|
||||
}
|
||||
} else {
|
||||
// New client has connected, not on first map.
|
||||
// TODO: Check if Timer_UpdateMinPlayers is needed, or if this works:
|
||||
// Never decrease abmExtraCount
|
||||
int newCount = GetRealSurvivorsCount();
|
||||
if(newCount > abmExtraCount && abmExtraCount > 4) {
|
||||
abmExtraCount = newCount;
|
||||
hMinPlayers.IntValue = abmExtraCount;
|
||||
|
||||
ConVar friendlyFireFactor = GetActiveFriendlyFireFactor();
|
||||
// TODO: Get previous default
|
||||
friendlyFireFactor.FloatValue = friendlyFireFactor.FloatValue - ((newCount - 4) * cvFFDecreaseRate.FloatValue);
|
||||
if(friendlyFireFactor.FloatValue < 0.0) {
|
||||
friendlyFireFactor.FloatValue = 0.01;
|
||||
}
|
||||
}
|
||||
// If 5 survivors, then set them up, TP them.
|
||||
if(newCount > 4) {
|
||||
CreateTimer(0.1, Timer_SetupNewClient, userid);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Plugin_Continue;
|
||||
|
||||
}
|
||||
public Action Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast) {
|
||||
if(StrEqual(gamemode, "hideandseek")) return Plugin_Continue;
|
||||
public void Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast) {
|
||||
if(!StrEqual(gamemode, "coop") && !StrEqual(gamemode, "realism")) return;
|
||||
|
||||
int user = event.GetInt("userid");
|
||||
int client = GetClientOfUserId(user);
|
||||
if(GetClientTeam(client) == 2) {
|
||||
|
@ -637,13 +670,15 @@ public Action Event_PlayerSpawn(Event event, const char[] name, bool dontBroadca
|
|||
SDKHook(client, SDKHook_WeaponEquip, Event_Pickup);
|
||||
}
|
||||
int count = GetRealSurvivorsCount();
|
||||
int threshold = hEPIHudState.IntValue == 1 ? 5 : 0;
|
||||
int threshold = 0;
|
||||
if(hEPIHudState.IntValue == 1) {
|
||||
threshold = L4D2_GetSurvivorSetMap() == 2 ? 4 : 5;
|
||||
}
|
||||
if(hEPIHudState.IntValue > 0 && count > threshold && updateHudTimer == null) {
|
||||
PrintToServer("[EPI] Creating new hud timer (player spawn)");
|
||||
updateHudTimer = CreateTimer(EXTRA_PLAYER_HUD_UPDATE_INTERVAL, Timer_UpdateHud, _, TIMER_REPEAT);
|
||||
}
|
||||
UpdatePlayerInventory(client);
|
||||
return Plugin_Continue;
|
||||
|
||||
}
|
||||
|
||||
|
@ -655,21 +690,19 @@ public Action Timer_CheckInventory(Handle h, int client) {
|
|||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public void Event_PlayerTeam(Event event, const char[] name, bool dontBroadcast) {
|
||||
if(event.GetBool("disconnect")) {
|
||||
int userid = event.GetInt("userid");
|
||||
int client = GetClientOfUserId(userid);
|
||||
int team = event.GetInt("team");
|
||||
if(client > 0 && team == 2) { //TODO: re-add && !event.GetBool("isbot")
|
||||
SaveInventory(client);
|
||||
PrintToServer("debug: Player %N (index %d, uid %d) now pending empty", client, client, userid);
|
||||
playerData[client].state = State_PendingEmpty;
|
||||
/*DataPack pack;
|
||||
CreateDataTimer(cvDropDisconnectTime.FloatValue, Timer_DropSurvivor, pack);
|
||||
pack.WriteCell(userid);
|
||||
pack.WriteCell(client);*/
|
||||
CreateTimer(cvDropDisconnectTime.FloatValue, Timer_DropSurvivor, client);
|
||||
}
|
||||
public void Event_PlayerDisconnect(Event event, const char[] name, bool dontBroadcast) {
|
||||
int userid = event.GetInt("userid");
|
||||
int client = GetClientOfUserId(userid);
|
||||
if(client > 0 && IsClientConnected(client) && IsClientInGame(client) && !IsFakeClient(client) && GetClientTeam(client) == 2) { //TODO: re-add && !event.GetBool("isbot")
|
||||
playerData[client].hasJoined = false;
|
||||
SaveInventory(client);
|
||||
PrintToServer("debug: Player %N (index %d, uid %d) now pending empty", client, client, userid);
|
||||
playerData[client].state = State_PendingEmpty;
|
||||
/*DataPack pack;
|
||||
CreateDataTimer(cvDropDisconnectTime.FloatValue, Timer_DropSurvivor, pack);
|
||||
pack.WriteCell(userid);
|
||||
pack.WriteCell(client);*/
|
||||
CreateTimer(cvDropDisconnectTime.FloatValue, Timer_DropSurvivor, client);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -677,8 +710,8 @@ public Action Timer_DropSurvivor(Handle h, int client) {
|
|||
if(playerData[client].state == State_PendingEmpty) {
|
||||
playerData[client].state = State_Empty;
|
||||
if(hMinPlayers != null) {
|
||||
PrintToServer("[EPI] Dropping survivor %d. hMinPlayers-pre:%d", client, hMinPlayers.IntValue);
|
||||
PrintToConsoleAll("[EPI] Dropping survivor %d. hMinPlayers-pre:%d", client, hMinPlayers.IntValue);
|
||||
PrintToServer("[EPI] Dropping survivor %d. hMinPlayers-pre:%d abmCount=%d", client, hMinPlayers.IntValue, abmExtraCount);
|
||||
PrintToConsoleAll("[EPI] Dropping survivor %d. hMinPlayers-pre:%d abmCount=%d", client, hMinPlayers.IntValue, abmExtraCount);
|
||||
hMinPlayers.IntValue = --abmExtraCount;
|
||||
if(hMinPlayers.IntValue < 4) {
|
||||
hMinPlayers.IntValue = 4;
|
||||
|
@ -712,12 +745,11 @@ public Action Timer_DropSurvivor(Handle h, int client) {
|
|||
/////// Events
|
||||
/////////////////////////////////////////
|
||||
|
||||
public Action 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"));
|
||||
if(client > 0) {
|
||||
UpdatePlayerInventory(client);
|
||||
}
|
||||
return Plugin_Continue;
|
||||
|
||||
}
|
||||
|
||||
|
@ -765,19 +797,34 @@ public Action Timer_SetupNewClient(Handle h, int userid) {
|
|||
char weaponName[64];
|
||||
|
||||
ArrayList tier2Weapons = new ArrayList(ByteCountToCells(32));
|
||||
ArrayList tier1Weapons = new ArrayList(ByteCountToCells(32));
|
||||
ArrayList secondaryWeapons = new ArrayList(ByteCountToCells(32));
|
||||
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
if(i != client && IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i)) {
|
||||
int wpn = GetPlayerWeaponSlot(i, 0);
|
||||
if(wpn > 0) {
|
||||
GetEdictClassname(wpn, weaponName, sizeof(weaponName));
|
||||
for(int j = 0; j < TIER2_WEAPON_COUNT; j++) {
|
||||
if(StrEqual(TIER2_WEAPONS[j], weaponName)) {
|
||||
tier2Weapons.PushString(weaponName);
|
||||
break;
|
||||
if(!StrEqual(weaponName, "weapon_grenade_launcher") && !StrEqual(weaponName, "weapon_rifle_m60")) {
|
||||
for(int j = 0; j < TIER2_WEAPON_COUNT; j++) {
|
||||
if(StrEqual(TIER2_WEAPONS[j], weaponName)) {
|
||||
tier2Weapons.PushString(weaponName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
tier1Weapons.PushString(weaponName);
|
||||
// playerWeapons.PushString(weaponName);
|
||||
}
|
||||
}
|
||||
wpn = GetPlayerWeaponSlot(i, 1);
|
||||
if(wpn > 0) {
|
||||
GetEdictClassname(wpn, weaponName, sizeof(weaponName));
|
||||
if(StrEqual(weaponName, "weapon_melee")) {
|
||||
// Get melee name, won't have weapon_ prefix
|
||||
GetEntPropString(wpn, Prop_Data, "m_strMapSetScriptName", weaponName, sizeof(weaponName));
|
||||
}
|
||||
secondaryWeapons.PushString(weaponName);
|
||||
}
|
||||
|
||||
float intensity = L4D_GetPlayerIntensity(i);
|
||||
if(intensity < lowestIntensity || lowestClient == -1) {
|
||||
|
@ -787,49 +834,70 @@ public Action Timer_SetupNewClient(Handle h, int userid) {
|
|||
}
|
||||
}
|
||||
|
||||
// Give player any random t2 weapon, if no one has one, fallback to t1, if no one has one, give them a magnum
|
||||
if(tier2Weapons.Length > 0) {
|
||||
tier2Weapons.GetString(GetRandomInt(0, tier2Weapons.Length - 1), weaponName, sizeof(weaponName));
|
||||
// Format(weaponName, sizeof(weaponName), "weapon_%s", weaponName);
|
||||
PrintToServer("[EPI/debug] Giving new client (%N) tier 2: %s", client, weaponName);
|
||||
} else {
|
||||
Format(weaponName, sizeof(weaponName), "weapon_%s", TIER1_WEAPONS[GetRandomInt(0, TIER1_WEAPON_COUNT - 1)]);
|
||||
GiveWeapon(client, weaponName, 3.0, 0);
|
||||
} else if(tier1Weapons.Length > 0) {
|
||||
// Format(weaponName, sizeof(weaponName), "weapon_%s", TIER1_WEAPONS[GetRandomInt(0, TIER1_WEAPON_COUNT - 1)]);
|
||||
tier1Weapons.GetString(GetRandomInt(0, tier1Weapons.Length - 1), weaponName, sizeof(weaponName));
|
||||
PrintToServer("[EPI/debug] Giving new client (%N) tier 1: %s", client, weaponName);
|
||||
GiveWeapon(client, weaponName, 3.0, 0);
|
||||
}
|
||||
PrintToServer("%N: Giving random secondary / %d", secondaryWeapons.Length, client);
|
||||
PrintToConsoleAll("%N: Giving random secondary / %d", secondaryWeapons.Length, client);
|
||||
if(secondaryWeapons.Length > 0) {
|
||||
secondaryWeapons.GetString(GetRandomInt(0, secondaryWeapons.Length - 1), weaponName, sizeof(weaponName));
|
||||
GiveWeapon(client, weaponName, 6.5, 1);
|
||||
}
|
||||
|
||||
if(lowestClient > 0) {
|
||||
float pos[3];
|
||||
GetClientAbsOrigin(lowestClient, pos);
|
||||
TeleportEntity(client, pos, NULL_VECTOR, NULL_VECTOR);
|
||||
}
|
||||
delete tier2Weapons;
|
||||
float pos[3];
|
||||
|
||||
if(L4D2_IsValidWeapon(weaponName)) {
|
||||
int wpn = CreateEntityByName(weaponName);
|
||||
DispatchSpawn(wpn);
|
||||
SetEntProp(wpn, Prop_Send, "m_iClip1", L4D2_GetIntWeaponAttribute(weaponName, L4D2IWA_ClipSize));
|
||||
L4D_SetReserveAmmo(client, wpn, L4D2_GetIntWeaponAttribute(weaponName, L4D2IWA_Bullets));
|
||||
GetClientAbsOrigin(client, pos);
|
||||
TeleportEntity(wpn, pos, NULL_VECTOR, NULL_VECTOR);
|
||||
DataPack pack;
|
||||
CreateDataTimer(0.2, Timer_GiveWeapon, pack);
|
||||
pack.WriteCell(userid);
|
||||
pack.WriteCell(wpn);
|
||||
} else {
|
||||
LogError("EPI: INVALID WEAPON: %s for %N", weaponName, client);
|
||||
}
|
||||
delete tier2Weapons;
|
||||
delete tier1Weapons;
|
||||
delete secondaryWeapons;
|
||||
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
void GiveWeapon(int client, const char[] weaponName, float delay = 0.3, int clearSlot = -1) {
|
||||
if(clearSlot > 0) {
|
||||
int oldWpn = GetPlayerWeaponSlot(client, clearSlot);
|
||||
if(oldWpn != -1) {
|
||||
AcceptEntityInput(oldWpn, "Kill");
|
||||
}
|
||||
}
|
||||
PrintToServer("%N: Giving %s", client, weaponName);
|
||||
PrintToConsoleAll("%N: Giving %s", client, weaponName);
|
||||
DataPack pack;
|
||||
CreateDataTimer(delay, Timer_GiveWeapon, pack);
|
||||
pack.WriteCell(GetClientUserId(client));
|
||||
pack.WriteString(weaponName);
|
||||
}
|
||||
|
||||
public Action Timer_GiveWeapon(Handle h, DataPack pack) {
|
||||
pack.Reset();
|
||||
int userid = pack.ReadCell();
|
||||
int wpn = pack.ReadCell();
|
||||
int client = GetClientOfUserId(userid);
|
||||
if(client > 0) {
|
||||
EquipPlayerWeapon(client, wpn);
|
||||
char wpnName[32];
|
||||
pack.ReadString(wpnName, sizeof(wpnName));
|
||||
CheatCommand(client, "give", wpnName, "");
|
||||
}
|
||||
return Plugin_Handled;
|
||||
}
|
||||
public Action Timer_RemoveInvincibility(Handle h, int client) {
|
||||
SDKUnhook(client, SDKHook_OnTakeDamage, OnInvincibleDamageTaken);
|
||||
public Action Timer_RemoveInvincibility(Handle h, int userid) {
|
||||
int client = GetClientOfUserId(userid);
|
||||
if(client > 0) {
|
||||
SetEntProp(client, Prop_Send, "m_iHealth", 100);
|
||||
SDKUnhook(client, SDKHook_OnTakeDamage, OnInvincibleDamageTaken);
|
||||
}
|
||||
return Plugin_Handled;
|
||||
}
|
||||
public Action OnInvincibleDamageTaken(int victim, int& attacker, int& inflictor, float& damage, int& damagetype, int& weapon, float damageForce[3], float damagePosition[3]) {
|
||||
|
@ -875,13 +943,13 @@ public void OnMapStart() {
|
|||
GiveStartingKits();
|
||||
}
|
||||
isFailureRound = false;
|
||||
}else if(!L4D_IsFirstMapInScenario()) {
|
||||
} else if(!L4D_IsFirstMapInScenario()) {
|
||||
//Re-set value incase it reset.
|
||||
//hMinPlayers.IntValue = abmExtraCount;
|
||||
currentChapter++;
|
||||
}else if(L4D_IsMissionFinalMap()) {
|
||||
} else if(L4D_IsMissionFinalMap()) {
|
||||
//Add extra kits for finales
|
||||
static char curMap[64];
|
||||
char curMap[64];
|
||||
GetCurrentMap(curMap, sizeof(curMap));
|
||||
|
||||
if(StrEqual(curMap, "c4m5_milltown_escape")) {
|
||||
|
@ -932,6 +1000,12 @@ public void OnMapStart() {
|
|||
L4D2_RunScript(HUD_SCRIPT_CLEAR);
|
||||
}
|
||||
|
||||
public void OnConfigsExecuted() {
|
||||
if(hUpdateMinPlayers.BoolValue && hMinPlayers != null) {
|
||||
hMinPlayers.IntValue = abmExtraCount;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void OnMapEnd() {
|
||||
for(int i = 0; i < ammoPacks.Length; i++) {
|
||||
|
@ -947,7 +1021,6 @@ public void OnMapEnd() {
|
|||
ammoPacks.Clear();
|
||||
playersLoadedIn = 0;
|
||||
abmExtraCount = 4;
|
||||
PrintToServer("[EPI] Stopping timer for map ending");
|
||||
delete updateHudTimer;
|
||||
}
|
||||
|
||||
|
@ -968,7 +1041,7 @@ public void EntityOutput_OnStartTouchSaferoom(const char[] output, int caller, i
|
|||
int extraPlayers = abmExtraCount - 4;
|
||||
float averageTeamHP = GetAverageHP();
|
||||
if(averageTeamHP <= 30.0) extraPlayers += (extraPlayers / 2); //if perm. health < 30, give an extra 4 on top of the extra
|
||||
else if(averageTeamHP <= 50.0) extraPlayers = (extraPlayers / 3); //if the team's average health is less than 50 (permament) then give another
|
||||
else if(averageTeamHP <= 50.0) extraPlayers += (extraPlayers / 3); //if the team's average health is less than 50 (permament) then give another
|
||||
//Chance to get an extra kit (might need to be nerfed or restricted to > 50 HP)
|
||||
if(GetRandomFloat() < 0.3 && averageTeamHP <= 80.0) ++extraPlayers;
|
||||
|
||||
|
@ -1217,11 +1290,12 @@ public Action Timer_UpdateHud(Handle h) {
|
|||
Format(players, sizeof(players), "%s%s %s\\n", players, prefix, data);
|
||||
}
|
||||
}
|
||||
if(hEPIHudState.IntValue == 3) {
|
||||
if(hEPIHudState.IntValue != 3) {
|
||||
RunVScriptLong(HUD_SCRIPT_DATA, players);
|
||||
} else {
|
||||
PrintHintTextToAll("DEBUG HUD TIMER");
|
||||
RunVScriptLong(HUD_SCRIPT_DEBUG, players);
|
||||
} else
|
||||
RunVScriptLong(HUD_SCRIPT_DATA, players);
|
||||
}
|
||||
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
@ -1267,6 +1341,12 @@ public void PopulateItems() {
|
|||
int spawner, count;
|
||||
for(int i = 0; i < sizeof(cabinets); i++) {
|
||||
if(cabinets[i].id == 0) break;
|
||||
GetEntityClassname(cabinets[i].id, classname, sizeof(classname));
|
||||
if(!StrEqual(classname, "prop_health_cabinet")) {
|
||||
PrintToServer("Cabinet %d (ent %d) is not a valid entity, is %s. Skipping", i, cabinets[i].id, classname);
|
||||
cabinets[i].id = 0;
|
||||
continue;
|
||||
}
|
||||
int spawnCount = GetEntProp(cabinets[i].id, Prop_Data, "m_pillCount");
|
||||
int extraAmount = RoundToCeil(float(abmExtraCount) * (float(spawnCount)/4.0) - spawnCount);
|
||||
bool hasASpawner;
|
||||
|
@ -1275,6 +1355,7 @@ public void PopulateItems() {
|
|||
for(int block = 0; block < CABINET_ITEM_BLOCKS; block++) {
|
||||
spawner = cabinets[i].items[block];
|
||||
if(spawner > 0) {
|
||||
if(!HasEntProp(spawner, Prop_Data, "m_itemCount")) continue;
|
||||
hasASpawner = true;
|
||||
count = GetEntProp(spawner, Prop_Data, "m_itemCount") + 1;
|
||||
SetEntProp(spawner, Prop_Data, "m_itemCount", count);
|
||||
|
|
|
@ -84,7 +84,7 @@ public void OnPluginStart() {
|
|||
hBotReverseFFDefend.AddChangeHook(Change_BotDefend);
|
||||
|
||||
RegAdminCmd("sm_ftl", Command_ListTheTrolls, ADMFLAG_GENERIC, "Lists all the trolls currently ingame.");
|
||||
RegAdminCmd("sm_ftm", Command_ListModes, ADMFLAG_KICK, "Lists all the troll modes and their description");
|
||||
RegAdminCmd("sm_ftm", Command_ListModes, ADMFLAG_GENERIC, "Lists all the troll modes and their description");
|
||||
RegAdminCmd("sm_ftr", Command_ResetUser, ADMFLAG_GENERIC, "Resets user of any troll effects.");
|
||||
RegAdminCmd("sm_fta", Command_ApplyUser, ADMFLAG_KICK, "Apply a troll mod to a player, or shows menu if no parameters.");
|
||||
RegAdminCmd("sm_ftas", Command_ApplyUserSilent, ADMFLAG_ROOT, "Apply a troll mod to a player, or shows menu if no parameters.");
|
||||
|
|
1252
scripting/l4d2_hats.sp
Normal file
1252
scripting/l4d2_hats.sp
Normal file
File diff suppressed because it is too large
Load diff
|
@ -67,6 +67,8 @@ public void OnPluginStart() {
|
|||
SetFailState("This plugin is for L4D2 only.");
|
||||
}
|
||||
|
||||
HookEvent("game_start", OnGameStart);
|
||||
|
||||
hPercentTotal = CreateConVar("l4d2_population_global_chance", "1.0", "The % chance that any the below chances occur.\n0.0 = NEVER, 1.0: ALWAYS");
|
||||
hPercentClown = CreateConVar("l4d2_population_clowns", "0.0", "The % chance that a common spawns as a clown.\n0.0 = OFF, 1.0 = ALWAYS", FCVAR_NONE, true, 0.0, true, 1.0);
|
||||
hPercentMud = CreateConVar("l4d2_population_mud", "0.0", "The % chance that a common spawns as a mud zombie.\n0.0 = OFF, 1.0 = ALWAYS", FCVAR_NONE, true, 0.0, true, 1.0);
|
||||
|
@ -87,6 +89,18 @@ public void OnPluginStart() {
|
|||
|
||||
//AutoExecConfig(true, "l4d2_population_control");
|
||||
}
|
||||
|
||||
public void OnGameStart(Event event, const char[] name, bool dontBroadcast) {
|
||||
hPercentTotal.FloatValue = 1.0;
|
||||
hPercentClown.FloatValue = 0.0;
|
||||
hPercentMud.FloatValue = 0.0;
|
||||
hPercentCeda.FloatValue = 0.0;
|
||||
hPercentWorker.FloatValue = 0.0;
|
||||
hPercentRiot.FloatValue = 0.0;
|
||||
hPercentJimmy.FloatValue = 0.0;
|
||||
hTotalZombies.FloatValue = 0.0;
|
||||
}
|
||||
|
||||
public void OnMapStart() {
|
||||
for(int i = 0; i < COMMON_MODELS_COUNT; i++) {
|
||||
PrecacheModel(INFECTED_MODELS[i], true);
|
||||
|
|
|
@ -37,7 +37,7 @@ public void OnPluginStart() {
|
|||
SetFailState("This plugin is for L4D/L4D2 only.");
|
||||
}
|
||||
|
||||
clients = new ArrayList(3);
|
||||
clients = new ArrayList(4);
|
||||
|
||||
HookEvent("player_hurt", Event_PlayerHurt);
|
||||
HookEvent("tank_spawn", Event_TankSpawn);
|
||||
|
@ -93,8 +93,8 @@ public Action L4D2_OnChooseVictim(int attacker, int &curTarget) {
|
|||
|
||||
clients.SortCustom(Sort_TankTargetter);
|
||||
curTarget = clients.Get(0);
|
||||
tankChosenVictim[attacker] = curTarget;
|
||||
targettingTank[curTarget] = attacker;
|
||||
// tankChosenVictim[attacker] = curTarget;
|
||||
// targettingTank[curTarget] = attacker;
|
||||
PrintToConsoleAll("[TankPriority] Player Selected to target: %N", curTarget);
|
||||
//TODO: Possibly clear totalTankDamage
|
||||
return Plugin_Changed;
|
||||
|
|
|
@ -160,7 +160,7 @@ void SetupTurret(int turret, float time = 0.0) {
|
|||
thinkTimer = CreateTimer(0.1, Timer_Think, _, TIMER_REPEAT);
|
||||
}
|
||||
// Clamp to 0 -> _TURRET_PHASE_TICKS - 1
|
||||
turretPhaseOffset[turret] = turretIds.Length % (_TURRET_PHASE_TICKS - 1);
|
||||
turretPhaseOffset[turret] = (turretIds.Length + 1) % (_TURRET_PHASE_TICKS - 1);
|
||||
turretIds.Push(turret);
|
||||
}
|
||||
Action Timer_ActivateTurret(Handle h, int turret) {
|
||||
|
@ -356,12 +356,15 @@ public Action Timer_Think(Handle h) {
|
|||
// Keep targetting if can view
|
||||
target = EntRefToEntIndex(turretActiveEntity[entity]);
|
||||
if(target > 0 && IsValidEntity(target)) {
|
||||
bool ragdoll = GetEntProp(target, Prop_Data, "m_bClientSideRagdoll") == 1;
|
||||
if(!ragdoll && CanSeeEntity(pos, target)) {
|
||||
if(target <= MaxClients) {
|
||||
if(IsPlayerAlive(target) && GetEntProp(target, Prop_Data, "m_bClientSideRagdoll") == 0 && CanSeeEntity(pos, target)) {
|
||||
FireTurretAuto(pos, target, turretDamage[entity]);
|
||||
continue;
|
||||
}
|
||||
} else if(CanSeeEntity(pos, target)) {
|
||||
FireTurretAuto(pos, target, turretDamage[entity]);
|
||||
continue;
|
||||
}
|
||||
entityActiveTurret[target] = 0;
|
||||
}
|
||||
DeactivateTurret(entity);
|
||||
}
|
||||
|
@ -381,9 +384,9 @@ public Action Timer_Think(Handle h) {
|
|||
CreateTimer(1.2, Timer_KillRock, EntIndexToEntRef(target));
|
||||
damage = 1000.0;
|
||||
}
|
||||
if(target == -1) target = FindNearestVisibleClient(TEAM_SPECIALS, pos, TURRET_MAX_RANGE_SPECIALS_OPTIMIZED);
|
||||
if(target <= 0) target = FindNearestVisibleClient(TEAM_SPECIALS, pos, TURRET_MAX_RANGE_SPECIALS_OPTIMIZED);
|
||||
}
|
||||
if(target == -1) target = FindNearestVisibleEntity("infected", pos, TURRET_MAX_RANGE_INFECTED_OPTIMIZED, entity);
|
||||
if(target <= 0) target = FindNearestVisibleEntity("infected", pos, TURRET_MAX_RANGE_INFECTED_OPTIMIZED, entity);
|
||||
if(target > 0) {
|
||||
turretDamage[entity] = damage;
|
||||
entityActiveTurret[target] = entity;
|
||||
|
@ -537,7 +540,7 @@ stock int FindNearestVisibleClient(int team, const float origin[3], float maxRan
|
|||
if(distance <= closestDist || client == -1) {
|
||||
if(CanSeePoint(origin, pos)) {
|
||||
// Priority: Pinned survivors
|
||||
if(L4D_GetPinnedSurvivor(i)) {
|
||||
if(L4D_GetPinnedSurvivor(i) > 0) {
|
||||
return i;
|
||||
}
|
||||
client = i;
|
||||
|
@ -553,6 +556,7 @@ stock int FindNearestVisibleEntity(const char[] classname, const float origin[3]
|
|||
int entity = INVALID_ENT_REFERENCE;
|
||||
static float pos[3];
|
||||
while ((entity = FindEntityByClassname(entity, classname)) != INVALID_ENT_REFERENCE) {
|
||||
// Skip entity, it's already being targetted
|
||||
if(entityActiveTurret[entity] > 0) continue;
|
||||
bool ragdoll = GetEntProp(entity, Prop_Data, "m_bClientSideRagdoll") == 1;
|
||||
if(ragdoll) continue;
|
||||
|
@ -568,7 +572,7 @@ stock int FindNearestVisibleEntity(const char[] classname, const float origin[3]
|
|||
}
|
||||
|
||||
stock bool CanSeePoint(const float origin[3], const float point[3]) {
|
||||
TR_TraceRay(origin, point, MASK_SOLID, RayType_EndPoint);
|
||||
TR_TraceRay(origin, point, MASK_SHOT, RayType_EndPoint);
|
||||
|
||||
return !TR_DidHit(); // Can see point if no collisions
|
||||
}
|
||||
|
@ -576,7 +580,7 @@ stock bool CanSeePoint(const float origin[3], const float point[3]) {
|
|||
stock bool CanSeeEntity(const float origin[3], int entity) {
|
||||
static float point[3];
|
||||
GetEntPropVector(entity, Prop_Send, "m_vecOrigin", point);
|
||||
TR_TraceRayFilter(origin, point, MASK_ALL, RayType_EndPoint, Filter_CanSeeEntity, entity);
|
||||
TR_TraceRayFilter(origin, point, MASK_SHOT, RayType_EndPoint, Filter_CanSeeEntity, entity);
|
||||
|
||||
return TR_GetEntityIndex() == entity; // Can see point if no collisions
|
||||
}
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
#include <sdktools>
|
||||
//#include <sdkhooks>
|
||||
|
||||
#define MIN_TIME_BETWEEN_NAME_CHANGES 10000
|
||||
#define MAX_NAME_COUNT 3
|
||||
#define MIN_TIME_BETWEEN_NAME_CHANGES 10000 // In seconds
|
||||
#define MAX_NAME_COUNT 3 // How many changes max within a MIN_TIME_BETWEEN_NAME_CHANGES
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
|
|
|
@ -29,6 +29,7 @@ static char query[1024];
|
|||
static char reason[256];
|
||||
static int WaitingForNotePlayer;
|
||||
static char menuNoteTarget[32];
|
||||
static char menuNoteTargetName[32];
|
||||
|
||||
enum struct PlayerData {
|
||||
char id[32];
|
||||
|
@ -52,6 +53,7 @@ public void OnPluginStart() {
|
|||
HookEvent("player_disconnect", Event_PlayerDisconnect);
|
||||
HookEvent("player_first_spawn", Event_FirstSpawn);
|
||||
|
||||
RegConsoleCmd("sm_rep", Command_RepPlayer, "+rep or -rep a player");
|
||||
RegAdminCmd("sm_note", Command_AddNote, ADMFLAG_KICK, "Add a note to a player");
|
||||
RegAdminCmd("sm_notes", Command_ListNotes, ADMFLAG_KICK, "List notes for a player");
|
||||
RegAdminCmd("sm_notedisconnected", Command_AddNoteDisconnected, ADMFLAG_KICK, "Add a note to any disconnected players");
|
||||
|
@ -69,6 +71,136 @@ public void OnPluginStart() {
|
|||
// PrintToServer("");
|
||||
}
|
||||
|
||||
void ShowRepMenu(int client, int targetUserid) {
|
||||
Menu menu = new Menu(RepFinalHandler);
|
||||
menu.SetTitle("Choose a rating");
|
||||
char id[8];
|
||||
Format(id, sizeof(id), "%d|1", targetUserid);
|
||||
menu.AddItem(id, "+Rep");
|
||||
Format(id, sizeof(id), "%d|1", targetUserid);
|
||||
menu.AddItem(id, "-Rep");
|
||||
menu.Display(client, 0);
|
||||
}
|
||||
|
||||
public int RepPlayerHandler(Menu menu, MenuAction action, int param1, int param2) {
|
||||
/* If an option was selected, tell the client about the item. */
|
||||
if (action == MenuAction_Select) {
|
||||
static char info[4];
|
||||
menu.GetItem(param2, info, sizeof(info));
|
||||
int targetUserid = StringToInt(info);
|
||||
int target = GetClientOfUserId(targetUserid);
|
||||
|
||||
if(target == 0) {
|
||||
ReplyToCommand(param1, "Could not acquire player");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ShowRepMenu(param1, targetUserid);
|
||||
} else if (action == MenuAction_End) {
|
||||
delete menu;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int RepFinalHandler(Menu menu, MenuAction action, int param1, int param2) {
|
||||
/* If an option was selected, tell the client about the item. */
|
||||
if (action == MenuAction_Select) {
|
||||
char info[8];
|
||||
menu.GetItem(param2, info, sizeof(info));
|
||||
char str[2][8];
|
||||
ExplodeString(info, "|", str, 2, 8, false);
|
||||
int targetUserid = StringToInt(str[0]);
|
||||
int target = GetClientOfUserId(targetUserid);
|
||||
int rep = StringToInt(str[1]);
|
||||
|
||||
if(target == 0) {
|
||||
ReplyToCommand(param1, "Could not acquire player");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ApplyRep(param1, target, rep);
|
||||
} else if (action == MenuAction_End) {
|
||||
delete menu;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public Action Command_RepPlayer(int client, int args) {
|
||||
if(client == 0) {
|
||||
ReplyToCommand(client, "You must be a player to use this command.");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
if(args == 0) {
|
||||
Menu menu = new Menu(RepPlayerHandler);
|
||||
menu.SetTitle("Choose a player to rep");
|
||||
char id[8], display[64];
|
||||
// int clientTeam = GetClientTeam(client);
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
if(i != client && IsClientConnected(i) && IsClientInGame(i) && !IsFakeClient(i)) {
|
||||
Format(id, sizeof(id), "%d", GetClientUserId(i));
|
||||
Format(display, sizeof(display), "%N", i);
|
||||
menu.AddItem(id, display);
|
||||
}
|
||||
}
|
||||
menu.Display(client, 0);
|
||||
return Plugin_Handled;
|
||||
} else if(args > 0) {
|
||||
char arg1[32];
|
||||
GetCmdArg(1, arg1, sizeof(arg1));
|
||||
char target_name[MAX_TARGET_LENGTH];
|
||||
int target_list[1], target_count;
|
||||
bool tn_is_ml;
|
||||
if ((target_count = ProcessTargetString(
|
||||
arg1,
|
||||
client,
|
||||
target_list,
|
||||
1,
|
||||
COMMAND_FILTER_ALIVE,
|
||||
target_name,
|
||||
sizeof(target_name),
|
||||
tn_is_ml)) <= 0)
|
||||
{
|
||||
/* This function replies to the admin with a failure message */
|
||||
ReplyToTargetError(client, target_count);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
if (args == 1) {
|
||||
ShowRepMenu(client, GetClientUserId(target_list[0]));
|
||||
} else {
|
||||
char arg2[2];
|
||||
GetCmdArg(2, arg2, sizeof(arg2));
|
||||
int rep;
|
||||
if(arg2[0] == 'y' || arg2[0] == '+' || arg2[0] == 'p') {
|
||||
rep = 1;
|
||||
} else if(arg2[0] == 'n' || arg2[0] == '-' || arg2[0] == 's') {
|
||||
rep = -1;
|
||||
} else {
|
||||
ReplyToCommand(client, "Invalid rep value: Use (y/+/p) for +rep or (n/-/s) for -rep");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
ApplyRep(client, target_list[0], rep);
|
||||
}
|
||||
}
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
void ApplyRep(int client, int target, int rep) {
|
||||
char[] msg = "+rep";
|
||||
if(rep == -1) msg[0] = '-';
|
||||
|
||||
LogAction(client, target, "\"%L\" %srep \"%L\"", client, msg, target);
|
||||
if(rep > 0)
|
||||
CShowActivity(client, "{green}+rep %N", target);
|
||||
else
|
||||
CShowActivity(client, "{yellow}-rep %N", target);
|
||||
|
||||
char activatorId[32], targetId[32];
|
||||
GetClientAuthId(client, AuthId_Steam2, activatorId, sizeof(activatorId));
|
||||
GetClientAuthId(target, AuthId_Steam2, targetId, sizeof(targetId));
|
||||
DB.Format(query, sizeof(query), "INSERT INTO `notes` (steamid, markedBy, content) VALUES ('%s', '%s', '%s')", targetId, activatorId, msg);
|
||||
DB.Query(DB_AddNote, query);
|
||||
}
|
||||
|
||||
public Action Command_AddNoteDisconnected(int client, int args) {
|
||||
if(lastPlayers.Length == 0) {
|
||||
ReplyToCommand(client, "No disconnected players recorded.");
|
||||
|
@ -87,8 +219,9 @@ public Action Command_AddNoteDisconnected(int client, int args) {
|
|||
|
||||
public int Menu_Disconnected(Menu menu, MenuAction action, int client, int item) {
|
||||
if (action == MenuAction_Select) {
|
||||
menu.GetItem(item, menuNoteTarget, sizeof(menuNoteTarget));
|
||||
PrintToChat(client, "Enter a note in the chat for %s: (or 'cancel' to cancel)", menuNoteTarget);
|
||||
int style;
|
||||
menu.GetItem(item, menuNoteTarget, sizeof(menuNoteTarget), style, menuNoteTargetName, sizeof(menuNoteTargetName));
|
||||
CPrintToChat(client, "Enter a note in the chat for {yellow}%s {olive}(%s){default}: (or 'cancel' to cancel)", menuNoteTargetName, menuNoteTarget);
|
||||
WaitingForNotePlayer = client;
|
||||
} else if (action == MenuAction_End)
|
||||
delete menu;
|
||||
|
@ -101,7 +234,7 @@ public Action OnClientSayCommand(int client, const char[] command, const char[]
|
|||
if(StrEqual(sArgs, "cancel", false)) {
|
||||
PrintToChat(client, "Note cancelled.");
|
||||
} else {
|
||||
int size = strlen(sArgs);
|
||||
int size = strlen(sArgs) + 1;
|
||||
char[] sArgsTrimmed = new char[size];
|
||||
strcopy(sArgsTrimmed, size, sArgs);
|
||||
TrimString(sArgsTrimmed);
|
||||
|
@ -109,9 +242,9 @@ public Action OnClientSayCommand(int client, const char[] command, const char[]
|
|||
GetClientAuthId(client, AuthId_Steam2, buffer, sizeof(buffer));
|
||||
DB.Format(query, sizeof(query), "INSERT INTO `notes` (steamid, markedBy, content) VALUES ('%s', '%s', '%s')", menuNoteTarget, buffer, sArgsTrimmed);
|
||||
DB.Query(DB_AddNote, query);
|
||||
LogAction(client, -1, "\"%L\" added note for \"%s\": \"%s\"", client, menuNoteTarget, sArgsTrimmed);
|
||||
LogAction(client, -1, "\"%L\" added note for \"%s\" (%s): \"%s\"", client, menuNoteTargetName, menuNoteTarget, sArgsTrimmed);
|
||||
Format(buffer, sizeof(buffer), "%N: ", client);
|
||||
CShowActivity2(client, buffer, "added a note for {green}%s: {default}\"%s\"", menuNoteTarget, sArgsTrimmed);
|
||||
CShowActivity2(client, buffer, "added a note for {green}%s: {default}\"%s\"", menuNoteTargetName, sArgsTrimmed);
|
||||
}
|
||||
return Plugin_Stop;
|
||||
}
|
||||
|
@ -126,6 +259,19 @@ public Action Command_AddNote(int client, int args) {
|
|||
GetCmdArg(1, target_name, sizeof(target_name));
|
||||
GetCmdArg(2, reason, sizeof(reason));
|
||||
TrimString(reason);
|
||||
if(args > 2) {
|
||||
// Correct commands that don't wrap message in quotes
|
||||
char buffer[64];
|
||||
for(int i = 3; i <= args; i++) {
|
||||
GetCmdArg(i, buffer, sizeof(buffer));
|
||||
Format(reason, sizeof(reason), "%s %s", reason, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
if(reason[0] == '\0') {
|
||||
ReplyToCommand(client, "Can't create an empty note");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
int target_list[1], target_count;
|
||||
bool tn_is_ml;
|
||||
|
@ -139,7 +285,11 @@ public Action Command_AddNote(int client, int args) {
|
|||
sizeof(target_name),
|
||||
tn_is_ml)) <= 0
|
||||
) {
|
||||
ReplyToTargetError(client, target_count);
|
||||
if(target_count == COMMAND_TARGET_NONE) {
|
||||
ReplyToCommand(client, "Could not find any online user. If user has disconnected, use sm_notedisconnected");
|
||||
} else {
|
||||
ReplyToTargetError(client, target_count);
|
||||
}
|
||||
return Plugin_Handled;
|
||||
}
|
||||
if(args == 1) {
|
||||
|
@ -261,19 +411,29 @@ public void DB_FindNotes(Database db, DBResultSet results, const char[] error, a
|
|||
static char noteCreator[32];
|
||||
CPrintChatToAdmins("{yellow}> Notes for %N", client);
|
||||
int actions = 0;
|
||||
int repP = 0, repN = 0;
|
||||
while(results.FetchRow()) {
|
||||
results.FetchString(0, reason, sizeof(reason));
|
||||
results.FetchString(1, noteCreator, sizeof(noteCreator));
|
||||
TrimString(reason);
|
||||
if(ParseActions(data, reason)) {
|
||||
actions++;
|
||||
} else if((reason[0] == '+' || reason[0] == '-') && reason[1] == 'r' && reason[2] == 'e' && reason[3] == 'p') {
|
||||
if(reason[0] == '+') {
|
||||
repP++;
|
||||
} else {
|
||||
repN--;
|
||||
}
|
||||
} else {
|
||||
CPrintChatToAdmins(" {olive}%s: {default}%s", noteCreator, reason);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(actions > 0) {
|
||||
CPrintChatToAdmins(" {olive}%d Auto Actions Applied", actions);
|
||||
CPrintChatToAdmins(" > {olive}%d Auto Actions Applied", actions);
|
||||
}
|
||||
if(repP > 0 || repN > 0) {
|
||||
CPrintChatToAdmins(" > {olive}%d +rep\t{yellow}-rep", repP, repN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue