From 011a5caad83d65b1f81bdf113271fd6c187b8783 Mon Sep 17 00:00:00 2001 From: Ero <56401411+just-ero@users.noreply.github.com> Date: Sun, 6 Mar 2022 17:05:39 +0100 Subject: [PATCH] Create repository. --- .github/ISSUE_TEMPLATE/commission.md | 39 ++ .github/ISSUE_TEMPLATE/fixes.md | 27 ++ .gitignore | 1 + A Proof of Concept/AProofOfConcept.asl | 143 ++++++ A Proof of Concept/readme.md | 12 + .../Alba.asl" | 229 ++++++++++ .../readme.md" | 11 + And All Would Cry Beware!/AAWCB.asl | 74 ++++ And All Would Cry Beware!/readme.md | 9 + Assemble with Care/AssembleWithCare.asl | 71 +++ Assemble with Care/readme.md | 10 + Devil Daggers/DevilDaggers.asl | 53 +++ Devil Daggers/readme.md | 13 + Donut County/DonutCounty.asl | 121 ++++++ Donut County/readme.md | 11 + .../DKAS.asl" | 203 +++++++++ .../readme.md" | 13 + Give Up, Robot/GiveUpRobot1.asl | 62 +++ Give Up, Robot/readme.md | 10 + Grapple!/Grapple.asl | 88 ++++ Grapple!/readme.md | 11 + Gunfire Reborn/GunfireReborn.asl | 129 ++++++ Gunfire Reborn/readme.md | 14 + Hello Neighbor/HelloNeighbor.asl | 32 ++ Hello Neighbor/readme.md | 12 + Here Comes Niko/HereComesNiko.asl | 411 ++++++++++++++++++ Here Comes Niko/readme.md | 12 + How Fish is Made/HowFishIsMade.asl | 79 ++++ How Fish is Made/readme.md | 10 + Just Shake It/JustShakeIt.asl | 48 ++ Just Shake It/readme.md | 9 + Lightmatter/Lightmatter.asl | 126 ++++++ Lightmatter/readme.md | 14 + MX Simulator/MXSimulator.asl | 230 ++++++++++ MX Simulator/readme.md | 9 + Maid of Sker/MaidOfSker.asl | 168 +++++++ Maid of Sker/readme.md | 12 + .../PP_MPsAB.asl" | 45 ++ .../readme.md" | 12 + Post Void/PostVoid.asl | 73 ++++ Post Void/readme.md | 12 + Postbird In Provence/PostbirdInProvence.asl | 142 ++++++ Postbird In Provence/readme.md | 10 + Project Warlock II/ProjectWarlockII.asl | 101 +++++ Project Warlock II/readme.md | 9 + .../RuffNightAtTheGallery.asl | 90 ++++ Ruff Night at the Gallery/readme.md | 10 + Sphere Complex/SphereComplex.asl | 24 + Sphere Complex/readme.md | 9 + .../Strobophagia.asl" | 125 ++++++ .../readme.md" | 12 + The Final Station/TheFinalStation.asl | 126 ++++++ The Final Station/readme.md | 11 + The Pedestrian/MemoryValues | 239 ++++++++++ The Pedestrian/ThePedestrian.asl | 71 +++ The Pedestrian/readme.md | 11 + The Spectrum Retreat/DayTime | 44 ++ The Spectrum Retreat/LevelNumbers | 67 +++ The Spectrum Retreat/TheSpectrumRetreat.asl | 160 +++++++ The Spectrum Retreat/readme.md | 10 + The Two of Us/TheTwoOfUs.asl | 79 ++++ The Two of Us/readme.md | 10 + .../ThereIsNoGame_Jam2015.asl | 104 +++++ There is no Game (Jam Edition 2015)/readme.md | 10 + .../Transformers2007.asl" | 89 ++++ .../readme.md" | 10 + Voidigo/Voidigo.asl | 127 ++++++ Voidigo/readme.md | 11 + .../YAPP.asl" | 47 ++ .../readme.md" | 10 + lil gator game/lilgatorgame.asl | 187 ++++++++ lil gator game/readme.md | 9 + readme.md | 37 ++ 73 files changed, 4669 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/commission.md create mode 100644 .github/ISSUE_TEMPLATE/fixes.md create mode 100644 .gitignore create mode 100644 A Proof of Concept/AProofOfConcept.asl create mode 100644 A Proof of Concept/readme.md create mode 100644 "Alba \342\200\223 A Wildlife Adventure/Alba.asl" create mode 100644 "Alba \342\200\223 A Wildlife Adventure/readme.md" create mode 100644 And All Would Cry Beware!/AAWCB.asl create mode 100644 And All Would Cry Beware!/readme.md create mode 100644 Assemble with Care/AssembleWithCare.asl create mode 100644 Assemble with Care/readme.md create mode 100644 Devil Daggers/DevilDaggers.asl create mode 100644 Devil Daggers/readme.md create mode 100644 Donut County/DonutCounty.asl create mode 100644 Donut County/readme.md create mode 100644 "Door Kickers \342\200\223 Action Squad/DKAS.asl" create mode 100644 "Door Kickers \342\200\223 Action Squad/readme.md" create mode 100644 Give Up, Robot/GiveUpRobot1.asl create mode 100644 Give Up, Robot/readme.md create mode 100644 Grapple!/Grapple.asl create mode 100644 Grapple!/readme.md create mode 100644 Gunfire Reborn/GunfireReborn.asl create mode 100644 Gunfire Reborn/readme.md create mode 100644 Hello Neighbor/HelloNeighbor.asl create mode 100644 Hello Neighbor/readme.md create mode 100644 Here Comes Niko/HereComesNiko.asl create mode 100644 Here Comes Niko/readme.md create mode 100644 How Fish is Made/HowFishIsMade.asl create mode 100644 How Fish is Made/readme.md create mode 100644 Just Shake It/JustShakeIt.asl create mode 100644 Just Shake It/readme.md create mode 100644 Lightmatter/Lightmatter.asl create mode 100644 Lightmatter/readme.md create mode 100644 MX Simulator/MXSimulator.asl create mode 100644 MX Simulator/readme.md create mode 100644 Maid of Sker/MaidOfSker.asl create mode 100644 Maid of Sker/readme.md create mode 100644 "PAW Patrol \342\200\223 Mighty Pups save Adventure Bay/PP_MPsAB.asl" create mode 100644 "PAW Patrol \342\200\223 Mighty Pups save Adventure Bay/readme.md" create mode 100644 Post Void/PostVoid.asl create mode 100644 Post Void/readme.md create mode 100644 Postbird In Provence/PostbirdInProvence.asl create mode 100644 Postbird In Provence/readme.md create mode 100644 Project Warlock II/ProjectWarlockII.asl create mode 100644 Project Warlock II/readme.md create mode 100644 Ruff Night at the Gallery/RuffNightAtTheGallery.asl create mode 100644 Ruff Night at the Gallery/readme.md create mode 100644 Sphere Complex/SphereComplex.asl create mode 100644 Sphere Complex/readme.md create mode 100644 "Strobophagia \342\200\223 Rave Horror/Strobophagia.asl" create mode 100644 "Strobophagia \342\200\223 Rave Horror/readme.md" create mode 100644 The Final Station/TheFinalStation.asl create mode 100644 The Final Station/readme.md create mode 100644 The Pedestrian/MemoryValues create mode 100644 The Pedestrian/ThePedestrian.asl create mode 100644 The Pedestrian/readme.md create mode 100644 The Spectrum Retreat/DayTime create mode 100644 The Spectrum Retreat/LevelNumbers create mode 100644 The Spectrum Retreat/TheSpectrumRetreat.asl create mode 100644 The Spectrum Retreat/readme.md create mode 100644 The Two of Us/TheTwoOfUs.asl create mode 100644 The Two of Us/readme.md create mode 100644 There is no Game (Jam Edition 2015)/ThereIsNoGame_Jam2015.asl create mode 100644 There is no Game (Jam Edition 2015)/readme.md create mode 100644 "Transformers \342\200\223 The Game/Transformers2007.asl" create mode 100644 "Transformers \342\200\223 The Game/readme.md" create mode 100644 Voidigo/Voidigo.asl create mode 100644 Voidigo/readme.md create mode 100644 "YAPP \342\200\223 Yet Another Puzzle Platformer/YAPP.asl" create mode 100644 "YAPP \342\200\223 Yet Another Puzzle Platformer/readme.md" create mode 100644 lil gator game/lilgatorgame.asl create mode 100644 lil gator game/readme.md create mode 100644 readme.md diff --git a/.github/ISSUE_TEMPLATE/commission.md b/.github/ISSUE_TEMPLATE/commission.md new file mode 100644 index 0000000..421f6dd --- /dev/null +++ b/.github/ISSUE_TEMPLATE/commission.md @@ -0,0 +1,39 @@ +--- +name: Need a new auto splitter or some new features? +about: Commission some work here! +title: "[Feature request, Commission] for [Game Name]" +labels: '' +assignees: '' + +--- + +For rates and other information, please refer to my commission info [here](././asl#commission-information). +- [ ] I have read and understood these terms. + +--- + +## *Feature Request* +*Context*: +* Game name (as it appears on this repo): + +*Please describe where the need for this feature arises from*: +Ex.: I'm frustrated with..., I want to add a split at..., I would like to track some information to..., Our community has decided... + +*Please elaborate on the exact feature(s) and their expected behavior*: +Good: I want the timer to reset when player health drops to 0..., I want the timer to split when `GameSceneManager.curSceneID` changes from 0 to 1... +Bad: I want the timer to stop when the run is over..., I want the timer to split when finishing a level... + +--- + +## *Auto Splitter Commission* +*Context*: +* Game name: +* Download/store link (+ price if applicable): + +*Features*: +- [ ] Start: Describe an event or events upon which the timer should start. +- [ ] Split: Describe an event or events upon which the timer should split. +- [ ] Reset: Describe an event or events upon which the timer should reset. +- [ ] Load removal / game time synchronization: Explain how your game's in-game time works. If not applicable, please explain when and how loading times should be removed. + +Please remove all unused or unimportant lines from this template. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/fixes.md b/.github/ISSUE_TEMPLATE/fixes.md new file mode 100644 index 0000000..1865300 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/fixes.md @@ -0,0 +1,27 @@ +--- +name: An auto splitter needs fixes! +about: Help me help you by providing me with some helpful information. +title: "[GAME NAME] auto splitter does not [start, split, reset, remove loads, sync to game time]" +labels: '' +assignees: '' + +--- + +Please refer to the [common issues](././asl#there-is-an-issue-with-an-auto-splitter-I'm-using-) before creating an issue. + +--- + +## *Describe your Issue* +- [ ] The game has recently received an update +- [ ] The features mentioned in the title do not work + +If there is any especially weird behavior with the timer, please describe what is happening: + + +--- + +## *Causes and Fixes* +(*for experienced users only*) + +Do you know the exact cause of the problem? Broken pointers, signatures can't be resolved, level names changed? Please elaborate. +Do you already have a fix? Please pull request this repository with your proposed changes. You will be properly credited. \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f17f831 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/Phasmophobia \ No newline at end of file diff --git a/A Proof of Concept/AProofOfConcept.asl b/A Proof of Concept/AProofOfConcept.asl new file mode 100644 index 0000000..7702323 --- /dev/null +++ b/A Proof of Concept/AProofOfConcept.asl @@ -0,0 +1,143 @@ +state("A Proof of Concept 1.1") +{ + int LevelID : 0x6C2DB8; + // int Time : 0x6C2DE0; +} + +state("Concept - v2.6") +{ + int LevelID : 0x6FFF60; +} + +startup +{ + string[,] _settings = + { + { "libSplits", "0 -> 1", "Enter Vault/Museum from Library" }, + { "libSplits", "0 -> 2", "Enter Main Frame from Library" }, + { "libSplits", "0 -> 6", "Enter Power Plant from Library" }, + { "libSplits", "0 -> 8", "Enter Gravitational Management from Library (2.0+)" }, + { "libSplits", "0 -> 11", "Enter SA * RC from Library (2.0+)" }, + { "libSplits", "0 -> 7", "Enter Woods from Library" }, + { "libSplits", "0 -> 4", "Enter Great Door from Library" }, + + { "libBackSplits", "1 -> 0", "Leave Museum/Vault to Library" }, + { "libBackSplits", "2 -> 0", "Leave Main Frame to Library" }, + { "libBackSplits", "5 -> 0", "Leave Back Door level to Library" }, + { "libBackSplits", "6 -> 0", "Leave Power Plant to Library" }, + { "libBackSplits", "9 -> 0", "Leave Centrifuge to Library (2.0+)" }, + { "libBackSplits", "12 -> 0", "Leave Event Horizon to Library (2.0+)" }, + { "libBackSplits", "14 -> 0", "Leave Continuum to Library (2.0+)" }, + { "libBackSplits", "7 -> 0", "Leave Woods to Library" }, + + { "inLevelSplits", "4 -> 3", "Enter Archive from Big Orb Room" }, + { "inLevelSplits", "mainFrame", "Splits in Main Frame:" }, + { "mainFrame", "2 -> 3", "Enter second half of Main Frame" }, + { "mainFrame", "3 -> 4", "Enter Back Door Corridor from Main Frame" }, + { "mainFrame", "4 -> 5", "Enter Back Door level from Corridor" }, + { "inLevelSplits", "centrifuge", "Splits in Centrifuge (2.0+):" }, + { "centrifuge", "8 -> 9", "Enter Centrifuge from Gravitational Management" }, + { "centrifuge", "9 -> 10", "Enter Warp Machine from Centrifuge" }, + { "inLevelSplits", "eventHorizon", "Splits in Event Horizon (2.0+):" }, + { "eventHorizon", "11 -> 12", "Enter Event Horizon from SA * RC" }, + { "eventHorizon", "11 -> 13", "Enter Professor's lab from SA * RC" }, + { "eventHorizon", "13 -> 14", "Enter Continuum from lab" } + }; + + settings.Add("libSplits", false, "Split when going into a level:"); + settings.Add("libBackSplits", false, "Split when finishing a level:"); + settings.Add("inLevelSplits", false, "Split within a level:"); + + for (int i = 0; i < _settings.GetLength(0); ++i) + { + string parent = _settings[i, 0]; + string id = _settings[i, 1]; + string desc = _settings[i, 2]; + + settings.Add(id, false, desc, _settings); + } + + vars.PrevOffset = timer.Run.Offset; +} + +onStart +{ + timer.Run.Offset = vars.PrevOffset; +} + +init +{ + switch (game.ProcessName) + { + case "A Proof of Concept 1.1": + vars.LevelIDByIndex = new List { 12, 5, 6, 7, 9, 10, 11, 14 }; + break; + case "Concept - v2.6": + vars.LevelIDByIndex = new List + { + 21, // Library + 12, // Museum + 13, // Main Frame + 15, // Main Frame 2 / Archive + 17, // Back Door 0.5 / Great Door + 18, // Back Door + 19, // Power Plant + 23, // Woods + 14, // Gravitational Management + 31, // Centrifuge + 4, // Warp Machine + 3, // SA * RC + 20, // Event Horizon + 5, // Professor's lab + 10 // Continuum + }; + break; + } +} + +start +{ + if (old.LevelID == current.LevelID) + return; + + switch (game.ProcessName) + { + case "A Proof of Concept 1.1" : + var start = old.LevelID == 1 && current.LevelID == 4; + break; + case "Concept - v2.6" : + var start = old.LevelID == 2 && current.LevelID == 11; + break; + } + + if (start) + { + vars.PrevOffset = timer.Run.Offset; + timer.Run.Offset = TimeSpan.FromSeconds(20f / 3f); + return true; + } +} + +split +{ + if (old.LevelID == current.LevelID) + return; + + int oldIndex = vars.LevelIDByIndex.IndexOf(old.LevelID); + int currIndex = vars.LevelIDByIndex.IndexOf(current.LevelID); + return settings[oldIndex + " -> " + currIndex]; +} + +reset +{ + if (old.LevelID == current.LevelID) + return; + + switch (game.ProcessName) + { + case "A Proof of Concept 1.1" : + return old.LevelID != 0 && old.LevelID != 4 && current.LevelID == 0; + case "Concept - v2.6" : + return current.LevelID == 2; + } +} \ No newline at end of file diff --git a/A Proof of Concept/readme.md b/A Proof of Concept/readme.md new file mode 100644 index 0000000..1f9aea9 --- /dev/null +++ b/A Proof of Concept/readme.md @@ -0,0 +1,12 @@ +# Auto Splitter for ***A Proof of Concept*** +## Features +Starts the timer when transitioning from the menu to the intro. +Splits are available in the settings. Does not split when reaching the end. +Resets the timer when returning to the main menu. + +Supports v1.1 (for 7 Cards runs) and v2.0+ (for 10 Cards runs). + +## Resources +*Leaderboards: [speedrun.com/apoc](https://speedrun.com/apoc)* +*Discord: [discord.gg/myGEa5u](https://discord.gg/myGEa5u)* +*Game: [bill-g.itch.io/a-proof-of-concept](https://bill-g.itch.io/a-proof-of-concept) (Free)* \ No newline at end of file diff --git "a/Alba \342\200\223 A Wildlife Adventure/Alba.asl" "b/Alba \342\200\223 A Wildlife Adventure/Alba.asl" new file mode 100644 index 0000000..049365b --- /dev/null +++ "b/Alba \342\200\223 A Wildlife Adventure/Alba.asl" @@ -0,0 +1,229 @@ +state("Alba") {} + +startup +{ + vars.Log = (Action)(output => print("[Alba] " + output)); + vars.Unity = Assembly.Load(File.ReadAllBytes(@"Components\UnityASL.bin")).CreateInstance("UnityASL.Unity"); + + string[,] _settings = + { + { "Main Quests", "QT_GetCameraGrandad", "Take Granddad's phone (Prologue)" }, + { "Main Quests", "QG_Explore_with_Ines", "Explore with Ines" }, + { "QG_Explore_with_Ines", "QT_Follow_Ines_to_the_Paella_in_the_resturant", "Follow Ines to the Paella in the restaurant" }, + { "QG_Explore_with_Ines", "QT_Follow_Ines_to_the_Ancient_Ruins", "Follow Ines to the Ancient Ruins" }, + { "Main Quests", "QG_Dolphin_Rescue", "Dolphin Rescue" }, + { "QG_Dolphin_Rescue", "QT_Investigate_the_stranded_dolphin", "Investigate the stranded dolphin" }, + { "QG_Dolphin_Rescue", "QT_Find_people_to_help_save_the_dolphin", "Find people to help save the dolphin" }, + { "QG_Dolphin_Rescue", "QT_Return_the_dolphin_to_the_sea", "Return the dolphin to the sea" }, + { "Main Quests", "QT_Scan_Sparrow", "Scan the Sparrow" }, + { "Main Quests", "QT_Find_the_Mayor_at_the_Nature_Reserve", "Find the Mayor at the Nature Reserve" }, + { "Main Quests", "QG_Nature_Reserve", "Nature Reserve" }, + { "QG_Nature_Reserve", "QT_Explore_the_Nature_Reserve", "Explore the Nature Reserve" }, + { "QG_Nature_Reserve", "QT_Take_a_photo_of_the_Shoveler", "Take a photo of the Shoveler" }, + { "QG_Nature_Reserve", "QT_Talk_to_the_Carpenter_in_town", "Talk to the Carpenter in town" }, + { "QG_Nature_Reserve", "QT_Fix_the_bridge_in_the_Nature_Reserve", "Fix the bridge in the Nature Reserve" }, + { "QG_Nature_Reserve", "QT_Scan_a_Grey_Heron_in_the_Nature_Reserve", "Scan a Grey Heron in the Nature Reserve" }, + { "QG_Nature_Reserve", "QT_Put_Heron_photo_on_sign_in_Nature_Reserve", "Put Heron photo on sign in Nature Reserve" }, + { "QG_Nature_Reserve", "QT_Clean_up_the_Nature_Reserve", "Clean up the Nature Reserve" }, + { "QG_Nature_Reserve", "QT_Fix_Carpenter_Birdbox", "Fix Carpenter Birdbox" }, + { "Main Quests", "QG_Mysterious_Goo", "Mysterious Goo" }, + { "QG_Mysterious_Goo", "QT_Follow_the_trail_of_green_goo", "Follow the trail of green goo" }, + { "QG_Mysterious_Goo", "QT_Ask_the_vet_in_town_for_help", "Ask the vet in town for help" }, + { "QG_Mysterious_Goo", "QT_Meet_the_vet_at_the_sick_squirrel", "Meet the vet at the sick squirrel" }, + { "QG_Mysterious_Goo", "QT_Find_and_heal_sick_animals_nearby", "Find and heal sick animals nearby" }, + { "QG_Mysterious_Goo", "QT_Talk_to_the_Vet_in_town", "Talk to the Vet in town on the next day" }, + { "QG_Mysterious_Goo", "QT_Follow_the_trail_of_sick_animals", "Follow the trail of sick animals" }, + { "QG_Mysterious_Goo", "QT_Find_the_farm_the_pesticides_came_from", "Find the farm the pesticides came from" }, + { "QG_Mysterious_Goo", "QT_Heal_the_farmers_sick_cat", "Heal the farmer's sick cat" }, + { "Main Quests", "QG_Castle", "Castle" }, + { "QG_Castle", "QT_Meet_Grandpa_at_the_Castle", "Meet Grandpa at the Castle" }, + { "QG_Castle", "QT_Fix_the_wooden_stairs_in_the_Castle", "Fix the wooden stairs in the Castle" }, + { "QG_Castle", "QT_Scan_the_Eagle_from_the_Castle_walls", "Scan the Eagle from the Castle walls" }, + { "Main Quests", "QG_The_Lynx", "The Lynx" }, + { "QG_The_Lynx", "QT_Meet_Ines_at_the_Chicken_Farm", "Meet Ines at the Chicken Farm" }, + { "QG_The_Lynx", "QT_Find_where_the_Lynx_went", "Find where the Lynx went" }, + { "QG_The_Lynx", "QT_Follow_the_Lynxs_trail_near_the_Farm", "Follow the Lynxs trail near the Farm" }, + { "QG_The_Lynx", "QT_Talk_to_Ines_outside_the_Lynxs_den", "Talk to Ines outside the Lynxs den" }, + { "QG_The_Lynx", "QT_Repair_the_Chicken_Farm_Fence", "Repair the Chicken Farm Fence" }, + { "QG_The_Lynx", "QT_Scan_the_Fox", "Scan the Fox" }, + { "Main Quests", "QG_ReturnToDolphinIsland", "Return to La Roqueta (Dolphin)" }, + { "QG_ReturnToDolphinIsland", "QT_ClaraDolphinBoat", "Get on the boat" }, + { "QG_ReturnToDolphinIsland", "QT_ClaraDolphin", "Take a photo of the dolphin" }, + { "Main Quests", "QG_Cleanup_North_Beach", "Clean up North Beach" }, + { "QG_Cleanup_North_Beach", "QT_IllegalDump_Meet_Juanito", "Meet Juanito" }, + { "QG_Cleanup_North_Beach", "QT_IllegalDump_Heal_the_Animals", "Heal the Animals" }, + { "QG_Cleanup_North_Beach", "QT_IllegalDump_Clean_the_Rubbish", "Clean the Rubbish" }, + { "Main Quests", "QT_Talk_to_the_Mayor_in_town", "Talk to the Mayor in town" }, + { "Main Quests", "QG_Mayor_Investigation", "Mayor Investigation" }, + { "QG_Mayor_Investigation", "QT_Follow_Ines_to_find_the_Mayor", "Follow Ines to find the Mayor" }, + { "QG_Mayor_Investigation", "QT_Scan_the_Mayor_1", "Scan the Mayor 1" }, + { "QG_Mayor_Investigation", "QT_Scan_the_Mayor_2", "Scan the Mayor 2" }, + { "QG_Mayor_Investigation", "QT_Scan_the_Mayor_3", "Scan the Mayor 3" }, + { "QG_Mayor_Investigation", "QT_Scan_the_Mayor_4", "Scan the Mayor 4" }, + { "QG_Mayor_Investigation", "QT_Use_the_Scanner_to_see_whats_in_the_briefcase", "Use the Scanner to see whats in the briefcase" }, + { "Main Quests", "QT_Scan_the_Lynx", "Scan the Lynx" }, + { "Main Quests", "QG_Summer_Festival", "Summer Festival" }, + { "QG_Summer_Festival", "QT_Summer_Festival_Enjoy", "Summer Festival Enjoy" }, + { "QG_Summer_Festival", "QT_Summer_Festival", "Summer Festival" }, + + { "Side Quests", "QG_Nature_Reserve_SQ", "Nature Reserve" }, + { "QG_Nature_Reserve_SQ", "QT_Fix_up_the_old_Nature_Reserve", "Fix up the old Nature Reserve" }, + { "QG_Nature_Reserve_SQ", "QT_Replace_damaged_photos_on_signs_in_Nature_Reserve", "Replace damaged photos on signs in Nature Reserve" }, + { "Side Quests", "QG_Animals_Discovered", "Animals Discovered" }, + { "QG_Animals_Discovered", "QT_ScanAnimals1", "Scan 5 Animals" }, + { "QG_Animals_Discovered", "QT_ScanAnimals2", "Scan 12 Animals" }, + { "QG_Animals_Discovered", "QT_ScanAnimals3", "Scan 20 Animals" }, + { "QG_Animals_Discovered", "QT_ScanAnimals4", "Scan 30 Animals" }, + { "QG_Animals_Discovered", "QT_ScanAnimals5", "Scan 40 Animals" }, + { "QG_Animals_Discovered", "QT_ScanAnimals6", "Scan 50 Animals" }, + { "QG_Animals_Discovered", "QT_ScanAnimals7", "Scan 61 Animals" }, + { "Side Quests", "QG_Animal_SIgn", "Photo Signs" }, + { "QG_Animal_SIgn", "QT_RiceFields_WildlifeSIgn", "Rice Fields" }, + { "QG_Animal_SIgn", "QT_Forest_WildlifeSIgn", "Forest" }, + { "QG_Animal_SIgn", "QT_Castle_WildlifeSign", "Castle" }, + { "QG_Animal_SIgn", "QT_Terraces_WildlifeSIgn", "Terraces" }, + { "QG_Animals_Discovered", "QT_All_Animals_Photographed", "All Animals Photographed" }, + { "Side Quests", "QG_WildlifeRescue", "Wildlife Rescue" }, + { "QG_WildlifeRescue", "QT_Heal_birds_covered_in_oil_on_East_Beach", "Heal birds covered in oil on East Beach" }, + { "QG_WildlifeRescue", "QT_Help_animals_caught_in_trash_on_Mountain", "Help animals caught in trash on Mountain" }, + { "Side Quests", "QG_Cleanup_Island", "Clean up Island" }, + { "QG_Cleanup_Island", "QT_Recycle_Encounters", "Pick up Rubbish" }, + { "QG_Cleanup_Island", "QT_Recycle_Encounter_TownBeach", "Town Beach" }, + { "QG_Cleanup_Island", "QT_Recycle_Encounter_TownPromenade", "Town Promenade" }, + { "QG_Cleanup_Island", "QT_Recycle_Encounter_MarshBeach", "Marsh Beach" }, + { "QG_Cleanup_Island", "QT_Recycle_Encounter_Woods", "Woods" }, + { "QG_Cleanup_Island", "QT_Recycle_Encounter_Mountain", "Mountain" }, + { "QG_Cleanup_Island", "QT_Recycle_Encounter_DolphinIsland", "La Roqueta" }, + { "Side Quests", "QG_Clara_Animal_Seeking", "Clara Animal Seeking" }, + { "QG_Clara_Animal_Seeking", "QT_Take_a_photo_of_the_Sparrowhawks_near_town", "Take a photo of the Sparrowhawks near town" }, + { "QG_Clara_Animal_Seeking", "QT_ClaraHoopoe", "Take a photo of a Hoopoe" }, + { "QG_Clara_Animal_Seeking", "QT_ClaraGlossyIbis", "Take a photo of a Glossy Ibis" }, + { "Side Quests", "QT_Finish_fixing_Castle", "Finish fixing Castle" }, + { "Side Quests", "QT_Scan_the_Owl", "Scan the Owl at the construction site" }, + { "Side Quests", "QT_TellPepeLolaHasFreeIceCream", "Tell Pepe Lola has free Ice Cream" }, + { "Side Quests", "QG_Marina_and_Socks", "Marina and Socks" }, + { "QG_Marina_and_Socks", "QT_Find_lost_dog", "Find lost dog" }, + { "QG_Marina_and_Socks", "QT_Return_lost_dog_to_owner", "Return lost dog to owner" } + }; + + settings.Add("dayEnd", true, "Split when the next day starts"); + settings.SetToolTip("dayEnd", "This splits when the player is able to\nperform the first input on the next day."); + settings.Add("Main Quests"); + settings.Add("Side Quests"); + + for (int i = 0; i < _settings.GetLength(0); ++i) + { + string parent = _settings[i, 0]; + string id = _settings[i, 1]; + string desc = _settings[i, 2]; + + settings.Add(id, false, desc, parent); + } +} + +init +{ + const int PTR_SIZE = 0x8; + + vars.Unity.TryOnLoad = (Func)(helper => + { + var list = helper.GetClass("mscorlib", "List`1"); + var str = helper.GetClass("mscorlib", "String"); + + var service = helper.GetClass("com.unity-common.core", "Service`1"); + var sm = helper.GetClass("Assembly-CSharp", "StageManager", 1); + + vars.Unity.Make(sm.Static, service["_instance"], sm["activeStage"]).Name = "activeStage"; + vars.Unity.Make(sm.Static, service["_instance"], sm["_time"]).Name = "time"; + + /*******************************************************************/ + var singleton = helper.GetClass("Assembly-CSharp", "Singleton`1"); + var qgm = helper.GetClass("Assembly-CSharp", "QuestGoalsManager", 1); + var qgc = helper.GetClass("Assembly-CSharp", "QuestGoalsContainer"); + + var qg = helper.GetClass("Assembly-CSharp", "QuestGoal"); + var qt = helper.GetClass("Assembly-CSharp", "QuestTask"); + + var quests = vars.Unity.MakeList(qgm.Static, singleton["_instance"], qgm["_container"], qgc["quests"]); + + vars.TaskValue = new MemoryWatcherList(); + vars.RequiredValue = new Dictionary(); + + for (int questId = 0; questId < quests.Count; ++questId) + { + var tasks = vars.Unity.MakeList(quests[questId], qg["_tasks"]); + + for (int taskId = 0; taskId < tasks.Count; ++taskId) + { + IntPtr task = tasks[taskId]; + var name = new DeepPointer(task + qt["displayName"], str["m_firstChar"]).DerefString(game, 128); + var max = game.ReadValue(task + (int)(qt["maxValue"])); + + vars.RequiredValue[name] = max; + vars.TaskValue.Add(new MemoryWatcher(new DeepPointer( + qgm.Static + singleton["_instance"], qgm["_container"], qgc["quests"], list["_items"], + PTR_SIZE * 4 + PTR_SIZE * questId, qg["_tasks"], list["_items"], + PTR_SIZE * 4 + PTR_SIZE * taskId, qt["currentValue"])) + { Name = name }); + } + } + + return true; + }); + + vars.Unity.Load(game); +} + +update +{ + if (!vars.Unity.Loaded) return false; + + vars.Unity.Update(); + + current.IGT = vars.Unity["time"].Current; + current.Stage = vars.Unity["activeStage"].Current; +} + +start +{ + return old.IGT == 0.0 && current.IGT > 0.0; +} + +split +{ + vars.TaskValue.UpdateAll(game); + foreach (var task in vars.TaskValue) + { + if (!task.Changed || !settings[task.Name]) + continue; + + return (int)(Math.Floor(task.Current)) == vars.RequiredValue[task.Name]; + } + + return current.Stage == old.Stage + 1 && settings["dayEnd"]; +} + +reset +{ + return old.IGT > 0.0 && current.IGT == 0.0; +} + +gameTime +{ + return TimeSpan.FromSeconds(current.IGT); +} + +isLoading +{ + return true; +} + +exit +{ + vars.Unity.Reset(); +} + +shutdown +{ + vars.Unity.Reset(); +} \ No newline at end of file diff --git "a/Alba \342\200\223 A Wildlife Adventure/readme.md" "b/Alba \342\200\223 A Wildlife Adventure/readme.md" new file mode 100644 index 0000000..872b542 --- /dev/null +++ "b/Alba \342\200\223 A Wildlife Adventure/readme.md" @@ -0,0 +1,11 @@ +# Auto Splitter for ***Alba: A Wildlife Adventure*** +## Features +Starts the timer when loading into the game. +Splits are available in the settings. Does not split upon reaching the credits. +Resets the timer when starting a new file or closing the game. +Syncs to in-game time. + +## Resources +*Leaderboards: [speedrun.com/alba](https://speedrun.com/alba)* +*Website: [albawildlife.com](https://albawildlife.com)* +*Game: [s.team/a/1337010](https://s.team/a/1337010) ($16.99)* \ No newline at end of file diff --git a/And All Would Cry Beware!/AAWCB.asl b/And All Would Cry Beware!/AAWCB.asl new file mode 100644 index 0000000..42446be --- /dev/null +++ b/And All Would Cry Beware!/AAWCB.asl @@ -0,0 +1,74 @@ +// Disclaimer: This is a very bad autosplitter in terms of execution. +// * Use of random pointers (can break) whose values I don't know. +// * Bunch of work-arounds due to that. + +state("AndAllWouldCryBeware") +{ + int Scene : "UnityPlayer.dll", 0x10B88A4, 0x18, 0x0, 0x70; // SceneManager.Loading[0].Index + int Entities : "UnityPlayer.dll", 0x106F6A0, 0x144, 0x84, 0x210; // ? +} + +startup +{ + dynamic[,] _settings = + { + { null, "areas", "Split when entering an area:", true }, + { "areas", "5 -> 2", "Wayfarer Offices", true }, + { "areas", "2 -> 3", "Mysterious Alien World", true }, + { "areas", "3 -> 4", "Rebekah's Lab", false }, + { "areas", "4 -> 10", "A Fresh Start", true }, + + { null, "events", "Split when doing certain events:", false }, + { "events", "7-6", "Pick up Pistol", false }, + { "events", "6-5", "Pick up Green Key", false }, + { "events", "5-4", "Destroy Elevator", false }, + { "events", "4-3", "Defeat Security Mech", false }, + { "events", "MAW", "Defeat any boss in the Mysterious Alien World or collect Fire Orb", false }, + { "events", "4 -> 11", "Defeat Transformed Rebekah", false } + }; + + for (int i = 0; i < _settings.GetLength(0); ++i) + { + string parent = _settings[i, 0]; + string id = _settings[i, 1]; + string desc = _settings[i, 2]; + bool state = _settings[i, 3]; + + settings.Add(id, state, desc, parent); + } +} + +update +{ + if (current.Scene == 7 || current.Scene == 8) // don't care about loading screen scenes + current.Scene = old.Scene; +} + +start +{ + return old.Scene == 1 && current.Scene == 5; +} + +split +{ + if (current.Entities == old.Entities - 1) + { + switch ((int)(current.Scene)) + { + case 2: return settings[old.Entities + "-" + current.Entities]; + case 3: return settings["MAW"]; + } + } + + if (old.Scene != current.Scene) + { + return settings[old.Scene + " -> " + current.Scene] || + old.Scene == 10 && current.Scene == 9 || + old.Scene == 11 && current.Scene == 9; + } +} + +reset +{ + return old.Scene != 0 && current.Scene == 0; +} \ No newline at end of file diff --git a/And All Would Cry Beware!/readme.md b/And All Would Cry Beware!/readme.md new file mode 100644 index 0000000..3269f9e --- /dev/null +++ b/And All Would Cry Beware!/readme.md @@ -0,0 +1,9 @@ +# Auto Splitter for ***And All Would Cry Beware!*** +## Features +Starts the timer when starting a new game. +Splits are available in the settings. +Resets the timer when returning to the main menu. + +## Resources +*Leaderboards: [speedrun.com/aawcb](https://speedrun.com/aawcb)* +*Game: [s.team/a/1071510](https://s.team/a/1071510) ($9.99)* \ No newline at end of file diff --git a/Assemble with Care/AssembleWithCare.asl b/Assemble with Care/AssembleWithCare.asl new file mode 100644 index 0000000..b34e2d9 --- /dev/null +++ b/Assemble with Care/AssembleWithCare.asl @@ -0,0 +1,71 @@ +state("AWC") {} + +startup +{ + vars.Log = (Action)(output => print("[Assemble with Care] " + output)); + vars.Unity = Assembly.Load(File.ReadAllBytes(@"Components\UnityASL.bin")).CreateInstance("UnityASL.Unity"); + vars.Unity.LoadSceneManager = true; +} + +init +{ + vars.Unity.TryOnLoad = (Func)(helper => + { + // LevelFlowService + var service = helper.GetClass("com.unity-common.core", "Service`1"); + var lfs = helper.GetClass("Assembly-CSharp", "LevelFlowService", 1); + var lc = helper.GetClass("Assembly-CSharp", "LevelConfig"); + + vars.Unity.Make(lfs.Static, service["_instance"], lfs["_state"]).Name = "levelState"; + + // GameStart + var gs = helper.GetClass("Assembly-CSharp", "GameStart"); + + vars.Unity.Make(gs.Static, gs["forceReloadGame"]).Name = "reloadGame"; + + return true; + }); + + vars.Unity.Load(game); +} + +update +{ + if (!vars.Unity.Loaded) return false; + + vars.Unity.Update(); + + current.State = vars.Unity["levelState"].Current; + current.Scene = vars.Unity.Scenes.Loading[0].Index; + current.Reload = vars.Unity["reloadGame"].Current; +} + +start +{ + return !old.Reload && current.Reload; +} + +split +{ + switch ((int)(old.Scene)) + { + case 2: return current.Scene == 1; + case 18: return old.State == 5 && current.State == 8; + default: return old.State == 4 && current.State == 5; + } +} + +reset +{ + return !old.Reload && current.Reload; +} + +exit +{ + vars.Unity.Reset(); +} + +shutdown +{ + vars.Unity.Reset(); +} \ No newline at end of file diff --git a/Assemble with Care/readme.md b/Assemble with Care/readme.md new file mode 100644 index 0000000..4ef7acc --- /dev/null +++ b/Assemble with Care/readme.md @@ -0,0 +1,10 @@ +# Auto Splitter for ***Assemble with Care*** +## Features +Starts the timer when resetting the progress. +Splits after completing every level (including the intro) and when skipping the final dialogue after the DLC level. +Resets the timer when resetting the progress. + +## Resources +*Leaderboards: [speedrun.com/awc](https://speedrun.com/awc)* +*Website: [assemblegame.com](https://assemblegame.com)* +*Game: [s.team/a/1202900](https://s.team/a/1202900) ($9.99)* \ No newline at end of file diff --git a/Devil Daggers/DevilDaggers.asl b/Devil Daggers/DevilDaggers.asl new file mode 100644 index 0000000..34d72d8 --- /dev/null +++ b/Devil Daggers/DevilDaggers.asl @@ -0,0 +1,53 @@ +state("dd") +{ + float Timer : 0x227BE0, 0x34; + int State : 0x227BE0, 0xF4; +} + +startup +{ + vars.TimerModel = new TimerModel { CurrentState = timer }; + + vars.WAVES = new[] + { + 003, 014, 019, 024, 039, 049, 064, 079, + 094, 109, 114, 119, 134, 144, 154, 164, + 174, 184, 189, 194, 199, 229, 239, 244, + 259, 274, 289, 304, 330, 350, 365, 370, + 375, 397, 400, 406, 412, 417, 418, 419, + 424, 427, 430, 440, 441, 442, 447, 449, + 451, 456, 459, 462, 467, 472, 473, 474, + 484, 485, 486, 491, 492, 497, 502, 507 + }; +} + +update +{ + if (old.State == 3 && current.State == 4) + vars.TimerModel.Pause(); +} + +start +{ + return old.State != 3 && current.State == 3; +} + +split +{ + return ((int[])vars.WAVES).Any(waveTime => old.Timer < waveTime && current.Timer >= waveTime); +} + +reset +{ + return old.State != current.State && current.State != 4; +} + +gameTime +{ + return TimeSpan.FromSeconds(current.Timer); +} + +isLoading +{ + return true; +} \ No newline at end of file diff --git a/Devil Daggers/readme.md b/Devil Daggers/readme.md new file mode 100644 index 0000000..a90beed --- /dev/null +++ b/Devil Daggers/readme.md @@ -0,0 +1,13 @@ +# Auto Splitter for ***Devil Daggers*** +## Features +Starts the timer when the player starts a new attempt. +Splits whenever a new set of enemies spawns until the first loop is completed. +Resets the timer when the player starts a new attempt. + +Pauses the timer when the player dies. + +## Resources +*Leaderboards: [speedrun.com/devil_daggers](https://speedrun.com/devil_daggers)* +*Discord: [discord.gg/NF32j8S](https://discord.gg/NF32j8S)* +*Website: [devildaggers.com](https://devildaggers.com)* +*Game: [s.team/a/422970](https://s.team/a/422970) ($4.99)* \ No newline at end of file diff --git a/Donut County/DonutCounty.asl b/Donut County/DonutCounty.asl new file mode 100644 index 0000000..8f98c26 --- /dev/null +++ b/Donut County/DonutCounty.asl @@ -0,0 +1,121 @@ +state("DonutCounty") {} + +startup +{ + vars.Log = (Action)(output => print("[Donut County] " + output)); + vars.Unity = Assembly.Load(File.ReadAllBytes(@"Components\UnityASL.bin")).CreateInstance("UnityASL.Unity"); + vars.CompletedSplits = new HashSet(); + + dynamic[,] _settings = + { + { "splits", "mira", "Mira's House", true }, + { "mira", "0", "Goose on a Scooter", false }, + { "mira", "1", "BK talking to Mira", true }, + + { "splits", "2", "Potter's Rock", true }, + { "splits", "3", "Ranger Station", true }, + { "splits", "4", "Riverbed", true }, + { "splits", "5", "Campground", true }, + { "splits", "6", "Hopper Springs", true }, + { "splits", "7", "Joshua Tree", true }, + { "splits", "8", "Beach Lot C", true }, + { "splits", "9", "Gecko Park", true }, + { "splits", "10", "Chicken Barn", true }, + { "splits", "11", "Honey Nut Forest", true }, + { "splits", "12", "Cat Soup", true }, + { "splits", "13", "Donut Shop", true }, + { "splits", "14", "Abandoned House", true }, + { "splits", "15", "Raccoon Lagoon", true }, + { "splits", "16", "The 405", true }, + { "splits", "17", "Above Donut County", false }, + { "splits", "18", "Raccoon HQ Exterior", false }, + { "splits", "20", "Biology Lab", true }, + { "splits", "22", "Anthropology Lab", true }, + { "splits", "24", "Trash King's Office", false } + }; + + settings.Add("splits", true, "Split after completing levels:"); + + for (int i = 0; i < _settings.GetLength(0); ++i) + { + string parent = _settings[i, 0]; + string id = _settings[i, 1]; + string desc = _settings[i, 2]; + bool state = _settings[i, 3]; + + settings.Add(id, state, desc, parent); + } +} + +onStart +{ + vars.CompletedSplits.Clear(); +} + +init +{ + vars.Unity.TryOnLoad = (Func)(helper => + { + var rm = helper.GetClass("Assembly-CSharp", "RM"); + var sm = helper.GetClass("Assembly-CSharp", "SceneManager"); + var ls = helper.GetClass("Assembly-CSharp", "LevelSettings"); + var d = helper.GetClass("Assembly-CSharp", "OS1Delivery"); + var t = helper.GetClass("Assembly-CSharp", "Tornado"); + + vars.Unity.MakeString(rm.Static, rm["sceneManager"], sm["nextLevel"]).Name = "nextLevel"; + vars.Unity.Make(rm.Static, rm["sceneManager"], sm["loading"]).Name = "loading"; + vars.Unity.Make(rm.Static, rm["sceneManager"], sm["_isLoadingScene"]).Name = "loadingScene"; + vars.Unity.Make(rm.Static, rm["levelSettings"], ls["deliveryData"], d["index"]).Name = "levelIndex"; + vars.Unity.Make(rm.Static, rm["tornado"], t["_numDestructables"]).Name = "tornadoDestructables"; + + return true; + }); + + vars.Unity.Load(game); +} + +update +{ + if (!vars.Unity.Loaded) return false; + + vars.Unity.Update(); + + current.Scene = vars.Unity["nextLevel"].Value; + current.Loading = vars.Unity["loading"].Current; + current.LoadingScene = vars.Unity["loadingScene"].Current; + current.LevelIndex = vars.Unity["levelIndex"].Current; + current.TornadoDestructables = vars.Unity["tornadoDestructables"].Current; +} + +start +{ + return old.Scene != current.Scene && + old.Scene == "titlescreen" && + current.Scene != "scn_credits"; +} + +split +{ + if (old.LevelIndex != current.LevelIndex && !vars.CompletedSplits.Contains(old.LevelIndex)) + { + vars.CompletedSplits.Add(old.LevelIndex); + return settings[old.LevelIndex.ToString()]; + } + + return old.TornadoDestructables == 3 && current.TornadoDestructables == 4; +} + +isLoading +{ + return current.Loading || current.LoadingScene; +} + +exit +{ + vars.Unity.Reset(); +} + +shutdown +{ + vars.Unity.Reset(); +} \ No newline at end of file diff --git a/Donut County/readme.md b/Donut County/readme.md new file mode 100644 index 0000000..ece2e0c --- /dev/null +++ b/Donut County/readme.md @@ -0,0 +1,11 @@ +# Auto Splitter for ***Donut County*** +## Features +Starts the timer when starting any level. +Splits are available in the settings. +Does not reset the timer. + +## Resources +*Leaderboards: [speedrun.com/donutcounty](https://speedrun.com/donutcounty)* +*Discord: [discord.gg/V3chU5S](https://discord.gg/V3chU5S)* +*Website: [donutcounty.com](https://donutcounty.com)* +*Game: [s.team/a/702670](https://s.team/a/702670) ($12.99)* \ No newline at end of file diff --git "a/Door Kickers \342\200\223 Action Squad/DKAS.asl" "b/Door Kickers \342\200\223 Action Squad/DKAS.asl" new file mode 100644 index 0000000..4255da7 --- /dev/null +++ "b/Door Kickers \342\200\223 Action Squad/DKAS.asl" @@ -0,0 +1,203 @@ +state("ActionSquad") +{ + double LevelRunTime : 0x38FDD0; + int LevelState : 0x3907BC; + int LevelEndTime : 0x391AB0; + + int Level : 0x3907D0; + int Chapter : 0x3907D4; + + int HostagesTotal : 0x391AC4; + int HostagesSaved : 0x391B18; + int TargetsTotal : 0x391AE0; + int TargetsArrested : 0x391AE4; +} + +startup +{ + string[,] _settings = + { + { null, "ch1", "Chapter 1:" }, + { "ch1", "ch0lvl0", "1.1: Slow Starters" }, + { "ch1", "ch0lvl1", "1.2: Knock Knock Knock" }, + { "ch1", "ch0lvl2", "1.3: Old Embassy" }, + { "ch1", "ch0lvl3", "1.4: Night Call" }, + { "ch1", "ch0lvl4", "1.5: Tread Carefully" }, + { "ch1", "ch0lvl5", "1.6: 255th Precinct" }, + { "ch1", "ch0lvl6", "1.7: High Rise" }, + { "ch1", "ch0lvl7", "1.8: The Warehouse" }, + { "ch1", "ch0lvl8", "1.9: Office Day" }, + { "ch1", "ch0lvl9", "1.10: Flashbang Junction" }, + { "ch1", "ch0lvl10", "1.11: Cleanup" }, + { "ch1", "ch0lvl11", "1.12: Meet Big Boss" }, + + { null, "ch2", "Chapter 2:" }, + { "ch2", "ch1lvl0", "2.1: Meeting Bart" }, + { "ch2", "ch1lvl1", "2.2: Volatile Motel" }, + { "ch2", "ch1lvl2", "2.3: Going Under" }, + { "ch2", "ch1lvl3", "2.4: The Bunker" }, + { "ch2", "ch1lvl4", "2.5: Midnight Folies" }, + { "ch2", "ch1lvl5", "2.6: Mountain Hideout" }, + { "ch2", "ch1lvl6", "2.7: Outpost" }, + { "ch2", "ch1lvl7", "2.8: Bombs Away" }, + { "ch2", "ch1lvl8", "2.9: The Block" }, + { "ch2", "ch1lvl9", "2.10: Caught in a Maze" }, + { "ch2", "ch1lvl10", "2.11: The Museum" }, + { "ch2", "ch1lvl11", "2.12: The Yard" }, + + { null, "ch3", "Chapter 3:" }, + { "ch3", "ch2lvl0", "3.1: Frankie the Fanatic" }, + { "ch3", "ch2lvl1", "3.2: Crisis at McDonuts" }, + { "ch3", "ch2lvl2", "3.3: Metro Madness" }, + { "ch3", "ch2lvl3", "3.4: Molotov Delievery" }, + { "ch3", "ch2lvl4", "3.5: Vertigo" }, + { "ch3", "ch2lvl5", "3.6: Water Works, Inc." }, + { "ch3", "ch2lvl6", "3.7: Cold Exposure" }, + { "ch3", "ch2lvl7", "3.8: Hipster Beer Factory" }, + { "ch3", "ch2lvl8", "3.9: Climbing the Shaft" }, + { "ch3", "ch2lvl9", "3.10: Laboratory Crisis" }, + { "ch3", "ch2lvl10", "3.11: Raid on Plaza" }, + { "ch3", "ch2lvl11", "3.12: Doomsday Train" }, + + { null, "ch4", "Chapter 4:" }, + { "ch4", "ch3lvl0", "4.1: Bank Heist" }, + { "ch4", "ch3lvl1", "4.2: Queen's Cross" }, + { "ch4", "ch3lvl2", "4.3: Fibonacci" }, + { "ch4", "ch3lvl3", "4.4: On the Anvil" }, + { "ch4", "ch3lvl4", "4.5: Secret Stronghold" }, + { "ch4", "ch3lvl5", "4.6: Caneville" }, + { "ch4", "ch3lvl6", "4.7: On Vacation" }, + { "ch4", "ch3lvl7", "4.8: Whine and Dine" }, + { "ch4", "ch3lvl8", "4.9: Lights Out" }, + { "ch4", "ch3lvl9", "4.10: To the Airport" }, + { "ch4", "ch3lvl10", "4.11: Around the Clock" }, + { "ch4", "ch3lvl11", "4.12: The Great Batsby" }, + + { null, "ch5", "Chapter 5:" }, + { "ch5", "ch4lvl0", "5.1: I am the Captain Now" }, + { "ch5", "ch4lvl1", "5.2: Reservoir Docks" }, + { "ch5", "ch4lvl2", "5.3: Dusty Neighbours" }, + { "ch5", "ch4lvl3", "5.4: The Shiny" }, + { "ch5", "ch4lvl4", "5.5: El Quarantino" }, + { "ch5", "ch4lvl5", "5.6: Joint Block" }, + { "ch5", "ch4lvl6", "5.7: Sky Scratchers" }, + { "ch5", "ch4lvl7", "5.8: NCtv Cribs" }, + { "ch5", "ch4lvl8", "5.9: Tinytanic" }, + { "ch5", "ch4lvl9", "5.10: Flying High" }, + { "ch5", "ch4lvl10", "5.11: Word on the Street" }, + { "ch5", "ch4lvl11", "5.12: The Crib" }, + + { null, "ch6", "Chapter 6:" }, + { "ch6", "ch5lvl0", "6.1: Fixer Upper" }, + { "ch6", "ch5lvl1", "6.2: Snitches Galore" }, + { "ch6", "ch5lvl2", "6.3: Graveyard Surprise" }, + { "ch6", "ch5lvl3", "6.4: Long Way Down" }, + { "ch6", "ch5lvl4", "6.5: Terror from the Terrace" }, + { "ch6", "ch5lvl5", "6.6: Hard Core" }, + { "ch6", "ch5lvl6", "6.7: It's Raining Men" }, + { "ch6", "ch5lvl7", "6.8: Pool Party Crashers" }, + { "ch6", "ch5lvl8", "6.9: Downstairs Cage" }, + { "ch6", "ch5lvl9", "6.10: Crates and Bombs" }, + { "ch6", "ch5lvl10", "6.11: The Express" }, + { "ch6", "ch5lvl11", "6.12: Consulate Crisis" }, + + { null, "ch7", "Chapter 7:" }, + { "ch7", "ch6lvl0", "7.1: Stoners Paradise" }, + { "ch7", "ch6lvl1", "7.2: Peace and Quiet" }, + { "ch7", "ch6lvl2", "7.3: High as a Kite" }, + { "ch7", "ch6lvl3", "7.4: Living on the Ledge" }, + { "ch7", "ch6lvl4", "7.5: Cutting Edge" }, + { "ch7", "ch6lvl5", "7.6: Earthworm Ben" }, + { "ch7", "ch6lvl6", "7.7: Switch and Destroy" }, + { "ch7", "ch6lvl7", "7.8: Home Box Office" }, + { "ch7", "ch6lvl8", "7.9: Leap of Faith" }, + { "ch7", "ch6lvl9", "7.10: Under Pressure" }, + { "ch7", "ch6lvl10", "7.11: Monty Hall" }, + { "ch7", "ch6lvl11", "7.12: No Excuse" }, + + { null, "ch8", "Chapter 8:" }, + { "ch8", "ch7lvl0", "8.1: Abandoned Mines" }, + { "ch8", "ch7lvl1", "8.2: Fare Well" }, + { "ch8", "ch7lvl2", "8.3: Wrong Way" }, + { "ch8", "ch7lvl3", "8.4: The Serai Abduction" }, + { "ch8", "ch7lvl4", "8.5: Get Out" }, + { "ch8", "ch7lvl5", "8.6: Out of Time" }, + { "ch8", "ch7lvl6", "8.7: Airborne Menace" }, + { "ch8", "ch7lvl7", "8.8: Queen of her Castle" }, + { "ch8", "ch7lvl8", "8.9: Sunken Stronghold" }, + { "ch8", "ch7lvl9", "8.10: Burj Al Tarab" }, + { "ch8", "ch7lvl10", "8.11: Underground Threat" }, + { "ch8", "ch7lvl11", "8.12: Castle Summit" } + }; + + settings.Add("leveled", true, "Split whenever a level is completed (3 stars not required)"); + settings.SetToolTip("leveled", "Unchecking this setting will make the auto splitter\nonly split when a level is completed with 3 stars."); + settings.Add("ilTimer", false, "Reset on mission restart or fail"); + + for (int i = 0; i < _settings.GetLength(0); ++i) + { + string parent = _settings[i, 0]; + string id = _settings[i, 1]; + string desc = _settings[i, 2]; + + settings.Add(id, true, desc, parent); + } + + vars.LogFile = "DKAS_ILTimes.log"; + timer.CurrentTimingMethod = TimingMethod.RealTime; +} + +onStart +{ + File.AppendAllText(vars.LogFile, "\nNew run started!\n----------------\n"); +} + +init +{ + vars.Log = (Action)(successOrFailed => + { + var output = string.Format( + "L{0}-{1:00} | MISSION {2} at {3:mm':'ss'.'fff} (Hostages: {4}/{5}, Arrest Targets: {6}/{7})\n", + current.Chapter + 1, current.Level + 1, + successOrFailed, TimeSpan.FromSeconds(current.LevelRunTime), + current.HostagesSaved, current.HostagesTotal, + current.TargetsArrested, current.TargetsTotal); + + var lines = File.ReadAllLines(vars.LogFile); + if (lines.Length >= 4096) File.WriteAllLines(vars.LogFile, lines.Skip(4096 - 512)); + + File.AppendAllText(vars.LogFile, output); + }); +} + +start +{ + return old.LevelRunTime == 0.0 && current.LevelRunTime > 0.0; +} + +split +{ + if (old.LevelEndTime == 0 && current.LevelEndTime > 0 && old.LevelState == 0 && current.LevelState == 1) + { + vars.Log("SUCCESS"); + + var is6_11 = current.Chapter == 5 && current.Level == 10; + var allTargetsArrested = current.TargetsTotal == 0 ? current.TargetsArrested == 0 : current.TargetsArrested == 1; + var allHostagesSaved = current.HostagesSaved == current.HostagesTotal - (is6_11 ? 1 : 0); + var splitCondition = settings["leveled"] ? true : allHostagesSaved && allTargetsArrested; + + return splitCondition && settings["ch" + current.Chapter + "lvl" + current.Level]; + } +} + +reset +{ + if (old.LevelEndTime == 0 && current.LevelEndTime > 0 && old.LevelState == 0 && current.LevelState == 2) + { + vars.Log("FAILED"); + + return settings["ilTimer"]; + } + + return settings["ilTimer"] && old.LevelRunTime > current.LevelRunTime; +} \ No newline at end of file diff --git "a/Door Kickers \342\200\223 Action Squad/readme.md" "b/Door Kickers \342\200\223 Action Squad/readme.md" new file mode 100644 index 0000000..9d6514d --- /dev/null +++ "b/Door Kickers \342\200\223 Action Squad/readme.md" @@ -0,0 +1,13 @@ +# Auto Splitter for ***Door Kickers: Action Squad*** +## Features +Starts the timer when the player enters a level. +Splits upon finishing a level when – depending on the user's settings – either all or a variable amount of stars have been awarded. +Resets the timer upon failing a mission or resetting the level – depending on the user's settings. + +This autosplitter additionally logs successful and failed missions to a file, complete with chapter number, level number, whether the level was successful or failed, the time and the amount of hostages and arrest targets and their respective totals. + +## Resources +*Leaderboards: [speedrun.com/dkas](https://speedrun.com/dkas)* +*Discord: [discord.gg/aB2RsC9](https://discord.gg/aB2RsC9)* +*Website: [inthekillhouse.com/actionsquad](https://inthekillhouse.com/actionsquad)* +*Game: [s.team/a/686200](https://s.team/a/686200) ($13.99)* \ No newline at end of file diff --git a/Give Up, Robot/GiveUpRobot1.asl b/Give Up, Robot/GiveUpRobot1.asl new file mode 100644 index 0000000..c02e22d --- /dev/null +++ b/Give Up, Robot/GiveUpRobot1.asl @@ -0,0 +1,62 @@ +state("GiveUpRobot") +{ + int TotalCount : "Adobe AIR.dll", 0x1393398, 0x4, 0x24, 0xA8, 0x18, 0x534, 0x10, 0x18, 0x20; + int PartialCount : "Adobe AIR.dll", 0x1393364, 0x80, 0xB4, 0x6D8, 0x64, 0x10, 0x78, 0x64; + int Level : "Adobe AIR.dll", 0x1393364, 0x80, 0xB4, 0x6D8, 0x64, 0x10, 0x78, 0x6C; +} + +state("flashplayer_32_sa", "Normal Flash Player") +{ + int TotalCount : 0xD1DD48, 0x8BC, 0x8, 0x68, 0xC, 0x8, 0x14, 0x69C, 0x10, 0x18, 0x20; + int PartialCount : 0xD1DD48, 0x8BC, 0x8, 0x68, 0xC, 0x8, 0x14, 0x2D8, 0x10, 0x78, 0x64; + int Level : 0xD1DD48, 0x8BC, 0x8, 0x68, 0xC, 0x8, 0x14, 0x2D8, 0x10, 0x78, 0x6C; +} + +state("flashplayer_32_sa_debug", "Debug Flash Player") +{ + int TotalCount : 0xDA07D8, 0xC, 0x818, 0x8, 0x24, 0xC8, 0x18, 0x69C, 0x10, 0x18, 0x20; + int PartialCount : 0xDA07D8, 0xC, 0x818, 0x8, 0x24, 0xC8, 0x18, 0x2D8, 0x10, 0x78, 0x64; + int Level : 0xDA07D8, 0xC, 0x818, 0x8, 0x24, 0xC8, 0x18, 0x2D8, 0x10, 0x78, 0x6C; +} + +startup +{ + if (timer.CurrentTimingMethod == TimingMethod.RealTime) + { + var mbox = MessageBox.Show( + "Give Up, Robot uses in-game time.\nWould you like to switch to it?", + "LiveSplit | Give Up, Robot", + MessageBoxButtons.YesNo); + + if (mbox == DialogResult.Yes) + timer.CurrentTimingMethod = TimingMethod.GameTime; + } +} + +start +{ + return current.Level == 1 && old.PartialCount == 0 && current.PartialCount > 0; +} + +split +{ + return current.Level == old.Level + 1 || + old.Level != current.Level && old.Level % 10 == 0 && old.Level != 80; +} + +reset +{ + return old.Level != 0 && current.Level == 0; +} + +gameTime +{ + return current.Level == 80 + ? TimeSpan.FromSeconds(current.TotalCount / 60f) + : TimeSpan.FromSeconds((current.TotalCount + current.PartialCount) / 60f); +} + +isLoading +{ + return true; +} \ No newline at end of file diff --git a/Give Up, Robot/readme.md b/Give Up, Robot/readme.md new file mode 100644 index 0000000..c04eeb7 --- /dev/null +++ b/Give Up, Robot/readme.md @@ -0,0 +1,10 @@ +# Auto Splitter for ***Give Up, Robot*** +## Features +Starts the timer when starting a new game. +Splits when finishing a level. +Resets the timer when reloading the .swf or when closing the game. +Syncs to in-game time. + +## Resources +*Leaderboards: [speedrun.com/gur1](https://speedrun.com/gur1)* +*Discord: [discord.gg/AEXM9dJyvy](https://discord.gg/AEXM9dJyvy)* \ No newline at end of file diff --git a/Grapple!/Grapple.asl b/Grapple!/Grapple.asl new file mode 100644 index 0000000..e88ff37 --- /dev/null +++ b/Grapple!/Grapple.asl @@ -0,0 +1,88 @@ +state("Grappler!") {} + +startup +{ + vars.Log = (Action)(output => print("[Grapple] " + output)); + vars.Unity = Assembly.Load(File.ReadAllBytes(@"Components\UnityASL.bin")).CreateInstance("UnityASL.Unity"); + vars.Unity.LoadSceneManager = true; + vars.TotalTime = 0f; + + settings.Add("ilReset", false, "Reset timer when restarting a level"); +} + +onStart +{ + vars.TotalTime = 0f; +} + +init +{ + vars.Unity.TryOnLoad = (Func)(helper => + { + var gpm = helper.GetClass("Assembly-CSharp", "GameplayManager"); + + vars.Unity.Make(gpm.Static, gpm["instance"], gpm["shouldIncrement"]).Name = "shouldIncrement"; + vars.Unity.Make(gpm.Static, gpm["instance"], gpm["reset"]).Name = "reset"; + vars.Unity.Make(gpm.Static, gpm["instance"], gpm["firstJump"]).Name = "firstJump"; + vars.Unity.Make(gpm.Static, gpm["instance"], gpm["timeElapsed"]).Name = "timeElapsed"; + + return true; + }); + + vars.Unity.Load(game, tryLoadTimeout: 1000); +} + +update +{ + if (!vars.Unity.Loaded) return false; + + vars.Unity.Update(); + + current.Scene = vars.Unity.Scenes.Loading[0].Index; + current.ShouldIncrement = vars.Unity["shouldIncrement"].Current; + current.Reset = vars.Unity["reset"].Current; + current.FirstJump = vars.Unity["firstJump"].Current; + current.Time = vars.Unity["timeElapsed"].Current; + + if (old.Scene != current.Scene) + vars.Log(old.Scene + " -> " + current.Scene); +} + +start +{ + return !old.FirstJump && current.FirstJump; +} + +split +{ + return old.Scene < current.Scene && current.Scene > 0; +} + +reset +{ + return old.Scene != 0 && current.Scene == 0 || + (!old.Reset && current.Reset || old.FirstJump && !current.FirstJump) && (settings["ilReset"] ? true : current.Scene == 1); +} + +gameTime +{ + if (old.FirstJump && !current.FirstJump) + vars.TotalTime += old.Time; + + return TimeSpan.FromSeconds(vars.TotalTime + current.Time); +} + +isLoading +{ + return true; +} + +exit +{ + vars.Unity.Reset(); +} + +shutdown +{ + vars.Unity.Reset(); +} \ No newline at end of file diff --git a/Grapple!/readme.md b/Grapple!/readme.md new file mode 100644 index 0000000..f57a4ad --- /dev/null +++ b/Grapple!/readme.md @@ -0,0 +1,11 @@ +# Auto Splitter for ***Grapple!*** +## Features +Starts the timer when starting any level. +Splits when switching levels. +Resets the timer when resetting/dying in level 1, exiting to the menu, or resetting/dying in any level and having the appropriate setting enabled. +Syncs to in-game time. + +## Resources +*Leaderboards: [speedrun.com/barjis_grappler](https://speedrun.com/barjis_grappler)* +*Discord: [discord.gg/Hkd3u5h](https://discord.gg/Hkd3u5h)* +*Game: [barji.itch.io/grapple](https://barji.itch.io/grapple) (Free)* \ No newline at end of file diff --git a/Gunfire Reborn/GunfireReborn.asl b/Gunfire Reborn/GunfireReborn.asl new file mode 100644 index 0000000..2bfabba --- /dev/null +++ b/Gunfire Reborn/GunfireReborn.asl @@ -0,0 +1,129 @@ +state("Gunfire Reborn") {} + +startup +{ + vars.Log = (Action)((output) => print("[Gunfire Reborn] " + output)); + vars.Unity = Assembly.Load(File.ReadAllBytes(@"Components\UnityASL.bin")).CreateInstance("UnityASL.Unity"); + vars.TimerModel = new TimerModel { CurrentState = timer }; + + vars.Stages = new List> + { + Tuple.Create("Longling Tomb", 5, false), + Tuple.Create("Anxi Tomb", 4, false), + Tuple.Create("Duo Fjord", 4, true), + Tuple.Create("Hyperborean", 3, true) + }; + + var count = vars.Stages.Count; + for (int stage = 1; stage <= count; ++stage) + { + var name = vars.Stages[stage - 1].Item1; + var levels = vars.Stages[stage - 1].Item2; + + settings.Add("layer" + stage, true, "Split after completing a stage in " + name + ":"); + + for (int level = 0; level <= levels; ++level) + { + if (level == 0 && stage != 0) + settings.Add(string.Format("{0}-0to{0}-1", stage), false, name + " Entrance", "layer" + stage); + else if (level > 0 && level < levels) + settings.Add(string.Format("{0}-{1}to{0}-{2}", stage, level, level + 1), true, "Stage " + level, "layer" + stage); + else if (level == levels) + settings.Add(string.Format("{0}-{1}to{2}-0", stage, level, stage + 1), true, name + " Boss", "layer" + stage); + } + } + + if (timer.CurrentTimingMethod == TimingMethod.RealTime) + { + var mbox = MessageBox.Show( + "Gunfire Reborn uses in-game time.\nWould you like to switch to it?", + "LiveSplit | Gunfire Reborn", + MessageBoxButtons.YesNo); + + if (mbox == DialogResult.Yes) + timer.CurrentTimingMethod = TimingMethod.GameTime; + } +} + +init +{ + vars.Unity.TryOnLoad = (Func)(helper => + { + var wc = helper.GetClass("Assembly-CSharp", "WarCache"); + var wli = helper.GetClass("Assembly-CSharp", "WarLevelInfo"); + var gsm = helper.GetClass("Assembly-CSharp", "GameSceneManager"); + var gu = helper.GetClass("Assembly-CSharp", "GameUtility"); + var fw = helper.GetClass("Assembly-CSharp", "netwarreward_GS2CRewardFinishWarClass"); + + vars.Unity.Make(wc.Static, fw["Type"] - wc["ReportData"]).Name = "EndType"; + vars.Unity.Make(wc.Static, wc["LevelInfo"], wli["CurLevel"]).Name = "Level"; + vars.Unity.Make(wc.Static, wc["LevelInfo"], wli["CurLayer"]).Name = "Layer"; + vars.Unity.Make(gsm.Static, gsm["isInWar"]).Name = "InWar"; + vars.Unity.Make(gu.Static, gu["ClientChallengeFrame"]).Name = "Time"; + + return true; + }); + + vars.Unity.Load(game); +} + +update +{ + if (!vars.Unity.Loaded) return false; + + vars.Unity.Update(); + current.EndType = vars.Unity["EndType"].Current; + current.Level = vars.Unity["Level"].Current; + current.Layer = vars.Unity["Layer"].Current; + current.IsInWar = vars.Unity["InWar"].Current; + current.HalfTime = vars.Unity["Time"].Current; + + if (old.EndType == 0 && current.EndType == 2) + vars.TimerModel.Pause(); +} + +start +{ + return current.Layer == 1 && current.Level == 1 && old.HalfTime == 0 && current.HalfTime > 0; +} + +split +{ + if (old.EndType == 0 && current.EndType == 3) + { + var layer = current.Layer; + var stageInfo = vars.Stages[layer - 1]; + if (stageInfo.Item3) + return settings[string.Format("{0}-{1}to{2}-0", layer, current.Level, layer + 1)]; + } + + if (old.Level != current.Level) + return settings[string.Format("{0}-{1}to{2}-{3}", old.Layer, old.Level, current.Layer, current.Level)]; +} + +reset +{ + return old.EndType == 0 && current.EndType == 1 || old.Layer != 0 && current.Layer == 0; +} + +gameTime +{ + if (current.EndType == 0) + return TimeSpan.FromMilliseconds(current.HalfTime * 20); +} + +isLoading +{ + return true; +} + +exit +{ + timer.IsGameTimePaused = true; + vars.Unity.Reset(); +} + +shutdown +{ + vars.Unity.Reset(); +} \ No newline at end of file diff --git a/Gunfire Reborn/readme.md b/Gunfire Reborn/readme.md new file mode 100644 index 0000000..597dd74 --- /dev/null +++ b/Gunfire Reborn/readme.md @@ -0,0 +1,14 @@ +# Auto Splitter for ***Gunfire Reborn*** +## Features +Starts the timer when loading into the first stage. +Splits are available in the settings. Does not split upon the start of the credits. +Resets the timer when the player quits to the main menu. +Syncs to in-game time. + +Pauses the timer when the player dies in any stage before Duo Fjord's boss. + +## Resources +*Leaderboards: [speedrun.com/gunfire_reborn](https://speedrun.com/gunfire_reborn)* +*Discord: [discord.gg/j7Ss7aY](https://discord.gg/j7Ss7aY)* +*Website: [gunfire.herojoys.com](https://gunfire.herojoys.com/en)* +*Game: [s.team/a/1217060](https://s.team/a/1217060) ($11.99)* \ No newline at end of file diff --git a/Hello Neighbor/HelloNeighbor.asl b/Hello Neighbor/HelloNeighbor.asl new file mode 100644 index 0000000..01c5382 --- /dev/null +++ b/Hello Neighbor/HelloNeighbor.asl @@ -0,0 +1,32 @@ +// Disclaimer: This is a very bad autosplitter in terms of execution. +// * Use of random pointers (can break) whose values I don't know. + +state("HelloNeighbor-Win64-Shipping") +{ + bool IsPlaying : 0x29C2C44; + bool InControl : 0x2C4C258, 0xC8, 0x258, 0xAE0, 0x1B8; +} + +startup +{ + if (timer.CurrentTimingMethod == TimingMethod.RealTime) + { + var mbox = MessageBox.Show( + "Removing loads from Hello Neighbor requires comparing against Game Time.\nWould you like to switch to it?", + "LiveSplit | Hello Neighbor", + MessageBoxButtons.YesNo); + + if (mbox == DialogResult.Yes) + timer.CurrentTimingMethod = TimingMethod.GameTime; + } +} + +start +{ + return !old.InControl && current.InControl; +} + +isLoading +{ + return !current.IsPlaying; +} \ No newline at end of file diff --git a/Hello Neighbor/readme.md b/Hello Neighbor/readme.md new file mode 100644 index 0000000..83659c2 --- /dev/null +++ b/Hello Neighbor/readme.md @@ -0,0 +1,12 @@ +# Load Remover for ***Hello Neighbor*** +## Features +Starts the timer when the player exits a cutscene. +Does not split. +Does not reset the timer. +Removes loading times. + +## Resources +*Leaderboards: [speedrun.com/helloneighbor](https://speedrun.com/helloneighbor)* +*Discord: [discord.gg/hvAbaeC](https://discord.gg/hvAbaeC)* +*Website: [helloneighborgame.com](https://helloneighborgame.com)* +*Game: [s.team/a/521890](https://s.team/a/521890) ($29.99)* \ No newline at end of file diff --git a/Here Comes Niko/HereComesNiko.asl b/Here Comes Niko/HereComesNiko.asl new file mode 100644 index 0000000..33024a4 --- /dev/null +++ b/Here Comes Niko/HereComesNiko.asl @@ -0,0 +1,411 @@ +state("Here Comes Niko!") {} + +startup +{ + vars.Log = (Action)(output => print("[Here Comes Niko!] " + output)); + vars.Unity = Assembly.Load(File.ReadAllBytes(@"Components\UnityASL.bin")).CreateInstance("UnityASL.Unity"); + + dynamic[,] _settings = + { + { "Home", "h_Coins", "Coins", true }, + { "h_Coins", "1_Fetch", "Low Frog: take lunch box to High Frog", true }, + + { "Home", "h_Letters", "Letters", false }, + { "h_Letters", "1_letter12", "on the rocks near the crane", false }, + + { "Home", "1_End", "Enter train to leave Home", true }, + + + { "Hairball City", "hc_Coins", "Coins", true }, + { "hc_Coins", "2_main", "Gunther: talk to Gunther", true }, + { "hc_Coins", "2_volley", "Travis: BIG VOLLEY", true }, + { "hc_Coins", "2_Dustan", "Dustan: get to the top of the lighthouse", true }, + { "hc_Coins", "2_flowerPuzzle", "Little Gabi: plant flowers in each of the plant beds", true }, + { "hc_Coins", "2_fishing", "Fischer: catch all 5 fish", true }, + { "hc_Coins", "2_bug", "Blessley: collect 30 butterflies", true }, + { "hc_Coins", "hc_Coins+", "Requires Contact List", false }, + { "hc_Coins+", "2_arcadeBone", "Arcade Machine: get 5 dog bones", false }, + { "hc_Coins+", "2_arcade", "Arcade Machine: get the coin", false }, + { "hc_Coins+", "2_hamsterball", "Moomy: collect 10 sunflower seeds", false }, + { "hc_Coins+", "2_carrynojump", "Serschel/Louist: bring Louist back to Serschel", false }, + { "hc_Coins+", "2_gamerQuest", "Game Kid: jump from the skyscraper into the water without touching ground", false }, + { "hc_Coins+", "2_graffiti", "Nina: paint 5 symbols on the ground", false }, + { "hc_Coins+", "2_cassetteCoin", "Mitch: trade 5 cassettes for a coin", false }, + { "hc_Coins+", "2_cassetteCoin2", "Mai: trade 5 cassettes for a coin", false }, + + { "Hairball City", "hc_Cassettes", "Cassettes", false }, + { "hc_Cassettes", "2_casHairballCity0", "on the rock near the whiteboard", false }, + { "hc_Cassettes", "2_casHairballCity4", "on the palm tree", false }, + { "hc_Cassettes", "2_casHairballCity5", "in the dark tunnel", false }, + { "hc_Cassettes", "2_casHairballCity2", "above the big red umbrellas near the scarecrow frog", false }, + { "hc_Cassettes", "2_casHairballCity3", "above the door of the lighthouse", false }, + { "hc_Cassettes", "2_casHairballCity7", "at the back side of the lighthouse", false }, + { "hc_Cassettes", "2_casHairballCity8", "under the ramp", false }, + { "hc_Cassettes", "2_casHairballCity6", "inside of a breakable box", false }, + { "hc_Cassettes", "2_casHairballCity1", "on the crown of the frog statue", false }, + { "hc_Cassettes", "2_casHairballCity9", "in the sky above the frog statue", false }, + + { "Hairball City", "hc_Letters", "Letters", false }, + { "hc_Letters", "2_letter1", "in the tree near the scarecrow frog", false }, + { "hc_Letters", "2_letter7", "on the other side of the train", false }, + + { "Hairball City", "2_End", "Enter train to leave Hairball City", true }, + + + { "Turbine Town", "tt_Coins", "Coins", true }, + { "tt_Coins", "3_main", "Pelly: get the wind turbine working", true }, + { "tt_Coins", "3_volley", "Trixie: AIR VOLLEY", true }, + { "tt_Coins", "3_Dustan", "Dustan: get to the top of the turbine", true }, + { "tt_Coins", "3_flowerPuzzle", "Little Gabi: plant flowers in each of the plant beds", true }, + { "tt_Coins", "3_fishing", "Fischer: catch all 5 fish", true }, + { "tt_Coins", "3_bug", "Blessley: collect 30 butterflies", true }, + { "tt_Coins", "tt_Coins+", "Requires Contact List", false }, + { "tt_Coins+", "3_arcadeBone", "Arcade Machine: get 5 dog bones", false }, + { "tt_Coins+", "3_arcade", "Arcade Machine: get the coin", false }, + { "tt_Coins+", "3_carrynojump", "Serschel/Louist: bring Louist back to Serschel", false }, + { "tt_Coins+", "3_cassetteCoin", "Mitch: trade 5 cassettes for a coin", false }, + { "tt_Coins+", "3_cassetteCoin2", "Mai: trade 5 cassettes for a coin", false }, + + { "Turbine Town", "tt_Cassettes", "Cassettes", false }, + { "tt_Cassettes", "3_Cassette (2)", "on top of the first set of shipping containers", false }, + { "tt_Cassettes", "3_Cassette (5)", "inside the container with a button in front of it", false }, + { "tt_Cassettes", "3_Cassette (4)", "inside the container with a fan blowing outwards", false }, + { "tt_Cassettes", "3_Cassette (3)", "behind Blessley's backdrop", false }, + { "tt_Cassettes", "3_Cassette (6)", "in the bushes on around the turbine", false }, + { "tt_Cassettes", "3_Cassette", "in the partially sunken container", false }, + { "tt_Cassettes", "3_Cassette (1)", "on a cube shaped rock around the back of the flower beds", false }, + { "tt_Cassettes", "3_Cassette (7)", "on a pile of cube rocks near the AIR VOLLEY arena", false }, + + { "Turbine Town", "tt_Letters", "Letters", false }, + { "tt_Letters", "3_letter8", "in the tree above the partially sunken container", false }, + { "tt_Letters", "3_letter2", "at the back side of the rock where you meet the Wind God", false }, + + { "Turbine Town", "tt_Keys", "Keys", false }, + { "tt_Keys", "3_containerKey", "inside the container with the breakable blocks", false }, + { "tt_Keys", "3_parasolKey", "on the stone pillar at the back side of the turbine", false }, + + { "Turbine Town", "tt_Misc", "Miscellaneous", false }, + { "tt_Misc", "3_TurbineLock", "unlock turbine ladder", false }, + + { "Turbine Town", "3_End", "Enter train to leave Turbine Town", true }, + + + { "Salmon Creek Forest", "scf_Coins", "Coins", true }, + { "scf_Coins", "4_main", "Stijn: talk to Melissa and bring them to the plateau", true }, + { "scf_Coins", "4_volley", "Trixie: SPORTVIVAL VOLLEY", true }, + { "scf_Coins", "4_Dustan", "Dustan: get to the top of the mountain", true }, + { "scf_Coins", "4_flowerPuzzle", "Little Gabi: plant flowers in each of the plant beds", true }, + { "scf_Coins", "4_fishing", "Fischer: catch all 5 fish", true }, + { "scf_Coins", "4_bug", "Blessley: collect 30 dragonflies", true }, + { "scf_Coins", "4_arcadeBone", "Arcade Machine: get 5 dog bones", true }, + { "scf_Coins", "4_hamsterball", "Moomy: collect 10 sunflower seeds", true }, + { "scf_Coins", "4_graffiti", "Nina: paint 5 symbols on the ground", true }, + { "scf_Coins", "4_cassetteCoin", "Mitch: trade 5 cassettes for a coin", true }, + { "scf_Coins", "4_cassetteCoin2", "Mai: trade 5 cassettes for a coin", true }, + { "scf_Coins", "4_tree", "Treeman: dive into the Treeman until a coin falls", true }, + { "scf_Coins", "4_behindWaterfall", "Secret of the Forest: jump through a waterfall and go to the end of the area", true }, + { "scf_Coins", "scf_Coins+", "Requires Contact List", false }, + { "scf_Coins+", "4_arcade", "Arcade Machine: get the coin", false }, + { "scf_Coins+", "4_carrynojump", "Serschel/Louist: bring Louist back to Serschel", false }, + { "scf_Coins+", "4_gamerQuest", "Game Kid: jump from the skyscraper into the water without touching ground", false }, + + { "Salmon Creek Forest", "scf_Cassettes", "Cassettes", false }, + { "scf_Cassettes", "4_Cassette", "on a large rock behind the train", false }, + { "scf_Cassettes", "4_Cassette (9)", "on a leaning tree a little past Treeman", false }, + { "scf_Cassettes", "4_Cassette (10)", "in the camp", false }, + { "scf_Cassettes", "4_Cassette (8)", "at the back side of the camp, on a grassy outcropping", false }, + { "scf_Cassettes", "4_Cassette (3)", "at the back side of the camp, on a stone outcropping", false }, + { "scf_Cassettes", "4_Cassette (1)", "on the bridge leading to one of the flower beds", false }, + { "scf_Cassettes", "4_Cassette (6)", "on a platform at the top of a tree near Nina and Melissa", false }, + { "scf_Cassettes", "4_Cassette (2)", "on the roof of the tree house", false }, + { "scf_Cassettes", "4_Cassette (7)", "on a rock just near the flower bed past Stijn's home", false }, + { "scf_Cassettes", "4_Cassette (4)", "to the right of the higher waterfall in the secret area", false }, + { "scf_Cassettes", "4_Cassette (5)", "in a breakable box in the secret area", false }, + + { "Salmon Creek Forest", "scf_Letters", "Letters", false }, + { "scf_Letters", "4_letter9", "behind a bush in the cave", false }, + { "scf_Letters", "4_letter3", "on the left at the end of the secret area", false }, + + { "Salmon Creek Forest", "scf_Keys", "Keys", false }, + { "scf_Keys", "4_2Key", "in the first pond", false }, + { "scf_Keys", "4_3Key", "in the small bush behind the frog statue", false }, + { "scf_Keys", "4_1Key", "on a rock by the sunken skyscraper", false }, + + { "Salmon Creek Forest", "scf_Misc", "Miscellaneous", false }, + { "scf_Misc", "4_lock2", "unlock the cave", false }, + + { "Salmon Creek Forest", "4_End", "Enter train to leave Salmon Creek Forest", true }, + + + { "Public Pool", "pp_Coins", "Coins", true }, + { "pp_Coins", "5_main", "Frogtective: solve the crime", true }, + { "pp_Coins", "5_fishing", "Fischer: catch all 5 fish", true }, + { "pp_Coins", "5_arcadeBone", "Arcade Machine: get 5 dog bones", true }, + { "pp_Coins", "5_arcade", "Arcade Machine: get the coin", true }, + { "pp_Coins", "5_cassetteCoin2", "Mai: trade 5 cassettes for a coin", true }, + { "pp_Coins", "5_2D", "Far Away Island: complete the 2D section", true }, + { "pp_Coins", "pp_Coins+", "Requires Contact List", false }, + { "pp_Coins+", "5_volley", "Trixie: WATER VOLLEY", false }, + { "pp_Coins+", "5_flowerPuzzle", "Little Gabi: plant flowers in each of the plant beds", false }, + { "pp_Coins+", "5_bug", "Blessley: collect 30 cicadas", false }, + { "pp_Coins+", "5_cassetteCoin", "Mitch: trade 5 cassettes for a coin", false }, + + { "Public Pool", "pp_Cassettes", "Cassettes", false }, + { "pp_Cassettes", "5_Cassette (4)", "in the pool with the lily pads", false }, + { "pp_Cassettes", "5_Cassette (5)", "in the corner of the deep pool", false }, + { "pp_Cassettes", "5_Cassette (1)", "at the end of the mid-height diving board", false }, + { "pp_Cassettes", "5_Cassette (9)", "in front of the highest level diving board", false }, + { "pp_Cassettes", "5_Cassette (2)", "in a bush behind Blessley's backdrop", false }, + { "pp_Cassettes", "5_Cassette (3)", "at the line of donuts behind Blessley's backdrop", false }, + { "pp_Cassettes", "5_Cassette (8)", "in a breakable box in the kiddie pool", false }, + { "pp_Cassettes", "5_Cassette", "on top of the green frog statue", false }, + { "pp_Cassettes", "5_Cassette (6)", "in the ring of donuts behind the green frog statue", false }, + { "pp_Cassettes", "5_Cassette (7)", "in a palm tree near the guinea pigs", false }, + + { "Public Pool", "pp_Letters", "Letters", false }, + { "pp_Letters", "5_letter10", "on the far left of the 2D section", false }, + { "pp_Letters", "5_letter4", "on the far right of the 2D section", false }, + + { "Public Pool", "pp_Keys", "Keys", false }, + { "pp_Keys", "5_testKey", "on the island with the fan covered by a breakable box", false }, + + { "Public Pool", "pp_Misc", "Miscellaneous", false }, + { "pp_Misc", "5_lock1", "unlock the second Arcade Machine", false }, + + { "Public Pool", "5_End", "Enter train to leave Public Pool", true }, + + + { "The Bathhouse" ,"tb_Coins", "Coins", true }, + { "tb_Coins", "6_main", "Poppy: check on Paul and then find Skippy", true }, + { "tb_Coins", "6_volley", "Travis: LONG VOLLEY", true }, + { "tb_Coins", "6_Dustan", "Dustan: get to the top of the main bathhouse", true }, + { "tb_Coins", "6_hamsterball", "Moomy: collect 10 sunflower seeds", true }, + { "tb_Coins", "6_carrynojump", "Serschel/Louist: bring Louist back to Serschel", true }, + { "tb_Coins", "6_gamerQuest", "Game Kid: get from the marked lamp to the marked hot tub without touching snow", true }, + { "tb_Coins", "6_graffiti", "Nina: paint 5 symbols on the ground", true }, + { "tb_Coins", "6_cassetteCoin", "Mitch: trade 5 cassettes for a coin", true }, + { "tb_Coins", "6_cassetteCoin2", "Mai: trade 5 cassettes for a coin", true }, + { "tb_Coins", "tb_Coins+", "Requires Contact List", false }, + { "tb_Coins+", "6_flowerPuzzle", "Little Gabi: plant flowers in each of the plant beds", false }, + { "tb_Coins+", "6_fishing", "Fischer: catch all 5 fish", false }, + { "tb_Coins+", "6_bug", "Blessley: collect 30 cicadas", false }, + { "tb_Coins+", "6_arcadeBone", "Arcade Machine: get 5 dog bones", false }, + { "tb_Coins+", "6_arcade", "Arcade Machine: get the coin", false }, + + { "The Bathhouse" ,"tb_Cassettes", "Cassettes", false }, + { "tb_Cassettes" ,"6_Cassette", "behind the golden frog statue", false }, + { "tb_Cassettes" ,"6_Cassette (8)", "along a pipe below the entrace to the main bathhouse", false }, + { "tb_Cassettes" ,"6_Cassette (4)", "on top of a lamp to the left of the main bathhouse", false }, + { "tb_Cassettes" ,"6_Cassette (6)", "behind a waterfall to the right of the main bathhouse", false }, + { "tb_Cassettes" ,"6_Cassette (9)", "in the secret frog hideout", false }, + { "tb_Cassettes" ,"6_Cassette (1)", "on a rock off the ledge where Masked Kid stands", false }, + { "tb_Cassettes" ,"6_Cassette (2)", "under the sunken tower", false }, + { "tb_Cassettes" ,"6_Cassette (3)", "on top of the giant frog statue", false }, + { "tb_Cassettes" ,"6_Cassette (7)", "above a tree near the giant frog statue", false }, + { "tb_Cassettes" ,"6_Cassette (5)", "behind Blessley's backdrop", false }, + + { "The Bathhouse" ,"tb_Letters", "Letters", false }, + { "tb_Letters" ,"6_letter11", "near the axolotl family", false }, + { "tb_Letters" ,"6_letter5", "on the other side of the wall where Game Kid is standing", false }, + + { "The Bathhouse" ,"tb_Keys", "Keys", false }, + { "tb_Keys" ,"6_underfloorKey", "under the floor of the main bathhouse", false }, + { "tb_Keys" ,"6_inpuzzleKey", "in a breakable box on the bottom level of one of the bathhouses", false }, + { "tb_Keys" ,"6_ontoriiKey", "on top of one of the red torii gates leading to Paul", false }, + + { "The Bathhouse" ,"tb_Miscellaneous", "Miscellaneous", false }, + { "tb_Miscellaneous" ,"6_mahjonglock", "unlock the frog hideout", false }, + + { "The Bathhouse", "6_End", "Enter train to leave The Bathhouse", true }, + + + { "Tadpole HQ", "thq_Coins", "Coins", true }, + { "thq_Coins", "7_main", "King Frog: listen to King Frog", true }, + { "thq_Coins", "7_volley", "Travis: HUGE VOLLEY", true }, + { "thq_Coins", "7_flowerPuzzle", "Little Gabi: plant flowers in each of the plant beds", true }, + { "thq_Coins", "7_fishing", "Fischer: catch all 5 fish", true }, + { "thq_Coins", "7_bug", "Blessley: collect 30 cicadas", true }, + { "thq_Coins", "7_arcadeBone", "Arcade Machine: get 5 dog bones", true }, + { "thq_Coins", "7_arcade", "Arcade Machine: get the coin", true }, + { "thq_Coins", "7_carrynojump", "Serschel/Louist: bring Louist back to Serschel", true }, + { "thq_Coins", "7_cassetteCoin", "Mitch: trade 5 cassettes for a coin", true }, + { "thq_Coins", "7_cassetteCoin2", "Mai: trade 5 cassettes for a coin", true }, + + { "Tadpole HQ", "thq_Cassettes", "Cassettes", false }, + { "thq_Cassettes", "7_Cassette (2)", "in a bush behind the bench with the contact list", false }, + { "thq_Cassettes", "7_Cassette (4)", "in a tree near the first pond (soda can to shoot into it)", false }, + { "thq_Cassettes", "7_Cassette (5)", "on top of the golden frog statue", false }, + { "thq_Cassettes", "7_Cassette (6)", "in a breakable box behind the first buildings to the right", false }, + { "thq_Cassettes", "7_Cassette (9)", "in the wall jump area of the largest building", false }, + { "thq_Cassettes", "7_Cassette (3)", "around the corner of the broken soda can", false }, + { "thq_Cassettes", "7_Cassette (7)", "on some rocks past Fischer", false }, + { "thq_Cassettes", "7_Cassette (8)", "under the giant red umbrella near Blessley", false }, + { "thq_Cassettes", "7_Cassette", "in the cross section between the buildings with the flower beds", false }, + { "thq_Cassettes", "7_Cassette (1)", "at the back side of the second-level building with the flower beds", false }, + + { "Tadpole HQ", "thq_Letters", "Letters", false }, + { "thq_Letters", "7_letter6", "on the second highest ledge leading to pepper", false }, + + { "Tadpole HQ", "thq_Misc", "Miscellaneous", false }, + { "thq_Misc", "7_lock1", "unlock the second Arcade Machine", false }, + + { "Tadpole HQ", "7_End", "Enter train to leave Tadpole HQ", false } + }; + + settings.Add("Home"); + settings.Add("Hairball City"); + settings.Add("Turbine Town"); + settings.Add("Salmon Creek Forest"); + settings.Add("Public Pool"); + settings.Add("The Bathhouse"); + settings.Add("Tadpole HQ"); + + for (int i = 0; i < _settings.GetLength(0); ++i) + { + string parent = _settings[i, 0]; + string id = _settings[i, 1]; + string desc = _settings[i, 2]; + bool state = _settings[i, 3]; + + settings.Add(id, state, desc, parent); + } + + vars.CompletedFlags = new List(); + vars.FlagNames = new[] { "coinFlags", "cassetteFlags", "fishFlags", "letterFlags", "miscFlags" }; + + if (timer.CurrentTimingMethod == TimingMethod.RealTime) + { + var mbox = MessageBox.Show( + "Removing loads from Here Comes Niko requires comparing against Game Time.\nWould you like to switch to it?", + "LiveSplit | Here Comes Niko", + MessageBoxButtons.YesNo); + + if (mbox == DialogResult.Yes) + timer.CurrentTimingMethod = TimingMethod.GameTime; + } +} + +onStart +{ + vars.CompletedFlags.Clear(); + vars.EndGame = false; + timer.Run.Offset = TimeSpan.Zero; + if (vars.OnStartPtr != IntPtr.Zero) + game.WriteValue((IntPtr)(vars.OnStartPtr), false); +} + +init +{ + vars.OnStartPtr = IntPtr.Zero; + vars.SpeedrunData = new MemoryWatcherList(); + + vars.Unity.TryOnLoad = (Func)(helper => + { + // SpeedRunData + var srd = helper.GetClass("Assembly-CSharp", "SpeedRunData"); + + vars.SpeedrunData = new MemoryWatcherList + { + new MemoryWatcher(srd.Static + srd["currentLevel"]) { Name = "currentLevel" }, + new MemoryWatcher(srd.Static + srd["onEndingScreen"]) { Name = "onEndingScreen" }, + new MemoryWatcher(srd.Static + srd["isLoading"]) { Name = "isLoading" }, + new MemoryWatcher(srd.Static + srd["onStartGame"]) { Name = "onStartGame" }, + new MemoryWatcher(srd.Static + srd["onLevelStart"]) { Name = "onLevelStart" } + }; + + + // WorldSaveData + var wsd = helper.GetClass("Assembly-CSharp", "scrWorldSaveDataContainer"); + + vars.Unity.MakeList(wsd.Static, wsd["instance"], wsd["coinFlags"]).Name = "coinFlags"; + vars.Unity.MakeList(wsd.Static, wsd["instance"], wsd["cassetteFlags"]).Name = "cassetteFlags"; + vars.Unity.MakeList(wsd.Static, wsd["instance"], wsd["fishFlags"]).Name = "fishFlags"; + vars.Unity.MakeList(wsd.Static, wsd["instance"], wsd["letterFlags"]).Name = "letterFlags"; + vars.Unity.MakeList(wsd.Static, wsd["instance"], wsd["miscFlags"]).Name = "miscFlags"; + + + vars.OnStartPtr = srd.Static + srd["onStartGame"]; + vars.StringClass = helper.GetClass("mscorlib", "String"); + + return true; + }); + + vars.Unity.Load(game); +} + +update +{ + if (!vars.Unity.Loaded) return false; + + vars.SpeedrunData.UpdateAll(game); + vars.Unity.Update(); + + current.Level = vars.SpeedrunData["currentLevel"].Current; + current.OnEnding = vars.SpeedrunData["onEndingScreen"].Current; + current.Loading = vars.SpeedrunData["isLoading"].Current; + current.GameStart = vars.SpeedrunData["onStartGame"].Current; + current.LevelStart = vars.SpeedrunData["onLevelStart"].Current; + + current.Counts = new int[] + { + vars.Unity["coinFlags"].Count, + vars.Unity["cassetteFlags"].Count, + vars.Unity["fishFlags"].Count, + vars.Unity["letterFlags"].Count, + vars.Unity["miscFlags"].Count + }; +} + +start +{ + return !old.GameStart && current.GameStart || + !old.LevelStart && current.LevelStart; +} + +split +{ + if (old.Level != current.Level && current.Level != 1) + return settings[old.Level + "_End"]; + + if (!old.OnEnding && current.OnEnding) + return true; + + for (int i = 0; i < current.Counts.Length; ++i) + { + if (current.Counts[i] != old.Counts[i] + 1) + continue; + + IntPtr addr = vars.Unity[vars.FlagNames[i]][current.Counts[i] - 1]; + int oLength = vars.StringClass["m_stringLength"], oFirst = vars.StringClass["m_firstChar"]; + + var length = game.ReadValue(addr + oLength); + var newFlag = current.Level + "_" + game.ReadString(addr + oFirst, length * 2); + + if (!vars.CompletedFlags.Contains(newFlag)) + { + vars.CompletedFlags.Add(newFlag); + return settings[newFlag]; + } + } +} + +reset +{ + return !old.GameStart && current.GameStart; +} + +isLoading +{ + return current.Loading; +} + +exit +{ + vars.OnStartPtr = IntPtr.Zero; + vars.Unity.Reset(); +} + +shutdown +{ + vars.Unity.Reset(); +} \ No newline at end of file diff --git a/Here Comes Niko/readme.md b/Here Comes Niko/readme.md new file mode 100644 index 0000000..69cfffd --- /dev/null +++ b/Here Comes Niko/readme.md @@ -0,0 +1,12 @@ +# Auto Splitter for ***Here Comes Niko!*** +## Features +Starts the timer with an offset when the first loading screen completes. +Splits are available in the settings. +Resets the timer when starting a new file. +Removes loading times. + +## Resources +*Leaderboards: [speedrun.com/hcn](https://speedrun.com/hcn)* +*Discord: [discord.gg/KZNdysECQd](https://discord.gg/KZNdysECQd)* +*Website: [herecomesniko.com](https://herecomesniko.com)* +*Game: [s.team/a/925950](https://s.team/a/925950) ($24.99)* \ No newline at end of file diff --git a/How Fish is Made/HowFishIsMade.asl b/How Fish is Made/HowFishIsMade.asl new file mode 100644 index 0000000..adbb1b4 --- /dev/null +++ b/How Fish is Made/HowFishIsMade.asl @@ -0,0 +1,79 @@ +state("How Fish is Made") {} + +startup +{ + vars.Log = (Action)(output => print("[How Fish is Made] " + output)); + vars.Unity = Assembly.Load(File.ReadAllBytes(@"Components\UnityASL.bin")).CreateInstance("UnityASL.Unity"); + vars.TimerModel = new TimerModel { CurrentState = timer }; +} + +onStart +{ + vars.HasSplitForEnd = false; + vars.Ragdolls = 0; +} + +init +{ + vars.Unity.TryOnLoad = (Func)(helper => + { + Thread.Sleep(3000); + + var mmm = helper.GetClass("Assembly-CSharp", "MainMenuManager"); + if (mmm.Static == IntPtr.Zero) return false; + + var gm = helper.GetClass("Assembly-CSharp", "GameManager"); + var dr = helper.GetClass("Assembly-CSharp", "DialogRenderer"); + var pfc = helper.GetClass("Assembly-CSharp", "PlayerFishController"); + + vars.Unity.Make(mmm.Static, mmm["instance"], mmm["start"]).Name = "onMainMenu"; + vars.Unity.Make(mmm.Static, mmm["instance"], mmm["manager"], gm["endCutsceneConvoRenderer"], dr["dialogRunning"]).Name = "endDialogRunning"; + vars.Unity.Make(mmm.Static, mmm["instance"], mmm["manager"], gm["player"], pfc["isRagdoll"]).Name = "isRagdoll"; + + return true; + }); + + vars.Unity.Load(game); + vars.HasSplitForEnd = false; + vars.Ragdolls = 0; +} + +update +{ + if (!vars.Unity.Loaded) return false; + + vars.Unity.UpdateAll(game); + + current.OnMainMenu = vars.Unity["onMainMenu"].Current; + current.EndDialogRunning = vars.Unity["endDialogRunning"].Current; + current.IsRagdoll = vars.Unity["isRagdoll"].Current; +} + +start +{ + return old.OnMainMenu && !current.OnMainMenu; +} + +split +{ + return old.IsRagdoll && !current.IsRagdoll || + !old.EndDialogRunning && current.EndDialogRunning; +} + +reset +{ + return false; +} + +exit +{ + if (settings.ResetEnabled && timer.CurrentPhase != TimerPhase.Ended) + vars.TimerModel.Reset(); + + vars.Unity.Reset(); +} + +shutdown +{ + vars.Unity.Reset(); +} \ No newline at end of file diff --git a/How Fish is Made/readme.md b/How Fish is Made/readme.md new file mode 100644 index 0000000..d7fcf80 --- /dev/null +++ b/How Fish is Made/readme.md @@ -0,0 +1,10 @@ +# Auto Splitter for ***How Fish is Made*** +## Features +Starts the timer when starting any level. +Splits are available in the settings. +Does not reset the timer. + +## Resources +*Leaderboards: [speedrun.com/hfim](https://speedrun.com/hfim)* +*Discord: [discord.gg/3XSnHpXcvV](https://discord.gg/3XSnHpXcvV)* +*Game: [s.team/a/1854430](https://s.team/a/1854430) (Free)* \ No newline at end of file diff --git a/Just Shake It/JustShakeIt.asl b/Just Shake It/JustShakeIt.asl new file mode 100644 index 0000000..939aab8 --- /dev/null +++ b/Just Shake It/JustShakeIt.asl @@ -0,0 +1,48 @@ +state("Just Shake It") +{ + bool IsEnding : "UnityPlayer.dll", 0x19B5F90, 0xB48, 0x720, 0xF48, 0x18, 0x48; + int Checkpoint : "UnityPlayer.dll", 0x19B5F90, 0xB48, 0x720, 0xF48, 0x40, 0x28; + float ms : "UnityPlayer.dll", 0x19B5F90, 0xB48, 0x720, 0xF48, 0x48, 0x28; + int min : "UnityPlayer.dll", 0x19B5F90, 0xB48, 0x720, 0xF48, 0x48, 0x2C; + int s : "UnityPlayer.dll", 0x19B5F90, 0xB48, 0x720, 0xF48, 0x48, 0x30; + bool IsStart : "UnityPlayer.dll", 0x19B5F90, 0xB48, 0x720, 0xF48, 0x48, 0x34; +} + +startup +{ + if (timer.CurrentTimingMethod == TimingMethod.RealTime) + { + var mbox = MessageBox.Show( + "Just Shake It uses in-game time.\nWould you like to switch to it?", + "LiveSplit | Just Shake It", + MessageBoxButtons.YesNo); + + if (mbox == DialogResult.Yes) + timer.CurrentTimingMethod = TimingMethod.GameTime; + } +} + +start +{ + return !old.IsStart && current.IsStart; +} + +split +{ + return old.Checkpoint != current.Checkpoint && current.Checkpoint > 1 || !old.IsEnding && current.IsEnding; +} + +reset +{ + return old.IsStart != current.IsStart; +} + +gameTime +{ + return TimeSpan.FromSeconds(current.min * 60 + current.s + current.ms); +} + +isLoading +{ + return true; +} \ No newline at end of file diff --git a/Just Shake It/readme.md b/Just Shake It/readme.md new file mode 100644 index 0000000..b9fc443 --- /dev/null +++ b/Just Shake It/readme.md @@ -0,0 +1,9 @@ +# Auto Splitter for ***Just Shake It*** +## Features +Starts the timer when crossing the start line. +Splits on every checkpoint. +Resets the timer when restarting a run or crossing the starting line. +Syncs to in-game time. + +## Resources +*Game: [s.team/a/1521960](https://s.team/a/1521960) ($6.99)* \ No newline at end of file diff --git a/Lightmatter/Lightmatter.asl b/Lightmatter/Lightmatter.asl new file mode 100644 index 0000000..8b0665c --- /dev/null +++ b/Lightmatter/Lightmatter.asl @@ -0,0 +1,126 @@ +state("LightmatterSub") {} + +startup +{ + vars.Log = (Action)(output => print("[Lightmatter] " + output)); + vars.Unity = Assembly.Load(File.ReadAllBytes(@"Components\UnityASL.bin")).CreateInstance("UnityASL.Unity"); + + settings.Add("ilTime", false, "Individual Level timer behavior (hover for info)"); + settings.SetToolTip("ilTime", "Restarts the timer when pressing \'RETRY\'\nSyncs to in-game time (set LiveSplit comparison to \'Game Time\')"); + + if (timer.CurrentTimingMethod == TimingMethod.RealTime) + { + var mbox = MessageBox.Show( + "Lightmatter uses in-game time.\nWould you like to switch to it?", + "LiveSplit | Lightmatter", + MessageBoxButtons.YesNo); + + if (mbox == DialogResult.Yes) + timer.CurrentTimingMethod = TimingMethod.GameTime; + } +} + +init +{ + vars.Unity.TryOnLoad = (Func)(helper => + { + // Player_Manager + var pm = helper.GetClass("Assembly-CSharp", "Player_Manager"); + var pc = helper.GetClass("Assembly-CSharp", "Player_Common"); + var lrm = helper.GetClass("Assembly-CSharp", "Loading_RootManager"); + + vars.Unity.Make(pm.Static, pm["Instance"], pm["Common"], pc["LevelSpecificMovementMultiplier"]).Name = "moveSpeed"; + vars.Unity.Make(pm.Static, pm["Instance"], pm["LoadingRootManager"], lrm["CurrentLevelIndex"]).Name = "level"; + + + // EventLogManager + var elm = helper.GetClass("Assembly-CSharp", "EventLogManager"); + + vars.Unity.Make(elm.Static, elm["TimeInlevel"]).Name = "levelTime"; + vars.Unity.Make(elm.Static, elm["TotalTimeInGame"]).Name = "gameTime"; + vars.Unity.Make(elm.Static, elm["TotalButtonPushCount"]).Name = "pushCount"; + + return true; + }); + + vars.Unity.Load(game); +} + +update +{ + if (!vars.Unity.Loaded) return false; + + vars.Unity.Update(); + + current.MoveSpeed = vars.Unity["moveSpeed"].Current; + current.Level = vars.Unity["level"].Current; + current.LevelTime = vars.Unity["levelTime"].Current; + current.GameTime = vars.Unity["gameTime"].Current; + current.PushCount = vars.Unity["pushCount"].Current; +} + +start +{ + if (settings["ilTime"]) + { + return old.LevelTime > current.LevelTime && old.Level == current.Level; + } + else + { + return old.GameTime == 0f && current.GameTime > 0f && current.Level == 0; + } +} + +split +{ + bool transitionedLevel = current.Level == old.Level + 1 && current.Level <= 37; + + if (settings["ilTime"]) + { + return old.LevelTime > current.LevelTime && transitionedLevel; + } + else + { + bool pressedInFinalRoom = current.PushCount == old.PushCount + 1 && current.MoveSpeed == 0.3f && current.Level == 37; + + return transitionedLevel || pressedInFinalRoom; + } +} + +reset +{ + bool returnToLvl1 = old.Level != 0 && current.Level == 0; + bool returnToMenu = current.Level == 0 && old.GameTime > 0f && current.GameTime == 0f; + bool timeResetSameLevel = old.LevelTime > current.LevelTime && old.Level == current.Level; + + return returnToLvl1 || returnToMenu || timeResetSameLevel && settings["ilTime"]; +} + +gameTime +{ + if (settings["ilTime"]) + { + if (current.LevelTime >= 0.01f) + return TimeSpan.FromSeconds(current.LevelTime); + } + else + { + if (current.GameTime >= 0.01f) + return TimeSpan.FromSeconds(current.GameTime); + } +} + +isLoading +{ + return true; +} + +exit +{ + vars.CancelSource.Cancel(); +} + +shutdown +{ + vars.CancelSource.Cancel(); +} \ No newline at end of file diff --git a/Lightmatter/readme.md b/Lightmatter/readme.md new file mode 100644 index 0000000..22833d3 --- /dev/null +++ b/Lightmatter/readme.md @@ -0,0 +1,14 @@ +# Auto Splitter for ***Lightmatter*** +## Features +Starts the timer when any level is chosen from the main menu. +Splits every time a level is finished and when the final switch is pressed. +Resets the timer when quitting to the main menu from any level. +Syncs to in-game time. + +Additionally, there is an "IL mode" in the settings: restarts the timer when pressing "Retry" in a level and splits when finishing one. + +## Resources +*Leaderboards: [speedrun.com/lightmatter](https://speedrun.co m/lightmatter)* +*Discord: [discord.gg/lightmatter](https://discord.gg/lightmatter)* +*Website: [tunnelvisiongames.com](https://tunnelvisiongames.com)* +*Game: [s.team/a/994140](https://s.team/a/994140) ($19.99)* \ No newline at end of file diff --git a/MX Simulator/MXSimulator.asl b/MX Simulator/MXSimulator.asl new file mode 100644 index 0000000..4899883 --- /dev/null +++ b/MX Simulator/MXSimulator.asl @@ -0,0 +1,230 @@ +state("mx") +{ + int PlayerID : 0x26F234; + int PlayersInRace : 0x323220; + + int FirstLapCPs : 0x320D4C; + int NormalLapCPs : 0x320D50; + + //double TickRate : 0x162B90; + int RaceTicks : 0x322300; + //int ServerStartTicks : 0x43248A0; + + string512 TrackName : 0x31ED10, 0x0; +} + +startup +{ + vars.TimerModel = new TimerModel { CurrentState = timer }; + + if (timer.CurrentTimingMethod == TimingMethod.RealTime) + { + var mbox = MessageBox.Show( + "MX Simulator uses in-game time.\nWould you like to switch to it?", + "LiveSplit | MX Simulator", + MessageBoxButtons.YesNo); + + if (mbox == DialogResult.Yes) + timer.CurrentTimingMethod = TimingMethod.GameTime; + } +} + +onStart +{ + vars.ValidLap = true; +} + +init +{ + #region Initializing Variables + current.CPs = 0; + current.IDinRace = 0; + vars.StartTicks = 0; + + vars.CPsChanged = false; + vars.OnFinalSplit = false; + vars.OnFirstCP = false; + vars.ValidLap = true; + vars.ShowMsg = false; + + vars.IDPtr = IntPtr.Zero; + vars.CPPtr = IntPtr.Zero; + #endregion + + #region Custom Functions + // Whenever the player's position changes in a race (ghost in time trials counts too), the checkpoint memory address needs to be updated. + vars.UpdatePtrs = (Action)(() => + { + if (current.PlayersInRace > 0) + { + for (int i = 0; i < current.PlayersInRace; ++i) + { + if (game.ReadValue((IntPtr)(vars.IDPtr = 0x322AA0 + 0xC * i)) == current.PlayerID) + { + vars.CPPtr = 0x322AA4 + 0xC * i; + break; + } + } + } + }); + + // A message box to pop up when the current number of splits or track name doesn't line up with the currently loaded track. + vars.TrackMsg = (Action)(() => + { + var mbox = DialogResult.None; + + if (!string.IsNullOrEmpty(current.TrackName) && (current.NormalLapCPs > 0 && timer.Run.Count != current.NormalLapCPs || timer.Run.CategoryName != current.TrackName)) { + mbox = MessageBox.Show( + "Current splits configuration:\n" + "\"" + timer.Run.CategoryName + "\" with " + timer.Run.Count + " segments\n\n" + + "Required configuration:\n" + "\"" + current.TrackName + "\" with " + current.NormalLapCPs + " segments\n\n" + + "Do you want to save your splits now and generate new ones for this track?", + "MX Simulator Auto Splitter", + MessageBoxButtons.YesNo, + MessageBoxIcon.Information + ); + } + + if (mbox == DialogResult.Yes) + { + timer.Form.ContextMenuStrip.Items["saveSplitsAsMenuItem"].PerformClick(); + + int currAmtSplits = timer.Run.Count; + + for (int gateNo = 1; gateNo <= current.NormalLapCPs; ++gateNo) + timer.Run.Add(new Segment("Gate " + gateNo)); + + for (int splitNo = 1; splitNo <= currAmtSplits; ++splitNo) + timer.Run.RemoveAt(0); + + timer.Run.GameName = "MX Simulator"; + timer.Run.CategoryName = current.TrackName; + } + }); + + // Using this instead of Thread.Sleep() so as to not block the thread. + vars.Wait = (Action)(time => System.Threading.Tasks.Task.Run(async () => await System.Threading.Tasks.Task.Delay(time)).Wait()); + #endregion + + vars.UpdatePtrs(); + vars.TrackMsg(); +} + +update +{ + if ((IntPtr)vars.IDPtr == IntPtr.Zero || (IntPtr)vars.CPPtr == IntPtr.Zero) + { + vars.UpdatePtrs(); + return false; + } + + #region Variable Updating + // Updating several variables according to our needs. + + current.CPs = game.ReadValue((IntPtr)vars.CPPtr); + current.IDinRace = game.ReadValue((IntPtr)vars.IDPtr); + + vars.CPsChanged = old.PlayerID == current.IDinRace && old.CPs != current.CPs || old.PlayerID != current.IDinRace && old.CPs == current.CPs; + vars.OnFinalSplit = timer.CurrentSplitIndex == timer.Run.Count - 1; + vars.OnFirstCP = (current.CPs - current.FirstLapCPs) % current.NormalLapCPs == 0; + + if (current.IDinRace != current.PlayerID) vars.UpdatePtrs(); + #endregion + + + #region Splits Message + // When the track the user is on changes, a messagebox will appear prompting them to save the splits and create new ones for this track. + + if (old.FirstLapCPs != old.FirstLapCPs || + old.NormalLapCPs != current.NormalLapCPs || + old.TrackName != current.TrackName && !string.IsNullOrEmpty(current.TrackName)) + { + vars.ShowMsg = true; + } + + if (vars.ShowMsg && current.RaceTicks > 0) + { + vars.ShowMsg = false; + vars.MsgShownForTrack = current.TrackName; + vars.TrackMsg(); + } + #endregion + + + #region Reset Handling + // To accomodate for TimerPhase.Ended, we need to do this outside of the reset {} block. + + if (settings.ResetEnabled) + { + if (old.RaceTicks > current.RaceTicks || + current.IDinRace != current.PlayerID || + vars.CPsChanged && vars.OnFirstCP && (!vars.OnFinalSplit || !vars.ValidLap) && timer.CurrentSplitIndex > 0) + { + vars.Wait(500); + vars.UpdatePtrs(); + vars.TimerModel.Reset(); + } + + if (timer.CurrentPhase == TimerPhase.Ended && old.PlayerID == current.IDinRace && old.CPs < current.CPs) + { + vars.TimerModel.Reset(); + vars.TimerModel.Start(); + + vars.Wait(20); + } + } + #endregion +} + +start +{ + if (old.CPs != current.CPs && current.CPs == current.FirstLapCPs || + current.CPs - current.FirstLapCPs > 0 && vars.OnFirstCP) + { + vars.StartTicks = current.RaceTicks; + return true; + } +} + +split +{ + if (vars.CPsChanged) + { + if (old.PlayersInRace < current.PlayersInRace) return false; + int expectedCP = old.CPs + 1, actualCP = current.CPs; + + if (expectedCP < actualCP) + { + vars.ValidLap = false; + for (int i = expectedCP; i < actualCP; ++i) + vars.TimerModel.SkipSplit(); + } + + if (vars.OnFirstCP) + { + vars.StartTicks = current.RaceTicks; + if (!vars.OnFinalSplit || !vars.ValidLap) return false; + } + + return true; + } +} + +reset +{ + return false; +} + +gameTime +{ + return TimeSpan.FromSeconds((current.RaceTicks - vars.StartTicks) * 0.0078125); +} + +isLoading +{ + return true; +} + +exit +{ + vars.TimerModel.Reset(); +} \ No newline at end of file diff --git a/MX Simulator/readme.md b/MX Simulator/readme.md new file mode 100644 index 0000000..17c9df6 --- /dev/null +++ b/MX Simulator/readme.md @@ -0,0 +1,9 @@ +# Auto Splitter for ***MX Simulator*** +## Features +Starts the timer when a new lap begins. +Splits on every checkpoint. +Resets the timer when a new lap begins. +Syncs to in-game time. + +## Resources +*Website: [mxsimulator.com](https://mxsimulator.com)* \ No newline at end of file diff --git a/Maid of Sker/MaidOfSker.asl b/Maid of Sker/MaidOfSker.asl new file mode 100644 index 0000000..1511951 --- /dev/null +++ b/Maid of Sker/MaidOfSker.asl @@ -0,0 +1,168 @@ +state("Maid of Sker") {} + +startup +{ + vars.Log = (Action)(output => print("[Maid of Sker] " + output)); + vars.Unity = Assembly.Load(File.ReadAllBytes(@"Components\UnityASL.bin")).CreateInstance("UnityASL.Unity"); + vars.Unity.LoadSceneManager = true; + + string[,] _settings = + { + { "scenes", "MOS_LVL_ForestIntro", "Forest Intro" }, + { "scenes", "MOS_LVL_GroundFloor", "Ground Floor" }, + { "scenes", "MOS_LVL_Basement", "Basement" }, + { "scenes", "MOS_LVL_ForestDark", "Forest Dark" }, + { "scenes", "MOS_LVL_Garden", "Garden" }, + { "scenes", "MOS_LVL_FirstFloor", "First Floor" }, + { "scenes", "MOS_LVL_SecondFloor", "Second Floor" }, + { "scenes", "MOS_LVL_ForestDark_End", "Forst Dark (End)" }, + { "musicSheets", "Music Sheet - 04", "Music Sheet: Thomas Evans" }, + { "musicSheets", "Music Sheet - 03", "Music Sheet: Henry Hughes" }, + { "musicSheets", "Music Sheet - 02", "Music Sheet: Matilda Norton" }, + { "musicSheets", "Music Sheet - 01", "Music Sheet: Arthur Morris" }, + { "musicCylinders", "Music Cylinder - Cerebrus", "Music Cylinder - Cerebrus" }, + { "musicCylinders", "Music Cylinder - Hero", "Music Cylinder - Hero" }, + { "musicCylinders", "Music Cylinder - Siren", "Music Cylinder - Siren" }, + { "musicCylinders", "Music Cylinder - Medusa", "Music Cylinder - Medusa" }, + { "keys", "Music Key", "Music Key" }, + { "keys", "Key", "Crown Key" }, + { "misc", "Phonic Modulator", "Phonic Modulator" } + }; + + settings.Add("items", false, "Split when collecting an item:"); + settings.Add("musicSheets", false, "Music Sheets", "items"); + settings.Add("musicCylinders", false, "Music Cylinders", "items"); + settings.Add("keys", false, "Keys", "items"); + settings.Add("misc", false, "Other items", "items"); + settings.Add("scenes", false, "Split after finishing an area:"); + + for (int i = 0; i < _settings.GetLength(0); ++i) + { + string parent = _settings[i, 0]; + string id = _settings[i, 1]; + string desc = _settings[i, 2]; + + settings.Add(id, false, desc, parent); + } + + vars.CollectedItems = new HashSet(); + + if (timer.CurrentTimingMethod == TimingMethod.RealTime) + { + var mbox = MessageBox.Show( + "Removing loads from Maid of Sker requires comparing against Game Time.\nWould you like to switch to it?", + "LiveSplit | Maid of Sker", + MessageBoxButtons.YesNo); + + if (mbox == DialogResult.Yes) + timer.CurrentTimingMethod = TimingMethod.GameTime; + } +} + +onStart +{ + vars.CollectedItems.Clear(); + vars.CutsceneNum = 0; +} + +init +{ + current.CutsceneNum = 0; + + const int PTR_SIZE = 0x8; + vars.Unity.TryOnLoad = (Func)(helper => + { + var str = helper.GetClass("mscorlib", "String"); + + // LevelManager + var lm = helper.GetClass("Assembly-CSharp", "WI_LevelManager", 1); + + vars.Unity.Make(lm.Static, PTR_SIZE * 2, lm["_IsLoading"]).Name = "loading"; + + // PlayerController + var pc = helper.GetClass("Assembly-CSharp", "WI_PlayerController_UFPS"); + var abl = helper.GetClass("Opsive.UltimateCharacterController", "Ability"); + + vars.Unity.Make(pc.Static, pc["_Instance"], pc["CutsceneAbility"], abl["m_StartTime"]).Name = "csStartTime"; + + // Inventory + var invM = helper.GetClass("Assembly-CSharp", "WI_InventoryManager"); + var invS = helper.GetClass("Assembly-CSharp", "WI_InventorySlot"); + var invI = helper.GetClass("Assembly-CSharp", "WI_InventoryItem"); + + var invSize = new MemoryWatcher(new DeepPointer(invM.Static + invM["Instance"], invM["Inventory"], PTR_SIZE * 3)); + + current.Item = "No item!"; + vars.UpdateInventory = (Action)(() => + { + if (invSize.Update(game) && invSize.Current != 0) + { + IntPtr itemNameAddr; + new DeepPointer( + invM.Static + invM["Instance"], invM["Inventory"], + PTR_SIZE * 2, PTR_SIZE * 4 + PTR_SIZE * (invSize.Current - 1), + invS["Item"], invI["Name"], 0x0).DerefOffsets(game, out itemNameAddr); + + var newItem = game.ReadString(itemNameAddr + (int)str["m_firstChar"], game.ReadValue(itemNameAddr + (int)str["m_stringLength"]) * 2); + if (!string.IsNullOrEmpty(newItem)) + current.Item = newItem; + } + }); + + return true; + }); + + vars.Unity.Load(game); +} + +update +{ + if (!vars.Unity.Loaded) return false; + + vars.Unity.Update(); + vars.UpdateInventory(); + + current.Scene = vars.Unity.Scenes.Loading[0].Name; + current.CutsceneStartTime = vars.Unity["csStartTime"].Current; + current.Loading = vars.Unity["loading"].Current; + + if (current.Scene == "MOS_LVL_GroundFloor" && old.CutsceneStartTime == -1 && current.CutsceneStartTime > 0) + current.CutsceneNum++; +} + +start +{ + return old.Item != current.Item && current.Item == "Music Sheet - 04"; +} + +split +{ + if (old.Item != current.Item && !vars.CollectedItems.Contains(current.Item)) + { + vars.CollectedItems.Add(current.Item); + return settings[current.Item]; + } + + return old.Scene != current.Scene && settings[old.Scene] || + old.CutsceneNum != 3 && current.CutsceneNum == 3; +} + +reset +{ + return old.Scene != current.Scene && current.Scene == "MOS_LVL_Frontend"; +} + +isLoading +{ + return current.Loading; +} + +exit +{ + vars.Unity.Reset(); +} + +shutdown +{ + vars.Unity.Reset(); +} \ No newline at end of file diff --git a/Maid of Sker/readme.md b/Maid of Sker/readme.md new file mode 100644 index 0000000..ff70010 --- /dev/null +++ b/Maid of Sker/readme.md @@ -0,0 +1,12 @@ +# Auto Splitter for ***Maid of Sker*** +## Features +Starts the timer when picking up the first musical note. +Splits are available in the settings. Does not split after choosing the final dialogue option. +Resets the timer when starting the opening cutscene. +Removes loading times. + +## Resources +*Leaderboards: [speedrun.com/mos](https://speedrun.com/mos)* +*Discord: [discord.gg/VGsH6bA](https://discord.gg/VGsH6bA)* +*Website: [walesinteractive.com/maidofsker](https://walesinteractive.com/maidofsker)* +*Game: [s.team/a/826940](https://s.team/a/826940) ($24.99)* \ No newline at end of file diff --git "a/PAW Patrol \342\200\223 Mighty Pups save Adventure Bay/PP_MPsAB.asl" "b/PAW Patrol \342\200\223 Mighty Pups save Adventure Bay/PP_MPsAB.asl" new file mode 100644 index 0000000..d88e0fb --- /dev/null +++ "b/PAW Patrol \342\200\223 Mighty Pups save Adventure Bay/PP_MPsAB.asl" @@ -0,0 +1,45 @@ +state("pp2") {} + +startup +{ + vars.Log = (Action)(output => print("[Paw Patrol 2] " + output)); + vars.Unity = Assembly.Load(File.ReadAllBytes(@"Components\UnityASL.bin")).CreateInstance("UnityASL.Unity"); +} + +init +{ + vars.Unity.TryOnLoad = (Func)(helper => + { + var st = helper.GetClass("Assembly-CSharp", "Singletons"); + var ls = helper.GetClass("Assembly-CSharp", "LoadSceneMgr"); + + vars.Unity.Make(st.Static, st["_singletons"], st["loadSceneMgr"], ls["_progress"]).Name = "loadProgress"; + vars.Unity.Make(st.Static, st["_singletons"], st["loadSceneMgr"], ls["_loadingScreen"]).Name = "loadingScreen"; + + return true; + }); + + vars.Unity.Load(game); +} + +update +{ + if (!vars.Unity.Loaded) return false; + + vars.Unity.Update(); +} + +isLoading +{ + return vars.Unity["loadProgress"].Current != 1f || vars.Unity["loadingScreen"].Current; +} + +exit +{ + vars.Unity.Reset(); +} + +shutdown +{ + vars.Unity.Reset(); +} \ No newline at end of file diff --git "a/PAW Patrol \342\200\223 Mighty Pups save Adventure Bay/readme.md" "b/PAW Patrol \342\200\223 Mighty Pups save Adventure Bay/readme.md" new file mode 100644 index 0000000..c045ce0 --- /dev/null +++ "b/PAW Patrol \342\200\223 Mighty Pups save Adventure Bay/readme.md" @@ -0,0 +1,12 @@ +# Auto Splitter for ***PAW Patrol – Mighty Pups save Adventure Bay*** +## Features +Starts the timer with an offset when the first loading screen completes. +Splits are available in the settings. +Resets the timer when starting a new file. +Removes loading times. + +## Resources +*Leaderboards: [speedrun.com/paw_patrol_mighty_pups_save_adventure_bay](https://speedrun.com/paw_patrol_mighty_pups_save_adventure_bay)* +*Discord: [discord.gg/vnAQRUTvmF](https://discord.gg/vnAQRUTvmF)* +*Website: [outrightgames.com/games/paw-patrol-mighty-pups](https://outrightgames.com/games/paw-patrol-mighty-pups)* +*Game: [s.team/a/1374990](https://s.team/a/1374990) ($39.99)* \ No newline at end of file diff --git a/Post Void/PostVoid.asl b/Post Void/PostVoid.asl new file mode 100644 index 0000000..abf2d75 --- /dev/null +++ b/Post Void/PostVoid.asl @@ -0,0 +1,73 @@ +state("Post Void") +{ + // double InGameScore : 0x128AE8, 0x50, 0x21C, 0x318, 0x0; + // double HitsTaken : 0x4B2780, 0x2C, 0x10, 0x18, 0x40; + // double Headshots : 0x4B2780, 0x2C, 0x10, 0x18, 0x60; + // double Shots : 0x4B2780, 0x2C, 0x10, 0x18, 0x70; + // double Hits : 0x4B2780, 0x2C, 0x10, 0x18, 0x80; + // double Kills : 0x4B2780, 0x2C, 0x10, 0x18, 0xA0; + double IGTLevel : 0x4B2780, 0x2C, 0x10, 0x18, 0xB0; + double IGTFull : 0x4B2780, 0x2C, 0x10, 0x18, 0xC0; + double LevelID : 0x4B2780, 0x2C, 0x10, 0x18, 0xE0; +} + +startup +{ + vars.FinalLevel = false; + vars.TimerModel = new TimerModel { CurrentState = timer }; + + settings.Add("lvlSplits", true, "Choose which level(s) to split on:"); + settings.Add("99to0", true, "After the Tutorial", "lvlSplits"); + settings.Add("0to1", true, "After Level 1", "lvlSplits"); + settings.Add("1to2", true, "After Level 2", "lvlSplits"); + settings.Add("2to3", true, "After Level 3", "lvlSplits"); + settings.Add("3to4", true, "After Level 4", "lvlSplits"); + settings.Add("4to5", true, "After Level 5", "lvlSplits"); + settings.Add("5to6", true, "After Level 6", "lvlSplits"); + settings.Add("6to7", true, "After Level 7", "lvlSplits"); + settings.Add("7to8", true, "After Level 8", "lvlSplits"); + settings.Add("8to9", true, "After Level 9", "lvlSplits"); + settings.Add("9to10", true, "After Level 10", "lvlSplits"); + settings.Add("finalSplit", true, "After Level 11", "lvlSplits"); +} + +onStart +{ + vars.FinalLevel = false; +} + +start +{ + return old.IGTFull == 0 && current.IGTFull > 0; +} + +split +{ + if (current.LevelID == 10 && old.IGTLevel == 0 && current.IGTLevel > 0) + vars.FinalLevel = true; + + return old.LevelID != current.LevelID && settings[old.LevelID + "to" + current.LevelID] || + vars.FinalLevel && old.IGTLevel > 0 && current.IGTLevel == 0 && settings["finalSplit"]; +} + +reset +{ + return old.LevelID != 99 && current.IGTFull == 0 && old.IGTFull > 0; +} + +gameTime +{ + if (current.IGTFull != 0) + return TimeSpan.FromSeconds(current.IGTFull); +} + +isLoading +{ + return true; +} + +exit +{ + if (timer.CurrentPhase != TimerPhase.Ended) + vars.TimerModel.Reset(); +} \ No newline at end of file diff --git a/Post Void/readme.md b/Post Void/readme.md new file mode 100644 index 0000000..4f899d0 --- /dev/null +++ b/Post Void/readme.md @@ -0,0 +1,12 @@ +# Auto Splitter for ***Post Void*** +## Features +Starts the timer when starting the game. +Splits are available in the settings. +Resets the timer when a new run is begun. +Syncs to in-game time. + +## Resources +*Leaderboards: [speedrun.com/post_void](https://speedrun.com/post_void)* +*Discord: [discord.gg/TYeu73t](https://discord.gg/TYeu73t)* +*Website: [whycjwhy.com](https://whycjwhy.com)* +*Game: [s.team/a/1285670](https://s.team/a/1285670) ($2.99)* \ No newline at end of file diff --git a/Postbird In Provence/PostbirdInProvence.asl b/Postbird In Provence/PostbirdInProvence.asl new file mode 100644 index 0000000..4e253dd --- /dev/null +++ b/Postbird In Provence/PostbirdInProvence.asl @@ -0,0 +1,142 @@ +state("PostbirdInProvence") +{ + int Managers : "GameAssembly.dll", 0x203BA50, 0xB8, 0x0, 0x28, 0x30, 0x18; + string64 Dialogue : "GameAssembly.dll", 0x203BA50, 0xB8, 0x0, 0x28, 0x60, 0x30, 0x10, 0x30, 0x50, 0x10, 0x14; + int Node : "GameAssembly.dll", 0x203BA50, 0xB8, 0x0, 0x28, 0x60, 0x30, 0x10, 0x30, 0x50, 0x18; + uint Day : "GameAssembly.dll", 0x203BA50, 0xB8, 0x0, 0x28, 0xA8; +} + +startup +{ + vars.Dbg = (Action) ((output) => print("[Postbird ASL] " + output)); + + dynamic[,] _settings = + { + { null, "Split when finishing the day", true }, + { null, "Community manager", true }, + { "Community manager", "Enchanté Henri", false }, + { "Community manager", "Enchanté Daphné", false }, + { "Community manager", "Enchanté Charline", false }, + { "Community manager", "Enchanté Charles", false }, + { "Community manager", "Enchanté Charlie", false }, + { "Community manager", "Enchanté Rosette", false }, + { "Community manager", "Enchanté Louis", false }, + { "Community manager", "Enchanté Jean", false }, + { "Community manager", "Enchanté Léon", false }, + + { null, "Tourist", true }, + { "Tourist", "The viewpoint", false }, + { "Tourist", "The island", false }, + { "Tourist", "The marina", false }, + { "Tourist", "The picnic area", false }, + { "Tourist", "The main plaza", false }, + + { null, "Fear of silence", true }, + { "Fear of silence", "Montélimace FM", false }, + + { null, "Philatelist", true }, + { "Philatelist", "Only 9 left!", false }, + + { null, "Honk honk", true }, + { null, "E.T.", true }, + { null, "Crazy driving", true }, + { null, "Use splash!", true }, + { null, "Ohlala", true }, + { null, "Inspector Marcel", true }, + { null, "Run like the wind", true }, + { null, "Handyman", true }, + { null, "Professional basketball player", true }, + { null, "The green thumb", true }, + { null, "The new champ", true }, + { null, "Postbird in Provence", true } + }; + + for (int i = 0; i < _settings.GetLength(0); ++i) + { + string parent = _settings[i, 0]; + string id = _settings[i, 1]; + bool state = _settings[i, 2]; + + settings.Add(id, state, id, parent); + } +} + +onStart +{ + vars.UpdateAchievements(); +} + +init +{ + string[] achvNames = + { + "Enchanté Henri", + "Enchanté Daphné", + "Enchanté Charline", + "Enchanté Charles", + "Enchanté Charlie", + "Enchanté Rosette", + "Enchanté Louis", + "Enchanté Jean", + "Enchanté Léon", + "The viewpoint", + "The island", + "The marina", + "The picnic area", + "The main plaza", + "Community manager", + "Tourist", + "Fear of silence", + "Montélimace FM", + "Philatelist", + "Only 9 left!", + "Honk honk", + "E.T.", + "Crazy driving", + "Use splash!", + "Ohlala", + "Inspector Marcel", + "Run like the wind", + "Handyman", + "Professional basketball player", + "The green thumb", + "The new champ", + "Postbird in Provence" + }; + + vars.Achievements = new MemoryWatcherList(); + vars.UpdateAchievements = (Action)(() => + { + vars.Achievements = new MemoryWatcherList(); + + for (int i = 0; i < 32; ++i) + { + var ptr = new DeepPointer("GameAssembly.dll", 0x203BA50, 0xB8, 0x0, 0x28, 0x80, 0x20, 0x20 + 0x8 * i, 0x70).Deref(game); + vars.Achievements.Add(new MemoryWatcher(ptr + 0x10) { Name = achvNames[i] }); + } + }); +} + +start +{ + return old.Node == 4 && current.Node > 4 && current.Dialogue == "Player.Dialogue.IntroCinematic"; +} + +split +{ + vars.Achievements.UpdateAll(game); + + foreach (MemoryWatcher watcher in vars.Achievements) + { + if (!watcher.Old && watcher.Current && settings[watcher.Name]) + return true; + } + + return old.Day < current.Day && settings["Split when finishing the day"] || + old.Dialogue == "Player.Dialogue.EndGameCutScene" && string.IsNullOrEmpty(current.Dialogue); +} + +reset +{ + return old.Managers != 2 && current.Managers == 2; +} \ No newline at end of file diff --git a/Postbird In Provence/readme.md b/Postbird In Provence/readme.md new file mode 100644 index 0000000..97737df --- /dev/null +++ b/Postbird In Provence/readme.md @@ -0,0 +1,10 @@ +# Auto Splitter for ***Postbird In Provence*** +## Features +Starts the timer when loading into the game. +Splits when closing the truck and having completed the tasks required for a valid run. +Resets the timer when that's not the case. +Removes loading times. + +## Resources +*Leaderboards: [speedrun.com/pbip](https://speedrun.com/pbip)* +*Game: [isart-digital.itch.io/postbirdinprovence](https://isart-digital.itch.io/postbirdinprovence) (Free)* \ No newline at end of file diff --git a/Project Warlock II/ProjectWarlockII.asl b/Project Warlock II/ProjectWarlockII.asl new file mode 100644 index 0000000..d316a15 --- /dev/null +++ b/Project Warlock II/ProjectWarlockII.asl @@ -0,0 +1,101 @@ +state("Project Warlock 2 Demo") {} + +startup +{ + vars.Log = (Action)(output => print("[Project Warlock II] " + output)); + vars.Unity = Assembly.Load(File.ReadAllBytes(@"Components\UnityASL.bin")).CreateInstance("UnityASL.Unity"); + + settings.Add("keys", false, "Split when collecting a key"); +} + +onStart +{ + vars.TimeBetweenDeaths = TimeSpan.Zero +} + +init +{ + vars.TimeBetweenDeaths = TimeSpan.Zero; + + vars.Unity.TryOnLoad = (Func)(helper => + { + var pm = helper.GetClass("Assembly-CSharp", "PlayerManager"); + var gsm = helper.GetClass("Assembly-CSharp", "GamestateManager"); + var tc = helper.GetClass("Assembly-CSharp", "GamestateManager"); + var pum = helper.GetClass("Assembly-CSharp", "PickupManager"); + + vars.Unity.Make(pm.Static, pm["instance"], pm["gamestateManager"], gsm["demoEnd"]).Name = "demoEnd"; + vars.Unity.Make(pm.Static, pm["instance"], pm["gamestateManager"], gsm["gamestate"]).Name = "gamestate"; + vars.Unity.Make(pm.Static, pm["instance"], pm["gamestateManager"], gsm["timeManager"], tc["seconds"]).Name = "seconds"; + vars.Unity.Make(pm.Static, pm["instance"], pm["gamestateManager"], gsm["timeManager"], tc["minutes"]).Name = "minutes"; + vars.Unity.Make(pm.Static, pm["instance"], pm["gamestateManager"], gsm["timeManager"], tc["hours"]).Name = "hours"; + vars.Unity.Make(pm.Static, pm["instance"], pm["gamestateManager"], gsm["timeManager"], tc["started"]).Name = "started"; + vars.Unity.MakeArray(pm.Static, pm["instance"], pm["pickupManager"], pum["pickedUpKeys"]).Name = "keys"; + + return true; + }); + + vars.Unity.Load(game); +} + +update +{ + if (!vars.Unity.Loaded) return false; + + vars.Unity.Update(); + + current.DemoEnd = vars.Unity["demoEnd"].Current; + current.GameState = vars.Unity["gamestate"].Current; + current.Started = vars.Unity["started"].Current; + current.Keys = new bool[] { vars.Unity["keys"][0], vars.Unity["keys"][1], vars.Unity["keys"][2] }; + current.GameTime = new TimeSpan(vars.Unity["hours"].Current, vars.Unity["minutes"].Current, vars.Unity["seconds"].Current); +} + +start +{ + return old.GameState == 3 && current.GameState == 1; +} + +split +{ + if (!old.DemoEnd && current.DemoEnd) + return true; + + if (!settings["keys"]) + return; + + for (int i = 0; i < 3; ++i) + { + if (!old.Keys[i] && current.Keys[i]) + return true; + } +} + +reset +{ + return old.GameState != 5 && current.GameState == 5; +} + +gameTime +{ + if (old.GameTime > current.GameTime) + vars.TimeBetweenDeaths += old.GameTime - current.GameTime; + + if (current.Started) + return current.GameTime + vars.TimeBetweenDeaths; +} + +isLoading +{ + return true; +} + +exit +{ + vars.Unity.Reset(); +} + +shutdown +{ + vars.Unity.Reset(); +} \ No newline at end of file diff --git a/Project Warlock II/readme.md b/Project Warlock II/readme.md new file mode 100644 index 0000000..e7000bd --- /dev/null +++ b/Project Warlock II/readme.md @@ -0,0 +1,9 @@ +# Auto Splitter for ***Project Warlock II* (Demo)** +## Features +Starts the timer when starting any level. +Splits are available in the settings. +Does not reset the timer. + +## Resources +*Leaderboards: [speedrun.com/pw2d](https://speedrun.com/pw2d)* +*Game: [s.team/a/1640300](https://s.team/a/1640300) (Free Demo)* \ No newline at end of file diff --git a/Ruff Night at the Gallery/RuffNightAtTheGallery.asl b/Ruff Night at the Gallery/RuffNightAtTheGallery.asl new file mode 100644 index 0000000..05e07fa --- /dev/null +++ b/Ruff Night at the Gallery/RuffNightAtTheGallery.asl @@ -0,0 +1,90 @@ +state("Nori-Win64-Shipping") {} + +startup +{ + string[,] _settings = + { + { "Room_Left_00", "First Lobby" }, + { "Room_Left_01", "Mona Lisa" }, + { "Room_Left_02", "American Gothic" }, + { "Room_Left_03", "Self-Portrait with Thorn Necklace and Hummingbird" }, + { "Room_Left_04", "Girl before a Mirror" }, + { "Room_Right_00", "Second Lobby" }, + { "Room_Right_01", "Girl with a Pearl Earring & Self-Portrait (van Gogh)" }, + { "Room_Right_02", "Girl before a Mirror, The Scream, & American Gothic" }, + { "Room_Right_04", "Room with 4 Portraits" }, + { "Room_Final_00", "Third Lobby" }, + { "Room_Final_01", "The Son of Man" }, + { "Room_Credits_00", "Credits" } + }; + + settings.Add("rooms", true, "Split upon leaving these rooms:"); + + for (int i = 0; i < _settings.GetLength(0); ++i) + { + string id = _settings[i, 0]; + string desc = _settings[i, 1]; + + settings.Add(id, true, desc, "rooms"); + } + + vars.CompletedRooms = new HashSet(); +} + +onStart +{ + vars.CompletedRooms.Clear(); +} + +init +{ + // UWorld.PersistentLevel.UNKNOWN.ACharacter.RootComponent.AbsolutePosition + vars.Position = new MemoryWatcher(new DeepPointer(0x3F29F40, 0x30, 0xA8, 0x68, 0x130, 0x100)); + + // UWorld.Nori_GameInstance.SaveGame.CurrentRoomID + vars.RoomID = new StringWatcher(new DeepPointer(0x3F29F40, 0x188, 0x198, 0x28, 0x0), 32) { FailAction = MemoryWatcher.ReadFailAction.SetZeroOrNull }; + + // UWorld.PersistentLevel.UNKNOWN.Nori_LevelInfo.OrderedRoomNodes[14].RoomGate.Locked + vars.FinalGateLocked = new MemoryWatcher(new DeepPointer(0x3F29F40, 0x30, 0x98, 0xB8, 0x238, 0x70, 0x268, 0x2A0)); +} + +update +{ + vars.Position.Update(game); + vars.RoomID.Update(game); + vars.FinalGateLocked.Update(game); +} + +start +{ + return vars.Position.Old.X == 250f && vars.Position.Current.X != 250f || + vars.Position.Old.Y == 1900f && vars.Position.Current.Y != 1900f; +} + +split +{ + if (string.IsNullOrEmpty(vars.RoomID.Current)) return; + + if (vars.RoomID.Changed && !vars.CompletedRooms.Contains(vars.RoomID.Old)) + { + vars.CompletedRooms.Add(vars.RoomID.Old); + return settings[vars.RoomID.Old]; + } + + return vars.FinalGateLocked.Old && !vars.FinalGateLocked.Current; +} + +reset +{ + return string.IsNullOrEmpty(vars.RoomID.Old) && vars.RoomID.Current == "Room_Left_00"; +} + +isLoading +{ + return string.IsNullOrEmpty(vars.RoomID.Current); +} + +shutdown +{ + timer.OnStart -= vars.TimerStart; +} diff --git a/Ruff Night at the Gallery/readme.md b/Ruff Night at the Gallery/readme.md new file mode 100644 index 0000000..9ccc124 --- /dev/null +++ b/Ruff Night at the Gallery/readme.md @@ -0,0 +1,10 @@ +# Auto Splitter for ***Ruff Night at the Gallery*** +## Features +Starts the timer on first movement when starting a new game. +Splits are available in the settings. +Resets the timer when returning to the menu. + +## Resources +*Leaderboards: [speedrun.com/ruff_night_at_the_gallery](https://speedrun.com/ruff_night_at_the_gallery)* +*Discord: [discord.gg/S3YZyUDZ73](https://discord.gg/S3YZyUDZ73)* +*Game: [s.team/a/1463470](https://s.team/a/1463470) (Free)* \ No newline at end of file diff --git a/Sphere Complex/SphereComplex.asl b/Sphere Complex/SphereComplex.asl new file mode 100644 index 0000000..211364b --- /dev/null +++ b/Sphere Complex/SphereComplex.asl @@ -0,0 +1,24 @@ +// Bad! Love UE4 though. :) + +state("SphereComplex-Win64-Shipping") +{ + float LevelTime : 0x2526B80, 0x30, 0x410, 0x480, 0x16C; + float EndTime : 0x2526B80, 0x30, 0x410, 0x480, 0x1D4; + string128 Map : 0x2512910, 0x678, 0x16; +} + +start +{ + return old.LevelTime == 0f && current.LevelTime > 0f; +} + +split +{ + return old.EndTime == 0f && current.EndTime > 0f; +} + +reset +{ + return (current.Map == "SP_BEGINNER_01" || current.Map == "SP_BEGINNER0" || current.Map == "SP_ADVANCED_01") && + old.LevelTime == 0f && current.LevelTime > 0f; +} \ No newline at end of file diff --git a/Sphere Complex/readme.md b/Sphere Complex/readme.md new file mode 100644 index 0000000..6961673 --- /dev/null +++ b/Sphere Complex/readme.md @@ -0,0 +1,9 @@ +# Auto Splitter for ***Sphere Complex*** +## Features +Starts the timer when the time in any level starts running. +Splits when finishing a level. +Does not reset the timer. + +## Resources +*Leaderboards: [speedrun.com/sphere_complex](https://speedrun.com/sphere_complex)* +*Game: [s.team/a/550600](https://s.team/a/550600) ($4.99)* \ No newline at end of file diff --git "a/Strobophagia \342\200\223 Rave Horror/Strobophagia.asl" "b/Strobophagia \342\200\223 Rave Horror/Strobophagia.asl" new file mode 100644 index 0000000..643181f --- /dev/null +++ "b/Strobophagia \342\200\223 Rave Horror/Strobophagia.asl" @@ -0,0 +1,125 @@ +state("Strobophagia") {} + +startup +{ + vars.Log = (Action)((output) => print("[Strobophagia] " + output)); + vars.Unity = Assembly.Load(File.ReadAllBytes(@"Components\UnityASL.bin")).CreateInstance("UnityASL.Unity"); + + dynamic[,] _settings = + { + { "SPLITS", "stage-Beginning", "FOOLS' BEACH.", false }, + { "SPLITS", "flag-21", "VOLUME.", false }, + { "SPLITS", "stage-EnteredCult", "I AM WILLING.", false }, + { "SPLITS", "stage-Initiation", "INITIATE.", false }, + { "SPLITS", "stage-HumanLabyrinth", "DA3DALUS.", false }, + { "SPLITS", "stage-RitesStarted", "SAMADHI.", true }, + { "SPLITS", "flag-24", "NAX-05", false }, + { "SPLITS", "flag-1", "GNOSIS.", true }, + { "SPLITS", "flag-4", "Liber::MASS", false }, + { "SPLITS", "flag-3", "SIGIL", true }, + { "SPLITS", "flag-22", "SERVITOR", true }, + { "SPLITS", "stage-Cave", "CAVE", false } + }; + + settings.Add("SPLITS"); + + for (int i = 0; i < _settings.GetLength(0); ++i) + { + string parent = _settings[i, 0]; + string id = _settings[i, 1]; + string desc = _settings[i, 2]; + bool state = _settings[i, 3]; + + settings.Add(id, state, desc, parent); + } + + vars.DoneFlags = new List(); +} + +onStart +{ + vars.DoneFlags.Clear(); +} + +init +{ + vars.Unity.TryOnLoad = (Func)(helper => + { + // var list = helper.GetClass("mscorlib", "List`1"); + + // CustomSceneManager + var csm = helper.GetClass("Assembly-CSharp", "CustomSceneManager"); + var hs = helper.GetClass("Assembly-CSharp", "HubStageSO"); + + vars.Unity.Make(csm.Static, csm["Instance"], csm["scenesLoading"]).Name = "scenesLoading"; + vars.Unity.MakeString(csm.Static, csm["Instance"], csm["activeStage"], hs["myName"]).Name = "stage"; + + // ProgressTracker + var pt = helper.GetClass("Assembly-CSharp", "ProgressTracker", 1); + var pd = helper.GetClass("Assembly-CSharp", "ProgressData"); + + vars.Unity.MakeList(pt.Static, 0x10, pt["progressData"], pd["flags"]).Name = "flags"; + vars.Unity["flags"].Offsets = new[] { 0x18, 0x10 }; + + return true; + }); + + vars.Unity.Load(game); +} + +update +{ + if (!vars.Unity.Loaded) return false; + + vars.Unity.Update(); + + current.Loading = vars.Unity["scenesLoading"].Current; + current.Stage = vars.Unity["stage"].Current; + current.Flags = vars.Unity["flags"].Current; +} + +start +{ + return old.Stage == "Main" && current.Stage != "Main"; +} + +split +{ + if (old.Stage != current.Stage) + return settings["stage-" + current.Stage]; + + if (!old.Loading && current.Loading) + return current.Stage == "Exit"; + + if (old.Flags.Count != current.Flags.Count) return; + + foreach (var flag in current.Flags) + { + if (vars.DoneFlags.Contains(flag)) + continue; + + vars.DoneFlags.Add(flag); + if (settings["flag-" + flag]) + return true; + } +} + +reset +{ + return old.Stage != "Main" && current.Stage == "Main"; +} + +isLoading +{ + return current.Loading; +} + +exit +{ + vars.Unity.Reset(); +} + +shutdown +{ + vars.Unity.Reset(); +} \ No newline at end of file diff --git "a/Strobophagia \342\200\223 Rave Horror/readme.md" "b/Strobophagia \342\200\223 Rave Horror/readme.md" new file mode 100644 index 0000000..b79e790 --- /dev/null +++ "b/Strobophagia \342\200\223 Rave Horror/readme.md" @@ -0,0 +1,12 @@ +# Auto Splitter for ***Strobophagia*** +## Features +Starts the timer when loading into the game. +Splits when closing the truck and having completed the tasks required for a valid run. +Resets the timer when that's not the case. +Removes loading times. + +## Resources +*Leaderboards: [speedrun.com/strobophagia](https://speedrun.com/strobophagia)* +*Discord: [discord.gg/FEpREJWWph](https://discord.gg/FEpREJWWph)* +*Website: [greentiledigital.com](https://greentiledigital.com)* +*Game: [s.team/a/1266680](https://s.team/a/1266680) ($19.99)* \ No newline at end of file diff --git a/The Final Station/TheFinalStation.asl b/The Final Station/TheFinalStation.asl new file mode 100644 index 0000000..47e4860 --- /dev/null +++ b/The Final Station/TheFinalStation.asl @@ -0,0 +1,126 @@ +// Bad. + +state("TheFinalStation") +{ + int QueuedScenes : 0x10479D8, 0xC; + int ActiveSceneID : 0x10479D8, 0x14, 0x0, 0x74; +} + +startup +{ + vars.Dbg = (Action) ((output) => print("[The Final Station ASL] " + output)); + + settings.Add("52", false, "Intro"); + + for (int i = 1, j = 1; i <= 51; ++i) + { + string id = i + (i > 46 ? "Pos" : string.Empty); + bool state = i % 2 == 1 || i > 46; + settings.Add(id, state, state ? "Level " + j : "Train Ride " + j); + + if (!state || i > 46) ++j; + } + + vars.Positions = new Dictionary + { + { 47, 421.9f }, + { 48, 411.1f }, + { 49, 235.1f }, + { 50, 537.2f }, + { 51, 308.1f } + }; + + vars.TimerStart = (EventHandler) ((s, e) => timer.Run.Offset = TimeSpan.Zero); + timer.OnStart += vars.TimerStart; + + if (timer.CurrentTimingMethod == TimingMethod.RealTime) + { + var result = MessageBox.Show( + "Removing loads from The Final Station requires comparing against Game Time.\nWould you like to switch to it?", + "The Final Station Autosplitter", + MessageBoxButtons.YesNo); + + if (result == DialogResult.Yes) timer.CurrentTimingMethod = TimingMethod.GameTime; + } +} + +init +{ + vars.TokenSource = new CancellationTokenSource(); + vars.MonoThread = new Thread(() => + { + var gameManager = IntPtr.Zero; + while (!vars.TokenSource.Token.IsCancellationRequested) + { + var mods = game.ModulesWow64Safe(); + if (mods.FirstOrDefault(m => m.ModuleName == "mono.dll") == null) + { + Thread.Sleep(2000); + continue; + } + + gameManager = (IntPtr)new DeepPointer("mono.dll", 0x1F63A4, 0x8, 0x4, 0x8, 0x8, 0x8, 0x4, 0x2B4, 0x194, 0xA4, 0x4, 0xC).Deref(game); + if (gameManager != IntPtr.Zero) break; + + Thread.Sleep(5000); + } + + vars.PlayerX = new MemoryWatcher(new DeepPointer(gameManager + 0x4, 0x10, 0x8, 0x5C, 0x2C)); + vars.State = new MemoryWatcher(new DeepPointer(gameManager, 0x18)); + }); + + vars.MonoThread.Start(); +} + +update +{ + if (vars.MonoThread.IsAlive) return; + vars.PlayerX.Update(game); + vars.State.Update(game); + + vars.Dbg(vars.State.Current); +} + +start +{ + if (old.ActiveSceneID == 0 && current.ActiveSceneID == 52) + { + timer.Run.Offset = TimeSpan.FromSeconds(4.534); + return true; + } +} + +split +{ + if (vars.Positions.ContainsKey(current.ActiveSceneID)) + { + float x = vars.Positions[current.ActiveSceneID]; + return vars.PlayerX.Old < x && vars.PlayerX.Current >= x; + } + else + { + return old.ActiveSceneID != current.ActiveSceneID && + settings[old.ActiveSceneID + "-" + current.ActiveSceneID]; + } +} + +reset +{ + return old.ActiveSceneID != 0 && current.ActiveSceneID == 0; +} + +isLoading +{ + return current.QueuedScenes > 1 || vars.State.Current == 1; +} + +exit +{ + vars.TokenSource.Cancel(); +} + +shutdown +{ + timer.OnStart -= vars.TimerStart; + vars.TokenSource.Cancel(); +} \ No newline at end of file diff --git a/The Final Station/readme.md b/The Final Station/readme.md new file mode 100644 index 0000000..8b4fbf3 --- /dev/null +++ b/The Final Station/readme.md @@ -0,0 +1,11 @@ +# Auto Splitter for ***The Final Station*** +## Features +Starts the timer when starting a new game. +Splits are available in the settings. +Resets the timer when returning to the main menu. +Removes loads and pauses the timer when the game is paused. + +## Resources +*Leaderboards: [speedrun.com/tfs](https://speedrun.com/tfs)* +*Website: [thefinalstation.com](https://thefinalstation.com)* +*Game: [s.team/a/435530](https://s.team/a/435530) ($14.99)* \ No newline at end of file diff --git a/The Pedestrian/MemoryValues b/The Pedestrian/MemoryValues new file mode 100644 index 0000000..53cab88 --- /dev/null +++ b/The Pedestrian/MemoryValues @@ -0,0 +1,239 @@ +GENERAL + +Player +Audio trigger: Music_PuzzleMode + + +INTRO + +Load Area 129 False +Load Initial Scene +Load Audio +Load Lobby_Warehouse +Load LobbyElevator + + +WAREHOUSE + +Choose +No savable object assigned to LobbySpawn +Audio trigger: LobbyFactory_Music_EnterMaintenance +(Audio trigger: LobbyFactory_Music_Progress) +Audio trigger: LobbyFactory_Music_Gap +Unlock Achievement: Warehouse +Plug +Unlock Achievement: Warehouse + + +ELEVATOR + +Load Area 111 True +On destroy Link +Load Audio +Load Subway +Load LobbyElevator +Load Train +Hub after start +Plug +Load Area finished 111 True +DONE + + +SUBWAY + +(Audio trigger: Subway_Music_Progress) +(1) +Crate +ComponentPiece +Plug +Unlock Achievement: Subway + + +ENTER TRAIN + +(Audio trigger: Subway_Music_Progress) +Prep area: 199 +Load Area 199 True +(On destroy Link) +Load Audio +Load Downtown +Load Train +Generic save +sequence save +(Hub after start) +(Initialize send audio event) +Crate +Plug +Plug +Load Area finished 199 True +DONE + + +DOWNTOWN + +Audio trigger: Downtown_Music_EndOfIntro +Unlock Achievement: Downtown +WireSign +Audio trigger: Downtown_Music_EndOfSewer +Audio trigger: Downtown_Music_Manhole +Crate +(Audio trigger: Downtown_Music_Progress) +(Key) +WireSign +Plug + + +TRAIN + +Prep area: 748 +Load Area 748 True +(On destroy Link) +Load Audio +Load University +Load Train +Generic save +Hub after start +(Initialize send audio event) +Plug +Plug +Plug +Load Area finished 748 True +DONE + + +UNIVERSITY + +(Audio trigger: University_Music_Progress) +Unlock Achievement: University +Crate +Unlock Achievement: University +WireSign +Unlock Achievement: University +ComponentPiece +Plug + + +TRAIN + +Prep area: 274 +Load Area 274 True +(On destroy Link) +Load Audio +Load Innercity +Load Train +Generic save +(sequence save) +(Hub after start) +(Initialize send audio event) +Plug +Plug +Plug +Plug +Load Area finished 274 True +DONE + + +INNERCITY + +(Audio trigger: Innercity_Music_Progress) +Key +WireSign +Plug +Unlock Achievement: InnerCity + + +TRAIN + +Prep area: 772 +Load Area 772 True +(On destroy Link) +Load Audio +Load RooftopsTrainStation +Load Train +Load Area finished 772 True +DONE + + +ROOFTOPS1 + +Unlock Achievement: Rooftops_1 + + +ELEVATOR + +Load Area 399 True +(On destroy Link) +Load Audio +Load Rooftops +Load MasterElevator +(sequence save) +(Hub after start) +(Initialize send audio event) +Plug +Plug +Plug +Plug +Plug +Load Area finished 399 True +DONE + + +ROOFTOPS + +(Crate) +(Key) +(Audio trigger: Rooftops_Music_Progress) +(Unlock Achievement: Rooftops_3) +WireSign +(Unlock Achievement: Rooftops_2) +Plug + + +ELEVATOR + +Load Area 444 True +(On destroy Link) +Load Audio +Load Finale +Load Finale_Stairwell +LevelName MainElevator +Load MasterElevator +Plug +Plug +Plug +Plug +Plug +Plug +Load Area finished 444 True +DONE + + +ROOF + +Unlock Achievement: Finale_1 +Plug +(Load 16) +(Bridge is now Used) +(On destroy Link) +(drop) + + +APARTMENTS + +Load Area 184 True +Load Audio +Load Apartments +Load Finale_Stairwell +Generic save +Initialize send audio event +Load Area finished 184 True +(Audio trigger: Apartment_Music_Progress) +DONE + +Machine Lever! +Power on +Computer signal! +Machine Button! +Audio trigger: Apartment_Music_Progress +Unlock Achievement: Apartment_1 +Overwrite save \ No newline at end of file diff --git a/The Pedestrian/ThePedestrian.asl b/The Pedestrian/ThePedestrian.asl new file mode 100644 index 0000000..1387db4 --- /dev/null +++ b/The Pedestrian/ThePedestrian.asl @@ -0,0 +1,71 @@ +// BAD! + +state("ThePed_Win_64") +{ + float Speed: "UnityPlayer.dll", 0x144FBD8, 0x8, 0xA8, 0x28, 0x9C; +} + +startup +{ + settings.Add("splits", true, "Splitting upon transitioning"); + settings.Add("111", true, "with the Elevator to the Subway (code 111)", "splits"); + settings.Add("199", false, "with the Train to Downtown (code 199)", "splits"); + settings.Add("748", false, "with the Train to University (code 748)", "splits"); + settings.Add("274", false, "with the Train to Innercity (code 274)", "splits"); + settings.Add("772", true, "with the Train to pre-Rooftops (code 772)", "splits"); + settings.Add("399", false, "with the Elevator to the Rooftops (code 399)", "splits"); + settings.Add("444", true, "with the Elevator to the final Level (code 444)", "splits"); + settings.Add("184", true, "into the Apartments", "splits"); +} + +init +{ + string logPath = Environment.GetEnvironmentVariable("appdata") + @"\..\LocalLow\Skookum Arts\The Pedestrian\output_log.txt"; + try { + FileStream fs = new FileStream(logPath, FileMode.Open, FileAccess.Write, FileShare.ReadWrite); + fs.SetLength(0); + fs.Close(); + } catch { + print("Can't open Ped log"); + } + vars.Line = String.Empty; + vars.Reader = new StreamReader(new FileStream(logPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)); + vars.FinalSplit = false; +} + +update +{ + if (vars.Reader == null) return false; + vars.Line = vars.Reader.ReadLine(); +} + +start +{ + return old.Speed == 0 && current.Speed != 0; +} + +split +{ + if (String.IsNullOrEmpty(vars.Line)) return; + + if (vars.Line.StartsWith("Load Area ")) + { + string AreaNumber = vars.Line.Split(' ')[2]; + //print("got " + AreaNumber); + return (settings[AreaNumber]); + } + + if (vars.Line.StartsWith("Machine Button!")) + vars.FinalSplit = true; + + if (vars.Line.StartsWith("Audio trigger: Apartment_Music_Progress") && vars.FinalSplit) + { + vars.FinalSplit = false; + return true; + } +} + +exit +{ + vars.Reader = null; +} \ No newline at end of file diff --git a/The Pedestrian/readme.md b/The Pedestrian/readme.md new file mode 100644 index 0000000..0f174b5 --- /dev/null +++ b/The Pedestrian/readme.md @@ -0,0 +1,11 @@ +# Auto Splitter for ***The Pedestrian*** +## Features +Starts the timer when the player starts moving. +Splits are available in the settings. Splits when entering the boat at the end of a run. +Does not reset the timer. + +## Resources +*Leaderboards: [speedrun.com/thepedestrian](https://speedrun.com/thepedestrian)* +*Discord: [discord.gg/aGmEnx9](https://discord.gg/aGmEnx9)* +*Website: [skookum-arts.com](https://skookum-arts.com)* +*Game: [s.team/a/466630](https://s.team/a/466630) ($19.99)* \ No newline at end of file diff --git a/The Spectrum Retreat/DayTime b/The Spectrum Retreat/DayTime new file mode 100644 index 0000000..02c55c1 --- /dev/null +++ b/The Spectrum Retreat/DayTime @@ -0,0 +1,44 @@ +day 1 + open door time 8 + enter code time 12 + exit level elevator time 22 + +day 2 + wake up time 7 + open door time 8 + meal done time 9 + enter floor 2 time 10 + enter code time 13 + exit level elevator time 22 + +day 3 + wake up time 7 + open door time 8 + meal done time 9 + enter floor 3 time 10 + enter code time 13 + exit level elevator time 22 + +day 4 + wake up time 7 + open door time 8 + meal done time 9 + enter floor 4 time 10 + enter code time 14 + exit level elevator time 22 + +day 5 + wake up time 7 + open door time 8 + meal done time 9 + enter floor 5 time 10 + enter code time 13 + exit level elevator time 22 + +day 6 + wake up time 7 + walk out time 9 + enter roof time 10 + +day 7 + credits time 8 \ No newline at end of file diff --git a/The Spectrum Retreat/LevelNumbers b/The Spectrum Retreat/LevelNumbers new file mode 100644 index 0000000..bc2d4f2 --- /dev/null +++ b/The Spectrum Retreat/LevelNumbers @@ -0,0 +1,67 @@ +floor1 113 + +1_01 47 +1_02 39 +1_03 26 +1_04 46 +1_05 135 + +floor1 113 + +floor2 8 + +2_01 46 +2_02 51 +2_03 59 +2_04 54 +2_05 46 +2_06 30 +2_07 26 +2_08 64 +2_09 53 7 +2_10 60 18 + +floor2 8 17 + +floor1 113 45 + +floor3 10 11 + +3_01 31 7 +3_02 50 11 +3_03 30 10 +3_04 31 10 +3_05 65 15 +3_06 58 18 +3_07 36 13 +3_08 64 15 +3_09 60 17 + +floor3 10 11 + +floor1 113 45 + +floor4 10 19 + +4_01 80 6 +4_02 41 7 +4_03 60 7 +4_04 44 6 +4_05 41 9 +4_06 46 6 +4_07 66 10 +4_08 65 13 + +floor4 10 19 + +floor1 113 45 + +floor5 8 9 + +5_01 292 58 + +floor5 8 9 + +floor1 113 45 + +roof 36 4 \ No newline at end of file diff --git a/The Spectrum Retreat/TheSpectrumRetreat.asl b/The Spectrum Retreat/TheSpectrumRetreat.asl new file mode 100644 index 0000000..5d10587 --- /dev/null +++ b/The Spectrum Retreat/TheSpectrumRetreat.asl @@ -0,0 +1,160 @@ +// Dogshit. + +state("Spectrum") +{ + int Screen : "mono.dll", 0x01F50AC, 0xA80, 0x20, 0x24; + int Mouse : "UnityPlayer.dll", 0x0FD789C, 0x38, 0x20, 0x8, 0x44; + int Level : "UnityPlayer.dll", 0x0FD8D74, 0x54, 0x1E0, 0x22C, 0x3A0; +} + +startup +{ + settings.Add("day1splits", true, "Day 1 splits:"); + settings.Add("day1time8", false, "Leave Room", "day1splits"); + settings.Add("day1time12", true, "Enter Code", "day1splits"); + settings.Add("day1and47to39", true, "Finishing 1_01", "day1splits"); + settings.Add("day1and39to26", true, "Finishing 1_02", "day1splits"); + settings.Add("day1and26to46", true, "Finishing 1_03", "day1splits"); + settings.Add("day1and46to135", true, "Finishing 1_04", "day1splits"); + settings.Add("day1and135to113", true, "Arrive back at Hotel (finishing 1_05)", "day1splits"); + + settings.Add("day2splits", true, "Day 2 splits:"); + settings.Add("day2time7", false, "Wake Up", "day2splits"); + settings.Add("day2time8", false, "Leave Room", "day2splits"); + settings.Add("day2time9", false, "Finish Breakfast", "day2splits"); + settings.Add("day2time10", false, "Exit Elevator to Floor 2", "day2splits"); + settings.Add("day2time13", true, "Enter Code", "day2splits"); + settings.Add("day2and46to51", true, "Finishing 2_01", "day2splits"); + settings.Add("day2and51to59", true, "Finishing 2_02", "day2splits"); + settings.Add("day2and59to54", true, "Finishing 2_03", "day2splits"); + settings.Add("day2and54to46", true, "Finishing 2_04", "day2splits"); + settings.Add("day2and9to30", true, "Finishing 2_05", "day2splits"); + settings.Add("day2and30to26", true, "Finishing 2_06", "day2splits"); + settings.Add("day2and26to64", true, "Finishing 2_07", "day2splits"); + settings.Add("day2and64to53", true, "Finishing 2_08", "day2splits"); + settings.Add("day2and53to60", true, "Finishing 2_09", "day2splits"); + settings.Add("day2and18to8", true, "Arrive back at Hotel (finishing 2_10)", "day2splits"); + settings.Add("day2and8to113", false, "Taking Elevator to Floor 1", "day2splits"); + + settings.Add("day3splits", true, "Day 3 splits:"); + settings.Add("day3time7", false, "Wake Up", "day3splits"); + settings.Add("day3time8", false, "Leave Room", "day3splits"); + settings.Add("day3time9", false, "Finish Breakfast", "day3splits"); + settings.Add("day3time10", false, "Exit Elevator to Floor 3", "day3splits"); + settings.Add("day3time13", true, "Enter Code", "day3splits"); + settings.Add("day3and31to50", true, "Finishing 3_01", "day3splits"); + settings.Add("day3and50to30", true, "Finishing 3_02", "day3splits"); + settings.Add("day3and30to31", false, "Finishing 3_03 (no skip)", "day3splits"); + settings.Add("day3and30to65", true, "Finishing 3_03 (with skip)", "day3splits"); + settings.Add("day3and31to65", true, "Finishing 3_04", "day3splits"); + settings.Add("day3and65to58", true, "Finishing 3_05", "day3splits"); + settings.Add("day3and58to36", true, "Finishing 3_06", "day3splits"); + settings.Add("day3and36to64", true, "Finishing 3_07", "day3splits"); + settings.Add("day3and64to60", false, "Finishing 3_08 (no skip)", "day3splits"); + settings.Add("day3and64to10", true, "Finishing 3_08 (with skip)", "day3splits"); + settings.Add("day3and60to10", true, "Arrive back at Hotel (finishing 3_09)", "day3splits"); + settings.Add("day3and10to113", false, "Taking Elevator to Floor 1", "day3splits"); + + settings.Add("day4splits", true, "Day 4 splits:"); + settings.Add("day4time7", false, "Wake Up", "day4splits"); + settings.Add("day4time8", false, "Leave Room", "day4splits"); + settings.Add("day4time9", false, "Finish Breakfast", "day4splits"); + settings.Add("day4time10", false, "Exit Elevator to Floor 4", "day4splits"); + settings.Add("day4time14", true, "Enter Code", "day4splits"); + settings.Add("day4and80to41", true, "Finishing 4_01", "day4splits"); + settings.Add("day4and41to60", true, "Finishing 4_02", "day4splits"); + settings.Add("day4and60to44", false, "Finishing 4_03", "day4splits"); + settings.Add("day4and44to41", true, "Finishing 4_04", "day4splits"); + settings.Add("day4and41to46", true, "Finishing 4_05", "day4splits"); + settings.Add("day4and46to66", true, "Finishing 4_06", "day4splits"); + settings.Add("day4and66to65", true, "Finishing 4_07", "day4splits"); + settings.Add("day4and65to10", true, "Arrive back at Hotel (finishing 4_08)", "day4splits"); + settings.Add("day4and10to113", false, "Taking Elevator to Floor 1", "day4splits"); + + settings.Add("day5splits", true, "Day 5 splits:"); + settings.Add("day5time7", false, "Wake Up", "day5splits"); + settings.Add("day5time8", false, "Leave Room", "day5splits"); + settings.Add("day5time9", false, "Finish Breakfast", "day5splits"); + settings.Add("day5time10", false, "Exit Elevator to Floor 5", "day5splits"); + settings.Add("day5time13", true, "Enter Code", "day5splits"); + settings.Add("day5and160to8", true, "Arrive back at Hotel (finishing 5_01)", "day5splits"); + settings.Add("day5and8to113", false, "Taking Elevator to Floor 1", "day5splits"); + + settings.Add("day6splits", false, "Day 6 splits:"); + settings.Add("day6time7", false, "Wake Up", "day6splits"); + settings.Add("day6time9", false, "Leave Room", "day6splits"); + settings.Add("day6time10", false, "Exit Elevator to Roof", "day6splits"); +} + +init +{ + string logPath = Environment.GetEnvironmentVariable("appdata")+"\\..\\LocalLow\\Ripstone\\The Spectrum Retreat\\output_log.txt"; + vars.Line = String.Empty; + vars.Reader = new StreamReader(new FileStream(logPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)); + + vars.Time = String.Empty; + vars.Day = "1"; + vars.Date = String.Empty; + vars.RoofHelp = 0; + vars.LastRealLevel = 0; + vars.StoreNewLevel = 0; + + vars.Reader.BaseStream.Seek(0, SeekOrigin.End); +} + +update { + if (vars.Reader == null) return false; + vars.Line = vars.Reader.ReadLine(); + + if (old.Level != current.Level && current.Level != 0) + { + vars.LastRealLevel = vars.StoreNewLevel; + vars.StoreNewLevel = current.Level; + print(">>>>> storeNewLevel changed to " + vars.StoreNewLevel + " and lastRealLevel changed to " + vars.LastRealLevel); + } + + if (!String.IsNullOrEmpty(vars.Line) && vars.Line.StartsWith("Time advanced to ")) + { + vars.Time = vars.Line.Split(' ')[3]; + vars.Day = vars.Line.Split(' ')[6]; + vars.Date = "day" + vars.Day.ToString() + "time" + vars.Time.ToString(); + } +} + +start +{ + if (current.Screen != 14 && old.Screen == 14) + { + vars.RoofHelp = 0; + return true; + } +} + +split +{ + if (String.IsNullOrEmpty(vars.Line)) return; + + if (vars.Date == "day6time10" && old.Mouse == 257 && current.Mouse == 0) + { + ++vars.RoofHelp; + return vars.RoofHelp == 2; + } + + if (old.Level != current.Level && vars.LastRealLevel != vars.StoreNewLevel && settings["day" + vars.Day + "and" + vars.LastRealLevel + "to" + vars.StoreNewLevel]) + { + vars.LastRealLevel = vars.StoreNewLevel; + return true; + } + + return vars.Line.StartsWith("Time advanced to ") && settings[vars.Date]; +} + +reset +{ + return current.Screen == 14 && old.Screen == 18; +} + +exit +{ + vars.Reader = null; +} \ No newline at end of file diff --git a/The Spectrum Retreat/readme.md b/The Spectrum Retreat/readme.md new file mode 100644 index 0000000..7c5e183 --- /dev/null +++ b/The Spectrum Retreat/readme.md @@ -0,0 +1,10 @@ +# Auto Splitter for ***The Spectrum Retreat*** +## Features +Starts the timer after loading into the game (set timer offset to -0.5). +Splits are available in the settings. Splits at the end of the game. +Resets the timer when starting a new game. + +## Resources +*Leaderboards: [speedrun.com/spectrum_retreat](https://speedrun.com/spectrum_retreat)* +*Website: [dansmithstudios.com/the-spectrum-retreat](https://dansmithstudios.com/the-spectrum-retreat)* +*Game: [s.team/a/763250](https://s.team/a/763250) ($12.99)* \ No newline at end of file diff --git a/The Two of Us/TheTwoOfUs.asl b/The Two of Us/TheTwoOfUs.asl new file mode 100644 index 0000000..7a324f2 --- /dev/null +++ b/The Two of Us/TheTwoOfUs.asl @@ -0,0 +1,79 @@ +state("The Two of Us") {} + +startup +{ + vars.Log = (Action)(output => print("[The Two of Us] " + output)); + vars.Unity = Assembly.Load(File.ReadAllBytes(@"Components\UnityASL.bin")).CreateInstance("UnityASL.Unity"); + vars.Unity.LoadSceneManager = true; + + vars.StartTime = 0f; +} + +onStart +{ + vars.StartTime = current.IGT; +} + +init +{ + vars.Unity.TryOnLoad = (Func)(helper => + { + var asd = helper.GetClass("Assembly-CSharp", "AutoSplitterData"); + + vars.Unity.Make(asd.Static, asd["inGameTime"]).Name = "inGameTime"; + vars.Unity.Make(asd.Static, asd["levelID"]).Name = "levelID"; + // vars.Unity.Make(asd.Static, asd["runState"]).Name = "runState"; + + return true; + }); + + vars.Unity.Load(game); +} + +update +{ + if (!vars.Unity.Loaded) return false; + + vars.Unity.Update(); + + current.IGT = vars.Unity["inGameTime"].Current; + current.Level = vars.Unity["levelID"].Current; + // current.State = vars.Unity["runState"].Current; // currently unusable + current.Scene = vars.Unity.Scenes.Active.Index; +} + +start +{ + // return old.State == 0 && current.State == 1; + return old.Scene == 0 && (current.Scene == 1 || current.Scene == 10); +} + +split +{ + return old.Level < current.Level; +} + +reset +{ + // return old.State == 1 && current.State == 0; +} + +gameTime +{ + return TimeSpan.FromSeconds(current.IGT - vars.StartTime); +} + +isLoading +{ + return true; +} + +exit +{ + vars.Unity.Reset(); +} + +shutdown +{ + vars.Unity.Reset(); +} \ No newline at end of file diff --git a/The Two of Us/readme.md b/The Two of Us/readme.md new file mode 100644 index 0000000..c82ce72 --- /dev/null +++ b/The Two of Us/readme.md @@ -0,0 +1,10 @@ +# Auto Splitter for ***The Two Of Us*** +## Features +Starts the timer when the first level loads. +Splits when either player touches the trophy. +Resets the timer when returning to the lobby or when closing the game. + +## Resources +*Leaderboards: [speedrun.com/ttou](https://speedrun.com/the_two_of_us)* +*Discord: [discord.gg/6ay2DwEvKP](https://discord.gg/6ay2DwEvKP)* +*Game: [s.team/a/1820170](https://s.team/a/1820170) (Free)* \ No newline at end of file diff --git a/There is no Game (Jam Edition 2015)/ThereIsNoGame_Jam2015.asl b/There is no Game (Jam Edition 2015)/ThereIsNoGame_Jam2015.asl new file mode 100644 index 0000000..a52cf93 --- /dev/null +++ b/There is no Game (Jam Edition 2015)/ThereIsNoGame_Jam2015.asl @@ -0,0 +1,104 @@ +state("Ting_Jam") +{ + string128 TriggerEvent : "GameAssembly.dll", 0xC94A38, 0xB88, 0x78, 0x1C, 0xC; + float DialogueLength : "GameAssembly.dll", 0xC6A9CC, 0x5C, 0x0, 0x30; + int TreeState : "GameAssembly.dll", 0xCC1C7C, 0x5C, 0x0, 0x1C, 0x90, 0x54, 0x10; + int BrickInstance : "UnityPlayer.dll", 0x1253A80, 0x10, 0x8, 0x18, 0x78, 0x4C, 0x2C, 0xC, 0x14; + short BrickScore : "UnityPlayer.dll", 0x1253A80, 0x10, 0x8, 0x18, 0x78, 0x4C, 0x2C, 0xC, 0x14, 0x1C; +} + +startup +{ + settings.Add("Splits"); + settings.CurrentDefaultParent = "Splits"; + + settings.Add("Mute the developer for the 3rd time", false); + settings.Add("THERE IS A"); + settings.Add("TREE", false, "TREE", "THERE IS A"); + settings.Add("GOAT", false, "GOAT", "THERE IS A"); + settings.Add("GAME", true, "GAME", "THERE IS A"); + settings.Add("Reach 3000 points in BreakOut (glitches)"); + settings.Add("Grow tree all the way"); + settings.Add("Trade the squirrel a nut for the key", false); + settings.Add("Free the goat", false); + settings.Add("Choose Yes or No at the end"); + + vars.SplitDialogues = new Dictionary + { + { 2.208752632f, "Mute the developer for the 3rd time" }, + { 2.779047489f, "Free the goat" }, + { 1.221632719f, "Trade the squirrel a nut for the key" } + }; + + vars.DoneWords = new List(); +} + +onStart +{ + vars.DoneWords.Clear(); +} + +init +{ + var sB = new StringBuilder(); + vars.UpdateWord = (Func)(() => + { + sB.Clear(); + for (int i = 0; i < 4; ++i) + { + string slot = new DeepPointer("GameAssembly.dll", 0xC94A38, 0xBB8, 0x1C, 0x10 + 0x4 * i, 0xC).DerefString(game, 1); + sB.Append(slot ?? ""); + } + + return sB.ToString(); + }); + + current.Word = vars.UpdateWord(); +} + +update +{ + current.Word = vars.UpdateWord(); + current.TriggerEvent = current.TriggerEvent ?? old.TriggerEvent; +} + +start +{ + if (old.TriggerEvent != current.TriggerEvent) + { + return current.TriggerEvent.StartsWith("EVT_OnFlag"); + } +} + +split +{ + if (old.BrickScore == 2900 && current.BrickScore == 3000) + return settings["Reach 3000 points in BreakOut (glitches)"]; + + if (old.TreeState == 2 && current.TreeState == 3) + return settings["Grow tree all the way"]; + + if (old.TriggerEvent != current.TriggerEvent && current.TriggerEvent.StartsWith("EVT_Click")) + return settings["Choose Yes or No at the end"]; + + if (old.DialogueLength != current.DialogueLength) + { + if (!vars.SplitDialogues.ContainsKey(current.DialogueLength)) return; + + string setting = vars.SplitDialogues[current.DialogueLength]; + return settings[setting]; + } + + if (old.Word != current.Word) + { + if (!settings.ContainsKey(current.Word) || vars.DoneWords.Contains(current.Word)) return; + vars.DoneWords.Add(current.Word); + return settings[current.Word]; + } +} + +reset +{ + return old.TriggerEvent != current.TriggerEvent && current.TriggerEvent.StartsWith("EVT_OnFlag") || + old.BrickInstance != current.BrickInstance; +} \ No newline at end of file diff --git a/There is no Game (Jam Edition 2015)/readme.md b/There is no Game (Jam Edition 2015)/readme.md new file mode 100644 index 0000000..936b285 --- /dev/null +++ b/There is no Game (Jam Edition 2015)/readme.md @@ -0,0 +1,10 @@ +# Auto Splitter for ***There is no Game*** +## Features +Starts the timer when choosing a language. +Splits are available in the settings. +Resets the timer when returning to the language menu, when choosing a language or when closing the game. + +## Resources +*Leaderboards: [speedrun.com/ting](https://speedrun.com/ting)* +*Discord: [discord.gg/VcZj2bvyu9](https://discord.gg/VcZj2bvyu9)* +*Game: [s.team/a/1241700](https://s.team/a/1241700) (Free)* \ No newline at end of file diff --git "a/Transformers \342\200\223 The Game/Transformers2007.asl" "b/Transformers \342\200\223 The Game/Transformers2007.asl" new file mode 100644 index 0000000..583be34 --- /dev/null +++ "b/Transformers \342\200\223 The Game/Transformers2007.asl" @@ -0,0 +1,89 @@ +state ("Transformers") +{ + int State : 0x2FB208; // Player state. 1 = can move and is on foot, 0 = cannot move or is driving. + int Level : 0x2FB284; // Current level ID. Starts at 0 for level 1. + int Chapter : 0x313630; // Current mission ID. Starts at 0, changes the instant a mission is entered. + int Saving : 0x314454; // Changes from 0 to 3 when the 'Autosaving' screen comes up. + float IGT : 0x2FB5B8; // In-game time. + // int progress : 0x282ADC0; +} + +startup +{ + settings.Add("autobots", true, "Autobots"); + settings.Add("lvl0Splits", true, "The Suburbs", "autobots"); + settings.Add("lvl0c0", true, "Chapter 1 - Uninvited Guests", "lvl0Splits"); + settings.Add("lvl0c1", true, "Chapter 2 - Guardian Angel", "lvl0Splits"); + settings.Add("lvl0c2", true, "Chapter 3 - Protect and Serve", "lvl0Splits"); + settings.Add("lvl0c3", true, "Chapter 4 - Air Traffic Control", "lvl0Splits"); + + settings.Add("lvl1Splits", true, "More Than Meets The Eye", "autobots"); + settings.Add("lvl1c0", true, "Chapter 1 - Obstruction of Justice", "lvl1Splits"); + settings.Add("lvl1c1", true, "Chapter 2 - A Friend in Need", "lvl1Splits"); + settings.Add("lvl1c2", true, "Chapter 3 - Flight of the Bumblebee", "lvl1Splits"); + settings.Add("lvl1c3", true, "Chapter 4 - Heavy Weapon", "lvl1Splits"); + + settings.Add("lvl2Splits", true, "Inside Hoover Dam", "autobots"); + settings.Add("lvl2c0", true, "Chapter 1 - Breakout!", "lvl2Splits"); + settings.Add("lvl2c1", true, "Chapter 2 - Tunnel Vision", "lvl2Splits"); + settings.Add("lvl2c2", true, "Chapter 3 - Power Drain", "lvl2Splits"); + settings.Add("lvl2c3", true, "Chapter 4 - Waking Giant", "lvl2Splits"); + + settings.Add("lvl4Splits", true, "The Last Stand"); + settings.Add("lvl4c0", true, "Chapter 1 - Exterminator", "lvl4Splits"); + settings.Add("lvl4c1", true, "Chapter 2 - Unfriendly Skies", "lvl4Splits"); + settings.Add("lvl4c2", true, "Chapter 3 - For the Fallen", "lvl4Splits"); + settings.Add("lvl4c3", true, "Chapter 4 - Keep Away", "lvl4Splits"); + + + settings.Add("decepticons", true, "Decepticons"); + settings.Add("lvl6Splits", true, "SOCCENT Military Base", "decepticons"); + settings.Add("lvl6c0", true, "Chapter 1 - Sand Storm", "lvl6Splits"); + settings.Add("lvl6c1", true, "Chapter 2 - Communication Breakdown", "lvl6Splits"); + settings.Add("lvl6c2", true, "Chapter 3 - Seek and Destroy", "lvl6Splits"); + settings.Add("lvl6c3", true, "Chapter 4 - Fire in the Sky", "lvl6Splits"); + + settings.Add("lvl7Splits", true, "The Hunt for Sam Witwicky", "decepticons"); + settings.Add("lvl7c0", true, "Chapter 1 - Rough Justice", "lvl7Splits"); + settings.Add("lvl7c1", true, "Chapter 2 - Race for Frenzy", "lvl7Splits"); + settings.Add("lvl7c2", true, "Chapter 3 - Pursuit", "lvl7Splits"); + settings.Add("lvl7c3", true, "Chapter 4 - Plight of the Bumblebee", "lvl7Splits"); + + settings.Add("lvl8Splits", true, "A Gathering Force", "decepticons"); + settings.Add("lvl8c0", true, "Chapter 1 - Clearing the Air", "lvl8Splits"); + settings.Add("lvl8c1", true, "Chapter 2 - Sinister Savior", "lvl8Splits"); + settings.Add("lvl8c2", true, "Chapter 3 - Fireworks", "lvl8Splits"); + settings.Add("lvl8c3", true, "Chapter 4 - Warpath", "lvl8Splits"); + + settings.Add("lvl10Splits", true, "City of the Machines", "decepticons"); + settings.Add("lvl10c0", true, "Chapter 1 - Nowhere to Run", "lvl10Splits"); + settings.Add("lvl10c1", true, "Chapter 2 - Energon Overload", "lvl10Splits"); + settings.Add("lvl10c2", true, "Chapter 3 - The Mighty will Fall", "lvl10Splits"); + settings.Add("lvl10c3", true, "Chapter 4 - Devestation", "lvl10Splits"); + + if (timer.CurrentTimingMethod == TimingMethod.RealTime) + { + var mbox = MessageBox.Show( + "Removing loads from Transformers: The Game requires comparing against Game Time.\nWould you like to switch to it?", + "LiveSplit | Transformers: The Game", + MessageBoxButtons.YesNo); + + if (mbox == DialogResult.Yes) + timer.CurrentTimingMethod = TimingMethod.GameTime; + } +} + +start +{ + return old.State == 0 && current.State == 1 && current.IGT > 7.5 && current.IGT < 8.5; +} + +split +{ + return old.Saving == 0 && current.Saving == 1 && settings["lvl" + current.Level + "c" + current.Chapter]; +} + +isLoading +{ + return old.IGT == current.IGT; +} \ No newline at end of file diff --git "a/Transformers \342\200\223 The Game/readme.md" "b/Transformers \342\200\223 The Game/readme.md" new file mode 100644 index 0000000..cbec83c --- /dev/null +++ "b/Transformers \342\200\223 The Game/readme.md" @@ -0,0 +1,10 @@ +# Auto Splitter for ***Transformers: The Game*** +## Features +Starts the timer when loading into the game. +Splits are available in the settings. Does not split upon killing the final boss. +Does not reset the timer. +Removes loading times. + +## Resources +*Leaderboards: [speedrun.com/ttg](https://speedrun.com/ttg)* +*Discord: [discord.gg/0k5xzgwIRccNbKxG](https://discord.gg/0k5xzgwIRccNbKxG)* \ No newline at end of file diff --git a/Voidigo/Voidigo.asl b/Voidigo/Voidigo.asl new file mode 100644 index 0000000..93fe534 --- /dev/null +++ b/Voidigo/Voidigo.asl @@ -0,0 +1,127 @@ +state("Voidigo") {} + +startup +{ + settings.Add("wrldSplits", true, "Split after leaving an area:"); + settings.Add("Antlantis", true, "Antlantis", "wrldSplits"); + settings.Add("Porko Land", true, "Porko Land", "wrldSplits"); + settings.Add("The Void", true, "The Void", "wrldSplits"); + settings.Add("Antivoid", false, "Antivoid", "wrldSplits"); + + settings.Add("bossSplit", true, "Split when a boss becomes corrupted"); + settings.Add("beaconSplit", false, "Split when activating a beacon"); + + if (timer.CurrentTimingMethod == TimingMethod.RealTime) + { + var result = MessageBox.Show( + "Voidigo uses in-game time.\nWould you like to switch to it?", + "Voidigo Autosplitter", + MessageBoxButtons.YesNo); + + if (result == DialogResult.Yes) timer.CurrentTimingMethod = TimingMethod.GameTime; + } +} + +init +{ + Stopwatch SigTimeout = new Stopwatch(); + ProcessModuleWow64Safe Module = modules.FirstOrDefault(x => x.ModuleName.Equals("voidigo_livesplit.dll")); + SignatureScanner ModuleScanner = new SignatureScanner(game, Module.BaseAddress, Module.ModuleMemorySize); + + SigScanTarget MagicStringSig = new SigScanTarget("40 40 56 6F 69 64 69 67 6F 2D 6C 69 76 65 73 70 6C 69 74 40 40"); + IntPtr MagicStringAddr = IntPtr.Zero; + + vars.SigFound = false; + SigTimeout.Start(); + while (!vars.SigFound) + { + MagicStringAddr = ModuleScanner.Scan(MagicStringSig); + vars.SigFound = MagicStringAddr != IntPtr.Zero; + if (SigTimeout.ElapsedMilliseconds >= 1000) break; + } + SigTimeout.Reset(); + + if (vars.SigFound) + { + //print("Found MagicString: 0x" + MagicStringAddr.ToString("X")); + + vars.RUNTIME = MagicStringAddr + 0x20 + 0x100 * 0; + vars.ALL_BOSSES_CORRUPTED = MagicStringAddr + 0x20 + 0x100 * 1; + vars.WORLD = MagicStringAddr + 0x20 + 0x100 * 2; + vars.BEACONS_ACTIVATED = MagicStringAddr + 0x20 + 0x100 * 3; + vars.COMPLETED_RUN = MagicStringAddr + 0x20 + 0x100 * 4; + } + else + { + MessageBox.Show( + "Signature scan timed out!", + "Voidigo Autosplitter", + MessageBoxButtons.OK, MessageBoxIcon.Error + ); + } + + timer.IsGameTimePaused = false; +} + +update +{ + if (!vars.SigFound) return false; + + float f; + int i; + bool b; + string s = game.ReadString((IntPtr)vars.WORLD, 256); + + current.World = String.IsNullOrEmpty(s) ? "None" : s; + + current.IGT = Single.TryParse(game.ReadString((IntPtr)vars.RUNTIME, 256), out f) ? f : 69f; + + Boolean.TryParse(game.ReadString((IntPtr)vars.ALL_BOSSES_CORRUPTED, 256), out b); + current.CorruptedBosses = b; + + Int32.TryParse(game.ReadString((IntPtr)vars.BEACONS_ACTIVATED, 256), out i); + current.Beacons = i; + + Boolean.TryParse(game.ReadString((IntPtr)vars.COMPLETED_RUN, 256), out b); + current.Finished = b; +} + +start +{ + return old.IGT > current.IGT && current.World != "None" && current.World != "Camp"; +} + +split +{ + if (old.World != current.World && old.World != "The Void") + return settings[old.World]; + + if (!old.CorruptedBosses && current.CorruptedBosses) + return settings["bossSplit"]; + + if (old.Beacons < current.Beacons) + return settings["beaconSplit"]; + + return !old.Finished && current.Finished && settings["The Void"]; +} + +reset +{ + return old.IGT > current.IGT || old.World != current.World && (current.World == "Camp" || String.IsNullOrEmpty(current.World)); +} + +gameTime +{ + if (new[]{"None", "Camp"}.All(x => x != current.World)) + return TimeSpan.FromSeconds(current.IGT); +} + +isLoading +{ + return true; +} + +exit +{ + timer.IsGameTimePaused = true; +} \ No newline at end of file diff --git a/Voidigo/readme.md b/Voidigo/readme.md new file mode 100644 index 0000000..b7a8f0d --- /dev/null +++ b/Voidigo/readme.md @@ -0,0 +1,11 @@ +# Auto Splitter for ***Voidigo*** +## Features +Starts the timer when starting a new run. +Splits are available in the settings. +Resets the timer when leaving to Camp or when quick restarting. +Syncs to in-game time. + +## Resources +*Leaderboards: [speedrun.com/alba](https://speedrun.com/alba)* +*Website: [albawildlife.com](https://albawildlife.com)* +*Game: [s.team/a/1337010](https://s.team/a/1337010) ($17.99)* \ No newline at end of file diff --git "a/YAPP \342\200\223 Yet Another Puzzle Platformer/YAPP.asl" "b/YAPP \342\200\223 Yet Another Puzzle Platformer/YAPP.asl" new file mode 100644 index 0000000..fcf89c8 --- /dev/null +++ "b/YAPP \342\200\223 Yet Another Puzzle Platformer/YAPP.asl" @@ -0,0 +1,47 @@ +state("yapp") +{ + int Location : "mono.dll", 0x263110, 0xA0, 0x20, 0x30; + // int xPos : "mono.dll", 0x263110, 0xA0, 0x20, 0x38; + // int yPos : "mono.dll", 0x263110, 0xA0, 0x20, 0x3C; + // string1800 dataString : "mono.dll", 0x263110, 0xA0, 0x28, 0x14; +} + +startup +{ + for (int wrld = 1; wrld <= 7; ++wrld) + { + string parent = "World " + wrld + " Splits:"; + settings.Add(parent); + + for (int lvl = 1; lvl <= 8; ++lvl) + settings.Add("w" + wrld + "l" + lvl, true, lvl == 8 ? "Castle" : "Level " + lvl, parent); + } +} + +init +{ + vars.SolvedWatchers = new MemoryWatcherList(); + + for (int wrld = 1; wrld <= 7; ++wrld) + for (int lvl = 1; lvl <= 8; ++lvl) + vars.SolvedWatchers.Add(new MemoryWatcher(new DeepPointer("mono.dll", 0x263110, 0xA0, 0x20, 0x10, 0x20 + 10 * wrld + lvl)) {Name = "w" + wrld + "l" + lvl}); +} + +start +{ + return old.Location == 1 && current.Location == 2; +} + +split +{ + vars.SolvedWatchers.UpdateAll(game); + + foreach (var watcher in vars.SolvedWatchers) + if (watcher.Changed && watcher.Current) + return settings[watcher.Name]; +} + +reset +{ + return old.Location == 2 && current.Location == 1; +} \ No newline at end of file diff --git "a/YAPP \342\200\223 Yet Another Puzzle Platformer/readme.md" "b/YAPP \342\200\223 Yet Another Puzzle Platformer/readme.md" new file mode 100644 index 0000000..29b84a8 --- /dev/null +++ "b/YAPP \342\200\223 Yet Another Puzzle Platformer/readme.md" @@ -0,0 +1,10 @@ +# Auto Splitter for ***YAPP: Yet Another Puzzle Platformer*** +## Features +Starts the timer when starting a new file. +Splits are available in the settings. +Resets the timer when the player clears the file (level 1 must have been completed). + +## Resources +*Leaderboards: [speedrun.com/yapp](https://speedrun.com/yapp)* +*Discord: [discord.gg/kPy8Yy2UnB](https://discord.gg/kPy8Yy2UnB)* +*Game: [s.team/a/745250](https://s.team/a/745250) ($1.99)* \ No newline at end of file diff --git a/lil gator game/lilgatorgame.asl b/lil gator game/lilgatorgame.asl new file mode 100644 index 0000000..be9f659 --- /dev/null +++ b/lil gator game/lilgatorgame.asl @@ -0,0 +1,187 @@ +state("Lil Gator Game") {} + +startup +{ + vars.Log = (Action) ((output) => print("[lil gator game ASL] " + output)); + + settings.Add("QP_Act1_Jill", true, "Finish Jill's Quest"); + settings.Add("QP_Act1_Martin", true, "Finish Martin's Quest"); + settings.Add("QP_Act1_Avery", true, "Finish Avery's Quest"); + + vars.TimerStart = (EventHandler) ((s, e) => + { + foreach (var quest in current.Quests) + quest.Value = false; + }); + + timer.OnStart += vars.TimerStart; +} + +init +{ + vars.CancelSource = new CancellationTokenSource(); + vars.MonoThread = new Thread(() => + { + vars.Log("Starting mono thread."); + + int class_count = 0; + IntPtr class_cache = IntPtr.Zero, scMgr = IntPtr.Zero; + ProcessModuleWow64Safe uPlayer = null; + var scMgrTrg = new SigScanTarget(3, "48 8B 1D ???????? 0F 57 C0") { OnFound = (p, s, ptr) => ptr + 0x4 + p.ReadValue(ptr) }; + + var token = vars.CancelSource.Token; + while (!token.IsCancellationRequested) + { + var mods = game.ModulesWow64Safe(); + uPlayer = mods.FirstOrDefault(m => m.ModuleName == "UnityPlayer.dll"); + if (uPlayer != null && mods.FirstOrDefault(m => m.ModuleName == "mono-2.0-bdwgc.dll") != null) + break; + + vars.Log("Modules not found."); + Thread.Sleep(2000); + } + + while (!token.IsCancellationRequested) + { + var scanner = new SignatureScanner(game, uPlayer.BaseAddress, uPlayer.ModuleMemorySize); + + if ((scMgr = scanner.Scan(scMgrTrg)) != IntPtr.Zero) + { + vars.Log("Found 'SceneManager' at 0x" + scMgr.ToString("X")); + break; + } + + vars.Log("SceneManager not found."); + Thread.Sleep(2000); + } + + while (!token.IsCancellationRequested) + { + int size = new DeepPointer("mono-2.0-bdwgc.dll", 0x49A0C8, 0x18).Deref(game); + var table = new DeepPointer("mono-2.0-bdwgc.dll", 0x49A0C8, 0x10, 0x8 * (int)(0xFA381AED % size)).Deref(game); + + for (; table != IntPtr.Zero; table = game.ReadPointer(table + 0x10)) + { + if (new DeepPointer(table, 0x0).DerefString(game, 32) != "Assembly-CSharp") continue; + + class_count = new DeepPointer(table + 0x8, 0x4D8).Deref(game); + class_cache = new DeepPointer(table + 0x8, 0x4E0).Deref(game); + } + + if (class_cache != IntPtr.Zero) + break; + + vars.Log("Assembly-CSharp not found."); + Thread.Sleep(2000); + } + + var mono = new Dictionary + { + { "QuestProfile", IntPtr.Zero }, + { "DialogueManager", IntPtr.Zero } + }; + + while (!token.IsCancellationRequested) + { + bool allFound = false; + + for (int i = 0; i < class_count; ++i) + { + var klass = game.ReadPointer(class_cache + 0x8 * i); + for (; klass != IntPtr.Zero; klass = game.ReadPointer(klass + 0x108)) + { + string class_name = new DeepPointer(klass + 0x48, 0x0).DerefString(game, 32); + if (!mono.ContainsKey(class_name)) continue; + + mono[class_name] = new DeepPointer(klass + 0xD0, 0x8, 0x60).Deref(game); + vars.Log("Found '" + class_name + "' at 0x" + mono[class_name].ToString("X") + "."); + + if (allFound = mono.Values.All(ptr => ptr != IntPtr.Zero)) + break; + } + + if (allFound) + break; + } + + if (allFound) + { + vars.Mono = mono; + vars.Watchers = new MemoryWatcherList + { + new MemoryWatcher(new DeepPointer(scMgr, 0x50, 0x0, 0x98)) { Name = "SceneIndex" }, + new MemoryWatcher(new DeepPointer(mono["QuestProfile"], 0x18)) { Name = "QuestCount" }, + new StringWatcher(new DeepPointer(mono["DialogueManager"], 0x18, 0x30, 0x14), 512) { Name = "Dialogue" }, + new MemoryWatcher(new DeepPointer(mono["DialogueManager"], 0xBC)) { Name = "InDialogue" } + }; + + break; + } + + vars.Log("Not all classes found."); + Thread.Sleep(5000); + } + + vars.Log("Exiting mono thread."); + }); + + vars.MonoThread.Start(); + current.Quests = new Dictionary + { + { "QP_Act1_Jill", false }, + { "QP_Act1_Martin", false }, + { "QP_Act1_Avery", false }, + { "QP_Act1_Friends", false } + }; +} + +update +{ + if (vars.MonoThread.IsAlive) return false; + + vars.Watchers.UpdateAll(game); + + current.SceneIndex = vars.Watchers["SceneIndex"].Current; + current.Dialogue = vars.Watchers["Dialogue"].Current; + current.InDialogue = vars.Watchers["InDialogue"].Current; +} + +start +{ + return old.SceneIndex == 1 && current.SceneIndex == -1; +} + +split +{ + var questSplit = false; + var count = vars.Watchers["QuestCount"].Current; + for (int i = 4; i > 0; --i) + { + var ptr = new DeepPointer((IntPtr)vars.Mono["QuestProfile"], 0x10, 0x20 + 0x8 * (count - i)).Deref(game); + string id = new DeepPointer(ptr + 0x18, 0x14).DerefString(game, 32); + bool state = game.ReadValue(ptr + 0x41); + + if (!old.Quests[id] && state) + questSplit = true; + + current.Quests[id] = state; + } + + return questSplit || old.InDialogue && !current.InDialogue && current.Dialogue == "but i can't :) because this is a demo" + Environment.NewLine + ":)"; +} + +reset +{ + return old.SceneIndex == -1 && current.SceneIndex == 0; +} + +exit +{ + vars.CancelSource.Cancel(); +} + +shutdown +{ + timer.OnStart -= vars.TimerStart; + vars.CancelSource.Cancel(); +} \ No newline at end of file diff --git a/lil gator game/readme.md b/lil gator game/readme.md new file mode 100644 index 0000000..e8c52ea --- /dev/null +++ b/lil gator game/readme.md @@ -0,0 +1,9 @@ +# Auto Splitter for ***lil gator game*** +## Features +Starts the timer when loading into the game. +Splits are available in the settings. +Resets the timer when returning to the main menu. + +## Resources +*Leaderboards: [speedrun.com/lgg](https://speedrun.com/lgg)* +*Game: [s.team/a/1586800](https://s.team/a/1586800) (Free Demo)* \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..0b228de --- /dev/null +++ b/readme.md @@ -0,0 +1,37 @@ +# LiveSplit Auto Splitters *(by Ero)* +## How do I activate an auto splitter? +All of my auto splitters are available through LiveSplit's [`LiveSplit.AutoSplitters.xml`](https://github.com/LiveSplit/LiveSplit.AutoSplitters/blob/master/LiveSplit.AutoSplitters.xml). +To activate them, simply follow these steps: +* open any split file +* right-click LiveSplit and choose "Edit Splits..." +* set the game name to your desired game +* click the "Activate" button below + +Then check and edit the settings to your liking. +Don't forget to set your timing method to "Game Time" for auto splitters with load removal. + +## There is an issue with an auto splitter I'm using! +Please first refer to these common problems to try and troubleshoot the issue yourself: +
+ +> The auto splitter does not remove loads / sync to the in-game time! + +Please make sure your *Current Timing Method* is set to *Game Time* within LiveSplit. It could be that your *Timer* component (in your *Layout*) is set to *Real Time* as well. +
+ +> The auto splitter doesn't load at all! No settings are showing up and Start/Split/Reset are grayed out. + +This can happen if you're on a LiveSplit version before 1.8.18. This version introduced `onStart`, `onSplit`, and `onReset` actions, which can't be loaded on earlier versions. +
+ +> The auto splitter cannot be activated from the splits editor! + +This occasionally happens and does not have a fix. Attempt launching LiveSplit as administrator. If the issue persists, the bug is on LiveSplit's side. +
+ +Your issue isn't listed? Please [create an issue here on GitHub](https://github.com/just-ero/asl/issues/new/choose). +Want to try and help me out with the fixes? Set up [Trace Spy](https://gist.github.com/just-ero/1e3b784fa63059f04f8dd2810dfa8f13) and add any error messages you see to the GitHub issue! + +## Commission Information +I take commissions for creating auto splitters. I am especially proficient in ***Unity*** and ***Unreal Engine*** games, with a decent hint of ***GameMaker Studio***. Auto splitters I make on those engines will never use strange work-arounds and always be solid. +