From 07360e59e378317cc3aa548bffc6824b7d04e5bd Mon Sep 17 00:00:00 2001 From: Jackz Date: Fri, 26 May 2023 08:02:59 -0500 Subject: [PATCH] Support editing lump --- scripting/l4d2_randomizer.sp | 639 ++++++++++++++++++++++++++++++----- 1 file changed, 546 insertions(+), 93 deletions(-) diff --git a/scripting/l4d2_randomizer.sp b/scripting/l4d2_randomizer.sp index b877154..f408ee7 100644 --- a/scripting/l4d2_randomizer.sp +++ b/scripting/l4d2_randomizer.sp @@ -5,6 +5,7 @@ #define PLUGIN_VERSION "1.0" #define DEBUG_SCENE_PARSE 1 +#define DEBUG_BLOCKERS 1 #include #include @@ -12,9 +13,20 @@ #include #include #include +#include + +int g_iLaserIndex; +#if defined DEBUG_BLOCKERS +#include +#endif #define ENT_PROP_NAME "l4d2_randomizer" +#define ENT_ENV_NAME "l4d2_randomizer" +#define ENT_BLOCKER_NAME "l4d2_randomizer" #include +#define MAX_SCENE_NAME_LENGTH 32 +#define MAX_INPUTS_CLASSNAME_LENGTH 64 + public Plugin myinfo = { name = "L4D2 Randomizer", @@ -25,6 +37,11 @@ public Plugin myinfo = }; ConVar cvarEnabled; +enum struct ActiveSceneData { + char name[MAX_SCENE_NAME_LENGTH]; + int variantIndex; +} +MapData g_MapData; public void OnPluginStart() { EngineVersion g_Game = GetEngineVersion(); @@ -35,15 +52,53 @@ public void OnPluginStart() { RegAdminCmd("sm_rcycle", Command_CycleRandom, ADMFLAG_CHEATS); RegAdminCmd("sm_expent", Command_ExportEnt, ADMFLAG_GENERIC); + HookEvent("round_start", Event_RoundStart); + cvarEnabled = CreateConVar("sm_randomizer_enabled", "0"); + + g_MapData.activeScenes = new ArrayList(sizeof(ActiveSceneData)); +} + +char currentMap[64]; + +// TODO: on round start +public void OnMapStart() { + g_iLaserIndex = PrecacheModel("materials/sprites/laserbeam.vmt", true); + GetCurrentMap(currentMap, sizeof(currentMap)); +} + +public void OnMapInit(const char[] map) { + if(cvarEnabled.BoolValue) { + if(LoadMapData(currentMap, FLAG_NONE) && g_MapData.lumpEdits.Length > 0) { + Log("Found %d lump edits, running...", g_MapData.lumpEdits.Length); + LumpEditData lump; + for(int i = 0; i < g_MapData.lumpEdits.Length; i++) { + g_MapData.lumpEdits.GetArray(i, lump); + lump.Trigger(); + } + } + } +} + +void Event_RoundStart(Event event, const char[] name, bool dontBroadcast) { + if(cvarEnabled.BoolValue) { + CreateTimer(10.0, Timer_LoadMap); + } +} + +Action Timer_LoadMap(Handle h) { + if(cvarEnabled.BoolValue) { + RunMap(currentMap, FLAG_NONE); + } + return Plugin_Handled; } public void OnMapEnd() { - DeleteCustomEnts(); + Cleanup(); } -int GetLookingEntity(int client, TraceEntityFilter filter) { - static float pos[3], ang[3]; +stock int GetLookingEntity(int client, TraceEntityFilter filter) { + float pos[3], ang[3]; GetClientEyePosition(client, pos); GetClientEyeAngles(client, ang); TR_TraceRayFilter(pos, ang, MASK_SOLID, RayType_Infinite, filter, client); @@ -53,57 +108,96 @@ int GetLookingEntity(int client, TraceEntityFilter filter) { return -1; } +stock int GetLookingPosition(int client, TraceEntityFilter filter, float pos[3]) { + float ang[3]; + GetClientEyePosition(client, pos); + GetClientEyeAngles(client, ang); + TR_TraceRayFilter(pos, ang, MASK_SOLID, RayType_Infinite, filter, client); + if(TR_DidHit()) { + TR_GetEndPosition(pos); + return TR_GetEntityIndex(); + } + return -1; +} + + public Action Command_CycleRandom(int client, int args) { - DeleteCustomEnts(); - char map[64]; - GetCurrentMap(map, sizeof(map)); - LoadMap(map); - ReplyToCommand(client, "Done."); + if(args > 0) { + DeleteCustomEnts(); + + char arg1[8]; + GetCmdArg(1, arg1, sizeof(arg1)); + int flags = StringToInt(arg1) | view_as(FLAG_REFRESH); + RunMap(currentMap, flags); + if(client > 0) + PrintCenterText(client, "Cycled flags=%d", flags); + } else { + ReplyToCommand(client, "Active Scenes:"); + ActiveSceneData scene; + for(int i = 0; i < g_MapData.activeScenes.Length; i++) { + g_MapData.activeScenes.GetArray(i, scene); + ReplyToCommand(client, "\t%s: variant #%d", scene.name, scene.variantIndex); + } + } return Plugin_Handled; } public Action Command_ExportEnt(int client, int args) { - int entity = GetLookingEntity(client, Filter_IgnorePlayer); + float origin[3]; + int entity = GetLookingPosition(client, Filter_IgnorePlayer, origin); + float angles[3]; + float size[3]; + char arg1[32]; + GetCmdArg(1, arg1, sizeof(arg1)); if(entity > 0) { - float origin[3]; - float angles[3]; - float size[3]; + GetEntPropVector(entity, Prop_Send, "m_vecOrigin", origin); GetEntPropVector(entity, Prop_Send, "m_angRotation", angles); GetEntPropVector(entity, Prop_Send, "m_vecMaxs", size); char model[64]; - GetEntPropString(entity, Prop_Data, "m_ModelName", model, sizeof(model)); - ReplyToCommand(client, "{"); - ReplyToCommand(client, "\t\"model\": \"%s\",", model); + GetEntityClassname(entity, model, sizeof(model)); + if(StrContains(model, "prop_") == -1) { + ReplyToCommand(client, "\t\"scale\": [%.2f, %.2f, %.2f],", size[0], size[1], size[2]); + } + if(StrEqual(arg1, "hammerid")) { + int hammerid = GetEntProp(entity, Prop_Data, "m_iHammerID"); + ReplyToCommand(client, "\t\"type\": \"hammerid\","); + ReplyToCommand(client, "\t\"model\": \"%d\",", hammerid); + } else if(StrEqual(arg1, "targetname")) { + GetEntPropString(entity, Prop_Data, "m_iName", model, sizeof(model)); + ReplyToCommand(client, "\t\"type\": \"targetname\","); + ReplyToCommand(client, "\t\"model\": \"%s\",", model); + } else { + GetEntPropString(entity, Prop_Data, "m_ModelName", model, sizeof(model)); + ReplyToCommand(client, "\t\"model\": \"%s\",", model); + } ReplyToCommand(client, "\t\"origin\": [%.2f, %.2f, %.2f],", origin[0], origin[1], origin[2]); - ReplyToCommand(client, "\t\"angles\": [%.2f, %.2f, %.2f],", angles[0], angles[1], angles[2]); - ReplyToCommand(client, "\t\"size\": [%.2f, %.2f, %.2f]", size[0], size[1], size[2]); + ReplyToCommand(client, "\t\"angles\": [%.2f, %.2f, %.2f]", angles[0], angles[1], angles[2]); ReplyToCommand(client, "}"); } else { - PrintCenterText(client, "No entity found"); + if(!StrEqual(arg1, "cursor")) + GetEntPropVector(client, Prop_Send, "m_vecOrigin", origin); + GetEntPropVector(client, Prop_Send, "m_angRotation", angles); + ReplyToCommand(client, "{"); + ReplyToCommand(client, "\t\"type\": \"%s\",", arg1); + ReplyToCommand(client, "\t\"scale\": [%.2f, %.2f, %.2f],", size[0], size[1], size[2]); + ReplyToCommand(client, "\t\"origin\": [%.2f, %.2f, %.2f],", origin[0], origin[1], origin[2]); + ReplyToCommand(client, "\t\"angles\": [%.2f, %.2f, %.2f]", angles[0], angles[1], angles[2]); + ReplyToCommand(client, "}"); } return Plugin_Handled; } -public void OnMapStart() { - if(cvarEnabled.BoolValue) { - char map[64]; - GetCurrentMap(map, sizeof(map)); - LoadMap(map); - } -} -#define MAX_SCENE_NAME_LENGTH 32 enum struct SceneData { char name[MAX_SCENE_NAME_LENGTH]; float chance; - ArrayList exclusions; + char group[MAX_SCENE_NAME_LENGTH]; ArrayList variants; void Cleanup() { - delete this.exclusions; SceneVariantData choice; for(int i = 0; i < this.variants.Length; i++) { this.variants.GetArray(i, choice); @@ -115,26 +209,178 @@ enum struct SceneData { enum struct SceneVariantData { int weight; + ArrayList inputsList; ArrayList entities; void Cleanup() { + delete this.inputsList; delete this.entities; } } enum struct VariantEntityData { - char type[16]; + char type[32]; char model[64]; float origin[3]; float angles[3]; float scale[3]; + int color[4]; } -ArrayList scenes; +enum InputType { + Input_Classname, + Input_Targetname, + Input_HammerId +} +enum struct VariantInputData { + char name[MAX_INPUTS_CLASSNAME_LENGTH]; + InputType type; + char input[32]; -// Parses (mapname).json and runs chances -public bool LoadMap(const char[] map) { + void Trigger() { + int entity = -1; + switch(this.type) { + case Input_Classname: { + entity = FindEntityByClassname(entity, this.name); + this._trigger(entity); + } + case Input_Targetname: { + char targetname[32]; + while((entity = FindEntityByClassname(entity, "*")) != INVALID_ENT_REFERENCE) { + GetEntPropString(entity, Prop_Data, "m_iName", targetname, sizeof(targetname)); + if(StrEqual(targetname, this.name)) { + this._trigger(entity); + } + } + } + case Input_HammerId: { + int targetId = StringToInt(this.name); + while((entity = FindEntityByClassname(entity, "*")) != INVALID_ENT_REFERENCE) { + int hammerId = GetEntProp(entity, Prop_Data, "m_iHammerID"); + if(hammerId == targetId ) { + this._trigger(entity); + break; + } + } + } + } + } + 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]); + + Debug("_trigger(%d): %s (v=%s)", entity, this.input, cmd); + AcceptEntityInput(entity, this.input); + } + } + } +} + +enum struct LumpEditData { + char name[MAX_INPUTS_CLASSNAME_LENGTH]; + InputType type; + char action[32]; + char value[64]; + + int _findLumpIndex(int startIndex = 0, EntityLumpEntry entry) { + int length = EntityLump.Length(); + char val[64]; + Debug("Scanning for \"%s\" (type=%d)", this.name, this.type); + for(int i = startIndex; i < length; i++) { + entry = EntityLump.Get(i); + int index = entry.FindKey("hammerid"); + if(index != -1) { + entry.Get(index, "", 0, val, sizeof(val)); + if(StrEqual(val, this.name)) { + return i; + } + } + + index = entry.FindKey("classname"); + if(index != -1) { + entry.Get(index, "", 0, val, sizeof(val)); + Debug("%s vs %s", val, this.name); + if(StrEqual(val, this.name)) { + return i; + } + } + + index = entry.FindKey("targetname"); + if(index != -1) { + entry.Get(index, "", 0, val, sizeof(val)); + if(StrEqual(val, this.name)) { + return i; + } + } + delete entry; + } + Log("Warn: Could not find any matching lump for \"%s\" (type=%d)", this.name, this.type); + return -1; + } + + void Trigger() { + int index = 0; + EntityLumpEntry entry; + while((index = this._findLumpIndex(index, entry) != -1)) { + // for(int i = 0; i < entry.Length; i++) { + // entry.Get(i, a, sizeof(a), v, sizeof(v)); + // Debug("%s=%s", a, v); + // } + this._trigger(entry); + } + } + + void _updateKey(EntityLumpEntry entry, const char[] key, const char[] value) { + int index = entry.FindKey(key); + if(index != -1) { + Debug("update key %s = %s", key, value); + entry.Update(index, key, value); + } + } + + void _trigger(EntityLumpEntry entry) { + if(StrEqual(this.action, "setclassname")) { + this._updateKey(entry, "classname", this.value); + } + + delete entry; + } +} + +enum struct MapData { + ArrayList scenes; + ArrayList lumpEdits; + ArrayList activeScenes; +} + +enum loadFlags { + FLAG_NONE = 0, + FLAG_ALL_SCENES = 1, // Pick all scenes, no random chance + FLAG_ALL_VARIANTS = 2, // Pick all variants (for debug purposes), + FLAG_REFRESH = 4, // Load data bypassing cache +} + +// Reads (mapname).json file and parses it +public bool LoadMapData(const char[] map, int flags) { + Debug("Loading config for %s", map); char filePath[PLATFORM_MAX_PATH]; BuildPath(Path_SM, filePath, sizeof(filePath), "data/randomizer/%s.json", map); if(!FileExists(filePath)) { @@ -145,19 +391,24 @@ public bool LoadMap(const char[] map) { char buffer[65536]; File file = OpenFile(filePath, "r"); if(file == null) { - LogError("[Randomizer] Could not open map config file (data/randomizer/%s.json)", map); + LogError("Could not open map config file (data/randomizer/%s.json)", map); return false; } file.ReadString(buffer, sizeof(buffer)); JSON_Object data = json_decode(buffer); if(data == null) { json_get_last_error(buffer, sizeof(buffer)); - LogError("[Randomizer] Could not parse map config file (data/randomizer/%s.json): %s", map, buffer); + LogError("Could not parse map config file (data/randomizer/%s.json): %s", map, buffer); delete file; return false; } + + Debug("Starting parsing json data"); + Cleanup(); - scenes = new ArrayList(sizeof(SceneData)); + g_MapData.scenes = new ArrayList(sizeof(SceneData)); + g_MapData.lumpEdits = new ArrayList(sizeof(LumpEditData)); + g_MapData.activeScenes.Clear(); Profiler profiler = new Profiler(); profiler.Start(); @@ -166,30 +417,53 @@ public bool LoadMap(const char[] map) { char key[32]; for (int i = 0; i < length; i += 1) { data.GetKey(i, key, sizeof(key)); - if(data.GetType(key) != JSON_Type_Object) continue; - JSON_Object scene = data.GetObject(key); - // Parses scene data and inserts to scenes - loadGroup(key, scene); + if(key[0] == '_') { + if(StrEqual(key, "_lumps")) { + JSON_Array lumpsList = view_as(data.GetObject(key)); + if(lumpsList != null) { + for(int l = 0; l < lumpsList.Length; l++) { + loadLumpData(g_MapData.lumpEdits, lumpsList.GetObject(l)); + } + } + } else { + Debug("Unknown special entry \"%s\", skipping", key); + } + } else { + if(data.GetType(key) != JSON_Type_Object) { + Debug("Invalid normal entry \"%s\" (not an object), skipping", key); + continue; + } + JSON_Object scene = data.GetObject(key); + // Parses scene data and inserts to scenes + loadScene(key, scene); + } } - profiler.Stop(); - Log("Loaded %d scenes in %.1f seconds", scenes.Length, profiler.Time); - profiler.Start(); - - processGroups(); - - profiler.Stop(); - Log("Done processing in %.1f seconds", scenes.Length, profiler.Time); - json_cleanup_and_delete(data); - + profiler.Stop(); + Log("Parsed map file and found %d scenes in %.4f seconds", g_MapData.scenes.Length, profiler.Time); delete profiler; delete file; return true; } -void loadGroup(const char key[MAX_SCENE_NAME_LENGTH], JSON_Object sceneData) { +// Calls LoadMapData (read&parse (mapname).json) then select scenes +public bool RunMap(const char[] map, int flags) { + if(g_MapData.scenes == null || flags & view_as(FLAG_REFRESH)) { + LoadMapData(map, flags); + } + Profiler profiler = new Profiler(); + + profiler.Start(); + selectScenes(flags); + profiler.Stop(); + + Log("Done processing in %.4f seconds", g_MapData.scenes.Length, profiler.Time); + return true; +} + +void loadScene(const char key[MAX_SCENE_NAME_LENGTH], JSON_Object sceneData) { SceneData scene; scene.name = key; scene.chance = sceneData.GetFloat("chance"); @@ -197,46 +471,102 @@ void loadGroup(const char key[MAX_SCENE_NAME_LENGTH], JSON_Object sceneData) { LogError("Scene \"%s\" has invalid chance (%f)", scene.name, scene.chance); return; } - scene.exclusions = new ArrayList(ByteCountToCells(MAX_SCENE_NAME_LENGTH)); - JSON_Array exclusions = view_as(sceneData.GetObject("exclusions")); - if(exclusions != null) { - char id[MAX_SCENE_NAME_LENGTH]; - for(int i = 0; i < exclusions.Length; i ++) { - exclusions.GetString(i, id, sizeof(id)); - scene.exclusions.PushString(id); - } - } + sceneData.GetString("group", scene.group, sizeof(scene.group)); scene.variants = new ArrayList(sizeof(SceneVariantData)); JSON_Array variants = view_as(sceneData.GetObject("variants")); for(int i = 0; i < variants.Length; i++) { // Parses choice and loads to scene.choices loadChoice(scene, variants.GetObject(i)); } - scenes.PushArray(scene); + g_MapData.scenes.PushArray(scene); } void loadChoice(SceneData scene, JSON_Object choiceData) { SceneVariantData choice; choice.weight = choiceData.GetInt("weight", 1); choice.entities = new ArrayList(sizeof(VariantEntityData)); + choice.inputsList = new ArrayList(sizeof(VariantInputData)); JSON_Array entities = view_as(choiceData.GetObject("entities")); - for(int i = 0; i < entities.Length; i++) { - // Parses entities and loads to choice.entities - loadChoiceEntity(choice, entities.GetObject(i)); + if(entities != null) { + for(int i = 0; i < entities.Length; i++) { + // Parses entities and loads to choice.entities + loadChoiceEntity(choice.entities, entities.GetObject(i)); + } + } + JSON_Array inputsList = view_as(choiceData.GetObject("inputs")); + if(inputsList != null) { + for(int i = 0; i < inputsList.Length; i++) { + loadChoiceInput(choice.inputsList, inputsList.GetObject(i)); + } } scene.variants.PushArray(choice); } -void loadChoiceEntity(SceneVariantData choice, JSON_Object entityData) { +void loadChoiceInput(ArrayList list, JSON_Object inputData) { + VariantInputData input; + // Check classname -> targetname -> hammerid + if(!inputData.GetString("classname", input.name, sizeof(input.name))) { + if(inputData.GetString("targetname", input.name, sizeof(input.name))) { + input.type = Input_Targetname; + } else { + if(inputData.GetString("hammerid", input.name, sizeof(input.name))) { + input.type = Input_HammerId; + } else { + int id = inputData.GetInt("hammerid"); + if(id > 0) { + input.type = Input_HammerId; + IntToString(id, input.name, sizeof(input.name)); + } else { + LogError("Missing valid input specification (hammerid, classname, targetname)"); + return; + } + } + } + } + inputData.GetString("input", input.input, sizeof(input.input)); + list.PushArray(input); +} + +void loadLumpData(ArrayList list, JSON_Object inputData) { + LumpEditData input; + // Check classname -> targetname -> hammerid + if(!inputData.GetString("classname", input.name, sizeof(input.name))) { + if(inputData.GetString("targetname", input.name, sizeof(input.name))) { + input.type = Input_Targetname; + } else { + if(inputData.GetString("hammerid", input.name, sizeof(input.name))) { + input.type = Input_HammerId; + } else { + int id = inputData.GetInt("hammerid"); + if(id > 0) { + input.type = Input_HammerId; + IntToString(id, input.name, sizeof(input.name)); + } else { + LogError("Missing valid input specification (hammerid, classname, targetname)"); + return; + } + } + } + } + inputData.GetString("action", input.action, sizeof(input.action)); + inputData.GetString("value", input.value, sizeof(input.value)); + list.PushArray(input); +} + +void loadChoiceEntity(ArrayList list, JSON_Object entityData) { VariantEntityData entity; entityData.GetString("model", entity.model, sizeof(entity.model)); if(!entityData.GetString("type", entity.type, sizeof(entity.type))) { entity.type = "prop_dynamic"; + } else if(entity.type[0] == '_') { + LogError("Invalid custom entity type \"%s\"", entity.type); + return; } GetVector(entityData, "origin", entity.origin); GetVector(entityData, "angles", entity.angles); GetVector(entityData, "scale", entity.scale); - choice.entities.PushArray(entity); + GetColor(entityData, "color", entity.color); + list.PushArray(entity); } void GetVector(JSON_Object obj, const char[] key, float out[3]) { @@ -248,56 +578,175 @@ void GetVector(JSON_Object obj, const char[] key, float out[3]) { } } -void processGroups() { - SceneData scene; - for(int i = 0; i < scenes.Length; i++) { - scenes.GetArray(i, scene); - // TODO: Exclusions - if(GetURandomFloat() < scene.chance) { - selectScene(scene); - } +void GetColor(JSON_Object obj, const char[] key, int out[4]) { + JSON_Array vecArray = view_as(obj.GetObject(key)); + if(vecArray != null) { + out[0] = vecArray.GetInt(0); + out[1] = vecArray.GetInt(1); + out[2] = vecArray.GetInt(2); + if(vecArray.Length == 4) + out[3] = vecArray.GetInt(3); + else + out[3] = 255; + } else { + out[0] = 255; + out[1] = 255; + out[2] = 255; + out[3] = 255; } } -void selectScene(SceneData scene) { - // TODO: Weight +void selectScenes(int flags = 0) { + SceneData scene; + StringMap groups = new StringMap(); + ArrayList list; + for(int i = 0; i < g_MapData.scenes.Length; i++) { + g_MapData.scenes.GetArray(i, scene); + // TODO: Exclusions + // Select scene if not in group, or add to list of groups + if(scene.group[0] == '\0') { + selectScene(scene, flags); + } else { + if(!groups.GetValue(scene.group, list)) { + list = new ArrayList(); + } + list.Push(i); + groups.SetValue(scene.group, list); + } + } + + StringMapSnapshot snapshot = groups.Snapshot(); + char key[MAX_SCENE_NAME_LENGTH]; + for(int i = 0; i < snapshot.Length; i++) { + snapshot.GetKey(i, key, sizeof(key)); + groups.GetValue(key, list); + int index = GetURandomInt() % list.Length; + index = list.Get(index); + g_MapData.scenes.GetArray(index, scene); + Debug("Selected scene \"%s\" for group %s (%d members)", scene.name, key, list.Length); + selectScene(scene, flags); + delete list; + } + delete snapshot; + delete groups; +} + +void selectScene(SceneData scene, int flags) { + // Use the .chance field unless FLAG_ALL_SCENES is set + if(~flags & view_as(FLAG_ALL_SCENES) && GetURandomFloat() > scene.chance) { + return; + } + if(scene.variants.Length == 0) { LogError("Warn: No variants were found for scene \"%s\"", scene.name); return; } - Debug("Selected scene: \"%s\"", scene.name); ArrayList choices = new ArrayList(); SceneVariantData choice; + int index; // Weighted random: Push N times dependent on weight for(int i = 0; i < scene.variants.Length; i++) { scene.variants.GetArray(i, choice); - for(int c = 0; c < choice.weight; c++) { - choices.Push(i); + if(flags & view_as(FLAG_ALL_VARIANTS)) { + spawnVariant(choice); + } else { + for(int c = 0; c < choice.weight; c++) { + choices.Push(i); + } } - } - int index = GetURandomInt() % choices.Length; - index = choices.Get(index); - delete choices; - Debug("Selected variant: #%d", index); - scene.variants.GetArray(index, choice); - spawnVariant(choice); + if(flags & view_as(FLAG_ALL_VARIANTS)) { + delete choices; + } else { + index = GetURandomInt() % choices.Length; + index = choices.Get(index); + delete choices; + Log("Spawned scene \"%s\" with variant #%d", scene.name, index); + scene.variants.GetArray(index, choice); + spawnVariant(choice); + } + + ActiveSceneData aScene; + strcopy(aScene.name, sizeof(aScene.name), scene.name); + aScene.variantIndex = index; + g_MapData.activeScenes.PushArray(aScene); } void spawnVariant(SceneVariantData choice) { VariantEntityData entity; - // Weighted random: Push N times dependent on weight for(int i = 0; i < choice.entities.Length; i++) { choice.entities.GetArray(i, entity); spawnEntity(entity); } + + if(choice.inputsList.Length > 0) { + VariantInputData input; + for(int i = 0; i < choice.inputsList.Length; i++) { + choice.inputsList.GetArray(i, input); + input.Trigger(); + } + } } void spawnEntity(VariantEntityData entity) { - Debug("spawning \"%s\" at (%.1f %.1f %.1f) rot (%.0f %.0f %.0f)", entity.model, entity.origin[0], entity.origin[1], entity.origin[2], entity.angles[0], entity.angles[1], entity.angles[2]); - PrecacheModel(entity.model); - CreateProp(entity.type, entity.model, entity.origin, entity.angles); + 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, 0.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")) { + CreateDecal(entity.model, entity.origin); + } else if(StrContains(entity.type, "prop_") == 0) { + if(entity.model[0] == '\0') { + LogError("Missing model for entity with type \"%s\"", entity.type); + return; + } + PrecacheModel(entity.model); + 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) { + int ent = -1; + while((ent = FindEntityByClassname(ent, "*")) != INVALID_ENT_REFERENCE) { + int hammerId = GetEntProp(ent, Prop_Data, "m_iHammerID"); + if(hammerId == targetId) { + Debug("moved entity (hammerid=%d) to %.0f %.0f %.0f rot %.0f %.0f %.0f", targetId, entity.origin[0], entity.origin[1], entity.origin[2], entity.angles[0], entity.angles[1], entity.angles[2]); + TeleportEntity(ent, entity.origin, entity.angles, NULL_VECTOR); + return; + } + } + } + Debug("Warn: Could not find entity (hammerid=%d) (model=%s)", targetId, entity.model); + } else if(StrEqual(entity.type, "targetname")) { + int ent = -1; + char targetname[64]; + bool found = false; + while((ent = FindEntityByClassname(ent, "*")) != INVALID_ENT_REFERENCE) { + GetEntPropString(ent, Prop_Data, "m_iName", targetname, sizeof(targetname)); + if(StrEqual(entity.model, targetname)) { + Debug("moved entity (targetname=%s) to %.0f %.0f %.0f rot %.0f %.0f %.0f", entity.model, entity.origin[0], entity.origin[1], entity.origin[2], entity.angles[0], entity.angles[1], entity.angles[2]); + TeleportEntity(ent, entity.origin, entity.angles, NULL_VECTOR); + found = true; + } + } + if(!found) + Debug("Warn: Could not find entity (targetname=%s)", entity.model); + } else if(StrEqual(entity.type, "classname")) { + int ent = -1; + char classname[64]; + bool found; + while((ent = FindEntityByClassname(ent, classname)) != INVALID_ENT_REFERENCE) { + Debug("moved entity (classname=%s) to %.0f %.0f %.0f rot %.0f %.0f %.0f", entity.model, entity.origin[0], entity.origin[1], entity.origin[2], entity.angles[0], entity.angles[1], entity.angles[2]); + TeleportEntity(ent, entity.origin, entity.angles, NULL_VECTOR); + found = true; + } + if(!found) + Debug("Warn: Could not find entity (classname=%s)", entity.model); + } else { + LogError("Unknown entity type \"%s\"", entity.type); + } } void Debug(const char[] format, any ...) { @@ -321,12 +770,16 @@ void Log(const char[] format, any ...) { } void Cleanup() { - if(scenes != null) { + if(g_MapData.scenes != null) { SceneData scene; - for(int i = 0; i < scenes.Length; i++) { - scenes.GetArray(i, scene); + for(int i = 0; i < g_MapData.scenes.Length; i++) { + g_MapData.scenes.GetArray(i, scene); scene.Cleanup(); } - delete scenes; + delete g_MapData.scenes; } + delete g_MapData.lumpEdits; + + DeleteCustomEnts(); + g_MapData.activeScenes.Clear(); } \ No newline at end of file