mirror of
https://github.com/Jackzmc/sourcemod-plugins.git
synced 2025-05-05 17:23:20 +00:00
Compare commits
2 commits
28a4346e8d
...
3262a98203
Author | SHA1 | Date | |
---|---|---|---|
3262a98203 | |||
61698e20c4 |
8 changed files with 272 additions and 249 deletions
Binary file not shown.
Binary file not shown.
|
@ -12,6 +12,7 @@ ConVar directorSpawnChance; // Base chance of a special spawning, changed by pla
|
|||
#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.04 // The chance the director ceases spawning
|
||||
#define DIRECTOR_REST_MAX_COUNT 8 // The maximum amount of rest given (this * DIRECTOR_TIMER_INTERVAL)
|
||||
#define DIRECTOR_ESCAPE_TANK_MIN_TIME_S 40 // The min time in seconds that must elapse since escape vehicle arrival until tank can spawn
|
||||
|
||||
#define DIRECTOR_DEBUG_SPAWN 1 // Dont actually spawn
|
||||
|
||||
|
@ -155,6 +156,12 @@ void Director_CheckClient(int client) {
|
|||
static int g_newTankHealth = 0;
|
||||
void OnTankBotSpawn(int client) {
|
||||
if(!IsEPIActive() || !(cvEPISpecialSpawning.IntValue & 4)) return;
|
||||
if(g_finaleVehicleStartTime > 0 && GetTime() - g_finaleVehicleStartTime > DIRECTOR_ESCAPE_TANK_MIN_TIME_S) {
|
||||
PrintDebug(DEBUG_SPAWNLOGIC, "OnTankBotSpawn: Tank too early, killing");
|
||||
ForcePlayerSuicide(client);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if any finale is active
|
||||
if(g_newTankHealth > 0) {
|
||||
// A split tank has spawned, set its health
|
||||
|
@ -164,29 +171,29 @@ void OnTankBotSpawn(int client) {
|
|||
return;
|
||||
} else if(g_realSurvivorCount >= hExtraTankThreshold.IntValue && g_extraFinaleTankEnabled && hExtraFinaleTank.IntValue > 1) {
|
||||
// If we have hExtraTankThreshold or more and finale tanks enabled, spawn finale tanks:
|
||||
if(g_finaleStage == Stage_Active) {
|
||||
if(g_epiTankState == Stage_Active) {
|
||||
// 1st tank spawned
|
||||
PrintDebug(DEBUG_SPAWNLOGIC, "OnTankBotSpawn: [FINALE] 1st tank spawned");
|
||||
int health = CalculateExtraTankHealth(client);
|
||||
SetEntProp(client, Prop_Send, "m_iHealth", health);
|
||||
g_finaleStage = Stage_FirstTankSpawned;
|
||||
g_epiTankState = Stage_FirstTankSpawned;
|
||||
return;
|
||||
} else if(g_realSurvivorCount >= 6 && g_finaleStage == Stage_FirstTankSpawned) {
|
||||
} else if(g_realSurvivorCount >= 6 && g_epiTankState == Stage_FirstTankSpawned) {
|
||||
PrintDebug(DEBUG_SPAWNLOGIC, "OnTankBotSpawn: [FINALE] 2nd tank spawned");
|
||||
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);
|
||||
int health = CalculateExtraTankHealth(client);
|
||||
SetEntProp(client, Prop_Send, "m_iHealth", health);
|
||||
g_finaleStage = Stage_SecondTankSpawned;
|
||||
g_epiTankState = Stage_SecondTankSpawned;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// End finale logic:
|
||||
if(g_finaleStage == Stage_SecondTankSpawned) {
|
||||
if(g_epiTankState == Stage_SecondTankSpawned) {
|
||||
PrintDebug(DEBUG_SPAWNLOGIC, "OnTankBotSpawn: [FINALE] Health set, tank logic done");
|
||||
g_finaleStage = Stage_ActiveDone;
|
||||
g_epiTankState = Stage_ActiveDone;
|
||||
// We don't return, letting the 2nd 5+ finale tank get buffed:
|
||||
}
|
||||
// This should not run on active finales (different than finale maps, such as swamp fever's, where finale isnt full map)
|
||||
|
@ -200,7 +207,7 @@ void OnTankBotSpawn(int client) {
|
|||
(4) random chance set by hSplitTankChance
|
||||
Otherwise, just scale health based on survivor count
|
||||
*/
|
||||
if(g_finaleStage == Stage_Inactive && g_realSurvivorCount >= hExtraTankThreshold.IntValue && hExtraFinaleTank.IntValue & 1 && GetURandomFloat() <= hSplitTankChance.FloatValue) {
|
||||
if(g_epiTankState == Stage_Inactive && g_realSurvivorCount >= hExtraTankThreshold.IntValue && hExtraFinaleTank.IntValue & 1 && GetURandomFloat() <= hSplitTankChance.FloatValue) {
|
||||
float duration = GetRandomFloat(EXTRA_TANK_MIN_SEC, EXTRA_TANK_MAX_SEC);
|
||||
int splitHealth = health / 2;
|
||||
PrintDebug(DEBUG_SPAWNLOGIC, "OnTankBotSpawn: split tank in %.1fs, health=%d", duration, splitHealth);
|
||||
|
@ -303,7 +310,7 @@ void Director_PrintDebug(int client) {
|
|||
}
|
||||
PrintToConsole(client, "highestFlow = %f, g_minFlowSpawn = %f, current flow = %f", g_highestFlowAchieved, g_minFlowSpawn, L4D2Direct_GetFlowDistance(client));
|
||||
PrintToConsole(client, "g_maxStressIntensity = %f, current avg = %f", g_maxStressIntensity, L4D_GetAvgSurvivorIntensity());
|
||||
PrintToConsole(client, "TankInPlay=%b, FinaleStage=%d, FinaleEscapeReady=%b, DirectorTankCheck:%b", L4D2_IsTankInPlay(), g_finaleStage, g_isFinaleEnding, L4D2_IsTankInPlay() && !g_isFinaleEnding);
|
||||
PrintToConsole(client, "TankInPlay=%b, FinaleStage=%d, FinaleEscapeReady=%b, DirectorTankCheck:%b", L4D2_IsTankInPlay(), g_epiTankState, g_isFinaleEnding, L4D2_IsTankInPlay() && !g_isFinaleEnding);
|
||||
char buffer[128];
|
||||
float time = GetGameTime();
|
||||
PrintToConsole(client, "Last Spawn Deltas: (%.1f s) (min %f)", time - g_lastSpecialSpawnTime, DIRECTOR_MIN_SPAWN_TIME);
|
||||
|
|
|
@ -41,6 +41,7 @@ methodmap SceneSelection < ArrayList {
|
|||
}
|
||||
|
||||
public void Activate(MapData data, int flags = 0) {
|
||||
if(this == null) return;
|
||||
g_ropeIndex = 0;
|
||||
SelectedSceneData aScene;
|
||||
SceneData scene;
|
||||
|
@ -276,114 +277,6 @@ enum struct SceneVariantData {
|
|||
}
|
||||
}
|
||||
|
||||
enum propertyType {
|
||||
PROPERTY_NONE = -1,
|
||||
PROPERTY_STRING,
|
||||
PROPERTY_INTEGER,
|
||||
PROPERTY_FLOAT
|
||||
}
|
||||
|
||||
// This is horrible but we need a way to know what the type of the netprop to set is
|
||||
enum struct PropertyStore {
|
||||
JSONObject intKv;
|
||||
JSONObject stringKv;
|
||||
JSONObject floatKv;
|
||||
|
||||
void Cleanup() {
|
||||
if(this.intKv != null) delete this.intKv;
|
||||
if(this.stringKv != null) delete this.stringKv;
|
||||
if(this.floatKv != null) delete this.floatKv;
|
||||
}
|
||||
|
||||
bool GetInt(const char[] name, int &value) {
|
||||
if(this.intKv == null) return false;
|
||||
if(!this.intKv.HasKey(name)) return false;
|
||||
value = this.intKv.GetInt(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetString(const char[] name, char[] buffer, int maxlen) {
|
||||
if(this.stringKv == null) return false;
|
||||
if(!this.stringKv.HasKey(name)) return false;
|
||||
this.stringKv.GetString(name, buffer, maxlen);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetFloat(const char[] name, float &value) {
|
||||
if(this.floatKv == null) return false;
|
||||
if(!this.floatKv.HasKey(name)) return false;
|
||||
value = this.floatKv.GetFloat(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
propertyType GetPropertyType(const char[] key) {
|
||||
if(this.intKv != null && this.intKv.HasKey(key)) return PROPERTY_INTEGER;
|
||||
if(this.floatKv != null && this.floatKv.HasKey(key)) return PROPERTY_FLOAT;
|
||||
if(this.stringKv != null && this.stringKv.HasKey(key)) return PROPERTY_STRING;
|
||||
return PROPERTY_NONE;
|
||||
}
|
||||
|
||||
bool HasAny() {
|
||||
return this.intKv != null || this.floatKv != null || this.stringKv != null;
|
||||
}
|
||||
|
||||
ArrayList Keys() {
|
||||
char key[128];
|
||||
ArrayList list = new ArrayList(ByteCountToCells(128));
|
||||
JSONObjectKeys keys;
|
||||
if(this.stringKv != null) {
|
||||
keys = this.stringKv.Keys()
|
||||
while(keys.ReadKey(key, sizeof(key))) {
|
||||
list.PushString(key);
|
||||
}
|
||||
delete keys;
|
||||
}
|
||||
if(this.intKv != null) {
|
||||
keys = this.intKv.Keys()
|
||||
while(keys.ReadKey(key, sizeof(key))) {
|
||||
list.PushString(key);
|
||||
}
|
||||
delete keys;
|
||||
}
|
||||
if(this.floatKv != null) {
|
||||
keys = this.floatKv.Keys()
|
||||
while(keys.ReadKey(key, sizeof(key))) {
|
||||
list.PushString(key);
|
||||
}
|
||||
delete keys;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
StringMap Entries() {
|
||||
char key[128];
|
||||
StringMap kv = new StringMap();
|
||||
JSONObjectKeys keys;
|
||||
if(this.stringKv != null) {
|
||||
keys = this.stringKv.Keys()
|
||||
while(keys.ReadKey(key, sizeof(key))) {
|
||||
kv.SetValue(key, PROPERTY_STRING);
|
||||
}
|
||||
delete keys;
|
||||
}
|
||||
if(this.intKv != null) {
|
||||
keys = this.intKv.Keys()
|
||||
while(keys.ReadKey(key, sizeof(key))) {
|
||||
kv.SetValue(key, PROPERTY_INTEGER);
|
||||
}
|
||||
delete keys;
|
||||
}
|
||||
if(this.floatKv != null) {
|
||||
keys = this.floatKv.Keys()
|
||||
while(keys.ReadKey(key, sizeof(key))) {
|
||||
kv.SetValue(key, PROPERTY_FLOAT);
|
||||
}
|
||||
delete keys;
|
||||
}
|
||||
return kv;
|
||||
}
|
||||
}
|
||||
|
||||
enum struct VariantEntityData {
|
||||
char type[32];
|
||||
char model[128];
|
||||
|
@ -394,53 +287,108 @@ enum struct VariantEntityData {
|
|||
int color[4];
|
||||
|
||||
ArrayList keyframes;
|
||||
PropertyStore properties;
|
||||
JSONObject propertiesInt;
|
||||
JSONObject propertiesString;
|
||||
JSONObject propertiesFloat;
|
||||
// PropertyStore properties;
|
||||
// JSONObject propertiesInt;
|
||||
// JSONObject propertiesString;
|
||||
// JSONObject propertiesFloat;
|
||||
|
||||
JSONArray properties;
|
||||
|
||||
void Cleanup() {
|
||||
if(this.keyframes != null) {
|
||||
delete this.keyframes;
|
||||
// if(this.keyframes != null) {
|
||||
// delete this.keyframes;
|
||||
// }
|
||||
// this.properties.Cleanup();
|
||||
if(this.properties != null) {
|
||||
JSONObject obj;
|
||||
for(int i = 0; i < this.properties.Length; i++) {
|
||||
obj = view_as<JSONObject>(this.properties.Get(i));
|
||||
delete obj;
|
||||
}
|
||||
delete this.properties;
|
||||
}
|
||||
this.properties.Cleanup();
|
||||
}
|
||||
|
||||
void ApplyProperties(int entity) {
|
||||
if(!this.properties.HasAny()) return;
|
||||
char key[64], buffer[128];
|
||||
ArrayList keys = this.properties.Keys();
|
||||
for(int i = 0; i < keys.Length; i++) {
|
||||
keys.GetString(i, key, sizeof(key));
|
||||
// Only want to apply netprops (m_ prefix)
|
||||
if(key[0] == 'm' && key[1] == '_') {
|
||||
propertyType type = this.properties.GetPropertyType(key);
|
||||
Debug("netprop %s type %d", key, type);
|
||||
switch(type) {
|
||||
case PROPERTY_STRING: {
|
||||
this.properties.GetString(key, buffer, sizeof(buffer));
|
||||
Debug("Applying netprop %s (val=%s) on %d", key, buffer, entity);
|
||||
SetEntPropString(entity, Prop_Send, key, buffer);
|
||||
break;
|
||||
}
|
||||
case PROPERTY_INTEGER: {
|
||||
int val;
|
||||
this.properties.GetInt(key, val);
|
||||
Debug("Applying netprop %s (val=%d) on %d", key, val, entity);
|
||||
SetEntProp(entity, Prop_Send, key, val);
|
||||
break;
|
||||
}
|
||||
case PROPERTY_FLOAT: {
|
||||
float val;
|
||||
this.properties.GetFloat(key, val);
|
||||
Debug("Applying netprop %s (val=%f) on %d", key, val, entity);
|
||||
SetEntPropFloat(entity, Prop_Send, key, val);
|
||||
break;
|
||||
// if(!this.properties.HasAny()) return;
|
||||
// char key[64];
|
||||
// ArrayList keys = this.properties.Keys();
|
||||
// for(int i = 0; i < keys.Length; i++) {
|
||||
// keys.GetString(i, key, sizeof(key));
|
||||
// // Only want to apply netprops (m_ prefix)
|
||||
// if(key[0] == 'm' && key[1] == '_') {
|
||||
// this._ApplyNetprop(entity, key);
|
||||
// } else if(key[0] == "_") {
|
||||
// this._ApplyCustom(entity, key);
|
||||
// } else {
|
||||
// this._ApplyKeyvalue(entity, key);
|
||||
// }
|
||||
// }
|
||||
// delete keys;
|
||||
|
||||
if(this.properties != null) {
|
||||
JSONObject obj;
|
||||
char type[64], key[64];
|
||||
for(int i = 0; i < this.properties.Length; i++) {
|
||||
obj = view_as<JSONObject>(this.properties.Get(i));
|
||||
if(!obj.HasKey("key")) {
|
||||
LogError("Missing \"key\" in property object for entity %d", entity);
|
||||
} else {
|
||||
obj.GetString("type", type, sizeof(type));
|
||||
obj.GetString("key", key, sizeof(key));
|
||||
if(StrEqual(type, "netprop")) {
|
||||
this._ApplyProperty(Prop_Send, entity, key, obj);
|
||||
} else if(StrEqual(type, "datamap")) {
|
||||
this._ApplyProperty(Prop_Data, entity, key, obj);
|
||||
} else if(StrEqual(type, "keyvalue")) {
|
||||
this._ApplyKeyvalue(entity, key, obj);
|
||||
} else if(StrEqual(type, "input")) {
|
||||
this._ApplyInput(entity, key, obj);
|
||||
} else {
|
||||
this._ApplyCustom(entity, key, obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
delete keys;
|
||||
}
|
||||
|
||||
void _ApplyProperty(PropType propType, int entity, const char[] key, JSONObject obj) {
|
||||
char buffer[128];
|
||||
if(obj.HasKey("string")) {
|
||||
obj.GetString("string", buffer, sizeof(buffer));
|
||||
Debug("Setting property %s (val=%s) on %d", key, buffer, entity);
|
||||
SetEntPropString(entity, propType, key, buffer);
|
||||
} else if(obj.HasKey("integer")) {
|
||||
int val = obj.GetInt("integer");
|
||||
Debug("Setting property %s (val=%d) on %d", key, val, entity);
|
||||
SetEntProp(entity, propType, key, val);
|
||||
} else if(obj.HasKey("float")) {
|
||||
float val = obj.GetFloat("float");
|
||||
Debug("Setting property %s (val=%f) on %d", key, val, entity);
|
||||
SetEntPropFloat(entity, propType, key, val);
|
||||
} else {
|
||||
LogError("no value provided for entity %d with key %s", entity, key);
|
||||
}
|
||||
}
|
||||
|
||||
void _ApplyInput(int entity, const char[] key, JSONObject obj) {
|
||||
Debug("Applying input %s on %d", key, entity);
|
||||
TriggerEntityInput(entity, key);
|
||||
}
|
||||
|
||||
void _ApplyCustom(int entity, const char[] key, JSONObject obj) {
|
||||
Debug("_ApplyCustom(%d, \"%s\"): unknown custom key", entity, key);
|
||||
}
|
||||
|
||||
void _ApplyKeyvalue(int entity, const char[] key, JSONObject obj) {
|
||||
if(obj.HasKey("buffer")) {
|
||||
char buffer[255];
|
||||
obj.GetString("string", buffer, sizeof(buffer));
|
||||
Debug("Dispatching kv %s: %s on %d", key, buffer, entity);
|
||||
DispatchKeyValue(entity, key, buffer);
|
||||
} else {
|
||||
LogError("no value provided for entity %d with key %s", entity, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -496,37 +444,41 @@ enum struct VariantInputData {
|
|||
|
||||
void _trigger(int entity) {
|
||||
if(entity > 0 && IsValidEntity(entity)) {
|
||||
if(StrEqual(this.input, "_allow_ladder")) {
|
||||
if(HasEntProp(entity, Prop_Send, "m_iTeamNum")) {
|
||||
SetEntProp(entity, Prop_Send, "m_iTeamNum", 0);
|
||||
} else {
|
||||
Log("Warn: Entity (%d) with id \"%s\" has no teamnum for \"_allow_ladder\"", entity, this.name);
|
||||
}
|
||||
} else if(StrEqual(this.input, "_lock")) {
|
||||
AcceptEntityInput(entity, "Close");
|
||||
AcceptEntityInput(entity, "Lock");
|
||||
} else if(StrEqual(this.input, "_lock_nobreak")) {
|
||||
AcceptEntityInput(entity, "Close");
|
||||
AcceptEntityInput(entity, "Lock");
|
||||
AcceptEntityInput(entity, "SetUnbreakable");
|
||||
} else {
|
||||
char cmd[32];
|
||||
// Split input "a b" to a with variant "b"
|
||||
int len = SplitString(this.input, " ", cmd, sizeof(cmd));
|
||||
if(len > -1) {
|
||||
SetVariantString(this.input[len]);
|
||||
AcceptEntityInput(entity, cmd);
|
||||
Debug("_trigger(%d): %s (v=%s)", entity, cmd, this.input[len]);
|
||||
} else {
|
||||
Debug("_trigger(%d): %s", entity, this.input);
|
||||
AcceptEntityInput(entity, this.input);
|
||||
}
|
||||
|
||||
}
|
||||
TriggerEntityInput(entity, this.input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TriggerEntityInput(int entity, const char[] input) {
|
||||
if(StrEqual(input, "_allow_ladder")) {
|
||||
if(HasEntProp(entity, Prop_Send, "m_iTeamNum")) {
|
||||
SetEntProp(entity, Prop_Send, "m_iTeamNum", 0);
|
||||
} else {
|
||||
Log("Warn: Entity (%d) has no teamnum for \"_allow_ladder\"", entity);
|
||||
}
|
||||
} else if(StrEqual(input, "_lock")) {
|
||||
AcceptEntityInput(entity, "Close");
|
||||
AcceptEntityInput(entity, "Lock");
|
||||
} else if(StrEqual(input, "_lock_nobreak")) {
|
||||
AcceptEntityInput(entity, "Close");
|
||||
AcceptEntityInput(entity, "Lock");
|
||||
AcceptEntityInput(entity, "SetUnbreakable");
|
||||
} else {
|
||||
char cmd[32];
|
||||
// Split input "a b" to a with variant "b"
|
||||
int len = SplitString(input, " ", cmd, sizeof(cmd));
|
||||
if(len > -1) {
|
||||
SetVariantString(input[len]);
|
||||
AcceptEntityInput(entity, cmd);
|
||||
Debug("_trigger(%d): %s (v=%s)", entity, cmd, input[len]);
|
||||
} else {
|
||||
Debug("_trigger(%d): %s", entity, input);
|
||||
AcceptEntityInput(entity, input);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
enum struct LumpEditData {
|
||||
char name[MAX_INPUTS_CLASSNAME_LENGTH];
|
||||
InputType type;
|
||||
|
|
|
@ -259,10 +259,11 @@ void loadChoiceEntity(ArrayList list, JSONObject entityData) {
|
|||
GetVector(entityData, "scale", entity.scale);
|
||||
GetColor(entityData, "color", entity.color, DEFAULT_COLOR);
|
||||
if(entityData.HasKey("properties")) {
|
||||
JSONObject propRoot = view_as<JSONObject>(entityData.Get("properties"));
|
||||
if(propRoot.HasKey("int")) entity.properties.intKv = view_as<JSONObject>(propRoot.Get("int"));
|
||||
if(propRoot.HasKey("float")) entity.properties.floatKv = view_as<JSONObject>(propRoot.Get("float"));
|
||||
if(propRoot.HasKey("string")) entity.properties.stringKv = view_as<JSONObject>(propRoot.Get("string"));
|
||||
entity.properties = view_as<JSONArray>(entityData.Get("properties"));
|
||||
// JSONObject propRoot = view_as<JSONObject>(entityData.Get("properties"));
|
||||
// if(propRoot.HasKey("int")) entity.properties.intKv = view_as<JSONObject>(propRoot.Get("int"));
|
||||
// if(propRoot.HasKey("float")) entity.properties.floatKv = view_as<JSONObject>(propRoot.Get("float"));
|
||||
// if(propRoot.HasKey("string")) entity.properties.stringKv = view_as<JSONObject>(propRoot.Get("string"));
|
||||
}
|
||||
list.PushArray(entity);
|
||||
}
|
82
scripting/include/randomizer/spawn_functions.sp
Normal file
82
scripting/include/randomizer/spawn_functions.sp
Normal file
|
@ -0,0 +1,82 @@
|
|||
int R_CreateFire(VariantEntityData data) {
|
||||
int entity = CreateEntityByName("env_fire");
|
||||
if(entity == -1) return -1;
|
||||
DispatchKeyValue(entity, "spawnflags", "13");
|
||||
DispatchKeyValue(entity, "targetname", ENT_ENV_NAME);
|
||||
DispatchKeyValueFloat(entity, "firesize", 20.0);
|
||||
DispatchKeyValueFloat(entity, "fireattack", 100.0);
|
||||
DispatchKeyValueFloat(entity, "damagescale", 1.0);
|
||||
TeleportEntity(entity, data.origin, NULL_VECTOR, NULL_VECTOR);
|
||||
data.ApplyProperties(entity);
|
||||
DispatchSpawn(entity);
|
||||
AcceptEntityInput(entity, "Enable");
|
||||
AcceptEntityInput(entity, "StartFire");
|
||||
#if defined DEBUG_LOG_MAPSTART
|
||||
|
||||
PrintToServer("spawn env_fire at %.1f %.1f %.1f", pos[0], pos[1], pos[2]);
|
||||
#endif
|
||||
return entity;
|
||||
}
|
||||
|
||||
int R_CreateLight(VariantEntityData data) {
|
||||
int entity = CreateEntityByName("light_dynamic");
|
||||
if(entity == -1) return -1;
|
||||
DispatchKeyValue(entity, "targetname", ENT_PROP_NAME);
|
||||
DispatchKeyValueInt(entity, "brightness", data.color[3]);
|
||||
DispatchKeyValueFloat(entity, "distance", data.scale[0]);
|
||||
DispatchKeyValueFloat(entity, "_inner_cone", data.angles[0]);
|
||||
DispatchKeyValueFloat(entity, "_cone", data.angles[1]);
|
||||
DispatchKeyValueFloat(entity, "pitch", data.angles[2]);
|
||||
// DispatchKeyValueInt()
|
||||
TeleportEntity(entity, data.origin, NULL_VECTOR, NULL_VECTOR);
|
||||
data.ApplyProperties(entity);
|
||||
if(!DispatchSpawn(entity)) return -1;
|
||||
SetEntityRenderColor(entity, data.color[0], data.color[1], data.color[2], data.color[3]);
|
||||
SetEntityRenderMode(entity, RENDER_TRANSCOLOR);
|
||||
AcceptEntityInput(entity, "TurnOn");
|
||||
return entity;
|
||||
}
|
||||
|
||||
int R_CreateEnvBlockerScaled(VariantEntityData data) {
|
||||
int entity = CreateEntityByName(data.type);
|
||||
DispatchKeyValue(entity, "targetname", ENT_BLOCKER_NAME);
|
||||
DispatchKeyValue(entity, "initialstate", "1");
|
||||
DispatchKeyValueInt(entity, "BlockType", StrEqual(data.type, "env_physics_blocker") ? 4 : 0);
|
||||
static float mins[3];
|
||||
mins = data.scale;
|
||||
NegateVector(mins);
|
||||
DispatchKeyValueVector(entity, "boxmins", mins);
|
||||
DispatchKeyValueVector(entity, "boxmaxs", data.scale);
|
||||
DispatchKeyValueVector(entity, "mins", mins);
|
||||
DispatchKeyValueVector(entity, "maxs", data.scale);
|
||||
|
||||
TeleportEntity(entity, data.origin, NULL_VECTOR, NULL_VECTOR);
|
||||
data.ApplyProperties(entity);
|
||||
if(DispatchSpawn(entity)) {
|
||||
#if defined DEBUG_LOG_MAPSTART
|
||||
PrintToServer("spawn blocker scaled %.1f %.1f %.1f scale [%.0f %.0f %.0f]", pos[0], pos[1], pos[2], scale[0], scale[1], scale[2]);
|
||||
#endif
|
||||
SetEntPropVector(entity, Prop_Send, "m_vecMaxs", data.scale);
|
||||
SetEntPropVector(entity, Prop_Send, "m_vecMins", mins);
|
||||
AcceptEntityInput(entity, "Enable");
|
||||
#if defined DEBUG_BLOCKERS
|
||||
Effect_DrawBeamBoxRotatableToAll(data.origin, mins, data.scale, NULL_VECTOR, g_iLaserIndex, 0, 0, 0, 150.0, 0.1, 0.1, 0, 0.0, {255, 0, 0, 255}, 0);
|
||||
#endif
|
||||
return entity;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void R_CreateDecal(VariantEntityData data) {
|
||||
CreateDecal(data.model, data.origin);
|
||||
}
|
||||
|
||||
int R_CreateProp(VariantEntityData data) {
|
||||
int entity = StartPropCreate(data.type, data.model, data.origin, data.angles, NULL_VECTOR);
|
||||
if(entity == -1) return -1;
|
||||
data.ApplyProperties(entity);
|
||||
if(DispatchSpawn(entity)) {
|
||||
return entity;
|
||||
}
|
||||
return -1;
|
||||
}
|
|
@ -99,6 +99,7 @@ static char g_currentGamemode[32];
|
|||
static bool g_isGamemodeAllowed;
|
||||
int g_survivorCount, g_realSurvivorCount;
|
||||
bool g_isFinaleEnding;
|
||||
int g_finaleVehicleStartTime;
|
||||
static bool g_epiEnabled;
|
||||
bool g_isOfficialMap;
|
||||
|
||||
|
@ -230,14 +231,15 @@ enum struct Cabinet {
|
|||
}
|
||||
static Cabinet cabinets[10]; //Store 10 cabinets
|
||||
|
||||
enum FinaleStage {
|
||||
enum EPI_FinaleTankState {
|
||||
Stage_Inactive = 0,
|
||||
Stage_Active = 1, // Finale has started
|
||||
Stage_FirstTankSpawned = 2,
|
||||
Stage_SecondTankSpawned = 3,
|
||||
Stage_ActiveDone = 10 // No more logic to be done
|
||||
}
|
||||
FinaleStage g_finaleStage;
|
||||
EPI_FinaleTankState g_epiTankState;
|
||||
int g_finaleState;
|
||||
|
||||
//// Definitions completSe
|
||||
|
||||
|
@ -295,6 +297,7 @@ public void OnPluginStart() {
|
|||
HookEvent("finale_vehicle_incoming", Event_FinaleVehicleIncoming);
|
||||
HookEvent("player_bot_replace", Event_PlayerToIdle);
|
||||
HookEvent("bot_player_replace", Event_PlayerFromIdle);
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -384,7 +387,9 @@ public void OnPluginStart() {
|
|||
}
|
||||
|
||||
void Event_FinaleVehicleIncoming(Event event, const char[] name, bool dontBroadcast) {
|
||||
PrintDebug(DEBUG_INFO, "Finale vehicle incoming, preventing early tank spawns");
|
||||
g_isFinaleEnding = true;
|
||||
g_finaleVehicleStartTime = GetTime();
|
||||
}
|
||||
|
||||
Action Timer_ForceUpdateInventories(Handle h) {
|
||||
|
@ -1331,6 +1336,7 @@ void Debug_GetAttributes(int attributes, char[] output, int maxlen) {
|
|||
}
|
||||
|
||||
public void L4D2_OnChangeFinaleStage_Post(int stage) {
|
||||
g_finaleState = stage;
|
||||
if(stage == 1 && IsEPIActive()) {
|
||||
IncreaseKits(true);
|
||||
}
|
||||
|
@ -1387,7 +1393,7 @@ public void OnMapStart() {
|
|||
HookEntityOutput("info_changelevel", "OnStartTouch", EntityOutput_OnStartTouchSaferoom);
|
||||
HookEntityOutput("trigger_changelevel", "OnStartTouch", EntityOutput_OnStartTouchSaferoom);
|
||||
|
||||
g_finaleStage = Stage_Inactive;
|
||||
g_epiTankState = Stage_Inactive;
|
||||
|
||||
L4D2_RunScript(HUD_SCRIPT_CLEAR);
|
||||
Director_OnMapStart();
|
||||
|
@ -1488,11 +1494,12 @@ public void OnMapEnd() {
|
|||
}
|
||||
}
|
||||
delete updateHudTimer;
|
||||
g_finaleVehicleStartTime = 0;
|
||||
Director_OnMapEnd();
|
||||
}
|
||||
|
||||
void Event_FinaleStart(Event event, const char[] name, bool dontBroadcast) {
|
||||
g_finaleStage = Stage_Active;
|
||||
g_epiTankState = Stage_Active;
|
||||
}
|
||||
public void OnClientSpeaking(int client) {
|
||||
g_isSpeaking[client] = true;
|
||||
|
@ -1836,7 +1843,6 @@ void PopulateItemSpawns(int minWalls = 4) {
|
|||
navs.Sort(Sort_Random, Sort_Integer);
|
||||
float pos[3];
|
||||
float percentage = hExtraSpawnBasePercentage.FloatValue * (g_survivorCount - 4);
|
||||
PrintToServer("[EPI] Populating extra item spawns based on player count (%d-4) | Percentage %.2f%%", g_survivorCount, percentage * 100);
|
||||
int tier;
|
||||
// On first chapter, 10% chance to give tier 2
|
||||
if(g_currentChapter == 1) tier = GetRandomFloat() < 0.15 ? 1 : 0;
|
||||
|
@ -1844,10 +1850,11 @@ void PopulateItemSpawns(int minWalls = 4) {
|
|||
int count;
|
||||
|
||||
float mapFlowMax = L4D2Direct_GetMapMaxFlowDistance();
|
||||
PrintToServer("[EPI] PopulateItemSpawns: flow[0, %f]", mapFlowMax);
|
||||
int maxSpawns = RoundFloat(mapFlowMax / MAX_RANDOM_SPAWNS);
|
||||
int defibCount = CalculateExtraDefibCount();
|
||||
bool isFinale = L4D_IsMissionFinalMap();
|
||||
PrintToServer("[EPI] Populating extra item spawns based on player count (%d-4) | Percentage %.2f%%", g_survivorCount, percentage * 100);
|
||||
PrintToServer("[EPI] PopulateItemSpawns: flow[0, %f] tier=%d maxSpawns=%d defibCount=%d", mapFlowMax, tier, maxSpawns, defibCount);
|
||||
|
||||
for(int i = 0; i < navs.Length; i++) {
|
||||
Address nav = navs.Get(i);
|
||||
|
|
|
@ -39,6 +39,7 @@ bool randomizerRan = false;
|
|||
#include <randomizer/util.sp>
|
||||
#include <randomizer/loader_functions.sp>
|
||||
#include <randomizer/select_functions.sp>
|
||||
#include <randomizer/spawn_functions.sp>
|
||||
#include <randomizer/rbuild.sp>
|
||||
#include <randomizer/caralarm.sp>
|
||||
|
||||
|
@ -725,24 +726,6 @@ void AssignGascan(int gascan, GascanSpawnerData spawner) {
|
|||
Debug("Assigning gascan %d to spawner at %.0f %.0f %.0f", gascan, spawner.origin[0], spawner.origin[1], spawner.origin[2]);
|
||||
}
|
||||
|
||||
int CreateLight(const float origin[3], const float angles[3], const int color[4] = { 255, 255, 255, 1 }, float distance = 100.0) {
|
||||
int entity = CreateEntityByName("light_dynamic");
|
||||
if(entity == -1) return -1;
|
||||
DispatchKeyValue(entity, "targetname", ENT_PROP_NAME);
|
||||
DispatchKeyValueInt(entity, "brightness", color[3]);
|
||||
DispatchKeyValueFloat(entity, "distance", distance);
|
||||
DispatchKeyValueFloat(entity, "_inner_cone", angles[0]);
|
||||
DispatchKeyValueFloat(entity, "_cone", angles[1]);
|
||||
DispatchKeyValueFloat(entity, "pitch", angles[2]);
|
||||
// DispatchKeyValueInt()
|
||||
TeleportEntity(entity, origin, NULL_VECTOR, NULL_VECTOR);
|
||||
if(!DispatchSpawn(entity)) return -1;
|
||||
SetEntityRenderColor(entity, color[0], color[1], color[2], color[3]);
|
||||
SetEntityRenderMode(entity, RENDER_TRANSCOLOR);
|
||||
AcceptEntityInput(entity, "TurnOn");
|
||||
return entity;
|
||||
}
|
||||
|
||||
void AddGascanSpawner(VariantEntityData data) {
|
||||
if(g_MapData.gascanSpawners == null) {
|
||||
g_MapData.gascanSpawners = new ArrayList(sizeof(GascanSpawnerData));
|
||||
|
@ -756,45 +739,14 @@ void AddGascanSpawner(VariantEntityData data) {
|
|||
}
|
||||
|
||||
void spawnEntity(VariantEntityData entity) {
|
||||
// if(entity.type[0] == '_') {
|
||||
// if(StrEqual(entity.type, "_gascan")) {
|
||||
// AddGascanSpawner(entity);
|
||||
// }
|
||||
// }
|
||||
if(StrEqual(entity.type, "_gascan")) {
|
||||
AddGascanSpawner(entity);
|
||||
} else if(StrEqual(entity.type, "env_fire")) {
|
||||
Debug("spawning \"%s\" at (%.1f %.1f %.1f) rot (%.0f %.0f %.0f)", entity.type, entity.origin[0], entity.origin[1], entity.origin[2], entity.angles[0], entity.angles[1], entity.angles[2]);
|
||||
CreateFire(entity.origin, 20.0, 100.0, 1.0);
|
||||
} else if(StrEqual(entity.type, "light_dynamic")) {
|
||||
CreateLight(entity.origin, entity.angles, entity.color, entity.scale[0]);
|
||||
Effect_DrawBeamBoxRotatableToAll(entity.origin, { -5.0, -5.0, -5.0}, { 5.0, 5.0, 5.0}, NULL_VECTOR, g_iLaserIndex, 0, 0, 0, 40.0, 0.1, 0.1, 0, 0.0, {255, 255, 0, 255}, 0);
|
||||
} else if(StrEqual(entity.type, "env_physics_blocker") || StrEqual(entity.type, "env_player_blocker")) {
|
||||
CreateEnvBlockerScaled(entity.type, entity.origin, entity.scale);
|
||||
} else if(StrEqual(entity.type, "infodecal")) {
|
||||
Effect_DrawBeamBoxRotatableToAll(entity.origin, { -1.0, -5.0, -5.0}, { 1.0, 5.0, 5.0}, NULL_VECTOR, g_iLaserIndex, 0, 0, 0, 40.0, 0.1, 0.1, 0, 0.0, {73, 0, 130, 255}, 0);
|
||||
CreateDecal(entity.model, entity.origin);
|
||||
} else if(StrContains(entity.type, "prop_") == 0 || StrEqual(entity.type, "prop_fuel_barrel")) {
|
||||
if(entity.model[0] == '\0') {
|
||||
LogError("Missing model for entity with type \"%s\"", entity.type);
|
||||
return;
|
||||
} else if(!PrecacheModel(entity.model)) {
|
||||
LogError("Precache of entity model \"%s\" with type \"%s\" failed", entity.model, entity.type);
|
||||
return;
|
||||
if(entity.type[0] == '_') {
|
||||
if(StrEqual(entity.type, "_gascan")) {
|
||||
AddGascanSpawner(entity);
|
||||
} else if(StrContains(entity.type, "_car") != -1) {
|
||||
SpawnCar(entity);
|
||||
} else {
|
||||
Log("WARN: Unknown custom entity type \"%s\", skipped", entity.type);
|
||||
}
|
||||
int prop = CreateProp(entity.type, entity.model, entity.origin, entity.angles);
|
||||
SetEntityRenderColor(prop, entity.color[0], entity.color[1], entity.color[2], entity.color[3]);
|
||||
entity.ApplyProperties(prop);
|
||||
} else if(StrContains(entity.type, "weapon_") == 0) {
|
||||
if(entity.model[0] == '\0') {
|
||||
LogError("Missing model for entity with type \"%s\"", entity.type);
|
||||
return;
|
||||
} else if(!PrecacheModel(entity.model)) {
|
||||
LogError("Precache of entity model \"%s\" with type \"%s\" failed", entity.model, entity.type);
|
||||
return;
|
||||
}
|
||||
int prop = CreateProp(entity.type, entity.model, entity.origin, entity.angles);
|
||||
SetEntityRenderColor(prop, entity.color[0], entity.color[1], entity.color[2], entity.color[3]);
|
||||
} else if(StrEqual(entity.type, "hammerid")) {
|
||||
int targetId = StringToInt(entity.model);
|
||||
if(targetId > 0) {
|
||||
|
@ -834,8 +786,26 @@ void spawnEntity(VariantEntityData entity) {
|
|||
}
|
||||
if(!found)
|
||||
Debug("Warn: Could not find entity (classname=%s)", entity.model);
|
||||
} else if(StrContains(entity.type, "_car") != -1) {
|
||||
SpawnCar(entity);
|
||||
} else if(StrEqual(entity.type, "env_fire")) {
|
||||
Debug("spawning \"%s\" at (%.1f %.1f %.1f) rot (%.0f %.0f %.0f)", entity.type, entity.origin[0], entity.origin[1], entity.origin[2], entity.angles[0], entity.angles[1], entity.angles[2]);
|
||||
R_CreateFire(entity);
|
||||
} else if(StrEqual(entity.type, "light_dynamic")) {
|
||||
R_CreateLight(entity);
|
||||
Effect_DrawBeamBoxRotatableToAll(entity.origin, { -5.0, -5.0, -5.0}, { 5.0, 5.0, 5.0}, NULL_VECTOR, g_iLaserIndex, 0, 0, 0, 40.0, 0.1, 0.1, 0, 0.0, {255, 255, 0, 255}, 0);
|
||||
} else if(StrEqual(entity.type, "env_physics_blocker") || StrEqual(entity.type, "env_player_blocker")) {
|
||||
R_CreateEnvBlockerScaled(entity);
|
||||
} else if(StrEqual(entity.type, "infodecal")) {
|
||||
Effect_DrawBeamBoxRotatableToAll(entity.origin, { -1.0, -5.0, -5.0}, { 1.0, 5.0, 5.0}, NULL_VECTOR, g_iLaserIndex, 0, 0, 0, 40.0, 0.1, 0.1, 0, 0.0, {73, 0, 130, 255}, 0);
|
||||
R_CreateDecal(entity);
|
||||
} else if(StrContains(entity.type, "weapon_") == 0 || StrContains(entity.type, "prop_") == 0 || StrEqual(entity.type, "prop_fuel_barrel")) {
|
||||
if(entity.model[0] == '\0') {
|
||||
LogError("Missing model for entity with type \"%s\"", entity.type);
|
||||
return;
|
||||
} else if(!PrecacheModel(entity.model)) {
|
||||
LogError("Precache of entity model \"%s\" with type \"%s\" failed", entity.model, entity.type);
|
||||
return;
|
||||
}
|
||||
R_CreateProp(entity);
|
||||
} else if(StrEqual(entity.type, "move_rope")) {
|
||||
if(!PrecacheModel(entity.model)) {
|
||||
LogError("Precache of entity model \"%s\" with type \"%s\" failed", entity.model, entity.type);
|
||||
|
@ -847,7 +817,7 @@ void spawnEntity(VariantEntityData entity) {
|
|||
}
|
||||
CreateRope(entity);
|
||||
} else {
|
||||
LogError("Unknown entity type \"%s\"", entity.type);
|
||||
LogError("Unsupported entity type \"%s\"", entity.type);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -897,6 +867,10 @@ int _CreateRope(const char[] type, const char[] targetname, const char[] nextKey
|
|||
return entity;
|
||||
}
|
||||
|
||||
// void DebugBox(const float origin[3], const float scale[3], int color[4]) {
|
||||
// Effect_DrawBeamBoxRotatableToAll(entity.origin, { -5.0, -5.0, -5.0}, { 5.0, 5.0, 5.0}, NULL_VECTOR, g_iLaserIndex, 0, 0, 0, 40.0, 0.1, 0.1, 0, 0.0, {255, 255, 0, 255}, 0);
|
||||
// }
|
||||
|
||||
void Debug(const char[] format, any ...) {
|
||||
#if defined DEBUG_SCENE_PARSE
|
||||
char buffer[192];
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue