state("Pigface") { } startup { // Load asl-help binary and instantiate it - will inject code into the asl in the background Assembly.Load(File.ReadAllBytes("Components/asl-help")).CreateInstance("Unity"); vars.Helper.LoadSceneManager = true; vars.Helper.GameName = "PIGFACE"; vars.Helper.AlertLoadless(); #region TextComponent //Dictionary to cache created/reused layout components by their left-hand label (Text1) vars.lcCache = new Dictionary(); //Function to set (or update) a text component vars.SetText = (Action)((text1, text2) => { const string FileName = "LiveSplit.Text.dll"; LiveSplit.UI.Components.ILayoutComponent lc; //Try to find an existing layout component with matching Text1 (label) if (!vars.lcCache.TryGetValue(text1, out lc)) { lc = timer.Layout.LayoutComponents.Reverse().Cast() .FirstOrDefault(llc => llc.Path.EndsWith(FileName) && llc.Component.Settings.Text1 == text1) ?? LiveSplit.UI.Components.ComponentManager.LoadLayoutComponent(FileName, timer); //Cache it for later reference vars.lcCache.Add(text1, lc); } //If it hasn't been added to the layout yet, add it if (!timer.Layout.LayoutComponents.Contains(lc)) timer.Layout.LayoutComponents.Add(lc); //Set the label (Text1) and value (Text2) of the text component dynamic tc = lc.Component; tc.Settings.Text1 = text1; tc.Settings.Text2 = text2.ToString(); }); //Function to remove a single text component by its label vars.RemoveText = (Action)(text1 => { LiveSplit.UI.Components.ILayoutComponent lc; //If it's cached, remove it from the layout and the cache if (vars.lcCache.TryGetValue(text1, out lc)) { timer.Layout.LayoutComponents.Remove(lc); vars.lcCache.Remove(text1); } }); //Function to remove all text components that were added via this script vars.RemoveAllTexts = (Action)(() => { //Remove each one from the layout foreach (var lc in vars.lcCache.Values) timer.Layout.LayoutComponents.Remove(lc); //Clear the cache vars.lcCache.Clear(); }); #endregion #region setting creation dynamic[,] _settings = { { "IL Autoreset", false, "IL Autoreset - NOTE: will reset timer whenever pressing Retry and upon death", null }, { "SplitOptions", true, "Autosplit Options", null }, { "LevelSplits", true, "Level Splits: Autosplits when HP goes to 10000 at end of level", "SplitOptions" }, { "ApartmentSplits", true, "Apartment Splits: Autosplits when leaving Van going into a level", "SplitOptions" }, { "ObjectiveSplits", false, "Objective Splits: Autosplits on objective completions", "SplitOptions" }, { "gameInfo", false, "Game Info", null }, { "MainObj", true, "Main Obj Count", "gameInfo" }, { "SideObj", true, "Side Obj Count", "gameInfo" }, { "Health", true, "Health", "gameInfo" }, { "Retry Pressed?", false, "Retry Pressed", "gameInfo" }, { "UnityInfo", false, "Unity Scene Info", null }, { "LScene Name: ", false, "Name of Loading Scene", "UnityInfo" }, { "AScene Name: ", true, "Name of Active Scene", "UnityInfo" }, { "DebugInfo", false, "Debug Info", null }, { "placeholder", false, "placeholder", "DebugInfo" }, }; vars.Helper.Settings.Create(_settings); #endregion } init { //Enable if having scene print issues - a custom function defined in init, the `scene` is the scene's address (e.g. vars.Helper.Scenes.Active.Address) vars.ReadSceneName = (Func)(scene => { string name = vars.Helper.ReadString(256, ReadStringType.UTF8, scene + 0x38); return name == "" ? null : name; }); // This is where we will load custom properties from the code vars.Helper.TryLoad = (Func)(mono => { vars.Helper["placeholder"] = mono.Make("PlayerController", "Instance", "speed"); vars.Helper["Health"] = mono.Make("PlayerHealth", "Instance", "_currentHealth"); vars.Helper["mainObjectiveCount"] = mono.Make("ObjectiveManager", "Instance", "_mainObjectiveCount"); vars.Helper["sideObjectiveCount"] = mono.Make("ObjectiveManager", "Instance", "_optionalObjectiveCount"); vars.Helper["RetryPressed"] = mono.Make("PlayerInput", "Instance", "playerOptions", "retryButton", "hasSelection"); //vars.Helper["totalGameDamage"] = mono.Make("DataPersistenceManager", "Instance", "gameData", "_totalGameDamage"); //vars.Helper["totalGameKills"] = mono.Make("DataPersistenceManager", "Instance", "gameData", "_totalGameKills"); //vars.Helper["totalDeathCount"] = mono.Make("DataPersistenceManager", "Instance", "gameData", "_totalDeathCount"); //vars.Helper["totalMoney"] = mono.Make("DataPersistenceManager", "Instance", "gameData", "_totalMoney"); //vars.Helper["payoutAmount"] = mono.Make("PayoutManager", "Instance", "_payoutAmount"); return true; }); //Clears errors when scene and other variables are null, will get updated once they get detected current.placeholder = false; current.Scene = ""; current.activeScene = ""; current.loadingScene = ""; current.Health = 0; current.RetryPressed = false; current.mainObjectiveCount = 9999; current.sideObjectiveCount = 9999; //Helper function that sets or removes text depending on whether the setting is enabled - only works in `init` or later because `startup` cannot read setting values vars.SetTextIfEnabled = (Action)((text1, text2) => { if (settings[text1]) //If the matching setting is checked vars.SetText(text1, text2); //Show the text else vars.RemoveText(text1); //Otherwise, remove it }); } update { vars.Helper.Update(); vars.Helper.MapPointers(); //Get the current active scene's name and set it to `current.activeScene` - sometimes, it is null, so fallback to old value current.activeScene = vars.Helper.Scenes.Active.Name ?? current.activeScene; //Usually the scene that's loading, a bit jank in this version of asl-help current.loadingScene = vars.Helper.Scenes.Loaded[0].Name ?? current.loadingScene; if(!String.IsNullOrWhiteSpace(vars.Helper.Scenes.Active.Name)) current.activeScene = vars.Helper.Scenes.Active.Name; if(!String.IsNullOrWhiteSpace(vars.Helper.Scenes.Loaded[0].Name)) current.loadingScene = vars.Helper.Scenes.Loaded[0].Name; if (settings["DebugInfo"]) { //Log changes to properties if(old.activeScene != current.activeScene) {vars.Log("activeScene: " + old.activeScene + " -> " + current.activeScene);} if(old.loadingScene != current.loadingScene) {vars.Log("loadingScene: " + old.loadingScene + " -> " + current.loadingScene);} if(old.Health == 0 && current.Health != 0) {vars.Log("Health: " + old.Health + " -> " + current.Health);} } //More text component stuff - checking for setting and then generating the text. No need for .ToString since we do that previously vars.SetTextIfEnabled("placeholder",current.placeholder); vars.SetTextIfEnabled("LScene Name: ",current.loadingScene); vars.SetTextIfEnabled("AScene Name: ",current.activeScene); vars.SetTextIfEnabled("Retry Pressed?",current.RetryPressed); vars.SetTextIfEnabled("Health",current.Health); vars.SetTextIfEnabled("MainObj",current.mainObjectiveCount); vars.SetTextIfEnabled("SideObj",current.sideObjectiveCount); } start { if (old.Health == 0 && current.Health == 100 && current.activeScene != "intro_cutscene") {return true;} } split { //Level Splits if (settings["LevelSplits"] && old.Health != 10000 && current.Health == 10000 && current.activeScene != "player_apt" && current.activeScene != "intro_cutscene" && current.activeScene != "outro_cutscene" && current.activeScene != "early_access") { return true; } //Objective Splits if(settings["ApartmentSplits"]) { if ( (old.activeScene == "kit_screen" && current.activeScene != "kit_screen") || (old.activeScene == "player_apt" && current.activeScene == "player_warehouse") ) { return true; } } //Objective Splits if(settings["ObjectiveSplits"]) { if ( (current.mainObjectiveCount < old.mainObjectiveCount && current.mainObjectiveCount != -1 && current.Health > 0) || (current.sideObjectiveCount < old.sideObjectiveCount && current.sideObjectiveCount != -1 && current.Health > 0) ) { return true; } } } isLoading { return current.loadingScene != current.activeScene || (current.Health <= 0 && current.activeScene != "player_apt"); } reset { if ( (settings["IL Autoreset"] && old.RetryPressed == false && current.RetryPressed == true) || (settings["IL Autoreset"] && old.Health > 0 && current.Health <= 0) ) {return true;} }