This commit is contained in:
Jackz 2023-11-23 08:33:53 -06:00
parent ea0bc8c543
commit 28796e46cf
No known key found for this signature in database
GPG key ID: E0BBD94CF657F603
19 changed files with 165 additions and 132 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -573,7 +573,6 @@ bool Filter_IgnoreForbidden(int entity, int mask, int data) {
bool CheckBlacklist(int entity) {
static char buffer[64];
GetEntityClassname(entity, buffer, sizeof(buffer));
PrintToServer("GrabEnt:CheckBlacklist | classname=\"%s\"", buffer);
for(int i = 0; i < MAX_FORBIDDEN_CLASSNAMES; i++) {
if(StrEqual(FORBIDDEN_CLASSNAMES[i], buffer)) {
return false;
@ -582,7 +581,6 @@ bool CheckBlacklist(int entity) {
if(StrContains(buffer, "prop_") > -1) {
GetEntPropString(entity, Prop_Data, "m_ModelName", buffer, sizeof(buffer));
for(int i = 0; i < MAX_FORBIDDEN_MODELS; i++) {
PrintToServer("GrabEnt:CheckBlacklist | model=\"%s\" FORBIDDEN_MODELS[%d] = \"%s\"", buffer, i, FORBIDDEN_MODELS[i]);
if(StrEqual(FORBIDDEN_MODELS[i], buffer)) {
return false;
}

View file

@ -8,7 +8,7 @@
#include <sdktools>
#include <sdkhooks>
static int disableFFClient, ffDamageCount; //client to disable FF for
static int disableFFClient = -1, ffDamageCount; //client to disable FF for
static ConVar forceKickFFThreshold;
public Plugin myinfo = {

View file

@ -152,11 +152,14 @@ void OnTankBotSpawn(int client) {
if(!IsEPIActive() || !(cvEPISpecialSpawning.IntValue & 4)) return;
// Only run on 6+ survivors
if(g_realSurvivorCount < 6) return;
if(g_finaleStage == Stage_FinaleTank2) {
if(hExtraFinaleTank.IntValue > 0 && g_extraFinaleTankEnabled) {
float duration = GetRandomFloat(EXTRA_TANK_MIN_SEC, EXTRA_TANK_MAX_SEC);
// Pass it 0, which doesnt make it a split tank, has default health
CreateTimer(duration, Timer_SpawnSplitTank, 0);
// Check if any finale is active
if(g_finaleStage != Stage_Inactive) {
if(g_finaleStage == Stage_FinaleTank2) {
if(hExtraFinaleTank.IntValue > 0 && g_extraFinaleTankEnabled) {
float duration = GetRandomFloat(EXTRA_TANK_MIN_SEC, EXTRA_TANK_MAX_SEC);
// Pass it 0, which doesnt make it a split tank, has default health
CreateTimer(duration, Timer_SpawnSplitTank, 0);
}
}
} else if(g_newTankHealth > 0) {
// A split tank has spawned, set its health
@ -164,11 +167,13 @@ void OnTankBotSpawn(int client) {
SetEntProp(client, Prop_Send, "m_iHealth", g_newTankHealth);
g_newTankHealth = 0;
} else {
// Normal (non-finale) tank spawned. Set its health
// This should not run on active finales (different than finale maps, such as swamp fever's, where finale isnt full map)
// Normal tank (not stage 2 / not secondary tank) spawned. Set its health and spawn split tank
int health = GetEntProp(client, Prop_Send, "m_iHealth");
float additionalHealth = float(g_survivorCount - 4) * cvEPITankHealth.FloatValue;
health += RoundFloat(additionalHealth);
// Only split if split tank chance, if enabled, and we aren't on 2nd finale tank
if(hExtraFinaleTank.IntValue & 1 && GetURandomFloat() <= hSplitTankChance.FloatValue) {
float duration = GetRandomFloat(EXTRA_TANK_MIN_SEC, EXTRA_TANK_MAX_SEC);
int splitHealth = health / 2;
@ -183,6 +188,7 @@ void OnTankBotSpawn(int client) {
}
Action Timer_SpawnSplitTank(Handle h, int health) {
PrintDebug(DEBUG_SPAWNLOGIC, "Timer_SpawnSplitTank(%d)", health);
g_newTankHealth = health;
DirectorSpawn(Special_Tank);
return Plugin_Handled;
@ -215,6 +221,7 @@ void Event_PlayerIncapped(Event event, const char[] name, bool dontBroadcast) {
TryGrantRest();
}
}
/// METHODS
@ -237,7 +244,6 @@ void InitExtraWitches() {
// Calculate the number of witches we want to spawn.
// We bias the dice roll to the right. We slowly increase min based on player count to shift distribution to the right
int min = RoundToFloor(float(count - 5) / 4.0);
// TODO: max based on count
int max = RoundToFloor(float(count) / 4.0);
// TODO: inc chance based on map max flow
@ -288,7 +294,7 @@ void Director_RandomizeLimits() {
}
void Director_RandomizeThings() {
g_maxStressIntensity = GetRandomFloat(DIRECTOR_STRESS_CUTOFF, 1.0);
g_minFlowSpawn = GetRandomFloat(FLOW_CUTOFF, FLOW_CUTOFF * 2);
g_minFlowSpawn = GetRandomFloat(500.0 + FLOW_CUTOFF, FLOW_CUTOFF * 2);
Director_RandomizeLimits();
}
@ -304,8 +310,6 @@ bool Director_ShouldRest() {
void TryGrantRest() {
if(GetURandomFloat() <= DIRECTOR_REST_CHANCE) {
g_restCount = GetRandomInt(0, DIRECTOR_REST_MAX_COUNT);
if(g_restCount > 0)
PrintDebug(DEBUG_SPAWNLOGIC, "new rest period: %.1f s", g_restCount * DIRECTOR_TIMER_INTERVAL);
}
}
@ -347,8 +351,7 @@ directorState Director_Think() {
// A. They reach minimum flow (little past start saferoom)
// B. Under the total limited (equal to player count)
// C. Special spawning is enabled
// TODO: scaling chance, low chance when hitting g_infectedCount, higher on 0
if(g_highestFlowAchieved < g_minFlowSpawn || ~cvEPISpecialSpawning.IntValue & 1) return DState_PendingMinFlowOrDisabled;
if( ~cvEPISpecialSpawning.IntValue & 1 || !L4D_HasAnySurvivorLeftSafeArea() || g_highestFlowAchieved < g_minFlowSpawn) return DState_PendingMinFlowOrDisabled;
// Check if a rest period is given
if(Director_ShouldRest()) {
@ -424,7 +427,7 @@ Action Timer_DirectorWitch(Handle h) {
void DirectorSpawn(specialType special, int player = -1) {
if(player <= 0)
player = GetSuitableVictim();
PrintDebug(DEBUG_SPAWNLOGIC, "Director: spawning %s(%d) around %N (cnt=%d,lim=%d)", SPECIAL_IDS[view_as<int>(special)], special, player, g_spawnCount[view_as<int>(special)], g_spawnLimit[view_as<int>(special)]);
// PrintDebug(DEBUG_SPAWNLOGIC, "Director: spawning %s(%d) around %N (cnt=%d,lim=%d)", SPECIAL_IDS[view_as<int>(special)], special, player, g_spawnCount[view_as<int>(special)], g_spawnLimit[view_as<int>(special)]);
if(special != Special_Witch && special != Special_Tank) {
// Bypass director
int bot = CreateFakeClient("EPI_BOT");

View file

@ -1,5 +1,8 @@
int BUILDER_COLOR[4] = { 0, 255, 0, 235 };
int WALL_COLOR[4] = { 255, 0, 0, 235 };
int GLOW_BLUE[4] = { 3, 148, 252 };
int GLOW_RED[4] = { 255, 0, 0, 235 };
int GLOW_WHITE[4] = { 255, 255, 255, 255 };
int GLOW_GREEN[4] = { 3, 252, 53 };
float ORIGIN_SIZE[3] = { 2.0, 2.0, 2.0 };
enum wallMode {
@ -27,13 +30,13 @@ enum struct WallBuilderData {
bool isCopy;
void Reset(bool initial = false) {
this.entity = INVALID_ENT_REFERENCE;
this.isCopy = false;
this.size[0] = this.size[1] = this.size[2] = 5.0;
this.angles[0] = this.angles[1] = this.angles[2] = 0.0;
this.axis = 1;
this.canScale = true;
this.moveDistance = 200.0;
this.entity = INVALID_ENT_REFERENCE;
this.hasCollision = true;
this.CalculateMins();
this.SetMode(INACTIVE);
@ -188,6 +191,7 @@ enum struct WallBuilderData {
DispatchKeyValueVector(blocker, "maxs", this.size);
DispatchKeyValueVector(blocker, "boxmins", this.mins);
DispatchKeyValueVector(blocker, "boxmaxs", this.size);
DispatchKeyValue(blocker, "excludednpc", "player");
DispatchKeyValueVector(blocker, "angles", this.angles);
DispatchKeyValue(blocker, "model", DUMMY_MODEL);
@ -207,8 +211,9 @@ enum struct WallBuilderData {
enteffects |= 32; //EF_NODRAW
SetEntProp(blocker, Prop_Send, "m_fEffects", enteffects);
AcceptEntityInput(blocker, "Enable");
SDKHook(blocker, SDKHook_Use, OnWallClicked);
this.Draw(WALL_COLOR, 5.0, 1.0);
this.Draw(GLOW_GREEN, 5.0, 1.0);
this.Reset();
return isEdit ? -2 : createdWalls.Push(EntIndexToEntRef(blocker));
}
@ -219,18 +224,22 @@ enum struct WallBuilderData {
GetEntityClassname(this.entity, classname, sizeof(classname));
int entity = CreateEntityByName(classname);
PrintToServer("Created %s: %d", classname, entity);
if(entity == -1) return -1;
GetEntPropString(this.entity, Prop_Data, "m_ModelName", classname, sizeof(classname));
DispatchKeyValueVector(entity, "origin", this.origin);
DispatchKeyValue(entity, "solid", "6");
DispatchKeyValue(entity, "model", classname);
PrintToServer("Set model %s: %d", classname, entity);
Format(classname, sizeof(classname), "hats_copy_%d", this.entity);
DispatchKeyValue(entity, "targetname", classname);
DispatchKeyValue(entity, "solid", "6");
DispatchSpawn(entity);
// SetEntProp(entity, Prop_Send, "m_CollisionGroup", 1);
// SetEntProp(entity, Prop_Send, "m_nSolidType", 6);
TeleportEntity(entity, this.origin, this.angles, NULL_VECTOR);
this.entity = entity;
this.isCopy = true;
SetEntProp(entity, Prop_Send, "m_nSolidType", 2);
return entity;
}
@ -255,6 +264,17 @@ enum struct WallBuilderData {
}
}
Action OnWallClicked(int entity, int activator, int caller, UseType type, float value) {
int wallId = FindWallId(entity);
if(wallId > 0) {
GlowWall(wallId, GLOW_BLUE);
AcceptEntityInput(entity, "Toggle");
} else {
PrintHintText(activator, "Invalid wall entity (%d)", entity);
}
return Plugin_Continue;
}
WallBuilderData WallBuilder[MAXPLAYERS+1];
@ -301,7 +321,7 @@ public Action Command_ManageWalls(int client, int args) {
if(args == 0) {
PrintToChat(client, "\x04[Hats]\x01 Created Walls: \x05%d\x01", createdWalls.Length);
for(int i = 1; i <= createdWalls.Length; i++) {
GlowWall(i, 20.0);
GlowWall(i, GLOW_WHITE, 20.0);
}
return Plugin_Handled;
}
@ -381,7 +401,7 @@ public Action Command_ManageWalls(int client, int args) {
for(int i = 1; i <= createdWalls.Length; i++) {
int entity = GetWallEntity(i);
AcceptEntityInput(entity, "Toggle");
GlowWall(i);
GlowWall(i, GLOW_BLUE);
}
PrintToChat(client, "\x04[Hats]\x01 Toggled \x05%d\x01 walls", walls);
} else {
@ -389,7 +409,7 @@ public Action Command_ManageWalls(int client, int args) {
if(id > -1) {
int entity = GetWallEntity(id);
AcceptEntityInput(entity, "Toggle");
GlowWall(id);
GlowWall(id, GLOW_BLUE);
PrintToChat(client, "\x04[Hats]\x01 Toggled Wall: \x05#%d\x01", id);
}
}
@ -487,7 +507,7 @@ int GetWallId(int client, const char[] arg) {
int entity = GetWallEntity(id);
if(!IsValidEntity(entity)) {
ReplyToCommand(client, "\x04[Hats]\x01 The wall with specified id no longer exists.");
createdWalls.Erase(id);
createdWalls.Erase(id - 1);
return -2;
}
return id;
@ -504,7 +524,19 @@ int GetWallEntity(int id) {
return createdWalls.Get(id - 1);
}
void GlowWall(int id, float lifetime = 5.0) {
/// Tries to find the id of the wall based off entity
int FindWallId(int entity) {
for(int i = 1; i <= createdWalls.Length; i++) {
int entRef = createdWalls.Get(i - 1);
int ent = EntRefToEntIndex(entRef);
if(ent == entity) {
return i;
}
}
return -1;
}
void GlowWall(int id, int glowColor[4], float lifetime = 5.0) {
int ref = GetWallEntity(id);
if(IsValidEntity(ref)) {
float pos[3], mins[3], maxs[3], angles[3];
@ -512,12 +544,12 @@ void GlowWall(int id, float lifetime = 5.0) {
GetEntPropVector(ref, Prop_Send, "m_vecOrigin", pos);
GetEntPropVector(ref, Prop_Send, "m_vecMins", mins);
GetEntPropVector(ref, Prop_Send, "m_vecMaxs", maxs);
Effect_DrawBeamBoxRotatableToAll(pos, mins, maxs, angles, g_iLaserIndex, 0, 0, 30, lifetime, 0.4, 0.4, 0, 1.0, WALL_COLOR, 0);
Effect_DrawBeamBoxRotatableToAll(pos, mins, maxs, angles, g_iLaserIndex, 0, 0, 30, lifetime, 0.4, 0.4, 0, 1.0, glowColor, 0);
}
}
void DeleteWall(int id) {
GlowWall(id);
GlowWall(id, GLOW_RED);
int ref = GetWallEntity(id);
if(IsValidEntity(ref)) {
RemoveEntity(ref);

View file

@ -964,4 +964,12 @@ stock void GetMenuDisplayName(int client, char[] display, int maxlen) {
else {
GetClientName(client, display, maxlen);
}
}
stock int MathMax(int a, int b) {
return a > b ? a : b;
}
stock int MathMin(int a, int b) {
return a < b ? a : b;
}

View file

@ -408,8 +408,8 @@ stock int GetWeaponName(WeaponId wepid, char[] nameBuffer, int length) {
return IsValidWeaponId(wepid) ? strcopy(nameBuffer, length, WeaponNames[wepid]) : 0;
}
stock int GetMeleeWeaponName(WeaponId wepid, char[] nameBuffer, int length) {
return IsValidWeaponId(wepid) ? strcopy(nameBuffer, length, MeleeWeaponNames[wepid]) : 0;
stock int GetMeleeWeaponName(MeleeWeaponId wepid, char[] nameBuffer, int length) {
return IsValidMeleeWeaponId(wepid) ? strcopy(nameBuffer, length, MeleeWeaponNames[wepid]) : 0;
}
/**

View file

@ -278,41 +278,22 @@ void Event_PlayerDisconnect(Event event, const char[] name, bool dontBroadcast)
Action Event_OnTakeDamage(int victim, int& attacker, int& inflictor, float& damage, int& damagetype, int& weapon, float damageForce[3], float damagePosition[3]) {
if(isEnabled && damage > 0.0 && victim <= MaxClients && attacker <= MaxClients && attacker > 0 && victim > 0) {
if(GetClientTeam(victim) != GetClientTeam(attacker) || attacker == victim)
if(GetClientTeam(attacker) != 2 || GetClientTeam(victim) != 2 || attacker == victim)
return Plugin_Continue;
else if(damagetype & DMG_BURN && hFFAutoScaleActivateTypes.IntValue & view_as<int>(RffActType_MolotovDamage) && IsFakeClient(attacker) && GetClientTeam(attacker) == 2) {
// Ignore damage from fire caused by bots (players who left after causing fire)
damage = 0.0;
return Plugin_Changed;
} else if((damagetype & DMG_BLAST || damagetype & DMG_BLAST_SURFACE) && hFFAutoScaleActivateTypes.IntValue & view_as<int>(RffActType_BlastDamage) && IsFakeClient(attacker) && GetClientTeam(attacker) == 2) {
damage = 0.0;
return Plugin_Changed;
else if(IsFakeClient(attacker) && attacker != victim) {
// Ignore damage from fire/explosives caused by bots (players who left after causing fire)
if(damagetype & DMG_BURN && hFFAutoScaleActivateTypes.IntValue & view_as<int>(RffActType_MolotovDamage)) {
damage = 0.0;
return Plugin_Changed;
} else if((damagetype & DMG_BLAST || damagetype & DMG_BLAST_SURFACE) && hFFAutoScaleActivateTypes.IntValue & view_as<int>(RffActType_BlastDamage)) {
damage = 0.0;
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) || 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;
}
//Allow friendly firing BOTS that aren't idle players:
//if(IsFakeClient(victim) && !HasEntProp(attacker, Prop_Send, "m_humanSpectatorUserID") || GetEntProp(attacker, Prop_Send, "m_humanSpectatorUserID") == 0) return Plugin_Continue;
// Stop all damage early if already marked as troll
else if(pData[attacker].isTroll) {
SDKHooks_TakeDamage(attacker, attacker, attacker, pData[attacker].autoRFFScaleFactor * damage);
return Plugin_Stop;
}
// Allow vanilla-damage if being attacked by special (example, charger carry)
else if(pData[victim].underAttack) return Plugin_Continue;
bool isAdmin = GetUserAdmin(attacker) != INVALID_ADMIN_ID;
// Is damage not caused by fire or pipebombs?
bool isDamageDirect = damagetype & (DMG_BURN) == 0;
int time = GetTime();
// If is a fall within first 2 minutes, do appropiate action
if(!isAdmin && damagetype & DMG_FALL && attacker == victim && damage > 0.0 && time - pData[victim].joinTime <= hJoinTime.IntValue * 60) {
if(damagetype & DMG_FALL && attacker == victim && damage > 0.0 && time - pData[victim].joinTime <= hJoinTime.IntValue * 60) {
pData[victim].jumpAttempts++;
float pos[3];
GetNearestPlayerPosition(victim, pos);
@ -336,6 +317,30 @@ Action Event_OnTakeDamage(int victim, int& attacker, int& inflictor, float& dam
}
return Plugin_Stop;
}
// Disregard any self inflicted damage from here:
else if(attacker == victim) return Plugin_Continue;
// Stop all damage early if already marked as troll
else if(pData[attacker].isTroll) {
SDKHooks_TakeDamage(attacker, attacker, attacker, pData[attacker].autoRFFScaleFactor * damage);
return Plugin_Stop;
}
// Otherwise if attacker is immune, is a bot, or victim is a bot, let it 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(GetEntProp(victim, Prop_Send, "m_isGoingToDie") && ~hFFAutoScaleActivateTypes.IntValue & view_as<int>(RffActType_BlackAndWhiteDamage)) {
return Plugin_Continue;
}
//Allow friendly firing BOTS that aren't idle players:
//if(IsFakeClient(victim) && !HasEntProp(attacker, Prop_Send, "m_humanSpectatorUserID") || GetEntProp(attacker, Prop_Send, "m_humanSpectatorUserID") == 0) return Plugin_Continue;
// Allow vanilla-damage if being attacked by special (example, charger carry)
// We don't want to track someone accidently ffing them when theres a window where you can
else if(pData[victim].underAttack) return Plugin_Continue;
// Is damage not caused by fire or pipebombs?
bool isDamageDirect = (damagetype & DMG_BURN) == 0;
// Forgive player teamkill based on threshold, resetting accumlated damage
if(time - pData[attacker].lastFFTime > hForgivenessTime.IntValue) {
@ -498,13 +503,13 @@ Action Command_TKInfo(int client, int args) {
float activeRate = GetActiveRate(target);
CReplyToCommand(client, "FF Frequency: {yellow}%d {default}(recent: %d, %d forgiven)", pData[target].totalFFCount, pData[target].ffCount, (pData[target].totalFFCount - pData[target].ffCount));
if(pData[client].lastFFTime == 0)
CReplyToCommand(client, "Total FF Damage: {yellow}%.1f HP{default} (last fff Never)", pData[target].totalDamageFF);
CReplyToCommand(client, "Total FF Damage: {yellow}%.1f HP{default} (last ff Never)", pData[target].totalDamageFF);
else
CReplyToCommand(client, "Total FF Damage: {yellow}%.1f HP{default} (last fff %.1f min ago)", pData[target].totalDamageFF, minutesSinceiLastFFTime);
CReplyToCommand(client, "Total FF Damage: {yellow}%.1f HP{default} (last ff %.1f min ago)", pData[target].totalDamageFF, minutesSinceiLastFFTime);
if(~pData[target].immunityFlags & Immune_TK)
CReplyToCommand(client, "Recent FF (TKDetectBuffer): {yellow}%.1f", pData[target].TKDamageBuffer);
if(~pData[target].immunityFlags & Immune_RFF)
CReplyToCommand(client, "Auto Reverse-FF: {yellow}%.1fx return rate", activeRate);
CReplyToCommand(client, "Auto Reverse-FF: {yellow}%.1fx rate", activeRate);
CReplyToCommand(client, "Attempted suicide jumps: {yellow}%d", pData[target].jumpAttempts);
} else {
for(int i = 1; i <= MaxClients; i++) {

View file

@ -48,7 +48,7 @@ Action Command_Status(int client, int args) {
int exceedRestartMin = exceedRestart / 60;
int exceedRestartHour = exceedRestartMin / 60;
ReplyToCommand(client, "Overdue restart time: %d hr / %d min / %d s", exceedRestartHour, exceedRestartMin, exceedRestart);
ReplyToCommand(client, "triesBots = %d\ttriesEmpty = %d / %d", triesBots, triesEmpty);
ReplyToCommand(client, "triesBots = %d\ttriesEmpty = %d / %d", triesBots, triesEmpty, 4);
return Plugin_Handled;
}

View file

@ -12,8 +12,6 @@
extraitems = (playerCount) * (cabinetAmount/4) - cabinetAmount
*/
//TODO: On 3rd/4th kit pickup in area, add more
//TODO: Add extra pills too, on pickup
#pragma semicolon 1
#pragma newdecls required
@ -35,7 +33,7 @@
#define FLOW_CUTOFF 500.0 // The cutoff of flow, so that witches / tanks don't spawn in saferooms / starting areas, [0 + FLOW_CUTOFF, MapMaxFlow - FLOW_CUTOFF]
#define EXTRA_TANK_MIN_SEC 2.0
#define EXTRA_TANK_MAX_SEC 20.0
#define EXTRA_TANK_MAX_SEC 16.0
#define DATE_FORMAT "%F at %I:%M %p"
@ -196,8 +194,8 @@ enum struct PlayerInventory {
bool isAlive;
WeaponId itemID[6]; //int -> char?
MeleeWeaponId meleeID; // If itemID[1] == WeaponId_Melee, pull from this
bool lasers;
char meleeID[32];
int primaryHealth;
int tempHealth;
@ -225,7 +223,7 @@ Restore from saved inventory
static StringMap weaponMaxClipSizes;
static StringMap pInv;
static int g_lastInvSave[MAXPLAYERS+1];
static Handle g_saveTimer[MAXPLAYERS+1];
static Handle g_saveTimer[MAXPLAYERS+1] = { null, ... };
static char HUD_SCRIPT_DATA[] = "eph <- { Fields = { players = { slot = g_ModeScript.HUD_RIGHT_BOT, dataval = \"%s\", flags = g_ModeScript.HUD_FLAG_ALIGN_LEFT | g_ModeScript.HUD_FLAG_TEAM_SURVIVORS | g_ModeScript.HUD_FLAG_NOBG } } }\nHUDSetLayout(eph)\nHUDPlace(g_ModeScript.HUD_RIGHT_BOT,0.78,0.77,0.3,0.3)\ng_ModeScript;";
@ -360,7 +358,6 @@ public void OnPluginStart() {
if(IsClientConnected(i) && IsClientInGame(i)) {
if(GetClientTeam(i) == 2) {
SaveInventory(i, true);
SDKHook(i, SDKHook_WeaponEquip, Event_Pickup);
}
playerData[i].Setup(i);
}
@ -539,7 +536,7 @@ public void Event_DifficultyChange(ConVar cvar, const char[] oldValue, const cha
/////////////////////////////////////
/// COMMANDS
////////////////////////////////////
void ValBool(int client, const char[] name, bool &value, const char[] input) {
public void ValBool(int client, const char[] name, bool &value, const char[] input) {
if(input[0] != '\0') {
value = input[0] == '1' || input[0] == 't';
CReplyToCommand(client, "Set {olive}%s{default} to {yellow}%b", name, value);
@ -547,7 +544,7 @@ void ValBool(int client, const char[] name, bool &value, const char[] input) {
CReplyToCommand(client, "Value of {olive}%s{default}: {yellow}%b", name, value);
}
}
void ValInt(int client, const char[] name, int &value, const char[] input) {
public void ValInt(int client, const char[] name, int &value, const char[] input) {
if(input[0] != '\0') {
value = StringToInt(input);
CReplyToCommand(client, "Set {olive}%s{default} to {yellow}%d", name, value);
@ -555,7 +552,7 @@ void ValInt(int client, const char[] name, int &value, const char[] input) {
CReplyToCommand(client, "Value of {olive}%s{default}: {yellow}%d", name, value);
}
}
void ValFloat(int client, const char[] name, float &value, const char[] input) {
public void ValFloat(int client, const char[] name, float &value, const char[] input) {
if(input[0] != '\0') {
value = StringToFloat(input);
CReplyToCommand(client, "Set {olive}%s{default} to {yellow}%f", name, value);
@ -794,7 +791,6 @@ Action Command_DebugStats(int client, int args) {
ReplyToCommand(client, "\x04%d. \x05%s", i, arg);
}
}
ReplyToCommand(client, "%s", inv.meleeID);
}
return Plugin_Handled;
}
@ -812,7 +808,7 @@ void Event_PlayerToIdle(Event event, const char[] name, bool dontBroadcast) {
if(GetClientTeam(client) != 2) return;
PrintToServer("%N -> idle %N", client, bot);
}
public Action L4D2_OnChangeFinaleStage(int &finaleType, const char[] arg) {
public void L4D2_OnChangeFinaleStage_PostHandled(int finaleType, const char[] arg) {
if(finaleType == FINALE_STARTED && g_realSurvivorCount > 4) {
g_finaleStage = Stage_FinaleActive;
PrintToConsoleAll("[EPI] Finale started and over threshold");
@ -822,10 +818,9 @@ public Action L4D2_OnChangeFinaleStage(int &finaleType, const char[] arg) {
PrintToConsoleAll("[EPI] First tank stage has started");
} else if(g_finaleStage == Stage_FinaleTank1) {
g_finaleStage = Stage_FinaleTank2;
PrintToConsoleAll("[EPI] Second stage started, waiting for tank");
PrintToConsoleAll("[EPI] Second tank stage started. Waiting for tank");
}
}
return Plugin_Continue;
}
void Event_TankSpawn(Event event, const char[] name, bool dontBroadcast) {
@ -962,7 +957,7 @@ void Event_PlayerFirstSpawn(Event event, const char[] name, bool dontBroadcast)
// New client has connected, late on the first chapter or on any other chapter
// If 5 survivors, then set them up, TP them.
if(g_realSurvivorCount > 4) {
CreateTimer(0.1, Timer_SetupNewClient, userid);
CreateTimer(0.2, Timer_SetupNewClient, userid);
}
}
}
@ -990,7 +985,6 @@ void Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast) {
}
}
CreateTimer(0.5, Timer_GiveClientKit, userid);
SDKHook(client, SDKHook_WeaponEquip, Event_Pickup);
}
TryStartHud();
UpdatePlayerInventory(client);
@ -1005,9 +999,10 @@ void Event_PlayerDisconnect(Event event, const char[] name, bool dontBroadcast)
playerData[client].nameCache[0] = '\0';
PrintToServer("debug: Player (index %d, uid %d) now pending empty", client, client, userid);
CreateTimer(cvDropDisconnectTime.FloatValue, Timer_DropSurvivor, client);
if(g_saveTimer[client] != null)
delete g_saveTimer[client];
}
if(g_saveTimer[client] != null)
delete g_saveTimer[client];
}
void Event_PlayerInfo(Event event, const char[] name, bool dontBroadcast) {
@ -1328,7 +1323,7 @@ public void OnMapEnd() {
public void Event_RoundFreezeEnd(Event event, const char[] name, bool dontBroadcast) {
CreateTimer(50.0, Timer_Populate);
}
public Action Timer_Populate(Handle h) {
Action Timer_Populate(Handle h) {
PopulateItems();
return Plugin_Continue;
@ -1368,13 +1363,12 @@ public void EntityOutput_OnStartTouchSaferoom(const char[] output, int caller, i
}
}
public Action Event_RoundEnd(Event event, const char[] name, bool dontBroadcast) {
void Event_RoundEnd(Event event, const char[] name, bool dontBroadcast) {
if(!g_isFailureRound) g_isFailureRound = true;
g_areItemsPopulated = false;
return Plugin_Continue;
}
public Action Event_MapTransition(Event event, const char[] name, bool dontBroadcast) {
void Event_MapTransition(Event event, const char[] name, bool dontBroadcast) {
#if defined DEBUG
PrintToServer("Map transition | %d Extra Kits", g_extraKitsAmount);
#endif
@ -1383,19 +1377,6 @@ public Action Event_MapTransition(Event event, const char[] name, bool dontBroad
// Update g_survivorCount, people may have dipped right before transition
UpdateSurvivorCount();
g_prevPlayerCount = g_survivorCount;
return Plugin_Continue;
}
//TODO: Possibly hacky logic of on third different ent id picked up, in short timespan, detect as set of 4 (pills, kits) & give extra
public Action Event_Pickup(int client, int weapon) {
static char name[32];
GetEntityClassname(weapon, name, sizeof(name));
if(StrEqual(name, "weapon_first_aid_kit", true)) {
if(playerData[client].itemGiven) return Plugin_Continue;
if((L4D_IsInFirstCheckpoint(client) || L4D_IsInLastCheckpoint(client)) && UseExtraKit(client)) {
return Plugin_Handled;
}
}
return Plugin_Continue;
}
public void OnEntityCreated(int entity, const char[] classname) {
@ -1416,7 +1397,7 @@ public void OnEntityCreated(int entity, const char[] classname) {
//TODO: Implement extra kit amount to this
//TODO: Possibly check ammo stash and kit (relv. distance). Would fire on Last Stand 2nd .
public Action Hook_CabinetItemSpawn(int entity) {
Action Hook_CabinetItemSpawn(int entity) {
int cabinet = FindNearestEntityInRange(entity, "prop_health_cabinet", 60.0);
if(cabinet > 0) {
int ci = FindCabinetIndex(cabinet);
@ -1432,11 +1413,10 @@ public Action Hook_CabinetItemSpawn(int entity) {
}
//If Cabinet is full, spawner can not be a part of cabinet and is ignored.
}
return Plugin_Continue;
return Plugin_Handled;
}
public Action Hook_CabinetSpawn(int entity) {
Action Hook_CabinetSpawn(int entity) {
for(int i = 0; i < sizeof(cabinets); i++) {
if(cabinets[i].id == 0) {
cabinets[i].id = entity;
@ -1444,11 +1424,10 @@ public Action Hook_CabinetSpawn(int entity) {
}
}
PrintDebug(DEBUG_SPAWNLOGIC, "Adding cabinet %d", entity);
return Plugin_Continue;
return Plugin_Handled;
}
public Action OnUpgradePackUse(int entity, int activator, int caller, UseType type, float value) {
Action OnUpgradePackUse(int entity, int activator, int caller, UseType type, float value) {
if (entity > 2048 || entity <= MaxClients || !IsValidEntity(entity)) return Plugin_Continue;
int primaryWeapon = GetPlayerWeaponSlot(activator, 0);
@ -1520,18 +1499,7 @@ public Action Hook_Use(int entity, int activator, int caller, UseType type, floa
Prioritize first aid kits somehow? Or split two groups: "utility" (throwables, kits, pill/shots), and "weapon" (all other spawns)
*/
public Action Timer_ResetAmmoPack(Handle h, int entity) {
if(IsValidEntity(entity)) {
int index = g_ammoPacks.FindValue(entity, AMMOPACK_ENTID);
if(index == -1) return Plugin_Continue;
ArrayList clients = g_ammoPacks.Get(index, AMMOPACK_USERS);
clients.Clear();
}
return Plugin_Continue;
}
public Action Timer_OpenSaferoomDoor(Handle h) {
Action Timer_OpenSaferoomDoor(Handle h) {
UnlockDoor(1);
return Plugin_Continue;
}
@ -1770,14 +1738,12 @@ Action Timer_SaveInventory(Handle h, int userid) {
int client = GetClientOfUserId(userid);
if(client > 0) {
// Force save to bypass our timeout
g_saveTimer[client] = null;
SaveInventory(client, true);
}
return Plugin_Stop;
}
void SaveInventory(int client, bool force = false) {
// TODO: dont save during join time
int time = GetTime();
if(!force) {
if(time - playerData[client].joinTime < MIN_JOIN_TIME) return;
@ -1787,6 +1753,8 @@ void SaveInventory(int client, bool force = false) {
if(g_saveTimer[client] != null)
delete g_saveTimer[client];
g_saveTimer[client] = CreateTimer(INV_SAVE_TIME, Timer_SaveInventory, GetClientUserId(client));
} else {
g_saveTimer[client] = null;
}
PlayerInventory inventory;
inventory.timestamp = time;
@ -1803,8 +1771,12 @@ void SaveInventory(int client, bool force = false) {
for(int i = 5; i >= 0; i--) {
weapon = GetPlayerWeaponSlot(client, i);
inventory.itemID[i] = IdentifyWeapon(weapon);
// If slot 1 is melee, get the melee ID
if(i == 1 && inventory.itemID[i] == WEPID_MELEE) {
inventory.meleeID = IdentifyMeleeWeapon(weapon);
}
}
if(inventory.itemID[0] != WEPID_MELEE && inventory.itemID[0] != WEPID_NONE)
if(inventory.itemID[0] != WEPID_NONE)
inventory.lasers = GetEntProp(weapon, Prop_Send, "m_upgradeBitVec") == 4;
GetClientAuthId(client, AuthId_Steam3, buffer, sizeof(buffer));
@ -1827,10 +1799,11 @@ void RestoreInventory(int client, PlayerInventory inventory) {
for(int i = 5; i >= 0; i--) {
WeaponId id = inventory.itemID[i];
if(id != WEPID_NONE) {
if(id == WEPID_MELEE)
if(id == WEPID_MELEE) {
GetWeaponName(id, buffer, sizeof(buffer));
else
GetMeleeWeaponName(id, buffer, sizeof(buffer));
} else {
GetMeleeWeaponName(inventory.meleeID, buffer, sizeof(buffer));
}
weapon = GiveClientWeapon(client, buffer);
}
}
@ -1872,7 +1845,6 @@ bool DoesInventoryDiffer(int client) {
return currentPrimary != storedPrimary || currentSecondary != storedSecondary;
}
// TODO: disable by cvar as well (replace abm_autohard)
bool IsEPIActive() {
return g_epiEnabled;
}
@ -1908,7 +1880,7 @@ void UpdateSurvivorCount() {
}
g_survivorCount = countTotal;
g_realSurvivorCount = countReal;
PrintDebug(DEBUG_GENERIC, "UpdateSurvivorCount: total=%d real=%d active=%d", countTotal, countReal, countActive);
// PrintDebug(DEBUG_GENERIC, "UpdateSurvivorCount: total=%d real=%d active=%d", countTotal, countReal, countActive);
// Temporarily for now use g_realSurvivorCount, as players joining have a brief second where they are 5 players
// 1 = 5+ official

View file

@ -93,6 +93,7 @@ public void OnPluginStart() {
GetEntPropString(entity, Prop_Data, "m_iName", targetName, sizeof(targetName));
if(StrContains(targetName, "l4d2_hats_") == 0) {
createdWalls.Push(EntIndexToEntRef(entity));
SDKHook(entity, SDKHook_Use, OnWallClicked);
}
}

View file

@ -563,6 +563,13 @@ public Action Timer_RemoveGlow(Handle h, int userid) {
return Plugin_Handled;
}
GameState prevState;
// #define HEARTBEAT_NEAR_DIST 1_000_000
// #define HEARTBEAT_CLOSE_DIST 250_000
#define HEARTBEAT_NEAR_DIST 1000
#define HEARTBEAT_CLOSE_DIST 300
public Action Timer_Music(Handle h) {
static float seekerLoc[3];
static float playerLoc[3];
@ -586,13 +593,14 @@ public Action Timer_Music(Handle h) {
for(int i = 1; i <= MaxClients; i++) {
if(IsClientConnected(i) && IsClientInGame(i) && i != currentSeeker) {
playerCount++;
GetClientAbsOrigin(i, playerLoc);
float dist = GetVectorDistance(seekerLoc, playerLoc, true);
if(dist <= 250000.0) {
// GetClientAbsOrigin(i, playerLoc);
// float dist = GetVectorDistance(seekerLoc, playerLoc, true);
float dist = GetFlowDistance(currentSeeker, i);
if(dist <= HEARTBEAT_CLOSE_DIST) {
StopSound(i, SNDCHAN_AUTO, SOUND_SUSPENSE_1);
EmitSoundToClient(i, SOUND_SUSPENSE_1_FAST, i, SNDCHAN_AUTO, SNDLEVEL_NORMAL, SND_CHANGEPITCH, 1.0, 100, currentSeeker, seekerLoc, playerLoc, true);
isNearbyPlaying[i] = true;
} else if(dist <= 1000000.0) {
} else if(dist <= HEARTBEAT_CLOSE_DIST) {
EmitSoundToClient(i, SOUND_SUSPENSE_1, i, SNDCHAN_AUTO, SNDLEVEL_NORMAL, SND_CHANGEPITCH, 0.4, 90, currentSeeker, seekerLoc, playerLoc, true);
isNearbyPlaying[i] = true;
StopSound(i, SNDCHAN_AUTO, SOUND_SUSPENSE_1_FAST);
@ -605,6 +613,12 @@ public Action Timer_Music(Handle h) {
}
return Plugin_Continue;
}
float GetFlowDistance(int survivorA, int survivorB) {
return FloatAbs(L4D2Direct_GetFlowDistance(survivorA) - L4D2Direct_GetFlowDistance(survivorB));
}
public Action Timer_RoundStart(Handle h) {
PrintToServer("[H&S] Running round entity tweaks");
CreateTimer(0.1, Timer_CheckWeapons);