mirror of
https://github.com/Jackzmc/sourcemod-plugins.git
synced 2025-05-07 12:43:21 +00:00
Update
This commit is contained in:
parent
ea0bc8c543
commit
28796e46cf
19 changed files with 165 additions and 132 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue