Skip to content

Commit

Permalink
add Cuphead asl
Browse files Browse the repository at this point in the history
  • Loading branch information
just-ero committed Jul 9, 2022
1 parent c587282 commit 88226aa
Show file tree
Hide file tree
Showing 5 changed files with 366 additions and 3 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
/Phasmophobia
66 changes: 66 additions & 0 deletions Cuphead/Cuphead.Splits.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<Splits>
<Split ID="scene_shop" Name="Shop (Enter Scene)" ToolTip="Splits when current scene is 'Shop'" Type="SCENE_ENTER"/>
<Split ID="scene_map_world_1" Name="Inkwell Isle 1 (Enter Scene)" ToolTip="Splits when first entering Inkwell Isle I" Type="SCENE_ENTER"/>
<Split ID="scene_map_world_2" Name="Inkwell Isle 2 (Enter Scene)" ToolTip="Splits when first entering Inkwell Isle II" Type="SCENE_ENTER"/>
<Split ID="scene_map_world_3" Name="Inkwell Isle 3 (Enter Scene)" ToolTip="Splits when first entering Inkwell Isle III" Type="SCENE_ENTER"/>
<Split ID="scene_map_world_4" Name="Inkwell Hell (Enter Scene)" ToolTip="Splits when first entering Inkwell Hell" Type="SCENE_ENTER"/>
<Split ID="scene_map_world_DLC" Name="Inkwell Isle DLC (Enter Scene)" ToolTip="Splits when first entering Inkwell Isle DLC" Type="SCENE_ENTER"/>

<Split ID="scene_level_tutorial" Name="Tutorial (Level)" ToolTip="Splits when leaving the Tutorial level" Type="SCENE_LEAVE"/>
<!-- Sort of guessing the chalice tutorial level name, long story short our current mod has some issues regarding that tutorial level and it doesn't display properly -->
<Split ID="scene_level_chalice_tutorial" Name="Chalice Tutorial (Level)" ToolTip="Splits when leaving the Chalice Tutorial level" Type="SCENE_LEAVE"/>

<Split ID="scene_level_veggies" Name="The Root Pack (Boss)" ToolTip="Splits when level is finished" Type="LEVEL_COMPLETE"/>
<Split ID="scene_level_slime" Name="Goopy Le Grande (Boss)" ToolTip="Splits when level is finished" Type="LEVEL_COMPLETE"/>
<Split ID="scene_level_flower" Name="Cagney Carnation (Boss)" ToolTip="Splits when level is finished" Type="LEVEL_COMPLETE"/>
<Split ID="scene_level_frogs" Name="Ribby And Croaks (Boss)" ToolTip="Splits when level is finished" Type="LEVEL_COMPLETE"/>
<Split ID="scene_level_flying_blimp" Name="Hilda Berg (Boss)" ToolTip="Splits when level is finished" Type="LEVEL_COMPLETE"/>

<Split ID="scene_level_baroness" Name="Baroness Von Bon Bon (Boss)" ToolTip="Splits when level is finished" Type="LEVEL_COMPLETE"/>
<Split ID="scene_level_flying_genie" Name="Djimmi The Great (Boss)" ToolTip="Splits when level is finished" Type="LEVEL_COMPLETE"/>
<Split ID="scene_level_clown" Name="Beppi The Clown (Boss)" ToolTip="Splits when level is finished" Type="LEVEL_COMPLETE"/>
<Split ID="scene_level_flying_bird" Name="Wally Warbles (Boss)" ToolTip="Splits when level is finished" Type="LEVEL_COMPLETE"/>
<Split ID="scene_level_dragon" Name="Grim Matchstick (Boss)" ToolTip="Splits when level is finished" Type="LEVEL_COMPLETE"/>

<Split ID="scene_level_bee" Name="Rumor Honeybottoms (Boss)" ToolTip="Splits when level is finished" Type="LEVEL_COMPLETE"/>
<Split ID="scene_level_pirate" Name="Captin Brineybeard (Boss)" ToolTip="Splits when level is finished" Type="LEVEL_COMPLETE"/>
<Split ID="scene_level_mouse" Name="Werner Werman (Boss)" ToolTip="Splits when level is finished" Type="LEVEL_COMPLETE"/>
<Split ID="scene_level_robot" Name="Dr. Kahl's Robot (Boss)" ToolTip="Splits when level is finished" Type="LEVEL_COMPLETE"/>
<Split ID="scene_level_sally_stage_play" Name="Sally Stageplay (Boss)" ToolTip="Splits when level is finished" Type="LEVEL_COMPLETE"/>
<Split ID="scene_level_flying_mermaid" Name="Cala Maria (Boss)" ToolTip="Splits when level is finished" Type="LEVEL_COMPLETE"/>
<Split ID="scene_level_train" Name="Phantom Express (Boss)" ToolTip="Splits when level is finished" Type="LEVEL_COMPLETE"/>

<Split ID="scene_level_old_man" Name="Glumstone The Giant (Boss)" ToolTip="Splits when level is finished" Type="LEVEL_COMPLETE"/>
<Split ID="scene_level_snow_cult" Name="Mortimer Freeze (Boss)" ToolTip="Splits when level is finished" Type="LEVEL_COMPLETE"/>
<Split ID="scene_level_airplane" Name="The Howling Aces (Boss)" ToolTip="Splits when level is finished" Type="LEVEL_COMPLETE"/>
<Split ID="scene_level_flying_cowboy" Name="Esther Winchester (Boss)" ToolTip="Splits when level is finished" Type="LEVEL_COMPLETE"/>
<Split ID="scene_level_rum_runners" Name="Moonshine Mob (Boss)" ToolTip="Splits when level is finished" Type="LEVEL_COMPLETE"/>
<Split ID="scene_level_saltbaker" Name="Chef Saltbaker (Boss)" ToolTip="Splits when level is finished" Type="LEVEL_COMPLETE"/>
<Split ID="scene_level_graveyard" Name="Demon and Angel (Boss)" ToolTip="Splits when level is finished" Type="LEVEL_COMPLETE"/>

<Split ID="scene_cutscene_kingdice" Name="King Dice (Contract Cutscene)" ToolTip="Splits when you get the cutscene trying to enter the King Dice fight without all contracts" Type="SCENE_ENTER"/>
<Split ID="scene_level_dice_palace_main" Name="King Dice (Boss)" ToolTip="Splits when you beat King Dice" Type="LEVEL_COMPLETE"/>
<Split ID="scene_level_devil" Name="Devil (Boss)" ToolTip="Splits when level is finished" Type="LEVEL_COMPLETE"/>

<Split ID="scene_cutscene_credits" Name="End Game (Credits)" ToolTip="Splits when entering the credits" Type="SCENE_ENTER"/>

<Split ID="scene_level_platforming_1_1F" Name="Forest Follies (Run 'n Gun)" ToolTip="Splits when level is finished" Type="LEVEL_COMPLETE"/>
<Split ID="scene_level_platforming_1_2F" Name="Treetop Trouble (Run 'n Gun)" ToolTip="Splits when level is finished" Type="LEVEL_COMPLETE"/>
<Split ID="scene_level_platforming_2_1F" Name="Funfair Fever (Run 'n Gun)" ToolTip="Splits when level is finished" Type="LEVEL_COMPLETE"/>
<Split ID="scene_level_platforming_2_2F" Name="Funhouse Frazzle (Run 'n Gun)" ToolTip="Splits when level is finished" Type="LEVEL_COMPLETE"/>
<Split ID="scene_level_platforming_3_1F" Name="Perilous Piers (Run 'n Gun)" ToolTip="Splits when level is finished" Type="LEVEL_COMPLETE"/>
<Split ID="scene_level_platforming_3_2F" Name="Rugged Ridge (Run 'n Gun)" ToolTip="Splits when level is finished" Type="LEVEL_COMPLETE"/>

<Split ID="scene_level_mausoleum_0" Name="Mausoleum I (Super)" ToolTip="Splits when level is finished" Type="ENDING"/>
<Split ID="scene_level_mausoleum_1" Name="Mausoleum II (Super)" ToolTip="Splits when level is finished" Type="ENDING"/>
<Split ID="scene_level_mausoleum_2" Name="Mausoleum III (Super)" ToolTip="Splits when level is finished" Type="ENDING"/>

<Split ID="scene_level_chess_pawn" Name="Chess Pawns (Boss)" ToolTip="Splits when level is finished" Type="LEVEL_COMPLETE"/>
<Split ID="scene_level_chess_knight" Name="Chess Knight (Boss)" ToolTip="Splits when level is finished" Type="LEVEL_COMPLETE"/>
<Split ID="scene_level_chess_bishop" Name="Chess Bishop (Boss)" ToolTip="Splits when level is finished" Type="LEVEL_COMPLETE"/>
<Split ID="scene_level_chess_rook" Name="Chess Rook (Boss)" ToolTip="Splits when level is finished" Type="LEVEL_COMPLETE"/>
<Split ID="scene_level_chess_queen" Name="Chess Queen (Boss)" ToolTip="Splits when level is finished" Type="LEVEL_COMPLETE"/>

<Split ID="ilEnter" Name="Enter Level (IL)" ToolTip="Splits when entering any level" Type="CUSTOM"/>
<Split ID="ilEnd" Name="End Level (IL)" ToolTip="Splits when ending any level" Type="CUSTOM"/>
</Splits>
283 changes: 283 additions & 0 deletions Cuphead/Cuphead.asl
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
state("Cuphead") {}

startup
{
vars.Log = (Action<object>)(output => print("[Cuphead] " + output));

var bytes = File.ReadAllBytes(@"Components\LiveSplit.ASLHelper.bin");
var type = Assembly.Load(bytes).GetType("ASLHelper.Unity");
vars.Helper = Activator.CreateInstance(type, timer, this);

vars.Splits = new Dictionary<string, string>();

settings.Add("splits", false, "Splits:");

var xml = System.Xml.Linq.XDocument.Load(@"Components\Cuphead.Splits.xml");
foreach (var split in xml.Element("Splits").Elements("Split"))
{
string id = split.Attribute("ID").Value, name = split.Attribute("Name").Value;
string tt = split.Attribute("ToolTip").Value, splitType = split.Attribute("Type").Value;

settings.Add(id, false, name, "splits");
settings.SetToolTip(id, tt);

vars.Splits[id] = splitType;
}

vars.CompletedSplits = new List<string>();
}

onStart
{
timer.IsGameTimePaused = true;
vars.CompletedSplits.Clear();
}

onSplit
{
// Set final split's time to accurate in-game time when player is doing IL attempts.
// This (like `reset`) assumes the runner only has 1 split.
// DevilSquirrel's code. /shrug
if (timer.CurrentPhase != TimerPhase.Ended || timer.Run.Count != 1)
return;

var time = timer.Run[0].SplitTime;
timer.Run[0].SplitTime = new Time(time.RealTime, TimeSpan.FromSeconds(current.Time));
}

onReset
{}

init
{
int PTR_SIZE = game.Is64Bit() ? 0x8 : 0x4;

current.SaveSlot = IntPtr.Zero;

vars.Helper.TryOnLoad = (Func<dynamic, bool>)(mono =>
{
#region PlayerData
var pd = mono.GetClass("PlayerData");

vars.Helper["inGame"] = pd.Make<bool>("inGame");
vars.Helper["saveSlotIndex"] = pd.Make<int>("_CurrentSaveFileIndex");
vars.Helper["saveFiles"] = pd.MakeArray<IntPtr>("_saveFiles");

vars.GetCurrentSave = (Func<IntPtr>)(() =>
{
var slot = vars.Helper["saveSlotIndex"].Current;
return vars.Helper["saveFiles"].Current[slot];
});

// Level Completion
var pldm = mono.GetClass("PlayerLevelDataManager");
var pldo = mono.GetClass("PlayerLevelDataObject");

vars.GetAllLevelsData = (Func<List<dynamic>>)(() =>
{
var levels = vars.Helper.ReadList<IntPtr>(current.SaveSlot + pd["levelDataManager"], pldm["levelObjects"]);
var ret = new List<dynamic>();

foreach (var level in levels)
{
var id = vars.Helper.Read<int>(level + pldo["levelID"]);
var completed = vars.Helper.Read<bool>(level + pldo["completed"]);
var grade = vars.Helper.Read<int>(level + pldo["grade"]);
var difficulty = vars.Helper.Read<int>(level + pldo["difficultyBeaten"]);

ret.Add(new
{
ID = id,
Completed = completed,
Grade = grade,
Difficulty = difficulty
});
}

return ret;
});

vars.IsLevelCompleted = (Func<int, int, int, bool>)((levelId, targetDifficulty, targetGrade) =>
{
foreach (var level in vars.GetAllLevelsData())
{
if (level.ID == levelId)
return level.Completed && level.Grade >= targetGrade && level.Difficulty >= targetDifficulty;
}

return false;
});
#endregion // PlayerData

#region Level
var lvl = mono.GetClass("Level");

vars.Helper["lvl"] = lvl.Make<int>("PreviousLevel");
vars.Helper["lvlTime"] = lvl.Make<float>("Current", "LevelTime");
vars.Helper["lvlDifficulty"] = lvl.Make<int>("Current", "mode");
vars.Helper["lvlEnding"] = lvl.Make<bool>("Current", "Ending");
vars.Helper["lvlWon"] = lvl.Make<bool>("Won");
#endregion // Level

#region SceneLoader
var sl = mono.GetClass("SceneLoader");

vars.Helper["sceneName"] = sl.MakeString("SceneName");
vars.Helper["doneLoading"] = sl.Make<bool>("_instance", "doneLoadingSceneAsync");
#endregion // SceneLoader

var x = mono.GetClass("PlayerStatsManager");

return true;
});

vars.Helper.Load();
}

update
{
if (!vars.Helper.Update())
return false;

current.SaveSlot = vars.GetCurrentSave();

current.InGame = vars.Helper["inGame"].Current;

current.Level = vars.Helper["lvl"].Current;
current.Time = vars.Helper["lvlTime"].Current;
current.Difficulty = vars.Helper["lvlDifficulty"].Current;
current.IsEnding = vars.Helper["lvlEnding"].Current;
current.HasWon = vars.Helper["lvlWon"].Current;

current.Loading = !vars.Helper["doneLoading"].Current;
current.Scene = vars.Helper["sceneName"].Current;

// vars.Log("InGame: " + current.InGame);
// vars.Log("Level: " + current.Level);
// vars.Log("Time: " + current.Time);
// vars.Log("Difficulty: " + current.Difficulty);
// vars.Log("IsEnding: " + current.IsEnding);
// vars.Log("HasWon: " + current.HasWon);
// vars.Log("Loading: " + current.Loading);
// vars.Log("Scene: " + current.Scene);
// vars.Log("---------------------------------");
}

start
{
return current.Scene == "scene_cutscene_intro" && current.InGame && current.Loading;
}

split
{
foreach (var split in vars.Splits)
{
string id = split.Key, type = split.Value;

if (!settings[id] || vars.CompletedSplits.Contains(id))
continue;

switch (type)
{
case "SCENE_ENTER":
{
if (old.Scene != id && current.Scene == id)
{
vars.Log("SCENE_ENTER | " + id);

vars.CompletedSplits.Add(id);
return true;
}

continue;
}

case "SCENE_LEAVE":
{
if (old.Scene == id && current.Scene != id)
{
vars.Log("SCENE_LEAVE | " + id);

vars.CompletedSplits.Add(id);
return true;
}

continue;
}

case "LEVEL_COMPLETE":
{
if (current.Scene == id && vars.IsLevelCompleted(current.Level, -1, -1))
{
vars.Log("LEVEL_COMPLETE | " + id);

vars.CompletedSplits.Add(id);
return true;
}

continue;
}

case "ENDING":
{
var scene = id.Substring(0, id.Length - 2);
var diff = int.Parse(id[id.Length - 1].ToString());
if (!old.IsEnding && current.IsEnding && current.Scene == scene && current.Difficulty == diff)
{
vars.Log("ENDING | " + id);

vars.CompletedSplits.Add(id);
return true;
}

continue;
}
}

// case "CUSTOM"
switch (id)
{
case "ilEnter":
{
if (old.Time == 0f && current.Time > 0f)
return true;

continue;
}

case "ilEnd":
{
if (current.Time > 0f && current.IsEnding)
return true;

continue;
}
}
}
}

reset
{
// Reset only when the runner is doing IL attempts.
// Kind of a big assumption, don't you think? Runners can do full game runs with only 1 split, too.
// DevilSquirrel's code. /shrug
return timer.Run.Count == 1 && current.Loading && current.Time == 0f;
}

gameTime
{}

isLoading
{
return current.Loading;
}

exit
{
vars.Helper.Dispose();
}

shutdown
{
vars.Helper.Dispose();
}
15 changes: 15 additions & 0 deletions Cuphead/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Auto Splitter for ***Cuphead***
## Disclaimer
This is a temporary attempt at fixing [DevilSquirrel's component](https://github.com/ShootMe/LiveSplit.Cuphead) for the new DLC.

## Features
Starts the timer when choosing any save file.
Splits are available in the settings.
Resets the timer when the runner only has one split.
Removes loading times.

## Resources
*Leaderboards: [speedrun.com/cuphead](https://speedrun.com/cuphead)*
*Discord: [discord.gg/2BcMwEd](https://discord.gg/2BcMwEd)*
*Website: [cupheadgame.com](https://cupheadgame.com)*
*Game: [s.team/a/268910](https://s.team/a/268910) ($19.99)*
4 changes: 2 additions & 2 deletions Donut County/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
## Features
Starts the timer when starting any level.
Splits are available in the settings.
Does not reset the timer.
Does not reset the timer.
Removes loading times.

## 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)*

0 comments on commit 88226aa

Please sign in to comment.