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

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);