mirror of
https://github.com/Jackzmc/sourcemod-plugins.git
synced 2025-05-06 00:53:21 +00:00
Update director
This commit is contained in:
parent
b0405a36d3
commit
54c35f46e4
2 changed files with 289 additions and 179 deletions
|
@ -5,21 +5,20 @@
|
||||||
#define DIRECTOR_WITCH_CHECK_TIME 30.0 // How often to check if a witch should be spawned
|
#define DIRECTOR_WITCH_CHECK_TIME 30.0 // How often to check if a witch should be spawned
|
||||||
#define DIRECTOR_WITCH_MAX_WITCHES 5 // The maximum amount of extra witches to spawn
|
#define DIRECTOR_WITCH_MAX_WITCHES 5 // The maximum amount of extra witches to spawn
|
||||||
#define DIRECTOR_WITCH_ROLLS 4 // The number of dice rolls, increase if you want to increase freq
|
#define DIRECTOR_WITCH_ROLLS 4 // The number of dice rolls, increase if you want to increase freq
|
||||||
#define DIRECTOR_MIN_SPAWN_TIME 12.0 // Possibly randomized, per-special
|
#define DIRECTOR_MIN_SPAWN_TIME 13.0 // Possibly randomized, per-special
|
||||||
#define DIRECTOR_SPAWN_CHANCE 0.05 // The raw chance of a spawn
|
#define DIRECTOR_SPAWN_CHANCE 0.04 // The raw chance of a spawn
|
||||||
#define DIRECTOR_CHANGE_LIMIT_CHANCE 0.05 // The chance that the maximum amount per-special is changed
|
#define DIRECTOR_CHANGE_LIMIT_CHANCE 0.05 // The chance that the maximum amount per-special is changed
|
||||||
#define DIRECTOR_SPECIAL_TANK_CHANCE 0.05 // The chance that specials can spawn when a tank is active
|
#define DIRECTOR_SPECIAL_TANK_CHANCE 0.05 // The chance that specials can spawn when a tank is active
|
||||||
#define DIRECTOR_STRESS_CUTOFF 0.75 // The minimum chance a random cut off stress value is chosen [this, 1.0]
|
#define DIRECTOR_STRESS_CUTOFF 0.75 // The minimum chance a random cut off stress value is chosen [this, 1.0]
|
||||||
#define DIRECTOR_REST_CHANCE 0.03 // The chance the director ceases spawning
|
#define DIRECTOR_REST_CHANCE 0.04 // The chance the director ceases spawning
|
||||||
#define DIRECTOR_REST_MAX_COUNT 10 // The maximum amount of rest given (this * DIRECTOR_TIMER_INTERVAL)
|
#define DIRECTOR_REST_MAX_COUNT 8 // The maximum amount of rest given (this * DIRECTOR_TIMER_INTERVAL)
|
||||||
|
|
||||||
#define DIRECTOR_DEBUG_SPAWN 1 // Dont actually spawn
|
#define DIRECTOR_DEBUG_SPAWN 1 // Dont actually spawn
|
||||||
|
|
||||||
/// DEFINITIONS
|
/// DEFINITIONS
|
||||||
#define NUM_SPECIALS 6
|
#define NUM_SPECIALS 6
|
||||||
#define TOTAL_NUM_SPECIALS 8
|
#define TOTAL_NUM_SPECIALS 8
|
||||||
char SPECIAL_IDS[TOTAL_NUM_SPECIALS+1][] = {
|
char SPECIAL_IDS[TOTAL_NUM_SPECIALS][] = {
|
||||||
"invalid",
|
|
||||||
"smoker",
|
"smoker",
|
||||||
"boomer",
|
"boomer",
|
||||||
"hunter",
|
"hunter",
|
||||||
|
@ -30,14 +29,14 @@ char SPECIAL_IDS[TOTAL_NUM_SPECIALS+1][] = {
|
||||||
"tank"
|
"tank"
|
||||||
};
|
};
|
||||||
enum specialType {
|
enum specialType {
|
||||||
Special_Smoker = 1,
|
Special_Smoker = 0,
|
||||||
Special_Boomer = 2,
|
Special_Boomer = 1,
|
||||||
Special_Hunter = 3,
|
Special_Hunter = 2,
|
||||||
Special_Spitter = 4,
|
Special_Spitter = 3,
|
||||||
Special_Jockey = 5,
|
Special_Jockey = 4,
|
||||||
Special_Charger = 6,
|
Special_Charger = 5,
|
||||||
Special_Witch = 7,
|
Special_Witch = 6,
|
||||||
Special_Tank = 8,
|
Special_Tank = 7,
|
||||||
};
|
};
|
||||||
enum directorState {
|
enum directorState {
|
||||||
DState_Normal,
|
DState_Normal,
|
||||||
|
@ -84,7 +83,7 @@ void Director_OnMapStart() {
|
||||||
InitExtraWitches();
|
InitExtraWitches();
|
||||||
}
|
}
|
||||||
float time = GetGameTime();
|
float time = GetGameTime();
|
||||||
for(int i = 1; i <= TOTAL_NUM_SPECIALS; i++) {
|
for(int i = 0; i < TOTAL_NUM_SPECIALS; i++) {
|
||||||
g_lastSpawnTime[i] = time;
|
g_lastSpawnTime[i] = time;
|
||||||
g_spawnLimit[i] = 1;
|
g_spawnLimit[i] = 1;
|
||||||
g_spawnCount[i] = 0;
|
g_spawnCount[i] = 0;
|
||||||
|
@ -123,10 +122,18 @@ void Director_CheckClient(int client) {
|
||||||
if(IsClientConnected(client) && GetClientTeam(client) == 3) {
|
if(IsClientConnected(client) && GetClientTeam(client) == 3) {
|
||||||
// To bypass director limits many plugins spawn an infected "bot" that immediately gets kicked, which allows a window to spawn a special
|
// To bypass director limits many plugins spawn an infected "bot" that immediately gets kicked, which allows a window to spawn a special
|
||||||
// The fake bot's class is usually 9, an invalid
|
// The fake bot's class is usually 9, an invalid
|
||||||
int class = GetEntProp(client, Prop_Send, "m_zombieClass");
|
int class = GetEntProp(client, Prop_Send, "m_zombieClass") - 1;
|
||||||
if(class > view_as<int>(Special_Tank)) {
|
if(class > view_as<int>(Special_Tank)) {
|
||||||
return;
|
return;
|
||||||
|
} else if(IsFakeClient(client)) {
|
||||||
|
// Sometimes the bot class is _not_ invalid, but usually has BOT in its name. Ignore those players.
|
||||||
|
char name[32];
|
||||||
|
GetClientName(client, name, sizeof(name));
|
||||||
|
if(StrContains(name, "bot", false) != -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(IsFakeClient(client) && class == view_as<int>(Special_Tank)) {
|
if(IsFakeClient(client) && class == view_as<int>(Special_Tank)) {
|
||||||
OnTankBotSpawn(client);
|
OnTankBotSpawn(client);
|
||||||
}
|
}
|
||||||
|
@ -143,6 +150,8 @@ void Director_CheckClient(int client) {
|
||||||
static int g_newTankHealth = 0;
|
static int g_newTankHealth = 0;
|
||||||
void OnTankBotSpawn(int client) {
|
void OnTankBotSpawn(int client) {
|
||||||
if(!IsEPIActive() || !(cvEPISpecialSpawning.IntValue & 4)) return;
|
if(!IsEPIActive() || !(cvEPISpecialSpawning.IntValue & 4)) return;
|
||||||
|
// Only run on 6+ survivors
|
||||||
|
if(g_realSurvivorCount < 6) return;
|
||||||
if(g_finaleStage == Stage_FinaleTank2) {
|
if(g_finaleStage == Stage_FinaleTank2) {
|
||||||
if(hExtraFinaleTank.IntValue > 0 && g_extraFinaleTankEnabled) {
|
if(hExtraFinaleTank.IntValue > 0 && g_extraFinaleTankEnabled) {
|
||||||
float duration = GetRandomFloat(EXTRA_TANK_MIN_SEC, EXTRA_TANK_MAX_SEC);
|
float duration = GetRandomFloat(EXTRA_TANK_MIN_SEC, EXTRA_TANK_MAX_SEC);
|
||||||
|
@ -151,7 +160,7 @@ void OnTankBotSpawn(int client) {
|
||||||
}
|
}
|
||||||
} else if(g_newTankHealth > 0) {
|
} else if(g_newTankHealth > 0) {
|
||||||
// A split tank has spawned, set its health
|
// A split tank has spawned, set its health
|
||||||
PrintDebug(DEBUG_SPAWNLOGIC, "OnTankBotSpawn: split tank spawned, setting health", g_newTankHealth);
|
PrintDebug(DEBUG_SPAWNLOGIC, "OnTankBotSpawn: split tank spawned, setting health to %d", g_newTankHealth);
|
||||||
SetEntProp(client, Prop_Send, "m_iHealth", g_newTankHealth);
|
SetEntProp(client, Prop_Send, "m_iHealth", g_newTankHealth);
|
||||||
g_newTankHealth = 0;
|
g_newTankHealth = 0;
|
||||||
} else {
|
} else {
|
||||||
|
@ -159,15 +168,15 @@ void OnTankBotSpawn(int client) {
|
||||||
int health = GetEntProp(client, Prop_Send, "m_iHealth");
|
int health = GetEntProp(client, Prop_Send, "m_iHealth");
|
||||||
float additionalHealth = float(g_survivorCount - 4) * cvEPITankHealth.FloatValue;
|
float additionalHealth = float(g_survivorCount - 4) * cvEPITankHealth.FloatValue;
|
||||||
health += RoundFloat(additionalHealth);
|
health += RoundFloat(additionalHealth);
|
||||||
PrintDebug(DEBUG_SPAWNLOGIC, "OnTankBotSpawn: Setting tank health to %d", health);
|
|
||||||
|
|
||||||
if(hExtraFinaleTank.IntValue & 1 && GetURandomFloat() <= hSplitTankChance.FloatValue) {
|
if(hExtraFinaleTank.IntValue & 1 && GetURandomFloat() <= hSplitTankChance.FloatValue) {
|
||||||
float duration = GetRandomFloat(EXTRA_TANK_MIN_SEC, EXTRA_TANK_MAX_SEC);
|
float duration = GetRandomFloat(EXTRA_TANK_MIN_SEC, EXTRA_TANK_MAX_SEC);
|
||||||
int splitHealth = health / 2;
|
int splitHealth = health / 2;
|
||||||
PrintDebug(DEBUG_SPAWNLOGIC, "OnTankBotSpawn: split tank in %.1fs, health=%d", duration, g_newTankHealth);
|
PrintDebug(DEBUG_SPAWNLOGIC, "OnTankBotSpawn: split tank in %.1fs, health=%d", duration, splitHealth);
|
||||||
CreateTimer(duration, Timer_SpawnSplitTank, splitHealth);
|
CreateTimer(duration, Timer_SpawnSplitTank, splitHealth);
|
||||||
SetEntProp(client, Prop_Send, "m_iHealth", splitHealth);
|
SetEntProp(client, Prop_Send, "m_iHealth", splitHealth);
|
||||||
} else {
|
} else {
|
||||||
|
PrintDebug(DEBUG_SPAWNLOGIC, "OnTankBotSpawn: Setting tank health to %d", health);
|
||||||
SetEntProp(client, Prop_Send, "m_iHealth", health);
|
SetEntProp(client, Prop_Send, "m_iHealth", health);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -184,7 +193,7 @@ void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast) {
|
||||||
if(client > 0) {
|
if(client > 0) {
|
||||||
int team = GetClientTeam(client);
|
int team = GetClientTeam(client);
|
||||||
if(team == 3) {
|
if(team == 3) {
|
||||||
int class = GetEntProp(client, Prop_Send, "m_zombieClass");
|
int class = GetEntProp(client, Prop_Send, "m_zombieClass") - 1;
|
||||||
if(class > view_as<int>(Special_Tank)) return;
|
if(class > view_as<int>(Special_Tank)) return;
|
||||||
g_spawnCount[class]--;
|
g_spawnCount[class]--;
|
||||||
if(g_spawnCount[class] < 0) {
|
if(g_spawnCount[class] < 0) {
|
||||||
|
@ -208,6 +217,17 @@ void Event_PlayerIncapped(Event event, const char[] name, bool dontBroadcast) {
|
||||||
}
|
}
|
||||||
/// METHODS
|
/// METHODS
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Extra Witch Algo:
|
||||||
|
On map start, knowing # of total players, compute a random number of witches.
|
||||||
|
The random number calculated by DiceRoll with 2 rolls and biased to the left. [min, 6]
|
||||||
|
The minimum number in the dice is shifted to the right by the # of players (abmExtraCount-4)/4 (1 extra=0, 10 extra=2)
|
||||||
|
|
||||||
|
Then, with the # of witches, as N, calculate N different flow values between [0, L4D2Direct_GetMapMaxFlowDistance()]
|
||||||
|
Timer_Director then checks if highest flow achieved (never decreases) is >= each flow value, if one found, a witch is spawned
|
||||||
|
(the witch herself is not spawned at the flow, just her spawning is triggered)
|
||||||
|
*/
|
||||||
void InitExtraWitches() {
|
void InitExtraWitches() {
|
||||||
float flowMax = L4D2Direct_GetMapMaxFlowDistance() - FLOW_CUTOFF;
|
float flowMax = L4D2Direct_GetMapMaxFlowDistance() - FLOW_CUTOFF;
|
||||||
// Just in case we don't have max flow or the map is extremely tiny, don't run:
|
// Just in case we don't have max flow or the map is extremely tiny, don't run:
|
||||||
|
@ -220,9 +240,10 @@ void InitExtraWitches() {
|
||||||
// TODO: max based on count
|
// TODO: max based on count
|
||||||
int max = RoundToFloor(float(count) / 4.0);
|
int max = RoundToFloor(float(count) / 4.0);
|
||||||
|
|
||||||
|
// TODO: inc chance based on map max flow
|
||||||
g_extraWitchCount = DiceRoll(min, DIRECTOR_WITCH_MAX_WITCHES, DIRECTOR_WITCH_ROLLS, BIAS_LEFT);
|
g_extraWitchCount = DiceRoll(min, DIRECTOR_WITCH_MAX_WITCHES, DIRECTOR_WITCH_ROLLS, BIAS_LEFT);
|
||||||
PrintDebug(DEBUG_SPAWNLOGIC, "InitExtraWitches: %d witches (min=%d, max=%d, rolls=%d) checkInterval=%f", g_extraWitchCount, min, max, DIRECTOR_WITCH_ROLLS, DIRECTOR_WITCH_CHECK_TIME);
|
PrintDebug(DEBUG_SPAWNLOGIC, "InitExtraWitches: %d witches (min=%d, max=%d, rolls=%d) checkInterval=%f", g_extraWitchCount, min, max, DIRECTOR_WITCH_ROLLS, DIRECTOR_WITCH_CHECK_TIME);
|
||||||
for(int i = 0; i <= g_extraWitchCount; i++) {
|
for(int i = 0; i < g_extraWitchCount; i++) {
|
||||||
g_extraWitchFlowPositions[i] = GetURandomFloat() * (flowMax-FLOW_CUTOFF) + FLOW_CUTOFF;
|
g_extraWitchFlowPositions[i] = GetURandomFloat() * (flowMax-FLOW_CUTOFF) + FLOW_CUTOFF;
|
||||||
PrintDebug(DEBUG_SPAWNLOGIC, "Witch position #%d: flow %.2f (%.0f%%)", i, g_extraWitchFlowPositions[i], g_extraWitchFlowPositions[i] / flowMax);
|
PrintDebug(DEBUG_SPAWNLOGIC, "Witch position #%d: flow %.2f (%.0f%%)", i, g_extraWitchFlowPositions[i], g_extraWitchFlowPositions[i] / flowMax);
|
||||||
}
|
}
|
||||||
|
@ -244,17 +265,17 @@ void Director_PrintDebug(int client) {
|
||||||
char buffer[128];
|
char buffer[128];
|
||||||
float time = GetGameTime();
|
float time = GetGameTime();
|
||||||
PrintToConsole(client, "Last Spawn Deltas: (%.1f s) (min %f)", time - g_lastSpecialSpawnTime, DIRECTOR_MIN_SPAWN_TIME);
|
PrintToConsole(client, "Last Spawn Deltas: (%.1f s) (min %f)", time - g_lastSpecialSpawnTime, DIRECTOR_MIN_SPAWN_TIME);
|
||||||
for(int i = 1; i <= TOTAL_NUM_SPECIALS; i++) {
|
for(int i = 0; i < TOTAL_NUM_SPECIALS; i++) {
|
||||||
Format(buffer, sizeof(buffer), "%s %s=%.1f", buffer, SPECIAL_IDS[i], time-g_lastSpawnTime[i]);
|
Format(buffer, sizeof(buffer), "%s %s=%.1f", buffer, SPECIAL_IDS[i], time-g_lastSpawnTime[i]);
|
||||||
}
|
}
|
||||||
PrintToConsole(client, "\t%s", buffer);
|
PrintToConsole(client, "\t%s", buffer);
|
||||||
buffer[0] = '\0';
|
buffer[0] = '\0';
|
||||||
PrintToConsole(client, "Spawn Counts: (%d/%d)", g_infectedCount, g_survivorCount - 4);
|
PrintToConsole(client, "Spawn Counts: (%d/%d)", g_infectedCount, g_survivorCount - 4);
|
||||||
for(int i = 1; i <= TOTAL_NUM_SPECIALS; i++) {
|
for(int i = 0; i < TOTAL_NUM_SPECIALS; i++) {
|
||||||
Format(buffer, sizeof(buffer), "%s %s=%d/%d", buffer, SPECIAL_IDS[i], g_spawnCount[i], g_spawnLimit[i]);
|
Format(buffer, sizeof(buffer), "%s %s=%d/%d", buffer, SPECIAL_IDS[i], g_spawnCount[i], g_spawnLimit[i]);
|
||||||
}
|
}
|
||||||
PrintToConsole(client, "\t%s", buffer);
|
PrintToConsole(client, "\t%s", buffer);
|
||||||
PrintToConsole(client, "timer interval=%.0f, rest count=%d", DIRECTOR_TIMER_INTERVAL, g_restCount);
|
PrintToConsole(client, "timer interval=%.0f, rest count=%d, rest time left=%.0fs", DIRECTOR_TIMER_INTERVAL, g_restCount, float(g_restCount) * DIRECTOR_TIMER_INTERVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Director_RandomizeLimits() {
|
void Director_RandomizeLimits() {
|
||||||
|
@ -268,7 +289,7 @@ void Director_RandomizeLimits() {
|
||||||
void Director_RandomizeThings() {
|
void Director_RandomizeThings() {
|
||||||
g_maxStressIntensity = GetRandomFloat(DIRECTOR_STRESS_CUTOFF, 1.0);
|
g_maxStressIntensity = GetRandomFloat(DIRECTOR_STRESS_CUTOFF, 1.0);
|
||||||
g_minFlowSpawn = GetRandomFloat(FLOW_CUTOFF, FLOW_CUTOFF * 2);
|
g_minFlowSpawn = GetRandomFloat(FLOW_CUTOFF, FLOW_CUTOFF * 2);
|
||||||
|
Director_RandomizeLimits();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Director_ShouldRest() {
|
bool Director_ShouldRest() {
|
||||||
|
@ -291,14 +312,14 @@ void TryGrantRest() {
|
||||||
// Little hacky, need to track when one leaves instead
|
// Little hacky, need to track when one leaves instead
|
||||||
void Director_CheckSpawnCounts() {
|
void Director_CheckSpawnCounts() {
|
||||||
if(!IsEPIActive()) return;
|
if(!IsEPIActive()) return;
|
||||||
for(int i = 1; i <= TOTAL_NUM_SPECIALS; i++) {
|
for(int i = 0; i < TOTAL_NUM_SPECIALS; i++) {
|
||||||
g_spawnCount[i] = 0;
|
g_spawnCount[i] = 0;
|
||||||
}
|
}
|
||||||
g_infectedCount = 0;
|
g_infectedCount = 0;
|
||||||
for(int i = 1; i <= MaxClients; i++) {
|
for(int i = 1; i <= MaxClients; i++) {
|
||||||
if(IsClientInGame(i) && GetClientTeam(i) == 3) {
|
if(IsClientInGame(i) && GetClientTeam(i) == 3) {
|
||||||
int class = GetEntProp(i, Prop_Send, "m_zombieClass") - 1; // make it 0-based
|
int class = GetEntProp(i, Prop_Send, "m_zombieClass") - 1; // make it 0-based
|
||||||
if(class == 8) continue;
|
if(class > view_as<int>(Special_Tank)) continue;
|
||||||
g_spawnCount[class]++;
|
g_spawnCount[class]++;
|
||||||
g_infectedCount++;
|
g_infectedCount++;
|
||||||
}
|
}
|
||||||
|
@ -329,7 +350,13 @@ directorState Director_Think() {
|
||||||
// TODO: scaling chance, low chance when hitting g_infectedCount, higher on 0
|
// TODO: scaling chance, low chance when hitting g_infectedCount, higher on 0
|
||||||
if(g_highestFlowAchieved < g_minFlowSpawn || ~cvEPISpecialSpawning.IntValue & 1) return DState_PendingMinFlowOrDisabled;
|
if(g_highestFlowAchieved < g_minFlowSpawn || ~cvEPISpecialSpawning.IntValue & 1) return DState_PendingMinFlowOrDisabled;
|
||||||
|
|
||||||
|
// Check if a rest period is given
|
||||||
|
if(Director_ShouldRest()) {
|
||||||
|
return DState_Resting;
|
||||||
|
}
|
||||||
|
|
||||||
// Only spawn more than one special within 2s at 10%
|
// Only spawn more than one special within 2s at 10%
|
||||||
|
// TODO: randomized time between spawns? 0, ?? instead of repeat timer?
|
||||||
if(time - g_lastSpecialSpawnTime < 2.0 && GetURandomFloat() > 0.5) return DState_MaxSpecialTime;
|
if(time - g_lastSpecialSpawnTime < 2.0 && GetURandomFloat() > 0.5) return DState_MaxSpecialTime;
|
||||||
|
|
||||||
if(GetURandomFloat() < DIRECTOR_CHANGE_LIMIT_CHANCE) {
|
if(GetURandomFloat() < DIRECTOR_CHANGE_LIMIT_CHANCE) {
|
||||||
|
@ -340,15 +367,12 @@ directorState Director_Think() {
|
||||||
// abmExtraCount=6 g_infectedCount=0 chance=1.0 ((abmExtraCount-g_infectedCount)/abmExtraCount)
|
// abmExtraCount=6 g_infectedCount=0 chance=1.0 ((abmExtraCount-g_infectedCount)/abmExtraCount)
|
||||||
// abmExtraCount=6 g_infectedCount=1 chance=0.9 ((6-1)/6)) = (5/6)
|
// abmExtraCount=6 g_infectedCount=1 chance=0.9 ((6-1)/6)) = (5/6)
|
||||||
// abmExtraCount=6 g_infectedCount=6 chance=0.2
|
// abmExtraCount=6 g_infectedCount=6 chance=0.2
|
||||||
|
// TODO: in debug calculate this
|
||||||
float eCount = float(g_survivorCount - 3);
|
float eCount = float(g_survivorCount - 3);
|
||||||
float chance = (eCount - float(g_infectedCount)) / eCount;
|
float chance = (eCount - float(g_infectedCount)) / eCount;
|
||||||
// TODO: verify (abmExtraCount-4)
|
// TODO: verify (abmExtraCount-4)
|
||||||
if(GetURandomFloat() > chance) return DState_PlayerChance;
|
if(GetURandomFloat() > chance) return DState_PlayerChance;
|
||||||
|
|
||||||
// Check if a rest period is given
|
|
||||||
if(Director_ShouldRest()) {
|
|
||||||
return DState_Resting;
|
|
||||||
}
|
|
||||||
|
|
||||||
float curAvgStress = L4D_GetAvgSurvivorIntensity();
|
float curAvgStress = L4D_GetAvgSurvivorIntensity();
|
||||||
// Don't spawn specials when tanks active, but have a small chance (DIRECTOR_SPECIAL_TANK_CHANCE) to bypass
|
// Don't spawn specials when tanks active, but have a small chance (DIRECTOR_SPECIAL_TANK_CHANCE) to bypass
|
||||||
|
@ -409,12 +433,14 @@ void DirectorSpawn(specialType special, int player = -1) {
|
||||||
CreateTimer(0.1, Timer_Kick, bot);
|
CreateTimer(0.1, Timer_Kick, bot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: dont use z_spawn_old, spawns too close!!
|
|
||||||
float pos[3];
|
float pos[3];
|
||||||
if(L4D_GetRandomPZSpawnPosition(player, view_as<int>(special), 10, pos)) {
|
if(L4D_GetRandomPZSpawnPosition(player, view_as<int>(special) + 1, 10, pos)) {
|
||||||
// They use 1-index
|
// They use 1-index
|
||||||
L4D2_SpawnSpecial(view_as<int>(special) + 1, pos, NULL_VECTOR);
|
if(special == Special_Tank) {
|
||||||
g_lastSpawnTime[view_as<int>(special)] = GetGameTime();
|
L4D2_SpawnTank(pos, NULL_VECTOR);
|
||||||
|
} else {
|
||||||
|
L4D2_SpawnSpecial(view_as<int>(special) + 1, pos, NULL_VECTOR);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,10 +23,13 @@
|
||||||
#define DEBUG_SPAWNLOGIC 2
|
#define DEBUG_SPAWNLOGIC 2
|
||||||
#define DEBUG_ANY 3
|
#define DEBUG_ANY 3
|
||||||
|
|
||||||
|
#define INV_SAVE_TIME 5.0 // How long after a save request do we actually save. Seconds.
|
||||||
|
#define MIN_JOIN_TIME 30 // The minimum amount of time after player joins where we can start saving
|
||||||
|
|
||||||
//Set the debug level
|
//Set the debug level
|
||||||
#define DEBUG_LEVEL DEBUG_ANY
|
#define DEBUG_LEVEL DEBUG_SPAWNLOGIC
|
||||||
#define EXTRA_PLAYER_HUD_UPDATE_INTERVAL 0.8
|
#define EXTRA_PLAYER_HUD_UPDATE_INTERVAL 0.8
|
||||||
//Sets abmExtraCount to this value if set
|
//Sets g_survivorCount to this value if set
|
||||||
// #define DEBUG_FORCE_PLAYERS 7
|
// #define DEBUG_FORCE_PLAYERS 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 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]
|
||||||
|
@ -80,8 +83,10 @@ public Plugin myinfo =
|
||||||
url = "https://github.com/Jackzmc/sourcemod-plugins"
|
url = "https://github.com/Jackzmc/sourcemod-plugins"
|
||||||
};
|
};
|
||||||
|
|
||||||
ConVar hExtraItemBasePercentage, hAddExtraKits, hMinPlayers, hUpdateMinPlayers, hMinPlayersSaferoomDoor, hSaferoomDoorWaitSeconds, hSaferoomDoorAutoOpen, hEPIHudState, hExtraFinaleTank, cvDropDisconnectTime, hSplitTankChance, cvFFDecreaseRate, cvZDifficulty, cvEPIHudFlags, cvEPISpecialSpawning, cvEPIGamemodes, hGamemode, cvEPITankHealth;
|
ConVar hExtraItemBasePercentage, hAddExtraKits, hMinPlayers, hUpdateMinPlayers, hMinPlayersSaferoomDoor, hSaferoomDoorWaitSeconds, hSaferoomDoorAutoOpen, hEPIHudState, hExtraFinaleTank, cvDropDisconnectTime, hSplitTankChance, cvFFDecreaseRate, cvZDifficulty, cvEPIHudFlags, cvEPISpecialSpawning, cvEPIGamemodes, hGamemode, cvEPITankHealth, cvEPIEnabledMode;
|
||||||
|
ConVar g_ffFactorCvar;
|
||||||
int g_extraKitsAmount, g_extraKitsStart, g_saferoomDoorEnt, g_prevPlayerCount;
|
int g_extraKitsAmount, g_extraKitsStart, g_saferoomDoorEnt, g_prevPlayerCount;
|
||||||
|
bool g_forcedSurvivorCount;
|
||||||
static int g_currentChapter;
|
static int g_currentChapter;
|
||||||
bool g_isCheckpointReached, g_isLateLoaded, g_startCampaignGiven, g_isFailureRound, g_areItemsPopulated;
|
bool g_isCheckpointReached, g_isLateLoaded, g_startCampaignGiven, g_isFailureRound, g_areItemsPopulated;
|
||||||
static ArrayList g_ammoPacks;
|
static ArrayList g_ammoPacks;
|
||||||
|
@ -93,6 +98,7 @@ static bool g_isGamemodeAllowed;
|
||||||
int g_survivorCount, g_realSurvivorCount;
|
int g_survivorCount, g_realSurvivorCount;
|
||||||
bool g_isFinaleEnding;
|
bool g_isFinaleEnding;
|
||||||
static bool g_epiEnabled;
|
static bool g_epiEnabled;
|
||||||
|
bool g_isOfficialMap;
|
||||||
|
|
||||||
bool g_isSpeaking[MAXPLAYERS+1];
|
bool g_isSpeaking[MAXPLAYERS+1];
|
||||||
|
|
||||||
|
@ -114,9 +120,10 @@ enum State {
|
||||||
State_Active
|
State_Active
|
||||||
}
|
}
|
||||||
#if defined DEBUG_LEVEL
|
#if defined DEBUG_LEVEL
|
||||||
char StateNames[3][] = {
|
char StateNames[4][] = {
|
||||||
"Empty",
|
"Empty",
|
||||||
"PendingEmpty",
|
"PendingEmpty",
|
||||||
|
"Pending",
|
||||||
"Active"
|
"Active"
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
@ -140,6 +147,7 @@ enum struct PlayerData {
|
||||||
bool isUnderAttack; //Is the player under attack (by any special)
|
bool isUnderAttack; //Is the player under attack (by any special)
|
||||||
State state;
|
State state;
|
||||||
bool hasJoined;
|
bool hasJoined;
|
||||||
|
int joinTime;
|
||||||
|
|
||||||
char nameCache[64];
|
char nameCache[64];
|
||||||
int scrollIndex;
|
int scrollIndex;
|
||||||
|
@ -216,7 +224,8 @@ Restore from saved inventory
|
||||||
|
|
||||||
static StringMap weaponMaxClipSizes;
|
static StringMap weaponMaxClipSizes;
|
||||||
static StringMap pInv;
|
static StringMap pInv;
|
||||||
|
static int g_lastInvSave[MAXPLAYERS+1];
|
||||||
|
static Handle g_saveTimer[MAXPLAYERS+1];
|
||||||
|
|
||||||
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;";
|
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;";
|
||||||
|
|
||||||
|
@ -304,6 +313,8 @@ public void OnPluginStart() {
|
||||||
|
|
||||||
HookEvent("witch_spawn", Event_WitchSpawn);
|
HookEvent("witch_spawn", Event_WitchSpawn);
|
||||||
HookEvent("finale_vehicle_incoming", Event_FinaleVehicleIncoming);
|
HookEvent("finale_vehicle_incoming", Event_FinaleVehicleIncoming);
|
||||||
|
HookEvent("player_bot_replace", Event_PlayerToIdle);
|
||||||
|
HookEvent("item_pickup", Event_ItemPickup);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -322,6 +333,7 @@ public void OnPluginStart() {
|
||||||
cvEPISpecialSpawning = CreateConVar("epi_sp_spawning", "2", "Determines what specials are spawned. Add bits together.\n1 = Normal specials\n2 = Witches\n4 = Tanks", FCVAR_NONE, true, 0.0);
|
cvEPISpecialSpawning = CreateConVar("epi_sp_spawning", "2", "Determines what specials are spawned. Add bits together.\n1 = Normal specials\n2 = Witches\n4 = Tanks", FCVAR_NONE, true, 0.0);
|
||||||
cvEPITankHealth = CreateConVar("epi_tank_chunkhp", "2500", "The amount of health added to tank, for each extra player", FCVAR_NONE, true, 0.0);
|
cvEPITankHealth = CreateConVar("epi_tank_chunkhp", "2500", "The amount of health added to tank, for each extra player", FCVAR_NONE, true, 0.0);
|
||||||
cvEPIGamemodes = CreateConVar("epi_gamemodes", "coop,realism,versus", "Gamemodes where plugin is active. Comma-separated", FCVAR_NONE);
|
cvEPIGamemodes = CreateConVar("epi_gamemodes", "coop,realism,versus", "Gamemodes where plugin is active. Comma-separated", FCVAR_NONE);
|
||||||
|
cvEPIEnabledMode = CreateConVar("epi_enabled", "1", "Is EPI 5+ spawning (if epi_sp_spawning enabled as well) enabled?\n0=OFF\n1=Auto (Official Maps Only)(5+)\n2=Auto (Any map) (5+)\n3=Forced on", FCVAR_NONE, true, 0.0, true, 3.0);
|
||||||
// TODO: hook flags, reset name index / ping mode
|
// TODO: hook flags, reset name index / ping mode
|
||||||
cvEPIHudFlags.AddChangeHook(Cvar_HudStateChange);
|
cvEPIHudFlags.AddChangeHook(Cvar_HudStateChange);
|
||||||
cvEPISpecialSpawning.AddChangeHook(Cvar_SpecialSpawningChange);
|
cvEPISpecialSpawning.AddChangeHook(Cvar_SpecialSpawningChange);
|
||||||
|
@ -331,20 +343,6 @@ public void OnPluginStart() {
|
||||||
if(hMinPlayers != null) PrintDebug(DEBUG_INFO, "Found convar abm_minplayers");
|
if(hMinPlayers != null) PrintDebug(DEBUG_INFO, "Found convar abm_minplayers");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(g_isLateLoaded) {
|
|
||||||
for(int i = 1; i <= MaxClients; i++) {
|
|
||||||
if(IsClientConnected(i) && IsClientInGame(i)) {
|
|
||||||
if(GetClientTeam(i) == 2) {
|
|
||||||
SaveInventory(i);
|
|
||||||
SDKHook(i, SDKHook_WeaponEquip, Event_Pickup);
|
|
||||||
}
|
|
||||||
playerData[i].Setup(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UpdateSurvivorCount();
|
|
||||||
TryStartHud();
|
|
||||||
}
|
|
||||||
|
|
||||||
char buffer[16];
|
char buffer[16];
|
||||||
cvZDifficulty = FindConVar("z_difficulty");
|
cvZDifficulty = FindConVar("z_difficulty");
|
||||||
cvZDifficulty.GetString(buffer, sizeof(buffer));
|
cvZDifficulty.GetString(buffer, sizeof(buffer));
|
||||||
|
@ -356,6 +354,20 @@ public void OnPluginStart() {
|
||||||
hGamemode.AddChangeHook(Event_GamemodeChange);
|
hGamemode.AddChangeHook(Event_GamemodeChange);
|
||||||
Event_GamemodeChange(hGamemode, g_currentGamemode, g_currentGamemode);
|
Event_GamemodeChange(hGamemode, g_currentGamemode, g_currentGamemode);
|
||||||
|
|
||||||
|
|
||||||
|
if(g_isLateLoaded) {
|
||||||
|
for(int i = 1; i <= MaxClients; i++) {
|
||||||
|
if(IsClientConnected(i) && IsClientInGame(i)) {
|
||||||
|
if(GetClientTeam(i) == 2) {
|
||||||
|
SaveInventory(i, true);
|
||||||
|
SDKHook(i, SDKHook_WeaponEquip, Event_Pickup);
|
||||||
|
}
|
||||||
|
playerData[i].Setup(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TryStartHud();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
AutoExecConfig(true, "l4d2_extraplayeritems");
|
AutoExecConfig(true, "l4d2_extraplayeritems");
|
||||||
|
|
||||||
|
@ -409,8 +421,12 @@ public void OnClientPutInServer(int client) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnClientDisconnect(int client) {
|
public void OnClientDisconnect(int client) {
|
||||||
if(!IsFakeClient(client) && IsClientInGame(client))
|
// For when bots disconnect in saferoom transitions, empty:
|
||||||
SaveInventory(client);
|
if(playerData[client].state == State_PendingEmpty)
|
||||||
|
playerData[client].state = State_Empty;
|
||||||
|
|
||||||
|
if(!IsFakeClient(client) && IsClientInGame(client) && GetClientTeam(client) == 2)
|
||||||
|
SaveInventory(client, true);
|
||||||
g_isSpeaking[client] = false;
|
g_isSpeaking[client] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -516,7 +532,8 @@ public void Event_DifficultyChange(ConVar cvar, const char[] oldValue, const cha
|
||||||
} else if(StrEqual(newValue, "impossible", false)) {
|
} else if(StrEqual(newValue, "impossible", false)) {
|
||||||
zDifficulty = Difficulty_Expert;
|
zDifficulty = Difficulty_Expert;
|
||||||
}
|
}
|
||||||
// Unknown difficulty, silently ignore
|
g_ffFactorCvar = GetActiveFriendlyFireFactor();
|
||||||
|
SetFFFactor(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////
|
/////////////////////////////////////
|
||||||
|
@ -550,11 +567,17 @@ Action Command_EpiVal(int client, int args) {
|
||||||
if(args == 0) {
|
if(args == 0) {
|
||||||
PrintToConsole(client, "epiEnabled = %b", g_epiEnabled);
|
PrintToConsole(client, "epiEnabled = %b", g_epiEnabled);
|
||||||
PrintToConsole(client, "isGamemodeAllowed = %b", g_isGamemodeAllowed);
|
PrintToConsole(client, "isGamemodeAllowed = %b", g_isGamemodeAllowed);
|
||||||
|
PrintToConsole(client, "isOfficialMap = %b", g_isOfficialMap);
|
||||||
PrintToConsole(client, "extraKitsAmount = %d", g_extraKitsAmount);
|
PrintToConsole(client, "extraKitsAmount = %d", g_extraKitsAmount);
|
||||||
PrintToConsole(client, "extraKitsStart = %d", g_extraKitsStart);
|
PrintToConsole(client, "extraKitsStart = %d", g_extraKitsStart);
|
||||||
PrintToConsole(client, "currentChapter = %d", g_currentChapter);
|
PrintToConsole(client, "currentChapter = %d", g_currentChapter);
|
||||||
PrintToConsole(client, "extraWitchCount = %d", g_extraWitchCount);
|
PrintToConsole(client, "extraWitchCount = %d", g_extraWitchCount);
|
||||||
|
PrintToConsole(client, "forcedSurvivorCount = %b", g_forcedSurvivorCount);
|
||||||
|
PrintToConsole(client, "survivorCount = %d %s", g_survivorCount, g_forcedSurvivorCount ? "(forced)" : "");
|
||||||
|
PrintToConsole(client, "realSurvivorCount = %d", g_realSurvivorCount);
|
||||||
PrintToConsole(client, "restCount = %d", g_restCount);
|
PrintToConsole(client, "restCount = %d", g_restCount);
|
||||||
|
PrintToConsole(client, "extraFinaleTankEnabled = %b", g_extraFinaleTankEnabled);
|
||||||
|
ReplyToCommand(client, "Values printed to console");
|
||||||
return Plugin_Handled;
|
return Plugin_Handled;
|
||||||
}
|
}
|
||||||
char arg[32], value[32];
|
char arg[32], value[32];
|
||||||
|
@ -574,6 +597,14 @@ Action Command_EpiVal(int client, int args) {
|
||||||
ValInt(client, "g_extraWitchCount", g_extraWitchCount, value);
|
ValInt(client, "g_extraWitchCount", g_extraWitchCount, value);
|
||||||
} else if(StrEqual(arg, "restCount")) {
|
} else if(StrEqual(arg, "restCount")) {
|
||||||
ValInt(client, "g_restCount", g_restCount, value);
|
ValInt(client, "g_restCount", g_restCount, value);
|
||||||
|
} else if(StrEqual(arg, "survivorCount")) {
|
||||||
|
ValInt(client, "g_survivorCount", g_survivorCount, value);
|
||||||
|
} else if(StrEqual(arg, "realSurvivorCount")) {
|
||||||
|
ValInt(client, "g_survivorCount", g_survivorCount, value);
|
||||||
|
} else if(StrEqual(arg, "forcedSurvivorCount")) {
|
||||||
|
ValBool(client, "g_forcedSurvivorCount", g_forcedSurvivorCount, value);
|
||||||
|
} else if(StrEqual(arg, "forcedSurvivorCount")) {
|
||||||
|
ValBool(client, "g_extraFinaleTankEnabled", g_extraFinaleTankEnabled, value);
|
||||||
} else {
|
} else {
|
||||||
ReplyToCommand(client, "Unknown value");
|
ReplyToCommand(client, "Unknown value");
|
||||||
}
|
}
|
||||||
|
@ -602,7 +633,7 @@ Action Command_SaveInventory(int client, int args) {
|
||||||
ReplyToCommand(client, "No player found");
|
ReplyToCommand(client, "No player found");
|
||||||
return Plugin_Handled;
|
return Plugin_Handled;
|
||||||
}
|
}
|
||||||
SaveInventory(player);
|
SaveInventory(player, true);
|
||||||
ReplyToCommand(client, "Saved inventory for %N", player);
|
ReplyToCommand(client, "Saved inventory for %N", player);
|
||||||
return Plugin_Handled;
|
return Plugin_Handled;
|
||||||
}
|
}
|
||||||
|
@ -649,6 +680,12 @@ Action Command_SetSurvivorCount(int client, int args) {
|
||||||
if(args > 0) {
|
if(args > 0) {
|
||||||
char arg[8];
|
char arg[8];
|
||||||
GetCmdArg(1, arg, sizeof(arg));
|
GetCmdArg(1, arg, sizeof(arg));
|
||||||
|
if(arg[0] == 'c') {
|
||||||
|
g_forcedSurvivorCount = false;
|
||||||
|
ReplyToCommand(client, "Cleared forced survivor count.");
|
||||||
|
UpdateSurvivorCount();
|
||||||
|
return Plugin_Handled;
|
||||||
|
}
|
||||||
int survivorCount = parseSurvivorCount(arg);
|
int survivorCount = parseSurvivorCount(arg);
|
||||||
int oldSurvivorCount = g_survivorCount;
|
int oldSurvivorCount = g_survivorCount;
|
||||||
if(survivorCount == -1) {
|
if(survivorCount == -1) {
|
||||||
|
@ -669,7 +706,8 @@ Action Command_SetSurvivorCount(int client, int args) {
|
||||||
g_realSurvivorCount = survivorCount;
|
g_realSurvivorCount = survivorCount;
|
||||||
}
|
}
|
||||||
g_survivorCount = survivorCount;
|
g_survivorCount = survivorCount;
|
||||||
ReplyToCommand(client, "Changed survivor count %d -> %d", oldSurvivorCount, survivorCount);
|
g_forcedSurvivorCount = true;
|
||||||
|
ReplyToCommand(client, "Forced survivor count %d -> %d", oldSurvivorCount, survivorCount);
|
||||||
} else {
|
} else {
|
||||||
ReplyToCommand(client, "Survivor Count = %d | Real Survivor Count = %d", g_survivorCount, g_realSurvivorCount);
|
ReplyToCommand(client, "Survivor Count = %d | Real Survivor Count = %d", g_survivorCount, g_realSurvivorCount);
|
||||||
}
|
}
|
||||||
|
@ -702,7 +740,7 @@ Action Command_ToggleDoorLocks(int client, int args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Action Command_GetKitAmount(int client, int args) {
|
Action Command_GetKitAmount(int client, int args) {
|
||||||
ReplyToCommand(client, "Extra kits available: %d (%d) | Survivors: %d", g_extraKitsAmount, g_extraKitsStart, GetSurvivorsCount());
|
ReplyToCommand(client, "Extra kits available: %d (%d) | Survivors: %d", g_extraKitsAmount, g_extraKitsStart, g_survivorCount);
|
||||||
ReplyToCommand(client, "isCheckpointReached %b, g_isLateLoaded %b, firstGiven %b", g_isCheckpointReached, g_isLateLoaded, g_startCampaignGiven);
|
ReplyToCommand(client, "isCheckpointReached %b, g_isLateLoaded %b, firstGiven %b", g_isCheckpointReached, g_isLateLoaded, g_startCampaignGiven);
|
||||||
return Plugin_Handled;
|
return Plugin_Handled;
|
||||||
}
|
}
|
||||||
|
@ -764,7 +802,16 @@ Action Command_DebugStats(int client, int args) {
|
||||||
/////////////////////////////////////
|
/////////////////////////////////////
|
||||||
/// EVENTS
|
/// EVENTS
|
||||||
////////////////////////////////////
|
////////////////////////////////////
|
||||||
|
void OnTakeDamageAlivePost(int victim, int attacker, int inflictor, float damage, int damagetype) {
|
||||||
|
if(GetClientTeam(victim) == 2 && !IsFakeClient(victim))
|
||||||
|
SaveInventory(victim);
|
||||||
|
}
|
||||||
|
void Event_PlayerToIdle(Event event, const char[] name, bool dontBroadcast) {
|
||||||
|
int bot = GetClientOfUserId(event.GetInt("bot"));
|
||||||
|
int client = GetClientOfUserId(event.GetInt("player"));
|
||||||
|
if(GetClientTeam(client) != 2) return;
|
||||||
|
PrintToServer("%N -> idle %N", client, bot);
|
||||||
|
}
|
||||||
public Action L4D2_OnChangeFinaleStage(int &finaleType, const char[] arg) {
|
public Action L4D2_OnChangeFinaleStage(int &finaleType, const char[] arg) {
|
||||||
if(finaleType == FINALE_STARTED && g_realSurvivorCount > 4) {
|
if(finaleType == FINALE_STARTED && g_realSurvivorCount > 4) {
|
||||||
g_finaleStage = Stage_FinaleActive;
|
g_finaleStage = Stage_FinaleActive;
|
||||||
|
@ -796,7 +843,7 @@ void Event_TankSpawn(Event event, const char[] name, bool dontBroadcast) {
|
||||||
} else if(g_finaleStage == Stage_FinaleDuplicatePending) {
|
} else if(g_finaleStage == Stage_FinaleDuplicatePending) {
|
||||||
PrintToConsoleAll("[EPI] Third & final tank spawned");
|
PrintToConsoleAll("[EPI] Third & final tank spawned");
|
||||||
RequestFrame(Frame_SetExtraTankHealth, user);
|
RequestFrame(Frame_SetExtraTankHealth, user);
|
||||||
} else if(g_finaleStage == Stage_Inactive && g_extraFinaleTankEnabled && hExtraFinaleTank.IntValue & 1 && GetSurvivorsCount() > 6) {
|
} else if(g_finaleStage == Stage_Inactive && g_extraFinaleTankEnabled && hExtraFinaleTank.IntValue & 1 && g_survivorCount > 6) {
|
||||||
g_finaleStage = Stage_TankSplit;
|
g_finaleStage = Stage_TankSplit;
|
||||||
if(GetRandomFloat() <= hSplitTankChance.FloatValue) {
|
if(GetRandomFloat() <= hSplitTankChance.FloatValue) {
|
||||||
// Half their HP, assign half to self and for next tank
|
// Half their HP, assign half to self and for next tank
|
||||||
|
@ -872,6 +919,7 @@ void Event_PlayerFirstSpawn(Event event, const char[] name, bool dontBroadcast)
|
||||||
int userid = event.GetInt("userid");
|
int userid = event.GetInt("userid");
|
||||||
int client = GetClientOfUserId(userid);
|
int client = GetClientOfUserId(userid);
|
||||||
if(GetClientTeam(client) != 2) return;
|
if(GetClientTeam(client) != 2) return;
|
||||||
|
UpdateSurvivorCount();
|
||||||
if(IsFakeClient(client)) {
|
if(IsFakeClient(client)) {
|
||||||
// Ignore any 'BOT' bots (ABMBot, etc), they are temporarily
|
// Ignore any 'BOT' bots (ABMBot, etc), they are temporarily
|
||||||
char classname[32];
|
char classname[32];
|
||||||
|
@ -893,15 +941,17 @@ void Event_PlayerFirstSpawn(Event event, const char[] name, bool dontBroadcast)
|
||||||
// Make the (real) player invincible as well:
|
// Make the (real) player invincible as well:
|
||||||
CreateTimer(1.5, Timer_RemoveInvincibility, userid);
|
CreateTimer(1.5, Timer_RemoveInvincibility, userid);
|
||||||
SDKHook(client, SDKHook_OnTakeDamage, OnInvincibleDamageTaken);
|
SDKHook(client, SDKHook_OnTakeDamage, OnInvincibleDamageTaken);
|
||||||
|
SDKHook(client, SDKHook_OnTakeDamageAlivePost, OnTakeDamageAlivePost);
|
||||||
|
|
||||||
playerData[client].state = State_Active;
|
playerData[client].state = State_Active;
|
||||||
|
playerData[client].joinTime = GetTime();
|
||||||
|
|
||||||
if(L4D_IsFirstMapInScenario() && !g_startCampaignGiven) {
|
if(L4D_IsFirstMapInScenario() && !g_startCampaignGiven) {
|
||||||
// Players are joining the campaign, but not all clients are ready yet. Once a client is ready, we will give the extra players their items
|
// Players are joining the campaign, but not all clients are ready yet. Once a client is ready, we will give the extra players their items
|
||||||
if(AreAllClientsReady()) {
|
if(AreAllClientsReady()) {
|
||||||
UpdateSurvivorCount();
|
g_startCampaignGiven = true;
|
||||||
if(g_realSurvivorCount > 4) {
|
if(g_realSurvivorCount > 4) {
|
||||||
PrintToServer("[EPI] First chapter kits given");
|
PrintToServer("[EPI] First chapter kits given");
|
||||||
g_startCampaignGiven = true;
|
|
||||||
//Set the initial value ofhMinPlayers
|
//Set the initial value ofhMinPlayers
|
||||||
PopulateItems();
|
PopulateItems();
|
||||||
CreateTimer(1.0, Timer_GiveKits);
|
CreateTimer(1.0, Timer_GiveKits);
|
||||||
|
@ -910,7 +960,6 @@ void Event_PlayerFirstSpawn(Event event, const char[] name, bool dontBroadcast)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// New client has connected, late on the first chapter or on any other chapter
|
// New client has connected, late on the first chapter or on any other chapter
|
||||||
UpdateSurvivorCount();
|
|
||||||
// If 5 survivors, then set them up, TP them.
|
// If 5 survivors, then set them up, TP them.
|
||||||
if(g_realSurvivorCount > 4) {
|
if(g_realSurvivorCount > 4) {
|
||||||
CreateTimer(0.1, Timer_SetupNewClient, userid);
|
CreateTimer(0.1, Timer_SetupNewClient, userid);
|
||||||
|
@ -924,18 +973,21 @@ void Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast) {
|
||||||
|
|
||||||
int userid = event.GetInt("userid");
|
int userid = event.GetInt("userid");
|
||||||
int client = GetClientOfUserId(userid);
|
int client = GetClientOfUserId(userid);
|
||||||
|
if(GetClientTeam(client) != 2) return;
|
||||||
UpdateSurvivorCount();
|
UpdateSurvivorCount();
|
||||||
if(GetClientTeam(client) == 2 && !IsFakeClient(client) && !L4D_IsFirstMapInScenario()) {
|
if(!IsFakeClient(client) && !L4D_IsFirstMapInScenario()) {
|
||||||
// Start door timeout:
|
// Start door timeout:
|
||||||
CreateTimer(hSaferoomDoorWaitSeconds.FloatValue, Timer_OpenSaferoomDoor, _, TIMER_FLAG_NO_MAPCHANGE);
|
if(g_saferoomDoorEnt != INVALID_ENT_REFERENCE) {
|
||||||
|
CreateTimer(hSaferoomDoorWaitSeconds.FloatValue, Timer_OpenSaferoomDoor, _, TIMER_FLAG_NO_MAPCHANGE);
|
||||||
|
|
||||||
if(g_prevPlayerCount > 0) {
|
if(g_prevPlayerCount > 0) {
|
||||||
// Open the door if we hit % percent
|
// Open the door if we hit % percent
|
||||||
float percentIn = float(g_realSurvivorCount) / float(g_prevPlayerCount);
|
float percentIn = float(g_realSurvivorCount) / float(g_prevPlayerCount);
|
||||||
if(percentIn > hMinPlayersSaferoomDoor.FloatValue)
|
if(percentIn > hMinPlayersSaferoomDoor.FloatValue)
|
||||||
|
UnlockDoor(2);
|
||||||
|
} else{
|
||||||
UnlockDoor(2);
|
UnlockDoor(2);
|
||||||
} else{
|
}
|
||||||
UnlockDoor(2);
|
|
||||||
}
|
}
|
||||||
CreateTimer(0.5, Timer_GiveClientKit, userid);
|
CreateTimer(0.5, Timer_GiveClientKit, userid);
|
||||||
SDKHook(client, SDKHook_WeaponEquip, Event_Pickup);
|
SDKHook(client, SDKHook_WeaponEquip, Event_Pickup);
|
||||||
|
@ -947,12 +999,14 @@ void Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast) {
|
||||||
void Event_PlayerDisconnect(Event event, const char[] name, bool dontBroadcast) {
|
void Event_PlayerDisconnect(Event event, const char[] name, bool dontBroadcast) {
|
||||||
int userid = event.GetInt("userid");
|
int userid = event.GetInt("userid");
|
||||||
int client = GetClientOfUserId(userid);
|
int client = GetClientOfUserId(userid);
|
||||||
if(client > 0 && GetClientTeam(client) == 2) {
|
if(client > 0 && IsClientInGame(client) && GetClientTeam(client) == 2 && playerData[client].state == State_Active) {
|
||||||
playerData[client].hasJoined = false;
|
playerData[client].hasJoined = false;
|
||||||
playerData[client].state = State_PendingEmpty;
|
playerData[client].state = State_PendingEmpty;
|
||||||
playerData[client].nameCache[0] = '\0';
|
playerData[client].nameCache[0] = '\0';
|
||||||
PrintToServer("debug: Player (index %d, uid %d) now pending empty", client, client, userid);
|
PrintToServer("debug: Player (index %d, uid %d) now pending empty", client, client, userid);
|
||||||
CreateTimer(cvDropDisconnectTime.FloatValue, Timer_DropSurvivor, client);
|
CreateTimer(cvDropDisconnectTime.FloatValue, Timer_DropSurvivor, client);
|
||||||
|
if(g_saveTimer[client] != null)
|
||||||
|
delete g_saveTimer[client];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -964,6 +1018,7 @@ void Event_PlayerInfo(Event event, const char[] name, bool dontBroadcast) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Action Timer_DropSurvivor(Handle h, int client) {
|
Action Timer_DropSurvivor(Handle h, int client) {
|
||||||
|
// Check that they are still pending empty (no one replaced them)
|
||||||
if(playerData[client].state == State_PendingEmpty) {
|
if(playerData[client].state == State_PendingEmpty) {
|
||||||
playerData[client].state = State_Empty;
|
playerData[client].state = State_Empty;
|
||||||
if(hMinPlayers != null) {
|
if(hMinPlayers != null) {
|
||||||
|
@ -985,8 +1040,9 @@ Action Timer_DropSurvivor(Handle h, int client) {
|
||||||
|
|
||||||
void Event_ItemPickup(Event event, const char[] name, bool dontBroadcast) {
|
void Event_ItemPickup(Event event, const char[] name, bool dontBroadcast) {
|
||||||
int client = GetClientOfUserId(event.GetInt("userid"));
|
int client = GetClientOfUserId(event.GetInt("userid"));
|
||||||
if(client > 0) {
|
if(client > 0 && GetClientTeam(client) == 2 && !IsFakeClient(client)) {
|
||||||
UpdatePlayerInventory(client);
|
UpdatePlayerInventory(client);
|
||||||
|
SaveInventory(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1015,6 +1071,12 @@ char TIER2_WEAPONS[9][] = {
|
||||||
Action Timer_SetupNewClient(Handle h, int userid) {
|
Action Timer_SetupNewClient(Handle h, int userid) {
|
||||||
int client = GetClientOfUserId(userid);
|
int client = GetClientOfUserId(userid);
|
||||||
if(client == 0) return Plugin_Handled;
|
if(client == 0) return Plugin_Handled;
|
||||||
|
if(HasSavedInventory(client)) {
|
||||||
|
PrintDebug(DEBUG_GENERIC, "%N has existing inventory", client);
|
||||||
|
// TODO: restore
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Incase their bot snagged a kit before we could give them one:
|
// Incase their bot snagged a kit before we could give them one:
|
||||||
if(!DoesClientHaveKit(client)) {
|
if(!DoesClientHaveKit(client)) {
|
||||||
int item = GivePlayerItem(client, "weapon_first_aid_kit");
|
int item = GivePlayerItem(client, "weapon_first_aid_kit");
|
||||||
|
@ -1049,6 +1111,7 @@ Action Timer_SetupNewClient(Handle h, int userid) {
|
||||||
// playerWeapons.PushString(weaponName);
|
// playerWeapons.PushString(weaponName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wpn = GetPlayerWeaponSlot(i, 1);
|
wpn = GetPlayerWeaponSlot(i, 1);
|
||||||
if(wpn > 0) {
|
if(wpn > 0) {
|
||||||
GetEdictClassname(wpn, weaponName, sizeof(weaponName));
|
GetEdictClassname(wpn, weaponName, sizeof(weaponName));
|
||||||
|
@ -1162,6 +1225,12 @@ Action Timer_GiveKits(Handle timer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnMapStart() {
|
public void OnMapStart() {
|
||||||
|
char map[5];
|
||||||
|
GetCurrentMap(map, sizeof(map));
|
||||||
|
// If map starts with c#m#, 98% an official map
|
||||||
|
if(map[0] == 'c' && IsCharNumeric(map[1]) && (map[2] == 'm' || map[3] == 'm')) {
|
||||||
|
g_isOfficialMap = true;
|
||||||
|
}
|
||||||
g_isCheckpointReached = false;
|
g_isCheckpointReached = false;
|
||||||
//If previous round was a failure, restore the amount of kits that were left directly after map transition
|
//If previous round was a failure, restore the amount of kits that were left directly after map transition
|
||||||
if(g_isFailureRound) {
|
if(g_isFailureRound) {
|
||||||
|
@ -1184,7 +1253,7 @@ public void OnMapStart() {
|
||||||
g_extraFinaleTankEnabled = false;
|
g_extraFinaleTankEnabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int extraKits = GetSurvivorsCount() - 4;
|
int extraKits = g_survivorCount - 4;
|
||||||
if(extraKits > 0) {
|
if(extraKits > 0) {
|
||||||
// Keep how many extra kits were left after we loaded in, for resetting on failure rounds
|
// Keep how many extra kits were left after we loaded in, for resetting on failure rounds
|
||||||
g_extraKitsAmount += extraKits;
|
g_extraKitsAmount += extraKits;
|
||||||
|
@ -1224,20 +1293,11 @@ public void OnMapStart() {
|
||||||
L4D2_RunScript(HUD_SCRIPT_CLEAR);
|
L4D2_RunScript(HUD_SCRIPT_CLEAR);
|
||||||
Director_OnMapStart();
|
Director_OnMapStart();
|
||||||
if(g_isLateLoaded) {
|
if(g_isLateLoaded) {
|
||||||
|
UpdateSurvivorCount();
|
||||||
g_isLateLoaded = false;
|
g_isLateLoaded = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
Extra Witch Algo:
|
|
||||||
On map start, knowing # of total players, compute a random number of witches.
|
|
||||||
The random number calculated by DiceRoll with 2 rolls and biased to the left. [min, 6]
|
|
||||||
The minimum number in the dice is shifted to the right by the # of players (abmExtraCount-4)/4 (1 extra=0, 10 extra=2)
|
|
||||||
|
|
||||||
Then, with the # of witches, as N, calculate N different flow values between [0, L4D2Direct_GetMapMaxFlowDistance()]
|
|
||||||
Timer_Director then checks if highest flow achieved (never decreases) is >= each flow value, if one found, a witch is spawned
|
|
||||||
(the witch herself is not spawned at the flow, just her spawning is triggered)
|
|
||||||
*/
|
|
||||||
|
|
||||||
public void OnConfigsExecuted() {
|
public void OnConfigsExecuted() {
|
||||||
if(hUpdateMinPlayers.BoolValue && hMinPlayers != null) {
|
if(hUpdateMinPlayers.BoolValue && hMinPlayers != null) {
|
||||||
|
@ -1429,7 +1489,7 @@ public Action OnUpgradePackUse(int entity, int activator, int caller, UseType ty
|
||||||
clients.Push(activator);
|
clients.Push(activator);
|
||||||
ClientCommand(activator, "play player/orch_hit_csharp_short.wav");
|
ClientCommand(activator, "play player/orch_hit_csharp_short.wav");
|
||||||
|
|
||||||
if(clients.Length >= GetSurvivorsCount()) {
|
if(clients.Length >= g_survivorCount) {
|
||||||
AcceptEntityInput(entity, "kill");
|
AcceptEntityInput(entity, "kill");
|
||||||
delete clients;
|
delete clients;
|
||||||
g_ammoPacks.Erase(index);
|
g_ammoPacks.Erase(index);
|
||||||
|
@ -1485,13 +1545,13 @@ void UnlockDoor(int flag) {
|
||||||
SDKUnhook(entity, SDKHook_Use, Hook_Use);
|
SDKUnhook(entity, SDKHook_Use, Hook_Use);
|
||||||
if(hSaferoomDoorAutoOpen.IntValue & flag) {
|
if(hSaferoomDoorAutoOpen.IntValue & flag) {
|
||||||
AcceptEntityInput(entity, "Open");
|
AcceptEntityInput(entity, "Open");
|
||||||
g_saferoomDoorEnt = INVALID_ENT_REFERENCE;
|
|
||||||
if(!g_areItemsPopulated)
|
|
||||||
PopulateItems();
|
|
||||||
}
|
}
|
||||||
|
SetVariantString("Unlock");
|
||||||
|
AcceptEntityInput(entity, "SetAnimation");
|
||||||
|
g_saferoomDoorEnt = INVALID_ENT_REFERENCE;
|
||||||
|
if(!g_areItemsPopulated)
|
||||||
|
PopulateItems();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Action Timer_UpdateHud(Handle h) {
|
Action Timer_UpdateHud(Handle h) {
|
||||||
|
@ -1674,11 +1734,63 @@ void DropDroppedInventories() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void SaveInventory(int client) {
|
// Used for EPI hud
|
||||||
PrintDebug(DEBUG_GENERIC, "Saving inventory for %N", client);
|
void UpdatePlayerInventory(int client) {
|
||||||
|
static char item[16];
|
||||||
|
if(GetClientWeaponName(client, 2, item, sizeof(item))) {
|
||||||
|
items[client].throwable[0] = CharToUpper(item[7]);
|
||||||
|
if(items[client].throwable[0] == 'V') {
|
||||||
|
items[client].throwable[0] = 'B'; //Replace [V]omitjar with [B]ile
|
||||||
|
}
|
||||||
|
items[client].throwable[1] = '\0';
|
||||||
|
} else {
|
||||||
|
items[client].throwable[0] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
if(GetClientWeaponName(client, 3, item, sizeof(item))) {
|
||||||
|
items[client].usable[0] = CharToUpper(item[7]);
|
||||||
|
items[client].usable[1] = '\0';
|
||||||
|
if(items[client].throwable[0] == 'F') {
|
||||||
|
items[client].throwable[0] = '+'; //Replace [V]omitjar with [B]ile
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
items[client].usable[0] = '-';
|
||||||
|
items[client].usable[1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
if(GetClientWeaponName(client, 4, item, sizeof(item))) {
|
||||||
|
items[client].consumable[0] = CharToUpper(item[7]);
|
||||||
|
items[client].consumable[1] = '\0';
|
||||||
|
} else {
|
||||||
|
items[client].consumable[0] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
// Queue their inventory to be saved after a timeout.
|
||||||
|
// Any time a save happens between prev save and timeout will delay the timeout.
|
||||||
|
// This should ensure that the saved inventory is most of the time up-to-date
|
||||||
|
if(g_saveTimer[client] != null)
|
||||||
|
delete g_saveTimer[client];
|
||||||
|
g_saveTimer[client] = CreateTimer(INV_SAVE_TIME, Timer_SaveInventory, GetClientUserId(client));
|
||||||
|
}
|
||||||
PlayerInventory inventory;
|
PlayerInventory inventory;
|
||||||
inventory.timestamp = GetTime();
|
inventory.timestamp = time;
|
||||||
inventory.isAlive = IsClientInGame(client) && IsPlayerAlive(client);
|
inventory.isAlive = IsPlayerAlive(client);
|
||||||
playerData[client].state = State_Active;
|
playerData[client].state = State_Active;
|
||||||
GetClientAbsOrigin(client, inventory.location);
|
GetClientAbsOrigin(client, inventory.location);
|
||||||
|
|
||||||
|
@ -1697,6 +1809,7 @@ void SaveInventory(int client) {
|
||||||
|
|
||||||
GetClientAuthId(client, AuthId_Steam3, buffer, sizeof(buffer));
|
GetClientAuthId(client, AuthId_Steam3, buffer, sizeof(buffer));
|
||||||
pInv.SetArray(buffer, inventory, sizeof(inventory));
|
pInv.SetArray(buffer, inventory, sizeof(inventory));
|
||||||
|
g_lastInvSave[client] = GetTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RestoreInventory(int client, PlayerInventory inventory) {
|
void RestoreInventory(int client, PlayerInventory inventory) {
|
||||||
|
@ -1737,6 +1850,12 @@ bool GetInventory(const char[] steamid, PlayerInventory inventory) {
|
||||||
return pInv.GetArray(steamid, inventory, sizeof(inventory));
|
return pInv.GetArray(steamid, inventory, sizeof(inventory));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool HasSavedInventory(int client) {
|
||||||
|
char buffer[32];
|
||||||
|
GetClientAuthId(client, AuthId_Steam3, buffer, sizeof(buffer));
|
||||||
|
return pInv.ContainsKey(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
bool DoesInventoryDiffer(int client) {
|
bool DoesInventoryDiffer(int client) {
|
||||||
static char buffer[32];
|
static char buffer[32];
|
||||||
GetClientAuthId(client, AuthId_Steam3, buffer, sizeof(buffer));
|
GetClientAuthId(client, AuthId_Steam3, buffer, sizeof(buffer));
|
||||||
|
@ -1753,47 +1872,69 @@ bool DoesInventoryDiffer(int client) {
|
||||||
return currentPrimary != storedPrimary || currentSecondary != storedSecondary;
|
return currentPrimary != storedPrimary || currentSecondary != storedSecondary;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: disable by cvar as well (replace abm_autohard)
|
||||||
bool IsEPIActive() {
|
bool IsEPIActive() {
|
||||||
return g_epiEnabled;
|
return g_epiEnabled;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
[Debug] UpdateSurvivorCount: total=4 real=4 active=4
|
||||||
|
[Debug] UpdateSurvivorCount: total=4 real=4 active=4
|
||||||
|
Player no longer idle
|
||||||
|
[Debug] UpdateSurvivorCount: total=5 real=4 active=5
|
||||||
|
[Debug] UpdateSurvivorCount: total=4 real=4 active=4
|
||||||
|
Player no longer idle
|
||||||
|
*/
|
||||||
void UpdateSurvivorCount() {
|
void UpdateSurvivorCount() {
|
||||||
|
#if defined DEBUG_FORCE_PLAYERS
|
||||||
|
g_survivorCount = DEBUG_FORCE_PLAYERS;
|
||||||
|
g_realSurvivorCount = DEBUG_FORCE_PLAYERS;
|
||||||
|
g_epiEnabled = g_realSurvivorCount > 4 && g_isGamemodeAllowed;
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
if(g_forcedSurvivorCount) return; // Don't update if forced
|
||||||
int countTotal = 0, countReal = 0, countActive = 0;
|
int countTotal = 0, countReal = 0, countActive = 0;
|
||||||
#if !defined DEBUG_FORCE_PLAYERS
|
|
||||||
for(int i = 1; i <= MaxClients; i++) {
|
for(int i = 1; i <= MaxClients; i++) {
|
||||||
if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2) {
|
if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2) {
|
||||||
if(!IsFakeClient(i)) {
|
// Count idle player's bots as well
|
||||||
|
if(!IsFakeClient(i) || L4D_GetIdlePlayerOfBot(i) > 0) {
|
||||||
countReal++;
|
countReal++;
|
||||||
}
|
}
|
||||||
|
// FIXME: counting idle players for a brief tick
|
||||||
countTotal++;
|
countTotal++;
|
||||||
if(playerData[i].state == State_Active) {
|
if(playerData[i].state == State_Active) {
|
||||||
countActive++;
|
countActive++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
g_survivorCount = countTotal;
|
g_survivorCount = countTotal;
|
||||||
g_realSurvivorCount = countReal;
|
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);
|
||||||
#endif
|
// Temporarily for now use g_realSurvivorCount, as players joining have a brief second where they are 5 players
|
||||||
#if defined DEBUG_FORCE_PLAYERS
|
|
||||||
g_survivorCount = DEBUG_FORCE_PLAYERS;
|
// 1 = 5+ official
|
||||||
g_realSurvivorCount = DEBUG_FORCE_PLAYERS;
|
// 2 = 5+ any map
|
||||||
#endif
|
// 3 = always on
|
||||||
|
bool isActive = g_isGamemodeAllowed;
|
||||||
if(g_survivorCount > 4) {
|
if(isActive && cvEPIEnabledMode.IntValue != 3) {
|
||||||
// Update friendly fire values to reduce accidental FF in crowded corridors
|
// Enable only if mode is 2 or is official map AND 5+
|
||||||
ConVar friendlyFireFactor = GetActiveFriendlyFireFactor();
|
isActive = (g_isOfficialMap || cvEPIEnabledMode.IntValue == 2) && g_realSurvivorCount > 4;
|
||||||
// TODO: Get previous default
|
|
||||||
friendlyFireFactor.FloatValue = friendlyFireFactor.FloatValue - ((g_realSurvivorCount - 4) * cvFFDecreaseRate.FloatValue);
|
|
||||||
if(friendlyFireFactor.FloatValue < 0.0) {
|
|
||||||
friendlyFireFactor.FloatValue = 0.01;
|
|
||||||
}
|
|
||||||
g_epiEnabled = g_isGamemodeAllowed;
|
|
||||||
} else {
|
|
||||||
g_epiEnabled = false;
|
|
||||||
}
|
}
|
||||||
|
g_epiEnabled = isActive;
|
||||||
|
SetFFFactor(g_epiEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: update hMinPlayers
|
void SetFFFactor(bool enabled) {
|
||||||
|
static float prevValue;
|
||||||
|
// Restore the previous value (we use the value for the calculations of new value)
|
||||||
|
if(g_ffFactorCvar == null) return; // Ignore invalid difficulties
|
||||||
|
g_ffFactorCvar.FloatValue = prevValue;
|
||||||
|
if(enabled) {
|
||||||
|
prevValue = g_ffFactorCvar.FloatValue;
|
||||||
|
g_ffFactorCvar.FloatValue = g_ffFactorCvar.FloatValue - ((g_realSurvivorCount - 4) * cvFFDecreaseRate.FloatValue);
|
||||||
|
if(g_ffFactorCvar.FloatValue < 0.01) {
|
||||||
|
g_ffFactorCvar.FloatValue = 0.01;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stock int FindFirstSurvivor() {
|
stock int FindFirstSurvivor() {
|
||||||
|
@ -1822,33 +1963,6 @@ stock void GiveStartingKits() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stock int GetSurvivorsCount() {
|
|
||||||
#if defined DEBUG_FORCE_PLAYERS
|
|
||||||
return DEBUG_FORCE_PLAYERS;
|
|
||||||
#endif
|
|
||||||
int count = 0;
|
|
||||||
for(int i = 1; i <= MaxClients; i++) {
|
|
||||||
if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2) {
|
|
||||||
++count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
stock int GetRealSurvivorsCount() {
|
|
||||||
#if defined DEBUG_FORCE_PLAYERS
|
|
||||||
return DEBUG_FORCE_PLAYERS;
|
|
||||||
#endif
|
|
||||||
int count = 0;
|
|
||||||
for(int i = 1; i <= MaxClients; i++) {
|
|
||||||
if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2) {
|
|
||||||
if(IsFakeClient(i) && HasEntProp(i, Prop_Send, "m_humanSpectatorUserID") && GetEntProp(i, Prop_Send, "m_humanSpectatorUserID") == 0) continue;
|
|
||||||
++count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
stock bool AreAllClientsReady() {
|
stock bool AreAllClientsReady() {
|
||||||
for(int i = 1; i <= MaxClients; i++) {
|
for(int i = 1; i <= MaxClients; i++) {
|
||||||
if(IsClientConnected(i) && !IsClientInGame(i)) {
|
if(IsClientConnected(i) && !IsClientInGame(i)) {
|
||||||
|
@ -1859,9 +1973,10 @@ stock bool AreAllClientsReady() {
|
||||||
}
|
}
|
||||||
|
|
||||||
stock bool DoesClientHaveKit(int client) {
|
stock bool DoesClientHaveKit(int client) {
|
||||||
char wpn[32];
|
if(IsClientConnected(client) && IsClientInGame(client)) {
|
||||||
if(IsClientConnected(client) && IsClientInGame(client) && GetClientWeaponName(client, 3, wpn, sizeof(wpn))) {
|
char wpn[32];
|
||||||
return StrEqual(wpn, "weapon_first_aid_kit");
|
if(GetClientWeaponName(client, 3, wpn, sizeof(wpn)))
|
||||||
|
return StrEqual(wpn, "weapon_first_aid_kit");
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1958,37 +2073,6 @@ int FindCabinetIndex(int cabinetId) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdatePlayerInventory(int client) {
|
|
||||||
static char item[16];
|
|
||||||
if(GetClientWeaponName(client, 2, item, sizeof(item))) {
|
|
||||||
items[client].throwable[0] = CharToUpper(item[7]);
|
|
||||||
if(items[client].throwable[0] == 'V') {
|
|
||||||
items[client].throwable[0] = 'B'; //Replace [V]omitjar with [B]ile
|
|
||||||
}
|
|
||||||
items[client].throwable[1] = '\0';
|
|
||||||
} else {
|
|
||||||
items[client].throwable[0] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
if(GetClientWeaponName(client, 3, item, sizeof(item))) {
|
|
||||||
items[client].usable[0] = CharToUpper(item[7]);
|
|
||||||
items[client].usable[1] = '\0';
|
|
||||||
if(items[client].throwable[0] == 'F') {
|
|
||||||
items[client].throwable[0] = '+'; //Replace [V]omitjar with [B]ile
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
items[client].usable[0] = '-';
|
|
||||||
items[client].usable[1] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
if(GetClientWeaponName(client, 4, item, sizeof(item))) {
|
|
||||||
items[client].consumable[0] = CharToUpper(item[7]);
|
|
||||||
items[client].consumable[1] = '\0';
|
|
||||||
} else {
|
|
||||||
items[client].consumable[0] = '\0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stock void RunVScriptLong(const char[] sCode, any ...) {
|
stock void RunVScriptLong(const char[] sCode, any ...) {
|
||||||
static int iScriptLogic = INVALID_ENT_REFERENCE;
|
static int iScriptLogic = INVALID_ENT_REFERENCE;
|
||||||
if(iScriptLogic == INVALID_ENT_REFERENCE || !IsValidEntity(iScriptLogic)) {
|
if(iScriptLogic == INVALID_ENT_REFERENCE || !IsValidEntity(iScriptLogic)) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue