state("SHOOT") { int roomId : 0x6561E0; string16 version : 0x00445C40, 0x60, 0x10, 0x2EC, 0x0, 0x0, 0x0; double microseconds : 0x00445C40, 0x60, 0x10, 0x13C, 0x40; double seconds : 0x00445C40, 0x60, 0x10, 0x13C, 0x50; double deaths : 0x00445C40, 0x60, 0x10, 0x13C, 0x60; double gameStarted : 0x00445C40, 0x60, 0x10, 0x13C, -48; //-0x30 // only any% double gameClear : 0x00445C40, 0x60, 0x10, 0x13C, -16; //-0x10 // 5 weapons, 4 in use byte80 unlockedWeapons : 0x00445C40, 0x60, 0x10, 0x220, 0x20, 0x4, 0x14, 0x100; // 50 gameflags ~39 in use // 16*50 = 800 byte800 gameFlags : 0x00445C40, 0x60, 0x10, 0xA3C, 0x20, 0x4, 0x14, 0x0; } startup { // ITEMS settings.Add("byItem", true, "Split Items"); settings.SetToolTip("byItem", "Split on weapons, items, and coins, including those that are stored as game flags."); settings.Add("revolver", true, "Revolver", "byItem"); // weapon 0 settings.Add("rocket", true, "Rocket Launcher", "byItem"); // weapon 3 settings.Add("railgun", true, "Railgun", "byItem"); // weapon 2 settings.Add("water", true, "Water Gun", "byItem"); // weapon 4 settings.Add("gunboats", false, "GunBoats", "byItem"); // flag 2 settings.Add("radio", false, "Pocket Radio", "byItem"); // flag 37 settings.Add("keycardBlue", false, "Keycard (Blue)", "byItem"); // flag 6 settings.Add("keycardRed", false, "Keycard (Red)", "byItem"); // flag 7 settings.Add("keycardGreen", false, "Keycard (Green)", "byItem"); // flag 8 settings.Add("greenCoins", true, "Green Coins", "byItem"); // flags 24-29 = 2 // ROOMS settings.Add("byRoom", true, "Split Important Rooms"); settings.SetToolTip("byRoom", "Split if the player's room ID matches that of important locations."); settings.Add("trash1", true, "Trash 1", "byRoom"); // rTrash1, 61 settings.Add("waterplant1", true, "Waterplant 1", "byRoom"); // rWaterplant1, 54 settings.Add("rooftops1", true, "Rooftops 1", "byRoom"); // rRooftops1, 97 settings.SetToolTip("rooftops1", "The room with the first save, not right out of the hotel."); settings.Add("climb1", true, "Hotel Climb 1", "byRoom"); // rClimb1, 130 settings.Add("bonusHub", false, "Bonus Hub", "byRoom"); // rBonusHub, 141 settings.Add("bonusRoomA", false, "Bonus Room A", "byRoom"); // rBonusRoomA, 142 / flag 24 = 1 settings.Add("bonusRoomB", false, "Bonus Room B", "byRoom"); // rBonusRoomB, 143 / flag 25 = 1 settings.Add("bonusRoomC", false, "Bonus Room C", "byRoom"); // rBonusRoomC, 144 / flag 26 = 1 settings.Add("bonusRoomD", false, "Bonus Room D", "byRoom"); // rBonusRoomD, 145 / flag 27 = 1 settings.Add("bonusRoomE", false, "Bonus Room E", "byRoom"); // rBonusRoomE, 146 / flag 28 = 1 settings.Add("bonusRoomF", false, "Bonus Room F", "byRoom"); // rBonusRoomF, 147 / flag 29 = 1 settings.Add("enterBoss1", false, "Enter Boss 1", "byRoom"); // rCityBoss, 33 settings.Add("beatBoss1", true, "Beat Boss 1", "byRoom"); // flag 1 settings.Add("enterBoss2", false, "Enter Boss 2", "byRoom"); // rTrashBoss, 89 settings.Add("beatBoss2", true, "Beat Boss 2", "byRoom"); // rTrashGravPostBoss, 90 vars.roomCount = 14; // MISCELLANEA settings.Add("byMisc", false, "Split on Miscellaneous"); settings.Add("activatePower", false, "Restore Power to City", "byMisc"); // flag 0 settings.Add("openBlueDoor", false, "Open Blue Door", "byMisc"); // flag 13 settings.Add("openRedDoor", false, "Open Red Door", "byMisc"); // flag 14 settings.Add("openGreenDoor", false, "Open Green Door", "byMisc"); // flag 12 settings.Add("openRevolverlessDoor", false, "Open Revolverless Door", "byMisc"); // flag 15 // ENDINGS settings.Add("byEnd", true, "Split for Ending"); settings.SetToolTip("byEnd", "Choose which ending(s) to split at."); settings.Add("sunsetEnding", true, "Sunset Ending", "byEnd"); // flag4, gameClear settings.Add("bonusEnding", true, "Bonus Ending", "byEnd"); settings.SetToolTip("bonusEnding", "Splits on entering the final room, rather than clear text."); // no cutscene flag on bonusEnding, rBonusPath2/3 (149, 150) // SPLITTER FUNCTIONALITY // derived from https://stackoverflow.com/questions/7832120 Func<byte[], double[]> ConvertBytes = data => { double[] doubles = new double[data.Length / 16]; for(int i = 0; i < doubles.Length; i++) doubles[i] = BitConverter.ToDouble(data, i * 16); return doubles; }; vars.ConvertBytes = ConvertBytes; } start { if (old.roomId != 14 && current.roomId == 14) // rCity1 return true; } onStart { // for not re-splitting if e.g. you die after collecting an item vars.noRepeatWeapons = new bool[6]; vars.noRepeatFlags = new bool[50]; vars.noRepeatRooms = new bool[vars.roomCount]; } reset { // return to title screen return (old.gameStarted == 1 && current.gameStarted == 0); } split { double[] oldFlags = vars.ConvertBytes(old.gameFlags); double[] newFlags = vars.ConvertBytes(current.gameFlags); int oldRoom = old.roomId; int newRoom = current.roomId; Func<int, bool> GetFlag = index => { return (oldFlags[index] == 0 && newFlags[index] == 1 && !vars.noRepeatFlags[index]); }; if (settings["byItem"]) { // weapons double[] oldWeapons = vars.ConvertBytes(old.unlockedWeapons); double[] newWeapons = vars.ConvertBytes(current.unlockedWeapons); for (int i = 0; i < oldWeapons.Length; i++) { if (i == 0 && !settings["revolver"]) continue; if (i == 3 && !settings["rocket"]) continue; if (i == 2 && !settings["railgun"]) continue; if (i == 4 && !settings["water"]) continue; if (oldWeapons[i] == 0 && newWeapons[i] == 1 && !vars.noRepeatWeapons[i]) { vars.noRepeatWeapons[i] = true; return true; } } // non-weapons { if (settings["gunboats"] && GetFlag(2)) { vars.noRepeatFlags[2] = true; return true; } if (settings["radio"] && GetFlag(37)) { vars.noRepeatFlags[37] = true; return true; } if (settings["keycardBlue"] && GetFlag(6)) { vars.noRepeatFlags[6] = true; return true; } if (settings["keycardRed"] && GetFlag(7)) { vars.noRepeatFlags[7] = true; return true; } if (settings["keycardGreen"] && GetFlag(8)) { vars.noRepeatFlags[8] = true; return true; } } // green coins if (settings["greenCoins"]) { if (oldFlags[24] == 1 && newFlags[24] == 2 && !vars.noRepeatFlags[24]) { vars.noRepeatFlags[24] = true; return true; } if (oldFlags[25] == 1 && newFlags[25] == 2 && !vars.noRepeatFlags[25]) { vars.noRepeatFlags[25] = true; return true; } if (oldFlags[26] == 1 && newFlags[26] == 2 && !vars.noRepeatFlags[26]) { vars.noRepeatFlags[26] = true; return true; } if (oldFlags[27] == 1 && newFlags[27] == 2 && !vars.noRepeatFlags[27]) { vars.noRepeatFlags[27] = true; return true; } if (oldFlags[28] == 1 && newFlags[28] == 2 && !vars.noRepeatFlags[28]) { vars.noRepeatFlags[28] = true; return true; } if (oldFlags[29] == 1 && newFlags[29] == 2 && !vars.noRepeatFlags[29]) { vars.noRepeatFlags[29] = true; return true; } } } if (settings["byRoom"]) { double[] rooms = new double[] { 61, 54, 97, 130, 141, 142, 143, 144, 145, 146, 147, 33, 89, 90 }; string[] roomOpts = new string[] { "trash1", "waterplant1", "rooftops1", "climb1", "bonusHub", "bonusRoomA", "bonusRoomB", "bonusRoomC", "bonusRoomD", "bonusRoomE", "bonusRoomF", "enterBoss1", "enterBoss2", "beatBoss2" }; for (int i = 0; i < rooms.Length; i++) { if (settings[roomOpts[i]] && oldRoom != rooms[i] && newRoom == rooms[i] && !vars.noRepeatRooms[i]) { vars.noRepeatRooms[i] = true; return true; } } if (settings["beatBoss1"] && GetFlag(1)) { vars.noRepeatFlags[1] = true; return true; } } if (settings["byMisc"]) { if (settings["activatePower"] && GetFlag(0)) { vars.noRepeatFlags[0] = true; return true; } if (settings["openBlueDoor"] && GetFlag(13)) { vars.noRepeatFlags[13] = true; return true; } if (settings["openRedDoor"] && GetFlag(14)) { vars.noRepeatFlags[14] = true; return true; } if (settings["openGreenDoor"] && GetFlag(12)) { vars.noRepeatFlags[12] = true; return true; } if (settings["openRevolverlessDoor"] && GetFlag(15)) { vars.noRepeatFlags[15] = true; return true; } } if (settings["byEnd"]) { if (settings["sunsetEnding"] && old.gameClear == 0 && current.gameClear == 1) { return true; } if (settings["bonusEnding"] && oldRoom != 149 && newRoom == 149) { return true; } } } gameTime { int sec = Convert.ToInt32(current.seconds); int milli = Convert.ToInt32(current.microseconds / 1000); return new TimeSpan(0, 0, 0, sec, milli); } isLoading { return true; }