diff --git a/scripting/include/randomizer/caralarm.sp b/scripting/include/randomizer/caralarm.sp index 2daf78e..8cc90cb 100644 --- a/scripting/include/randomizer/caralarm.sp +++ b/scripting/include/randomizer/caralarm.sp @@ -1,3 +1,27 @@ +#define WINDSHIELD_LIST_COUNT 1 +char VEHICLE_MODEL_WINDSHIELD_KEY[WINDSHIELD_LIST_COUNT][] = { + "models/props_vehicles/cement_truck01.mdl" +}; +char VEHICLE_MODEL_WINDSHIELD_VAL[WINDSHIELD_LIST_COUNT][] = { + "models/props_vehicles/cement_truck01_windows.mdl" +}; + +bool GetWindshieldModel(const char[] vehicleModel, char[] model, int modelSize) { + bool found = false; + for(int i = 0; i < WINDSHIELD_LIST_COUNT; i++) { + if(StrEqual(VEHICLE_MODEL_WINDSHIELD_KEY[i], vehicleModel)) { + strcopy(model, modelSize, VEHICLE_MODEL_WINDSHIELD_VAL[i]); + found = true; + break; + } + } + if(!found) { + strcopy(model, modelSize, vehicleModel); + ReplaceString(model, modelSize, ".mdl", "_glass.mdl"); + } + return PrecacheModel(model); +} + int SpawnCar(VariantEntityData entity) { if(entity.model[0] == '\0') { LogError("Missing model for entity with type \"%s\"", entity.type); @@ -12,14 +36,12 @@ int SpawnCar(VariantEntityData entity) { } char glassModel[64]; - strcopy(glassModel, sizeof(glassModel), entity.model); - ReplaceString(glassModel, sizeof(glassModel), ".mdl", "_glass.mdl"); if(StrEqual(entity.type, "_car_physics")) { vehicle = CreateProp("prop_physics", entity.model, entity.origin, entity.angles); } else { vehicle = CreateProp("prop_dynamic", entity.model, entity.origin, entity.angles); } - if(PrecacheModel(glassModel)) { + if(GetWindshieldModel(entity.model, glassModel, sizeof(glassModel))) { int glass = CreateProp("prop_dynamic", glassModel, entity.origin, entity.angles); if(glass != -1) { SetVariantString("!activator"); diff --git a/scripting/include/randomizer/defs.sp b/scripting/include/randomizer/defs.sp index 8d50126..74651f4 100644 --- a/scripting/include/randomizer/defs.sp +++ b/scripting/include/randomizer/defs.sp @@ -6,7 +6,7 @@ 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 -StringMap g_mapTraverseSelections; // For maps that traverse backwards, holds record of the selected scenes so they can be re-applied +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 @@ -16,7 +16,97 @@ int g_iLaserIndex; public void InitGlobals() { g_gascanSpawners = new AnyMap(); - g_mapTraverseSelections = new StringMap(); + 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) { + 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 { diff --git a/scripting/include/randomizer/loader_functions.sp b/scripting/include/randomizer/loader_functions.sp index 4cb7344..90f010e 100644 --- a/scripting/include/randomizer/loader_functions.sp +++ b/scripting/include/randomizer/loader_functions.sp @@ -1,5 +1,6 @@ public bool LoadGlobalMapData(const char[] map, int flags) { Cleanup(); + g_selection = null; return ParseMapData(g_MapData, map, flags); } diff --git a/scripting/include/randomizer/select_functions.sp b/scripting/include/randomizer/select_functions.sp index 93b494f..4db1730 100644 --- a/scripting/include/randomizer/select_functions.sp +++ b/scripting/include/randomizer/select_functions.sp @@ -1,29 +1,30 @@ +bool IsTraverseMapA(const char[] map) { + return String_EndsWith(map, "_a"); +} +bool IsTraverseMapB(const char[] map) { + // c4m1_milltown_a was added twice so _escape can pop it off and re-use + return StrEqual(map, "c4m5_milltown_escape") || String_EndsWith(map, "_b"); +} + public bool LoadRunGlobalMap(const char[] map, int flags) { // Unless FLAG_IGNORE_TRAVERSE_STORE, if the map is the _b variant, then load the stored _a value SceneSelection selection; // Only load map data if we don't already have it if(g_MapData.scenes == null || g_selection == null || flags & view_as(FLAG_REFRESH)) { - if(~flags & view_as(FLAG_IGNORE_TRAVERSE_STORE) && String_EndsWith(map, "_b")) { - // Switch _b to _a - char buffer[64]; - int len = strcopy(buffer, sizeof(buffer), map); - buffer[len-1] = 'a'; - - // Load the A variant - if(!LoadGlobalMapData(buffer, flags)) { - return false; + if(~flags & view_as(FLAG_IGNORE_TRAVERSE_STORE) && IsTraverseMapB(map) ) { + Log("LoadRunGlobal: Trying to load traverse selection"); + TraverseData traverse; + if(PopTraverseSelection(traverse)) { + Debug("traverse map: %s", traverse.map); + // Try Load the A variant + if(LoadGlobalMapData(traverse.map, flags)) { + selection = view_as(traverse.selection); + } } - - // Load selection from the traverse store, if it exists - ArrayList list; - if(g_mapTraverseSelections.GetValue(buffer, list)) { - Log("Loaded previously traversed map selection (c:%s p:%s)", map, buffer); - selection = view_as(list); - } else { - Log("Tried to load previously traversed map selection, but nothing stored (c:%s p:%s)", map, buffer); - } - } else if(selection == null) { + } + if(selection == null) { // This is called if not traverse map or previous data not found + Log("LoadRunGlobal: Loading & generating selection"); if(!LoadGlobalMapData(map, flags)) { return false; } @@ -163,62 +164,6 @@ void selectForcedScenes(SceneSelection selection, MapData data, int flags) { delete forcedScenes; } -// TODO: the scenes that are selected need variant index set -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) { - 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); - } - - public ArrayList AsList() { - return view_as(this); - } - - public void AddScene(SelectedSceneData aScene) { - view_as(this).PushArray(aScene); - } -} - // Selects what scenes and its variants to apply and returns list - does not activate SceneSelection selectScenes(MapData data, int flags = 0) { SceneData scene; diff --git a/scripting/l4d2_randomizer.sp b/scripting/l4d2_randomizer.sp index 9d2ef25..8eabb43 100644 --- a/scripting/l4d2_randomizer.sp +++ b/scripting/l4d2_randomizer.sp @@ -72,8 +72,8 @@ public void OnPluginStart() { } void Event_GameEnd(Event event, const char[] name ,bool dontBroadcast) { - // Purge the traverse list after a campaign is played - g_mapTraverseSelections.Clear(); + // Purge the traverse stack after a campaign is played + ClearTraverseStack(); } @@ -97,7 +97,6 @@ public void OnMapStart() { // We wait a while before running to prevent some edge cases i don't remember } - public void OnMapEnd() { randomizerRan = false; g_builder.Cleanup(); @@ -105,9 +104,15 @@ public void OnMapEnd() { // For maps that players traverse backwards, like hard rain (c4m1_milltown_a -> c4m3_milltown_b ) // We store the selection of the _a map, to later be loaded for _b maps // This is done at end of map just in case a user re-runs the cycle and generates a different selection - if(g_selection != null && String_EndsWith(currentMap, "_a")) { - Log("Storing %s in map traversal store", currentMap); - g_mapTraverseSelections.SetValue(currentMap, g_selection.AsList()); + if(g_selection != null) { + if(IsTraverseMapA(currentMap)) { + Log("Storing %s in map traversal store", currentMap); + StoreTraverseSelection(currentMap, g_selection); + } + // We want to store milltown_a twice, so the c4m5_milltown_escape can also pop it off + if(StrEqual(currentMap, "c4m1_milltown_a")) { + StoreTraverseSelection(currentMap, g_selection); + } } // don't clear entities because they will be deleted anyway (and errors if you tryq) Cleanup(false); @@ -176,7 +181,26 @@ Action Command_Debug(int client, int args) { ReplyToCommand(client, "No map data loaded"); } - } /*else if(StrEqual(arg, "identify")) { + } if(StrEqual(arg, "traverse")) { + TraverseData trav; + for(int i = 0; i < g_mapTraverseSelectionStack.Length; i++) { + g_mapTraverseSelectionStack.GetArray(i, trav, sizeof(trav)); + if(trav.selection == null) { + ReplyToCommand(client, " #%d - %s: ERROR", i, trav.map); + } else { + ReplyToCommand(client, " #%d - %s: %d scenes", i, trav.map, trav.selection.Length); + } + } + } else if(StrEqual(arg, "store")) { + char buffer[64]; + if(args == 1) { + strcopy(buffer, sizeof(buffer), currentMap); + } else { + GetCmdArg(2, buffer, sizeof(buffer)); + } + StoreTraverseSelection(buffer, g_selection); + ReplyToCommand(client, "Stored current selection as %s", buffer); + } /*else if(StrEqual(arg, "identify")) { if(args == 1) { ReplyToCommand(client, "Specify scene name"); } else if(!g_MapData.IsLoaded()) { @@ -200,7 +224,7 @@ Action Command_Debug(int client, int args) { ReplyToCommand(client, "Scene Selection: -"); } ReplyToCommand(client, "Builder Data: %s", g_builder.IsLoaded() ? "Loaded" : "-"); - ReplyToCommand(client, "Traverse Store: count=%d", g_mapTraverseSelections.Size); + ReplyToCommand(client, "Traverse Store: count=%d", g_mapTraverseSelectionStack.Length); if(g_gascanRespawnQueue != null) { ReplyToCommand(client, "Gascan Spawners: count=%d queue_size=%d", g_gascanSpawners.Size, g_gascanRespawnQueue.Length); } else { @@ -213,11 +237,13 @@ public Action Command_CycleRandom(int client, int args) { if(args > 0) { DeleteCustomEnts(); int flags = GetCmdArgInt(1); - if(flags != -1) { + if(flags < 0) { + ReplyToCommand(client, "Invalid flags"); + } else { LoadRunGlobalMap(currentMap, flags | view_as(FLAG_REFRESH)); + if(client > 0) + PrintCenterText(client, "Cycled flags=%d", flags); } - if(client > 0) - PrintCenterText(client, "Cycled flags=%d", flags); } else { if(g_selection == null) { ReplyToCommand(client, "No map selection active"); @@ -705,6 +731,7 @@ int CreateLight(const float origin[3], const float angles[3], const int color[4] 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; }