// AutoSplitter for The Binding of Isaac: Afterbirth+
// Code by Hyphen-ated
// Checkpoint code & pointer annotations by blcd/Zamiel
state("isaac-ng", "1.06.J168")
{
// 0x0053A0E0 old(0x005380E0) - GlobalsPtr
int wins: 0x0053A0E0, 0x80C;
int character: 0x0053A0E0, 0x76A0;
int winstreak: 0x0053A0E0, 0x230;
// 0x0053A0D0 - old(0x005380D0) - GamePtr (which is the same thing as the Lua "game" pointer)
int timer: 0x0053A0D0, 0x001F37C8;
int floor: 0x0053A0D0, 0x0;
int curse: 0x0053A0D0, 0xC;
// Checkpoint is a custom item planted at the end of a run in the Racing+ mod
int cpCount: 0x0053A0D0, 0x9768, 0x0, 0x2770, 0x8C4; // "Checkpoint" (ID 561) count
// Off Limits is a custom item used by the Racing+ mod to signal the AutoSplitter that the mod is sending the player back to the first character
int olCount: 0x0053A0D0, 0x9768, 0x0, 0x2770, 0x8C0; // "Off Limits" (ID 560) count
// Pre BP5
// Equivalent Lua: Game():GetPlayer(0):GetCollectibleNum(541)
// 0x9d8c - PlayerVectorPtr
// 0x0 - Player1
// 0x2764 - Player1 CollectibleNum Vector Ptr
//# 0x864 - Item 537 count
//# 0x86C - Item 539 count
// 0x874 - Item 541 count
// 0x87C - Item 543 count
// BP5
// Equivalent Lua: Game():GetPlayer(0):GetCollectibleNum(541)
// 0x94D8 - PlayerVectorPtr
// 0x0 - Player1
// 0x221C - Player1 CollectibleNum Vector Ptr
// 0x8BC - Item 559 count - current
// 0x8C4 - Item 561 count - current
}
startup
{
settings.Add("character_run", true, "Multi-character run");
settings.SetToolTip("character_run", "Disables auto-resetting when you're past the first split.");
settings.Add("racing_plus_custom_challenge", false, "You're using the Racing+ custom challenge for multi-character runs", "character_run");
settings.Add("floor_splits", false, "Split on floors");
settings.Add("grouped_floors", false, "Combine Basement, Caves, Depths, and Womb into one split each", "floor_splits");
settings.Add("blck_cndl", false, "You're using the \"BLCK CNDL\" seed (the \"Total Curse Immunity\" Easter Egg) or using the Racing+ mod (which disables curses)", "floor_splits");
}
init
{
vars.timer_during_floor_change = 0;
}
update
{
//print("wins: " + current.wins + ", floor: " + current.floor + ", character: " + current.character + ", timer: " + current.timer + ", curse: " + current.curse);
//print("cpCount: " + current.cpCount);
}
start
{
if (old.timer == 0 && current.timer != 0)
{
vars.timer_during_floor_change = 0;
return true;
}
}
reset
{
// old.timer is 0 immediately during a reset, and also when you're on the main menu
// this "current.timer < 10" is to stop a reset from happening when you s+q.
// (unless you s+q during the first 1/3 second of the run, but why would you)
if (old.timer == 0 && current.timer != 0 && current.timer < 10
&& (!settings["character_run"] || timer.CurrentSplitIndex == 0))
{
vars.timer_during_floor_change = 0;
return true;
}
if (settings["racing_plus_custom_challenge"] && current.olCount == 1 && old.olCount != 1)
{
return true;
}
}
split
{
if (current.wins == old.wins + 1)
{
return true;
}
if (settings["racing_plus_custom_challenge"] && current.cpCount == 1 && old.cpCount != 1)
{
return true;
}
if (settings["floor_splits"])
{
if (current.floor > old.floor && current.floor > 1 && old.floor > 0
&& (!settings["grouped_floors"] || (current.floor != 2 && current.floor != 4 && current.floor != 6 && current.floor != 8)))
{
// when using floor splits, if they just got into an xl floor, we are going to doublesplit
vars.timer_during_floor_change = current.timer;
return true;
}
if (vars.timer_during_floor_change != -1
&& current.timer > vars.timer_during_floor_change)
{
vars.timer_during_floor_change = -1;
// if they're in blck_cndl mode, there is no xl even if the xl curse looks like it's on
// similarly, with grouped floors, there's no split to skip
if (current.curse == 2 && !settings["blck_cndl"] && !settings["grouped_floors"])
{
var model = new TimerModel { CurrentState = timer };
model.SkipSplit();
}
}
}
}
Comments