state("I Am Your Beast") { } startup { Assembly.Load(File.ReadAllBytes("Components/asl-help")).CreateInstance("Unity"); vars.Helper.GameName = "I Am Your Beast"; settings.Add("ILs", false, "Individual Level Mode (start on every level, reset when level resets)"); settings.Add("igt", false, "Use in-game-time instead of real time (does not work for full game)", "ILs"); vars.Helper.AlertLoadless(); } init { vars.Helper.TryLoad = (Func<dynamic, bool>)(mono => { // used to identify if we're on Mercy (25) // note that this is only level number within a pack. which means ids <= 12 are ambiguous (multiple levels share that number) vars.Helper["level"] = mono.Make<int>("GameManager", "instance", "levelController", "informationSetter", "levelInformation", "levelNumber"); //Level states: 0 - intro; 1 - active; 2 - completed; 3 - failed. vars.Helper["levelState"] = mono.Make<byte>("GameManager", "instance", "levelController", "levelState"); //Transition states: 0 - TransitioningOut; 1 - Holding; 2 - TransitioningIn. //Timer pauses during 0 and 1 since the player can't control Harding during those states. vars.Helper["sceneTransition"] = mono.Make<byte>("GameManager", "instance", "activeSceneTransition", "state"); //Tracks if player is active. Makes split starts more precise. vars.Helper["tracking"] = mono.Make<bool>("GameManager", "instance", "levelController", "gameplayTracker", "tracking"); // Note that the following time values will stay whatever they last were while in the level select screen, // and only reset once a level has been loaded. // Total elapsed time since level start. vars.Helper["combatTime"] = mono.Make<float>("GameManager", "instance", "levelController", "combatTimer", "timer"); // Time "regained" from killing enemies, etc. (the IGT is combatTime - regainedCombatTime) vars.Helper["regainedCombatTime"] = mono.Make<float>("GameManager", "instance", "levelController", "combatTimer", "regainedTime"); // Self explanatory. Stays true while in the level complete screen. vars.Helper["timerStarted"] = mono.Make<bool>("GameManager", "instance", "levelController", "combatTimer", "timerStarted"); //Technically a transition destination, but works as a current scene vars.Helper["destination"] = mono.MakeString("GameManager", "instance", "activeSceneTransition", "destination"); //#25 Mercy split vars.Helper["cutsceneID"] = mono.Make<byte>("GameManager", "instance", "cutsceneInfoStorer", "sequence", "ID"); return true; }); vars.totalIGT = 0; vars.hasCompletedCurrentLevel = false; } onStart { vars.totalIGT = 0; vars.hasCompletedCurrentLevel = false; } update { if (!vars.hasCompletedCurrentLevel && old.levelState == 1 && current.levelState == 2) { // Level complete - so the next time the timer resets, do not add any time vars.hasCompletedCurrentLevel = true; } if (current.combatTime == 0 && old.combatTime != 0) { if (!vars.hasCompletedCurrentLevel) { // Player reset the level (do not give regained time, penalise them for it) vars.totalIGT += old.combatTime; } else { // Level has probably just been loaded into vars.hasCompletedCurrentLevel = false; } } if (old.levelState == 1 && current.levelState == 2) { // Player beat the level, give the regained time vars.totalIGT += current.combatTime - current.regainedCombatTime; } } isLoading { if (settings["igt"]) { // If we return false in isLoading then livesplit will continue to increment time. // If IGT mode is enabled, we want to be in full control of the timer, so we disable it here return true; } if (current.destination == "Scenes/!___STORY SCENES/#01a_Special_Tutorial" || current.destination == "#01c_Special_Tutorial") { return current.sceneTransition != 2; } return current.sceneTransition != 2 || current.destination == "Scenes/UI/Menus/LevelSelect" || current.levelState == 2 || current.levelState == 0; } gameTime { if (settings["igt"]) { // Add a base 60 (like the leaderboards have) to deal with negative times. // Someone could break this if they get more than 60 seconds of negative time in a run. // (LiveSplit will not save negative times) var baseTime = 60 + vars.totalIGT; if (current.levelState == 1) { // If the level is active, we should track whatever the current time is var currentLevelTimeSeconds = current.combatTime - current.regainedCombatTime; return TimeSpan.FromSeconds(baseTime + currentLevelTimeSeconds); } else { return TimeSpan.FromSeconds(baseTime); } } } start { if (settings["igt"]) { // Timer starts when the combat time starts for IGT, not when you reset the level return !old.timerStarted && current.timerStarted; } bool hasLoadedIntoLevel = current.sceneTransition == 2 && old.tracking == false && current.tracking == true; if (settings["ILs"]) { return hasLoadedIntoLevel; } return hasLoadedIntoLevel && current.destination == "Scenes/!___STORY SCENES/#01a_Special_Tutorial"; } split { if (current.cutsceneID == 22 && old.destination == "Scenes/UI/Cutscenes/Cutscene") { return old.destination == "Scenes/UI/Cutscenes/Cutscene" && current.destination == "Scenes/UI/Menus/LevelSelect"; } else { return (old.destination == "#01c_Special_Tutorial" && current.destination == "Scenes/UI/Menus/LevelSelect") || (old.levelState == 1 && current.levelState == 2); } } reset { // disable for Mercy since it transitions out before the level finishes if (settings["ILs"] && current.level != 25 && old.sceneTransition != 0 && current.sceneTransition == 0) { return true; } }