Skip to content

Instantly share code, notes, and snippets.

@argusdusty
Last active October 29, 2018 01:00
Show Gist options
  • Save argusdusty/7fea0526d09c74184f71c4d6c0911480 to your computer and use it in GitHub Desktop.
Save argusdusty/7fea0526d09c74184f71c4d6c0911480 to your computer and use it in GitHub Desktop.
Broforce Auto Splitter for Livesplit
state("Broforce_beta", "")
{
// The level of the current campaign
// If you need to scan for this: offset always ends with 0x18
byte level : "Broforce_beta.exe", 0x1020554, 0x64, 0xA4, 0x9C, 0xC8, 0x18, 0x18;
// The timer behind the 3...2...1...Go! countdown. See start for more details
float start_timer : "Broforce_beta.exe", 0xFE4CB0, 0x40, 0xA8;
// Indicates whether or not we're in the main menu. 1 when we are, 0 when we aren't
byte menu : "Broforce_beta.exe", 0xFE5210, 0x38;
float final_death_countdown : "mono.dll", 0x1F62F0, 0x50, 0x40C, 0x78, 0x8, 0x10, 0x1C4; // If you need to scan for this: offset always ends with 0x1C4
//int final_boss_health : "mono.dll", 0x1F62F0, 0x50, 0x40C, 0x78, 0x8, 0x10, 0x1E8; // If you need to scan for this: offset always ends with 0x1E8, value starts at 1043
// Counts down from 2.0 while level is running, reset to 2.0 upon completion of level/loading of new level
// Obtained from GameModeController object, always ends with offset 0x28
// Nice and short pointer path, hopefully means it works for everyone
float level_start_counter : "mono.dll", 0x1F62F0, 0x50, 0x528, 0x0, 0x28;
}
startup
{
settings.Add("campaign%", false, "Campaign Any% NG+");
settings.Add("all", false, "Split on all levels, rather than just on campaigns", "campaign%");
settings.Add("3first", false, "Do campaign 3 before campaign 2", "campaign%");
settings.Add("5first", false, "Do campaign 5 before campaign 4", "campaign%");
settings.Add("8first", false, "Do campaign 8 before campaign 7", "campaign%");
settings.Add("10first", false, "Do campaign 10 before campaign 9", "campaign%");
settings.Add("13first", false, "Do campaign 13 before campaign 12", "campaign%");
// Number of levels in each campaign of campaign mode
vars.campaigns = new byte[15] {4, 4, 4, 3, 3, 4, 3, 4, 4, 4, 6, 5, 5, 5, 14};
vars.initial_load = false;
}
isLoading
{
if (current.level_start_counter == 2) {
return true;
}
if (vars.initial_load) {
if (current.level == 0 && old.level_start_counter == 2 && current.level_start_counter != 2) {
vars.initial_load = false;
} else {
return true;
}
}
return false;
}
init
{
vars.campaign = 0;
// Order to campaign levels
vars.campaignOrder = new byte[15] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
// Swap the campaign order per settings
// Really, most of this is useless, since only 7 and 8 have differing numbers of levels
byte temp;
if (settings["3first"]) {
temp = vars.campaignOrder[2];
vars.campaignOrder[2] = vars.campaignOrder[1];
vars.campaignOrder[1] = temp;
}
if (settings["5first"]) {
temp = vars.campaignOrder[4];
vars.campaignOrder[4] = vars.campaignOrder[3];
vars.campaignOrder[3] = temp;
}
if (settings["8first"]) {
temp = vars.campaignOrder[7];
vars.campaignOrder[7] = vars.campaignOrder[6];
vars.campaignOrder[6] = temp;
}
if (settings["10first"]) {
temp = vars.campaignOrder[9];
vars.campaignOrder[9] = vars.campaignOrder[8];
vars.campaignOrder[8] = temp;
}
if (settings["13first"]) {
temp = vars.campaignOrder[12];
vars.campaignOrder[12] = vars.campaignOrder[11];
vars.campaignOrder[11] = temp;
}
}
start
{
// There are multiple start timers roughly in multiples of 400 (0, 400, ..., 1600)
// They are imprecise. Off by at most a few dozen from "correct" values
// Picked the 1200 timer, which hits 0 right as "Go" starts
// Putting the threshold under that for more accurate starts
// Extra validation is there so it doesn't randomly trigger (the timers are weird)
// Still a little finicky, try not to go too fast when starting
if (current.level == 0 &&
(current.start_timer < old.start_timer) &&
(current.start_timer < -300) &&
(current.start_timer > -400) &&
(old.start_timer - current.start_timer < 10*400/refreshRate)) {
vars.campaign = 0;
if (!settings["campaign%"]) {
vars.initial_load = true; // First level fix for Arcade
}
return true;
}
}
reset
{
if (current.menu == 1 && old.menu == 0) {
return true;
}
}
split
{
// Detect the level change
if (current.level == old.level+1) {
if (settings["campaign%"]) {
if (settings["all"]) {
return true;
} else if (!settings["all"]) {
if (current.level == vars.campaigns[vars.campaignOrder[vars.campaign]]) {
vars.campaign++;
return true;
}
}
} else {
return true;
}
}
// Detect the ending
bool is_final_level = false;
if (settings["campaign%"]) {
// Final level of final campaign
if (vars.campaign == 14 && current.level == vars.campaigns[vars.campaignOrder[vars.campaign]]-1) {
is_final_level = true;
}
} else if (current.level == 63) {
is_final_level = true;
}
if (is_final_level && current.final_death_countdown < 3 && current.final_death_countdown >= 1) {
return true;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment