#define MAX_SCENE_NAME_LENGTH 32 #define MAX_INPUTS_CLASSNAME_LENGTH 64 int DEFAULT_COLOR[4] = { 255, 255, 255, 255 }; MapData g_MapData; // The global map data SceneSelection g_selection; // The selected scenes from the global map data BuilderData g_builder; // The global instance of the builder ArrayList g_mapTraverseSelectionStack; // For maps that traverse backwards, holds record of the selected scenes so they can be re-applied int g_ropeIndex; // Unique id for ropes on spawn, is reset to 0 for every new spawn attempt ArrayList g_gascanRespawnQueue; // Queue that gascan respawns pop from to respawn to AnyMap g_gascanSpawners; // Mapping of , for when a can is destroyed it can be respawned in position int g_iLaserIndex; public void InitGlobals() { g_gascanSpawners = new AnyMap(); g_mapTraverseSelectionStack = new ArrayList(sizeof(TraverseData)); } enum struct TraverseData { char map[64]; ArrayList selection; } methodmap SceneSelection < ArrayList { public SceneSelection() { ArrayList selectedScenes = new ArrayList(sizeof(SelectedSceneData)); return view_as(selectedScenes); } property int Count { public get() { return (view_as(this)).Length; } } public void Cleanup() { delete this; } public void Activate(MapData data, int flags = 0) { if(this == null) return; g_ropeIndex = 0; SelectedSceneData aScene; SceneData scene; SceneVariantData choice; ArrayList list = view_as(this); for(int i = 0; i < list.Length; i++) { list.GetArray(i, aScene); Log("Activating scene \"%s\" with %d variants", aScene.name, aScene.selectedVariantIndexes.Length); // Fetch the scene that aScene marks if(!data.scenesKv.GetArray(aScene.name, scene, sizeof(scene))) { Log("WARN: Selected scene \"%s\" not found, skipping", aScene.name); continue; } for(int v = 0; v < aScene.selectedVariantIndexes.Length; v++) { int variantIndex = aScene.selectedVariantIndexes.Get(v); scene.variants.GetArray(variantIndex, choice); activateVariant(choice, flags); } } } public void Get(int sceneIndex, SelectedSceneData scene) { (view_as(this)).GetArray(sceneIndex, scene); } property ArrayList List { public get() { return view_as(this); } } public void AddScene(SelectedSceneData aScene) { view_as(this).PushArray(aScene); } } void StoreTraverseSelection(const char[] name, SceneSelection selection) { // Pushes selection and map to the stack TraverseData data; strcopy(data.map, sizeof(data.map), name); data.selection = selection.List.Clone(); g_mapTraverseSelectionStack.PushArray(data); } bool PopTraverseSelection(TraverseData data) { if(g_mapTraverseSelectionStack.Length == 0) { Log("WARN: PopTraverseSelection() called but stack is empty"); return false; } int index = g_mapTraverseSelectionStack.Length - 1; g_mapTraverseSelectionStack.GetArray(index, data); g_mapTraverseSelectionStack.Erase(index); return true; } void ClearTraverseStack() { TraverseData trav; for(int i = 0; i < g_mapTraverseSelectionStack.Length; i++) { g_mapTraverseSelectionStack.GetArray(i, trav, sizeof(trav)); delete trav.selection; } g_mapTraverseSelectionStack.Clear(); } enum struct SelectedSceneData { char name[MAX_SCENE_NAME_LENGTH]; ArrayList selectedVariantIndexes; } enum struct GascanSpawnerData { float origin[3]; float angles[3]; } enum struct MapData { StringMap scenesKv; ArrayList scenes; ArrayList lumpEdits; ArrayList activeScenes; ArrayList gascanSpawners; StringMap groups; void Cleanup() { SceneData scene; for(int i = 0; i < this.scenes.Length; i++) { this.scenes.GetArray(i, scene); scene.Cleanup(); } delete this.scenes; delete this.scenesKv; delete this.lumpEdits; delete this.activeScenes; delete this.gascanSpawners; delete this.groups; } SceneSelection GenerateSelection(int flags) { return selectScenes(this, flags); } bool ApplySelection(SceneSelection selection, int flags) { Profiler profiler = new Profiler(); profiler.Start(); selection.Activate(this, flags); spawnGascans(this); profiler.Stop(); // _ropeIndex = 0; Log("Done applying selection in %.4f seconds", profiler.Time); return true; } bool IsLoaded() { return this.scenes != null; } } 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 FLAG_FORCE_ACTIVE = 8, // Similar to ALL_SCENES, bypasses % chance FLAG_IGNORE_TRAVERSE_STORE = 16 // Do not load stored selection from the g_mapTraverseSelections } enum struct BuilderData { JSONObject mapData; JSONObject selectedSceneData; char selectedSceneId[64]; JSONObject selectedVariantData; int selectedVariantIndex; bool IsLoaded() { return this.mapData != null; } void Cleanup() { this.selectedSceneData = null; this.selectedVariantData = null; this.selectedVariantIndex = -1; this.selectedSceneId[0] = '\0'; if(this.mapData != null) delete this.mapData; // JSONcleanup_and_delete(this.mapData); } bool SelectScene(const char[] group) { if(!g_builder.mapData.HasKey(group)) return false; this.selectedSceneData = view_as(g_builder.mapData.Get(group)); strcopy(this.selectedSceneId, sizeof(this.selectedSceneId), group); return true; } /** * Select a variant, enter -1 to not select any (scene's entities) */ bool SelectVariant(int index = -1) { if(this.selectedSceneData == null) LogError("SelectVariant called, but no group selected"); JSONArray variants = view_as(this.selectedSceneData.Get("variants")); if(index >= variants.Length) return false; else if(index < -1) return false; else if(index > -1) { this.selectedVariantData = view_as(variants.Get(index)); } else { this.selectedVariantData = null; } this.selectedVariantIndex = index; return true; } void AddEntity(int entity, ExportType exportType = Export_Model) { JSONObject entityData = ExportEntity(entity, exportType); this.AddEntityData(entityData); } void AddEntityData(JSONObject entityData) { JSONArray entities; if(g_builder.selectedVariantData == null) { // Create .entities if doesn't exist: if(!g_builder.selectedSceneData.HasKey("entities")) { g_builder.selectedSceneData.Set("entities", new JSONArray()); } entities = view_as(g_builder.selectedSceneData.Get("entities")); } else { entities = view_as(g_builder.selectedVariantData.Get("entities")); } entities.Push(entityData); } } enum struct SceneData { char name[MAX_SCENE_NAME_LENGTH]; float chance; char group[MAX_SCENE_NAME_LENGTH]; ArrayList variants; void Cleanup() { SceneVariantData choice; for(int i = 0; i < this.variants.Length; i++) { this.variants.GetArray(i, choice); choice.Cleanup(); } delete this.variants; } } enum struct SceneVariantData { int weight; ArrayList inputsList; ArrayList entities; ArrayList forcedScenes; void Cleanup() { delete this.inputsList; VariantEntityData entity; for(int i = 0; i < this.entities.Length; i++) { this.entities.GetArray(i, entity, sizeof(entity)); entity.Cleanup(); } delete this.entities; delete this.forcedScenes; } } enum struct VariantEntityData { char type[32]; char model[128]; char targetname[128]; float origin[3]; float angles[3]; float scale[3]; int color[4]; ArrayList keyframes; // PropertyStore properties; // JSONObject propertiesInt; // JSONObject propertiesString; // JSONObject propertiesFloat; JSONArray properties; void Cleanup() { // 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(this.properties.Get(i)); delete obj; } delete this.properties; } } void ApplyProperties(int entity) { // 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(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); } } } } } 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); } } } enum InputType { Input_Classname, Input_Targetname, Input_HammerId } enum struct VariantInputData { char name[MAX_INPUTS_CLASSNAME_LENGTH]; InputType type; char input[64]; void Trigger() { int entity = -1; switch(this.type) { case Input_Classname: { while((entity = FindEntityByClassname(entity, this.name)) != INVALID_ENT_REFERENCE) { this._trigger(entity); } } case Input_Targetname: { char targetname[64]; int count = 0; while((entity = FindEntityByClassname(entity, "*")) != INVALID_ENT_REFERENCE) { GetEntPropString(entity, Prop_Data, "m_iName", targetname, sizeof(targetname)); if(StrEqual(targetname, this.name)) { this._trigger(entity); count++; } } if(count == 0) { PrintToServer("[Randomizer::WARN] Input TargetName=\"%s\" matched 0 entties", this.name); } } case Input_HammerId: { int targetId = StringToInt(this.name); int count = 0; while((entity = FindEntityByClassname(entity, "*")) != INVALID_ENT_REFERENCE) { int hammerId = GetEntProp(entity, Prop_Data, "m_iHammerID"); if(hammerId == targetId ) { this._trigger(entity); count++; break; } } if(count == 0) { PrintToServer("[Randomizer::WARN] Input HammerId=%d matched 0 entties", targetId); } } } } void _trigger(int entity) { if(entity > 0 && IsValidEntity(entity)) { 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; 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; } }