Skip to content

Instantly share code, notes, and snippets.

@jbzdarkid
Last active December 20, 2017 06:47
Show Gist options
  • Save jbzdarkid/4a97fa029a80d88560eeaa5096b8701f to your computer and use it in GitHub Desktop.
Save jbzdarkid/4a97fa029a80d88560eeaa5096b8701f to your computer and use it in GitHub Desktop.
Autosplitter for Braid
state("braid", "steam") {
int world : 0x1F718C;
int level : 0x1F7190;
bool flagPole : 0x1F942C;
int gameFrames : 0x1F9434;
}
state("braid", "standalone") {
int world : 0x1F110C;
int level : 0x1F1110;
bool flagPole : 0x1F33AC;
int gameFrames : 0x1F33B4;
}
state("braid", "kanban55") {
int world : 0x18B93C;
int level : 0x18B940;
bool flagPole : 0x18DBDC;
int gameFrames : 0x18DBE4;
}
startup {
settings.Add("Split when returning to the house", true);
settings.Add("Split when touching a flagpole", false);
settings.Add("Split when completing and exiting a puzzle level", false);
settings.Add("Split when initially collecting a piece", false);
settings.Add("Split when exiting The Cloud Bridge after collecting 2 pieces", false);
settings.Add("Split when exiting Leap of Faith after collecting 1 piece", false);
settings.Add("Split when exiting Lair in World 3", false);
settings.Add("Split when entering the Braid level", false);
settings.Add("Split when exiting the Braid level", false);
settings.Add("Split when exiting lobby levels", false);
settings.Add("Split when exiting the house", false);
}
init {
vars.piecesBase = 0x0; // location of first piece minus 0x338
if (modules.First().ModuleMemorySize == 7663616) {
version = "steam";
vars.piecesBase = 0x1F829C;
}
if (modules.First().ModuleMemorySize == 7639040) {
version = "standalone";
vars.piecesBase = 0x1F221C;
}
if (modules.First().ModuleMemorySize == 7229440) {
version = "kanban55";
vars.piecesBase = 0x18CA4C;
}
// IDs are in display order in the top left
vars.pieceMap = new Dictionary<string, List<int>> { // Name : List of piece indices
{"0-0", new List<int>()}, // House
{"0-1", new List<int>()}, // World 1 lobby
{"0-2", new List<int>()}, // World 2 lobby
{"0-3", new List<int>()}, // World 3 lobby
{"0-4", new List<int>()}, // World 4 lobby
{"0-5", new List<int>()}, // World 5 lobby
{"0-6", new List<int>()}, // World 6 lobby
{"0-8", new List<int>()}, // Epilogue
{"1-4", new List<int>()},
{"1-3", new List<int>()},
{"1-2", new List<int>()},
{"1-1", new List<int>()}, // Braid
{"2-1", new List<int>{6, 5, 1}}, // Three Easy Pieces
{"2-2", new List<int>{9, 10, 4, 2}}, // The Cloud Bridge
{"2-3", new List<int>{8}}, // Hunt!
{"2-4", new List<int>{11, 12, 7, 3}}, // Leap of Faith
{"3-1", new List<int>{}}, // The Pit
{"3-2", new List<int>{2}}, // There and Back Again
{"3-3", new List<int>{4, 6}}, // Phase
{"3-4", new List<int>{12, 7}}, // The Ground Beneath Her Feet
{"3-5", new List<int>{8, 1}}, // Tight Channels
{"3-6", new List<int>{3, 9, 10}}, // Irreversible
{"3-7", new List<int>{}}, // Lair
{"3-8", new List<int>{5, 11}}, // A Tingling
{"4-1", new List<int>{}}, // The Pit
{"4-2", new List<int>{5, 6, 9}}, // Jumpman
{"4-3", new List<int>{12, 10}}, // Just Out of Reach
{"4-4", new List<int>{7}}, // Hunt!
{"4-5", new List<int>{3, 8}}, // Movement by Degrees
{"4-6", new List<int>{4, 1}}, // Movement, Amplified
{"4-7", new List<int>{11, 2}}, // Fickle Companion
{"5-1", new List<int>{5}}, // The Pit
{"5-2", new List<int>{4, 9}}, // So Distant
{"5-3", new List<int>{2, 12}}, // [unnamed]
{"5-4", new List<int>{7, 8, 1}}, // Crossing the Gap
{"5-5", new List<int>{3, 10}}, // Window of Opportunity
{"5-6", new List<int>{11}}, // Lair
{"5-7", new List<int>{6}}, // Fragile Companion
{"6-1", new List<int>{}}, // The Pit?
{"6-2", new List<int>{11}}, // There and Back Again
{"6-3", new List<int>{8, 10}}, // Phase?
{"6-4", new List<int>{4, 12, 1}}, // Cascade
{"6-5", new List<int>{3, 9}}, // Impassable Foliage
{"6-6", new List<int>{5, 2, 6}}, // Elevator Action
{"6-7", new List<int>{7}}, // In Another Castle
};
vars.completedLevels = new Dictionary<string, bool>(); // Name : completion
foreach (string level in vars.pieceMap.Keys) vars.completedLevels[level] = false;
vars.collectedPieces = new Dictionary<int, bool>(); // ID : state
vars.currentPieces = new Dictionary<int, DeepPointer>(); // ID : pointer
for (int world=2; world<=6; world++) {
for (int piece=1; piece<=12; piece++) {
vars.collectedPieces[vars.piecesBase + (0x18C * world) + (0x20 * piece)] = false;
}
}
}
isLoading {
return true; // Disable gameTime approximation
}
gameTime {
return TimeSpan.FromSeconds(current.gameFrames/60.0);
}
start {
if (old.gameFrames == 0 && current.gameFrames > 0) {
var pieces = new List<int>(vars.collectedPieces.Keys);
foreach (int piece in pieces) {
vars.collectedPieces[piece] = false;
}
var worlds = new List<string>(vars.completedLevels.Keys);
foreach (string world in worlds) {
vars.completedLevels[world] = false;
}
return true;
}
}
reset {
return old.gameFrames > 0 && current.gameFrames == 0;
}
split {
if (old.world != current.world || old.level != current.level) {
string oldName = old.world + "-" + old.level;
string currentName = current.world + "-" + current.level;
print("Changed from " + oldName + " to " + currentName);
int missingPieces = 0;
foreach (int piece in vars.currentPieces.Keys) {
if (vars.collectedPieces[piece] == false) {
missingPieces++;
}
}
print(oldName + " still has " + missingPieces + " missing pieces");
// Prepare for the next level
vars.currentPieces.Clear();
foreach (var puzzleId in vars.pieceMap[currentName]) {
int address = vars.piecesBase + (0x18C * current.world) + (0x20 * puzzleId);
vars.currentPieces[address] = new DeepPointer(address);
}
// Ensure that we don't try to split for a level again. This value will be reset
// to false if a piece is collected in the level.
if (vars.completedLevels[oldName] == false) {
vars.completedLevels[oldName] = true;
if (oldName == "0-8" && currentName == "0-0") { // Exiting the Epilogue
return true;
}
if (old.world != 0 && currentName == "0-0") { // Returning to the house
return settings["Split when returning to the house"];
}
if (oldName == "1-2" && currentName == "1-1") { // Entering Braid
return settings["Split when entering the Braid level"];
}
if (oldName == "1-1" && currentName == "0-8") { // Exiting Braid
return settings["Split when exiting the Braid level"];
}
if (oldName == "3-7" && currentName == "3-8") { // Exiting Lair (3)
return settings["Split when exiting Lair in World 3"];
}
// missingPieces was defined above, before we cleared vars.curentPieces
if (oldName == "2-2" && missingPieces == 2) {
return settings["Split when exiting The Cloud Bridge after collecting 2 pieces"];
}
if (oldName == "2-4" && missingPieces == 3) {
return settings["Split when exiting Leap of Faith after collecting 1 piece"];
}
if (oldName == "0-0") {
vars.completedLevels[oldName] = false;
return settings["Split when exiting the house"];
}
if (old.world == 0) {
return settings["Split when exiting lobby levels"];
}
if (missingPieces == 0) {
return settings["Split when completing and exiting a puzzle level"];
}
}
}
// The "game is paused for the flagpole" logic is re-used on the meta-puzzles,
// so ensure that we're actually in an end-of-world level
if (old.flagPole == false && current.flagPole == true) {
string currentName = current.world + "-" + current.level;
if (currentName == "2-4" ||
currentName == "3-8" ||
currentName == "4-7" ||
currentName == "5-7" ||
currentName == "6-7") {
return settings["Split when touching a flagpole"];
}
}
foreach (int piece in vars.currentPieces.Keys) {
if (vars.collectedPieces[piece] == false) {
if (vars.currentPieces[piece].Deref<bool>(game) == true) {
print("Collected piece 0x"+piece.ToString("X"));
vars.collectedPieces[piece] = true;
// Collected a piece so the world is not complete yet
vars.completedLevels[current.world + "-" + current.level] = false;
return settings["Split when initially collecting a piece"];
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment