public void Randomize(Patch patch, Random r) { // Create list of default teleporter position values List <byte[]> coords = new List <byte[]> { new byte[] { 0x20, 0x3B }, // Teleporter X, Y (top-left) new byte[] { 0x20, 0x7B }, new byte[] { 0x20, 0xBB }, new byte[] { 0x70, 0xBB }, new byte[] { 0x90, 0xBB }, new byte[] { 0xE0, 0x3B }, new byte[] { 0xE0, 0x7B }, new byte[] { 0xE0, 0xBB } }; // Randomize them coords.Shuffle(r); // Write the new x-coordinates for (int i = 0; i < coords.Count; i++) { byte[] location = coords[i]; patch.Add((int)(EMiscAddresses.WarpXCoordinateStartAddress + i), location[0], String.Format("Teleporter {0} X-Pos", i)); } // Write the new y-coordinates for (int i = 0; i < coords.Count; i++) { byte[] location = coords[i]; patch.Add((int)(EMiscAddresses.WarpYCoordinateStartAddress + i), location[1], String.Format("Teleporter {0} Y-Pos", i)); } // These values will be copied over to $04b0 (y) and $0470 (x), which will be checked // for in real time to determine where Mega will teleport to }
protected void ChangeMetal(Patch in_Patch, ISeed in_Seed) { // Unused addresses //0x02CC2D - Projectile type //0x02CC29 - Metal Blade sound effect 0x20 // Metalman AI //0x02CC3F - Speed of Metal blade 4, do 2 to 9 in_Patch.Add(0x02CC3F, in_Seed.NextUInt8(2, 10), "Metalman Projectile Velocity Integer"); //0x02CC1D - Odd change to attack behavior, 0x06, only if different than 6. Give 25% chance. if (in_Seed.NextDouble() > 0.75) { in_Patch.Add(0x02CC1D, 0x05, "Metalman Alternate Attack Behavior"); } //0x02CBB5 - Jump Height 1 0x06, do from 03 - 07 ? higher than 7 bonks ceiling //0x02CBB6 - Jump Height 2 0x05 //0x02CBB7 - Jump Height 3 0x04 // Shuffle the list of jump heights to get three different heights List <Byte> jumpHeight = in_Seed.Shuffle(new Byte[] { 3, 4, 5, 6, 7 }).ToList(); for (Int32 i = 0; i < 3; i++) { in_Patch.Add(0x02CBB5 + i, jumpHeight[i], String.Format("Metalman Jump {0} Y-Velocity Integer", i + 1)); } }
/// <summary> /// This method patches the third and fourth lines in the intro text. /// </summary> /// <remarks> /// Intro Screen Line 3: 0x036EE0 - 0x036EEA (11 chars) /// Intro Screen Line 4: 0x036EEE - 0x036F06 (25 chars) /// </remarks> public static void PatchForUse(Patch in_Patch, ISeed in_Seed) { const String INTRO_LINE3_PREFIX = "FOR USE "; const Int32 INTRO_LINE3_ADDRESS = 0x036EE0; const Int32 INTRO_LINE4_ADDRESS = 0x036EEE; CountryNameSet countryNameSet = Properties.Resources.CountryNameConfig.Deserialize <CountryNameSet>(); IEnumerable <CountryName> countryNames = countryNameSet.Where(x => true == x.Enabled); CountryName countryName = in_Seed.NextElement(countryNames); Int32 line3NextCharacterAddress = in_Patch.Add( INTRO_LINE3_ADDRESS, INTRO_LINE3_PREFIX.AsIntroString(), $"Splash Text: {INTRO_LINE3_PREFIX}"); in_Patch.Add( line3NextCharacterAddress, countryName.GetFormattedPrefix(), $"Splash Text: {countryName.Prefix}"); in_Patch.Add( INTRO_LINE4_ADDRESS, countryName.GetFormattedName(), $"Splash Text: {countryName.Name}"); }
public void Randomize(Patch in_Patch, ISeed in_Seed) { // Create list of default teleporter position values List <Position> DEFAULT_TELEPORTER_POSITIONS = new List <Position> { new Position(0x20, 0x3B), // Teleporter X, Y (top-left) new Position(0x20, 0x7B), new Position(0x20, 0xBB), new Position(0x70, 0xBB), new Position(0x90, 0xBB), new Position(0xE0, 0x3B), new Position(0xE0, 0x7B), new Position(0xE0, 0xBB), }; IList <Position> newTeleporterPositions = in_Seed.Shuffle(DEFAULT_TELEPORTER_POSITIONS); // Write the new x-coordinates for (Int32 index = 0; index < newTeleporterPositions.Count; ++index) { in_Patch.Add( (Int32)(EMiscAddresses.WarpXCoordinateStartAddress + index), newTeleporterPositions[index].X, String.Format("Teleporter {0} X-Pos", index)); in_Patch.Add( (Int32)(EMiscAddresses.WarpYCoordinateStartAddress + index), newTeleporterPositions[index].Y, String.Format("Teleporter {0} Y-Pos", index)); } // These values will be copied over to $04b0 (y) and $0470 (x), which will be checked // for in real time to determine where Mega will teleport to }
/// <summary> /// This method makes some preliminary modifications to the Mega Man 2 ROM to increase the enemy variety /// by changing the sprite banks used by certain rooms. /// </summary> public void ChangeRoomSpriteBankSlots(Patch p) { p.Add(0x00b444, 0x90, "Custom Sprite Bank: Wood 9th room: slot 3->? (0x90 special slot)"); p.Add(0x00b445, 0xa2, "Custom Sprite Bank: Wood 10th room: slot 3->? (0xa2 special slot)"); p.Add(0x00b446, 0x00, "Custom Sprite Bank: Wood 11th room: slot 3->0"); p.Add(0x01743e, 0x24, "Custom Sprite Bank: Flash 3rd room: slot 0->2"); p.Add(0x01f43d, 0x48, "Custom Sprite Bank: Clash 2nd room: slot 2->7"); }
public static void DrawTitleScreenChanges(Patch p, int seed, bool isTourney) { // Draw version number System.Reflection.Assembly assembly = System.Reflection.Assembly.GetAssembly(typeof(RandomMM2)); string version = assembly.GetName().Version.ToString(); for (int i = 0; i < version.Length; i++) { byte value = TitleChars.GetChar(version[i]).ID; p.Add(0x0373C7 + i, value, "Title Screen Version Number"); } // Draw seed string seedAlpha = SeedConvert.ConvertBase10To26(seed); for (int i = 0; i < seedAlpha.Length; i++) { char ch = seedAlpha.ElementAt(i); byte charIndex = (byte)(Convert.ToByte(ch) - Convert.ToByte('A')); // 'A' starts at C1 in the pattern table p.Add(0x037387 + i, (byte)(0xC1 + charIndex), "Title Screen Seed"); } // Draw flags // Draw tournament mode if (isTourney) { // 0x037367 = Start of row beneath "seed" string flagsAlpha = "TOURNAMENT"; for (int i = 0; i < flagsAlpha.Length; i++) { char ch = flagsAlpha.ElementAt(i); byte charIndex = (byte)(Convert.ToByte(ch) - Convert.ToByte('A')); p.Add(0x037564 + i, (byte)(0xC1 + charIndex), "Title Screen Tournament Text"); } string flags2Alpha = "MODE"; for (int i = 0; i < flags2Alpha.Length; i++) { char ch = flags2Alpha.ElementAt(i); byte charIndex = (byte)(Convert.ToByte(ch) - Convert.ToByte('A')); p.Add(0x03756F + i, (byte)(0xC1 + charIndex), "Title Screen Tournament Text"); } // Draw Hash symbols // Use $B8-$BF with custom gfx, previously unused tiles after converting from MM2U to RM2 //p.Add(0x037367, (byte)(0xB0), "Title Screen Flags"); //p.Add(0x037368, (byte)(0xB1), "Title Screen Flags"); //p.Add(0x037369, (byte)(0xB2), "Title Screen Flags"); //p.Add(0x03736A, (byte)(0xB3), "Title Screen Flags"); } }
protected void ChangeQuick(Patch in_Patch, ISeed in_Seed) { // Other addresses with potential: //0x02C872 - Projectile type, 0x59 // Quickman's AI //0x02C86E - Number of Boomerangs, 3, do from 1 - 10 in_Patch.Add(0x02C86E, in_Seed.NextUInt8(1, 11), "Quickman Number of Boomerangs"); //0x02C882 - Boomerang: delay before arc 37. 0 for no arc, or above like 53. do from 5 to 53. in_Patch.Add(0x02C882, in_Seed.NextUInt8(5, 54), "Quickman Boomerang Delay 1"); //0x02C887 - Boomerang speed when appearing, 4, do from 1 to 7 in_Patch.Add(0x02C887, in_Seed.NextUInt8(1, 8), "Quickman Boomerang Velocity Integer 1"); //0x03B726 - Boomerang speed secondary, 4, does this affect anything else? in_Patch.Add(0x03B726, in_Seed.NextUInt8(1, 8), "Quickman Boomerang Velocity Integer 2"); // For all jumps, choose randomly from 2 to 10 //0x02C8A3 - Middle jump, 7 in_Patch.Add(0x02C8A3, in_Seed.NextUInt8(2, 11), "Quickman Jump Height 1 Integer"); //0x02C8A4 - High jump, 8 in_Patch.Add(0x02C8A4, in_Seed.NextUInt8(2, 11), "Quickman Jump Height 2 Integer"); //0x02C8A5 - Low jump, 4 in_Patch.Add(0x02C8A5, in_Seed.NextUInt8(2, 11), "Quickman Jump Height 3 Integer"); //0x02C8E4 - Running time, 62, do from 24 to 80 in_Patch.Add(0x02C8E4, in_Seed.NextUInt8(24, 81), "Quickman Running Time"); //0x02C8DF - Running speed, 2, do from 1 to 5 in_Patch.Add(0x02C8DF, in_Seed.NextUInt8(1, 6), "Quickman Running Velocity Integer"); }
/// <summary> /// Shuffle which Robot Master awards which weapon. /// </summary> public void Randomize(Patch in_Patch, ISeed in_Seed) { IList <BossRoomRandomComponent> bossRoomComponents = in_Seed.Shuffle(this.Components); //DEBUG test a boss in a particular boss room, also comment out the corresponding boss from the Components list above //Components.Insert(3, BubbleManComponent); // Write in new boss positions for (Int32 i = 0; i < 8; i++) { BossRoomRandomComponent bossroom = bossRoomComponents[i]; in_Patch.Add(0x02C15E + i, bossroom.IntroValue, $"Boss Intro Value for Boss Room {i}"); in_Patch.Add(0x02C057 + i, bossroom.AIPtrByte1, $"Boss AI Ptr Byte1 for Boss Room {i}"); in_Patch.Add(0x02C065 + i, bossroom.AIPtrByte2, $"Boss AI Ptr Byte2 for Boss Room {i}"); in_Patch.Add(0x02E4E9 + i, bossroom.GfxFix1, $"Boss GFX Fix 1 for Boss Room {i}"); in_Patch.Add(0x02C166 + i, bossroom.GfxFix1, $"Boss GFX Fix 2 for Boss Room {i}"); in_Patch.Add(0x02C14E + i, bossroom.YPosFix1, $"Boss Y-Pos Fix1 for Boss Room {i}"); in_Patch.Add(0x02C156 + i, bossroom.YPosFix2, $"Boss Y-Pos Fix2 for Boss Room {i}"); } // Adjust sprite banks for each boss room Int32[] spriteBankBossRoomAddresses = { 0x0034A6, // Heat room 0x0074A6, // Air room 0x00B4DC, // Wood room 0x00F4A6, // Bubble room 0x0134B8, // Quick room 0x0174A6, // Flash room 0x01B494, // Metal room 0x01F4DC, // Clash room }; for (Int32 i = 0; i < spriteBankBossRoomAddresses.Length; i++) { for (Int32 j = 0; j < bossRoomComponents[i].SpriteBankSlotRowsBytes.Length; j++) { in_Patch.Add(spriteBankBossRoomAddresses[i] + j, bossRoomComponents[i].SpriteBankSlotRowsBytes[j], $"Boss Room {i} Sprite Bank Swap {j}"); } } // Undo shuffling of damage values for each boss room Int32 contactDmgTbl = 0x2E9C2; Byte[] originalDmgVals = new Byte[] { 08, 08, 08, 04, 04, 04, 06, 04 }; Byte[] newDmgVals = new Byte[8]; for (Int32 i = 0; i < bossRoomComponents.Count; i++) { newDmgVals[i] = originalDmgVals[bossRoomComponents[i].OriginalBossIndex]; in_Patch.Add(contactDmgTbl + i, newDmgVals[i]); } this.Components = bossRoomComponents; }
/// <summary> /// Heatman AI 0x02C16E - 0x02C1FE /// </summary> protected void ChangeHeat(Patch in_Patch, ISeed in_Seed) { // // Projectile Y distances // //0x02C207 default 07, good from 03 - 08 in_Patch.Add(0x02C207, in_Seed.NextUInt8(3, 9), "Heatman Projectile 1 Y-Distance"); //0x02C208 default 05, good from 04 - 07 in_Patch.Add(0x02C208, in_Seed.NextUInt8(4, 8), "Heatman Projectile 2 Y-Distance"); //0x02C209 default 03, good from 03 - 05 in_Patch.Add(0x02C209, in_Seed.NextUInt8(3, 6), "Heatman Projectile 3 Y-Distance"); // // Projectile x distances, 0x3A 0x2E 0x1C // // The lower value, the faster speed. Different for each fireball. //0x02C20A - 1st value should be 71 to hit megaman, or, from 48 to 128 in_Patch.Add(0x02C20A, in_Seed.NextUInt8(48, 129), "Heatman Projectile 1 X-Distance"); //0x02C20B - 2nd value should be 46 to hit megaman, 0r, from 34 to 64 in_Patch.Add(0x02C20B, in_Seed.NextUInt8(34, 65), "Heatman Projectile 2 X-Distance"); //0x02C20C - 3rd value should be 23 to hit megaman, or, from 16 to 48 in_Patch.Add(0x02C20C, in_Seed.NextUInt8(16, 49), "Heatman Projectile 3 X-Distance"); // // 30/60/90 frame delay // Choose delay interval from 10-40 frames // Byte delay = in_Seed.NextUInt8(10, 41); //0x02C29D - Delay 1 0x1F in_Patch.Add(0x02C29D, delay, "Heatman Invuln Delay 1"); //0x02C29E - Delay 2 0x3E in_Patch.Add(0x02C29E, (Byte)(delay * 2), "Heatman Invuln Delay 2"); //0x02C29F - Delay 3 0x5D in_Patch.Add(0x02C29F, (Byte)(delay * 3), "Heatman Invuln Delay 3"); // //0x02C253 - Charge velocity(0x04, 0x08 or more usually puts him on side of screen) // in_Patch.Add(0x02C253, in_Seed.NextUInt8(2, 6), "Heatman Charge Velocity"); }
protected void ChangeBubble(Patch Patch, Random r) { //0x03D4AB - B x - speed on shoot (0x01) (do 1-3) int xVelShoot = r.Next(0x03) + 0x01; Patch.Add(0x03D4AB, (byte)xVelShoot, "(B) | X-Velocity Shoot (Integer)"); //0x03D4CF - B y - speed on shoot(0x02) (do 0-6) int yVelShoot = r.Next(0x06); Patch.Add(0x03D4CF, (byte)yVelShoot, "(B) | Y-Velocity Shoot (Integer)"); //0x03DB21 - B max shots (0x03) (do 2-5, i.e. 1-4 total projectiles) // Valid from 0x02 - 0x0F. Lags a bunch >= 0x06. int maxShots = r.Next(0x04) + 0x02; Patch.Add(0x03DB21, (byte)maxShots, "(B) | Max Shots"); //0x03DB2F - B weapon type(0x04) // Don't do //0x03DB34 - B sound effect ESoundID sound = GetRandomSound(r); Patch.Add(0x03DB34, (byte)sound, "(B) | Sound"); //0x03DB3D - B shots per ammo tick (0x02) (do 1-4) int magSize = r.Next(0x04) + 0x01; Patch.Add(0x03DB3D, (byte)magSize, "(B) | Shots Per Ammo Tick"); AmmoUsage.Add(1d / (double)magSize); //0x03DFA4 - B y - pos to embed in surface(0xFF) // Dumb //0x03DFA9 - B x - speed on surface (0x02) // 0x01 - 0x04 ? int xVelRoll = r.Next(0x04) + 0x01; Patch.Add(0x03DFA9, (byte)xVelRoll, "(B) | X-Velocity Surface (Integer)"); //0x03DFC0 - B x - speed after falling from ledge (0x00) // Make 50% chance to be 0, or 1-5 int xVelFall = 0x00; double rTestXFallSpeed = r.NextDouble(); if (rTestXFallSpeed > 0.5) { xVelFall = r.Next(0x05) + 0x01; } Patch.Add(0x03DFC0, (byte)xVelFall, "(B) | X-Velocity Fall (Integer)"); //0x03DFC8 - B y - speed after falling(0xFE) // Either 0xFA - 0xFF or 0x01 - 0x06 int[] yFallVels = new int[] { 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; int rIndex = r.Next(yFallVels.Length); int yFallVel = yFallVels[rIndex]; Patch.Add(0x03DFC8, (byte)yFallVel, "(B) | Y-Velocity Fall (Integer)"); }
/// <summary> /// /// </summary> /// <param name="p"></param> /// <param name="jVersion"></param> public static void SetWily5NoMusicChange(Patch p) { p.Add(0x0383DA, 0xEA, "Disable Music on Boss Defeat 1"); p.Add(0x0383DB, 0xEA, "Disable Music on Boss Defeat 2"); p.Add(0x0383DC, 0xEA, "Disable Music on Boss Defeat 3"); p.Add(0x03848A, 0xEA, "Disable Music on Boss Defeat 4"); p.Add(0x03848B, 0xEA, "Disable Music on Boss Defeat 5"); p.Add(0x03848C, 0xEA, "Disable Music on Boss Defeat 6"); p.Add(0x02E070, 0xEA, "Disable Music on Boss Defeat 7"); p.Add(0x02E071, 0xEA, "Disable Music on Boss Defeat 8"); p.Add(0x02E072, 0xEA, "Disable Music on Boss Defeat 9"); }
/// <summary> /// Split an existing patch in two. That is, creates a new patch (k) based on an existing one (j) /// </summary> /// <param name="j">The j.</param> private void SplitPatch(int j) { // create new patch soilCNPatch newPatch = new soilCNPatch(this); Patch.Add(newPatch); int k = Patch.Count - 1; // set the size of arrays Patch[k].ResizeLayerArrays(dlayer.Length); // set C and N variables to the same state as the 'mother' patch for (int layer = 0; layer < dlayer.Length; layer++) { Patch[k].urea[layer] = Patch[j].urea[layer]; Patch[k].nh4[layer] = Patch[j].nh4[layer]; Patch[k].no3[layer] = Patch[j].no3[layer]; Patch[k].inert_c[layer] = Patch[j].inert_c[layer]; Patch[k].biom_c[layer] = Patch[j].biom_c[layer]; Patch[k].biom_n[layer] = Patch[j].biom_n[layer]; Patch[k].hum_c[layer] = Patch[j].hum_c[layer]; Patch[k].hum_n[layer] = Patch[j].hum_n[layer]; Patch[k].fom_c_pool1[layer] = Patch[j].fom_c_pool1[layer]; Patch[k].fom_c_pool2[layer] = Patch[j].fom_c_pool2[layer]; Patch[k].fom_c_pool3[layer] = Patch[j].fom_c_pool3[layer]; Patch[k].fom_n_pool1[layer] = Patch[j].fom_n_pool1[layer]; Patch[k].fom_n_pool2[layer] = Patch[j].fom_n_pool2[layer]; Patch[k].fom_n_pool3[layer] = Patch[j].fom_n_pool3[layer]; } // store today's values Patch[k].InitCalc(); }
public static void FixM445PaletteGlitch(Patch p) { for (int i = 0; i < 3; i++) { p.Add(0x395BD + i, 0xEA, "M-445 Palette Glitch Fix"); } }
/// <summary> /// Shuffle which Robot Master awards Items 1, 2, and 3. /// </summary> public void Randomize(Patch patch, Random r) { // 0x03C291 - Item # from Heat Man // 0x03C292 - Item # from Air Man // 0x03C293 - Item # from Wood Man // 0x03C294 - Item # from Bubble Man // 0x03C295 - Item # from Quick Man // 0x03C296 - Item # from Flash Man // 0x03C297 - Item # from Metal Man // 0x03C298 - Item # from Crash Man List <EItemNumber> newItemOrder = new List <EItemNumber>(); for (byte i = 0; i < 5; i++) { newItemOrder.Add(EItemNumber.None); } newItemOrder.Add(EItemNumber.One); newItemOrder.Add(EItemNumber.Two); newItemOrder.Add(EItemNumber.Three); newItemOrder.Shuffle(r); for (int i = 0; i < 8; i++) { patch.Add((int)EItemStageAddress.HeatMan + i, (byte)newItemOrder[i], String.Format("{0}man Item Get", ((EDmgVsBoss.Offset)i).ToString())); } }
/// <summary> /// Shuffle which Robot Master awards Items 1, 2, and 3. /// </summary> public void Randomize(Patch in_Patch, ISeed in_Seed) { // 0x03C291 - Item # from Heat Man // 0x03C292 - Item # from Air Man // 0x03C293 - Item # from Wood Man // 0x03C294 - Item # from Bubble Man // 0x03C295 - Item # from Quick Man // 0x03C296 - Item # from Flash Man // 0x03C297 - Item # from Metal Man // 0x03C298 - Item # from Crash Man List <EItemNumber> itemGetList = new List <EItemNumber>() { EItemNumber.None, EItemNumber.None, EItemNumber.None, EItemNumber.None, EItemNumber.None, EItemNumber.One, EItemNumber.Two, EItemNumber.Three, }; IList <EItemNumber> itemGetOrder = in_Seed.Shuffle(itemGetList); for (Int32 index = 0; index < itemGetOrder.Count; ++index) { in_Patch.Add( (Int32)EItemStageAddress.HeatMan + index, (Byte)itemGetOrder[index], String.Format("{0}man Item Get", ((EDmgVsBoss.Offset)index).ToString())); } }
/// <summary> /// TODO /// </summary> public static void SetFastText(Patch p) { //int address = (jVersion) ? 0x037C51 : 0x037D4A; int address = 0x037D4A; p.Add(address, 0x04, "Weapon Get Text Write Delay"); }
public void FixWeaponLetters(Patch p, int[] permutation) { // Re-order the letters array to match the ordering of the shuffled weapons char[] newLettersPermutation = new char[9]; newLettersPermutation[0] = newWeaponLetters[0]; for (int i = 0; i < 8; i++) { newLettersPermutation[i + 1] = newWeaponLetters[permutation[i] + 1]; } // Write new weapon letters to weapon get screen for (int i = 1; i < 9; i++) { // Write to Weapon Get screen (note: Buster value is unused here) int newLetter = 0x41 + Alphabet.IndexOf(newLettersPermutation[i]); // unicode p.Add(offsetWpnGetLetters + i - 1, (byte)newLetter, $"Weapon Get {((EDmgVsBoss.Offset)i).Name} Letter: {newWeaponLetters[i]}"); } //// Write new weapon letters to pause menu //for (int i = 0; i < 9; i++) //{ //int[] pauseLetterBytes = PauseScreenCipher[newWeaponLetters[i + 1]]; //int wpnLetterAddress = PauseScreenWpnAddressByBossIndex[permutedIndex + 1]; //for (int j = 0; j < pauseLetterBytes.Length; j++) //{ // p.Add(wpnLetterAddress + j, (byte)pauseLetterBytes[j], $"Pause menu weapon letter GFX for \'{newWeaponLetters[permutedIndex]}\', byte #{j}"); //} //} }
protected void ChangeCrash(Patch in_Patch, ISeed in_Seed) { //0x03D4AD - C x-speed on shoot (04) (do 2-7) Int32 xVel = in_Seed.NextInt32(0x06) + 0x02; in_Patch.Add(0x03D4AD, (Byte)xVel, "(C) | X-Velocity (Integer)"); //0x03D4D7 - C y-speed integer for explosion (up only) (0) // TODO: Figure how this works more to apply in all directions // For now, 25% chance to make this move upward at 2px/fr Int32 yVelExplode = 0x00; Double rTestYVelExplode = in_Seed.NextDouble(); if (rTestYVelExplode > 0.75) { yVelExplode = in_Seed.NextInt32(2) + 0x01; } in_Patch.Add(0x03D4D7, (Byte)yVelExplode, "(C) | X-Velocity (Explosion)"); //0x03DB99 - C ammo per shot (04) (do 1-3) Int32 ammoUse = in_Seed.NextInt32(0x03) + 0x01; in_Patch.Add(0x03DB99, (Byte)ammoUse, "(C) | Ammo Usage"); AmmoUsage.Add(ammoUse); // 0x03DB9F - C explosion type? (02) // Change to 03 to "single explosion" type. Most other values break the game. // For now, 50% chance to change Int32 multiExplode = 0x02; Double rTestMultiExplode = in_Seed.NextDouble(); if (rTestMultiExplode > 0.50) { multiExplode = 0x03; } in_Patch.Add(0x03DB9F, (Byte)multiExplode, "(C) | Explosion Type"); //0x03DBA6 - C shoot sound effect (24) ESoundID sound = this.GetRandomSound(in_Seed); in_Patch.Add(0x03DBA6, (Byte)sound, "(C) | Sound Shoot"); //0x03E089 - C attach sound effect (2E) sound = this.GetRandomSound(in_Seed); in_Patch.Add(0x03E089, (Byte)sound, "(C) | Sound Attach"); //0x03E09C - C delay before explosion (7E) (do 01 to C0) Int32 delayExplosion = in_Seed.NextInt32(0xBF) + 0x01; in_Patch.Add(0x03E09C, (Byte)delayExplosion, "(C) | Explode Delay"); //0x03E0DA - C explode sound effect sound = this.GetRandomSound(in_Seed); in_Patch.Add(0x03E0DA, (Byte)sound, "(C) | Sound Explode"); }
/// <summary> /// This will change the delay defeating a boss and teleporting out of the field to be much shorter. /// The victory fanfare will not play, and you teleport out exactly 10 frames after landing the killing /// blow on a robot master, and faster for Wily bosses as well. This indirectly fixes the issue of /// potentially zipping out of Bubbleman or other robot masters' chambers, since you teleport immediately. /// </summary> /// <param name="p"></param> public static void SetFastBossDefeatTeleport(Patch p) { // 0x02E0AF: Time until teleport after fanfare starts. ($FD, change to $40) // 0x02E0A2: Time until boss-defeat fanfare starts. Note that if set too low without any additional // changes, a softlock may occur after some Wily bosses. Change from $FD to $10, then // modify other areas that set the intial value of $05A7 (address storing our comparison). // It turns out taht Mechadragon, Picopico-kun, and Gutsdozer set the intial value to $70 // (at 0x02D16F). Buebeam has its own special routine with extra explosions, setting the // initial value to $80 (at 0x02D386). Wily Machine and Alien do not call these subroutines // and no further modification is needed. // 0x02D170: Wily 1/2/3 boss defeat, time until fanfare starts. ($70, change to $10) // Must be less or equal to value above (at 0x02E0A2) // 0x02D386: Buebeam defeat, time until fanfare starts. ($80 change to $10) // Must be less or equal to value above (at 0x02E0A2) // // The original subroutine that uses 0x02E0A2 is as follows: // // BossDefeatWaitForTeleport: // 0B:A08B: 4E 21 04 LSR $0421 // Not sure what this is for, but it gets zeroed out after a couple loops // 0B:A08E: AD A7 05 LDA $05A7 // $05A7 frequently stores a frame-counter or a 'state' value // 0B:A091: C9 10 CMP #$FD // Compare value at $05A7 with 0xFD // 0B:A093: B0 04 BCS PlayFanfare_ThenWait // If value at $05A7 >= 0xFD, jump to PlayFanfare_ThenWait // 0B:A095: EE A7 05 INC $05A7 // Increase value at $05A7 by 1 // 0B:A098: 60 RTS // Return // PlayFanfare_ThenWait: // 0B:A099 // Play fanfare once, then wait to teleport.... // ... // // When defeating Wily 1, 2, 3, or 4, the BossDefeatWaitForTeleport subroutine is entered for the first time // with $05A7 having a value of 0x70 or 0x80; if you change the comparison value at $2E0A2 from 0xFD to a // value smaller than the intial $05A7, an infinite loop occurs. p.Add(0x02E0AF, 0x40, "Fast Boss Defeat Teleport: Teleport delay after fanfare"); p.Add(0x02E0A2, 0x10, "Fast Boss Defeat Teleport: Global delay before fanfare"); p.Add(0x02D170, 0x10, "Fast Boss Defeat Teleport: W1/2/3 boss delay before fanfare"); p.Add(0x02D386, 0x10, "Fast Boss Defeat Teleport: W4 boss delay before fanfare"); // Also, NOP out the code that plays the fanfare. It's too distorted sounding when immediately teleporting. // Or TODO in the future, change to a different sound? // 02E0B3: A9 15 LDA #$15 // Let A = the fanfare sound value (15) // 02E0B5: 20 51 C0 JSR PlaySound // Jump to "PlaySound" function, which plays the value in A for (int i = 0; i < 5; i++) { p.Add(0x02E0B3 + i, 0xEA, "Fast Boss Defeat Teleport: Fanfare sound NOP"); } }
protected void ChangeFlash(Patch in_Patch, ISeed in_Seed) { // Unused addresses //0x02CA71 - Projectile type 0x35 //0x02CA52 - "Length of time stopper / projectile frequency ?" // Flashman's AI //0x02C982 - Walk velocity integer 1, do from 0 to 3 in_Patch.Add(0x02C982, in_Seed.NextUInt8(4), "Flashman Walk Velocity Integer"); //0x02C97D - Walk velocity fraction 6, do 0 to 255 in_Patch.Add(0x02C97D, in_Seed.NextUInt8(256), "Flashman Walk Velocity Fraction"); //0x02C98B - Delay before time stopper 187 frames. Do from 30 frames to 240 frames in_Patch.Add(0x02C98B, in_Seed.NextUInt8(30, 241), "Flashman Delay Before Time Stopper"); //0x02CAC6 - Jump distance integer 0, do 0 to 3 // TODO do fraction also in_Patch.Add(0x02CAC6, in_Seed.NextUInt8(4), "Flashman Jump X-Velocity Integer"); //0x02CACE - Jump height 4, do 3 to 8 in_Patch.Add(0x02CACE, in_Seed.NextUInt8(3, 9), "Flashman Jump Y-Velocity Integer"); //0x02CA81 - Projectile speed 8, do 2 to 10 in_Patch.Add(0x02CA81, in_Seed.NextUInt8(2, 11), "Flashman Projectile Velocity Integer"); //0x02CA09 - Number of projectiles to shoot 6, do 3 to 16 in_Patch.Add(0x02CA09, in_Seed.NextUInt8(3, 17), "Flashman Number of Projectiles"); }
public void RandomizeAndWrite(Patch patch, Random rand, int setNumber) { Index = rand.Next(ColorBytes.Count); for (int i = 0; i < addresses.Length; i++) { patch.Add(addresses[i], (byte)ColorBytes[Index][i], String.Format("Color Set {0} (Index Chosen: {1}) Value #{2}", setNumber, Index, i)); } }
public void OldRando(Patch p, Random r) { List <EMusicID> newBGMOrder = new List <EMusicID>(); List <EMusicID> robos = new List <EMusicID>(); // Select 2 replacement tracks for the 2 extra instance of the boring W3/4/5 theme robos.Add(EMusicID.Flash); robos.Add(EMusicID.Heat); robos.Add(EMusicID.Air); robos.Add(EMusicID.Wood); robos.Add(EMusicID.Quick); robos.Add(EMusicID.Metal); robos.Add(EMusicID.Clash); robos.Add(EMusicID.Bubble); robos.Shuffle(r); newBGMOrder.Add(EMusicID.Flash); newBGMOrder.Add(EMusicID.Heat); newBGMOrder.Add(EMusicID.Air); newBGMOrder.Add(EMusicID.Wood); newBGMOrder.Add(EMusicID.Quick); newBGMOrder.Add(EMusicID.Metal); newBGMOrder.Add(EMusicID.Clash); newBGMOrder.Add(EMusicID.Bubble); newBGMOrder.Add(EMusicID.Wily12); newBGMOrder.Add(EMusicID.Wily12); // Wily 1/2 track will play twice newBGMOrder.Add(EMusicID.Wily345); // Wily 3/4/5 track only plays once newBGMOrder.Add(robos[0]); // Add extra Robot Master tracks to the set newBGMOrder.Add(robos[1]); // Randomize tracks newBGMOrder.Shuffle(r); // Start writing at Heatman BGM ID, both J and U // Loop through BGM addresses Heatman to Wily 5 (Wily 6 still silent) for (int i = 0; i < newBGMOrder.Count; i++) { EMusicID bgm = newBGMOrder[i]; p.Add(0x0381E0 + i, (byte)bgm, String.Format("BGM Stage {0}", i)); } // Finally, fix Wily 5 track when exiting a Teleporter to be the selected Wily 5 track instead of default p.Add(0x038489, (byte)newBGMOrder.Last(), "BGM Wily 5 Teleporter Exit Fix"); }
protected void ChangeHeat(Patch Patch, Random r) { int rInt = 0; // Heatman AI 0x02C16E - 0x02C1FE // projectile y - distances //0x02C207 default 07, good from 03 - 08 //0x02C208 default 05, good from 04 - 07 //0x02C209 default 03, good from 03 - 05 rInt = r.Next(6) + 0x03; Patch.Add(0x02C207, (byte)rInt, "Heatman Projectile 1 Y-Distance"); rInt = r.Next(4) + 0x04; Patch.Add(0x02C208, (byte)rInt, "Heatman Projectile 2 Y-Distance"); rInt = r.Next(3) + 0x03; Patch.Add(0x02C209, (byte)rInt, "Heatman Projectile 3 Y-Distance"); // projectile x-distances, 0x3A 0x2E 0x1C // - The lower value, the faster speed. Different for each fireball. //0x02C20A - 1st value should be 0x47 to hit megaman, Or, from 0x30 to 0x80 //0x02C20B - 2nd value should be 0x2E to hit megaman. Or, from 0x22 to 0x40 //0x02C20C - 3rd value should be 0x17 to hit megaman, Or, from 0x10 to 0x30 rInt = r.Next(0x80 - 0x30 + 1) + 0x30; Patch.Add(0x02C20A, (byte)rInt, "Heatman Projectile 1 X-Distance"); rInt = r.Next(0x40 - 0x22 + 1) + 0x22; Patch.Add(0x02C20B, (byte)rInt, "Heatman Projectile 2 X-Distance"); rInt = r.Next(0x30 - 0x10 + 1) + 0x10; Patch.Add(0x02C20C, (byte)rInt, "Heatman Projectile 3 X-Distance"); // 30/60/90 frame delay //0x02C29D - Delay 1 0x1F //0x02C29E - Delay 2 0x3E //0x02C29F - Delay 3 0x5D // Choose delay interval from 10-40 frames rInt = r.Next(31) + 10; Patch.Add(0x02C29D, (byte)rInt, "Heatman Invuln Delay 1"); Patch.Add(0x02C29E, (byte)(rInt * 2), "Heatman Invuln Delay 2"); Patch.Add(0x02C29F, (byte)(rInt * 3), "Heatman Invuln Delay 3"); //0x02C253 - Charge velocity(0x04, 0x08 or more usually puts him on side of screen) rInt = r.Next(4) + 0x02; Patch.Add(0x02C253, (byte)rInt, "Heatman Charge Velocity"); }
private static void ChangeW4FloorsSpikePit(Patch Patch, Random r) { // 5 tiles, but since two adjacent must construct a gap, 4 possible gaps. Choose 1 random gap. int gap = r.Next(4); for (int i = 0; i < 4; i++) { if (i == gap) { Patch.Add(0x00CB9A + i * 8, 0x9B, String.Format("Wily 4 Room 5 Tile {0} (gap on right)", i)); Patch.Add(0x00CB9A + i * 8 + 8, 0x9C, String.Format("Wily 4 Room 5 Tile {0} (gap on left)", i)); ++i; // skip next tile since we just drew it } else { Patch.Add(0x00CB9A + i * 8, 0x9D, String.Format("Wily 4 Room 5 Tile {0} (solid)", i)); } } }
public void RandomizeAndWrite(Patch in_Patch, ISeed in_Seed, Int32 setNumber) { this.Index = in_Seed.NextInt32(ColorBytes.Count); for (Int32 i = 0; i < this.addresses.Length; i++) { in_Patch.Add( this.addresses[i], (Byte)this.ColorBytes[this.Index][i], String.Format("Color Set {0} (Index Chosen: {1}) Value #{2}", setNumber, Index, i)); } }
// // Private Static Methods // /// <summary> /// This method patches the company name in the intro screen. /// </summary> /// <remarks> /// Intro Screen Line 1: 0x036EA8 - 0x036EBA (19 chars) /// ©2017 <company name> (13 chars for company, 19 total) /// </remarks> public static void PatchCompanyName(Patch in_Patch, CompanyName in_CompanyName) { const Int32 MAX_LINE_LENGTH = 19; const Int32 INTRO_LINE1_ADDRESS = 0x036EA8; String line = $"©{DateTime.Now.Year} {in_CompanyName.GetCompanyName()}".PadCenter(MAX_LINE_LENGTH); in_Patch.Add( INTRO_LINE1_ADDRESS, line.AsIntroString(), $"Splash Text: {line}"); }
/// <summary> /// TODO /// </summary> public static void SetBurstChaser(Patch p, bool jVersion) { p.Add(0x038147, 0x60, "READY Text Delay"); p.Add(0x038921, 0x03, "Mega Man Walk X-Velocity Integer"); p.Add(0x03892C, 0x00, "Mega Man Walk X-Velocity Fraction"); p.Add(0x038922, 0x03, "Mega Man Air X-Velocity Integer"); p.Add(0x03892D, 0x00, "Mega Man Air X-Velocity Fraction"); p.Add(0x0386EF, 0x01, "Mega Man Ladder Climb Up Integer"); p.Add(0x03872E, 0xFE, "Mega Man Ladder Climb Down Integer"); int address = (jVersion) ? 0x03D4A4 : 0x03D4A7; p.Add(address, 0x08, "Buster Projectile X-Velocity Integer"); }
protected void ChangeQuick(Patch Patch, Random r) { int rInt; // Other addresses with potential: //0x02C872 - Projectile type, 0x59 // Quickman's AI //0x02C86E - Number of Boomerangs, 0x03, do from 1 - 0x0A rInt = r.Next(0x0B) + 0x01; Patch.Add(0x02C86E, (byte)rInt, "Quickman Number of Boomerangs"); //0x02C882 - Boomerang: delay before arc 0x25. 0 for no arc, or above like 0x35. do from 5 to 0x35. rInt = r.Next(0x35 - 0x05 + 1) + 0x05; Patch.Add(0x02C882, (byte)rInt, "Quickman Boomerang Delay 1"); //0x02C887 - Boomerang speed when appearing, 0x04, do from 0x01 to 0x07 rInt = r.Next(0x07 - 0x01 + 1) + 0x01; Patch.Add(0x02C887, (byte)rInt, "Quickman Boomerang Velocity Integer 1"); //0x03B726 - Boomerang speed secondary, 0x04, does this affect anything else? rInt = r.Next(0x07 - 0x01 + 1) + 0x01; Patch.Add(0x03B726, (byte)rInt, "Quickman Boomerang Velocity Integer 2"); // For all jumps, choose randomly from 0x02 to 0x0A //0x02C8A3 - Middle jump, 0x07 rInt = r.Next(0x0A - 0x02 + 1) + 0x02; Patch.Add(0x02C8A3, (byte)rInt, "Quickman Jump Height 1 Integer"); //0x02C8A4 - High jump, 0x08 rInt = r.Next(0x0A - 0x02 + 1) + 0x02; Patch.Add(0x02C8A4, (byte)rInt, "Quickman Jump Height 2 Integer"); //0x02C8A5 - Low jump, 0x04 rInt = r.Next(0x0A - 0x02 + 1) + 0x02; Patch.Add(0x02C8A5, (byte)rInt, "Quickman Jump Height 3 Integer"); //0x02C8E4 - Running time, 0x3E, do from 0x18 to 0x50 rInt = r.Next(0x50 - 0x18 + 1) + 0x18; Patch.Add(0x02C8E4, (byte)rInt, "Quickman Running Time"); //0x02C8DF - Running speed, 0x02, do from 0x05 to 0x01 rInt = r.Next(0x05 - 0x01 + 1) + 0x01; Patch.Add(0x02C8DF, (byte)rInt, "Quickman Running Velocity Integer"); }
/// <summary> /// This method patches the intro story text. /// </summary> /// <remarks> /// Intro Story: 0x036D56 - 0x036E64 (270 chars) /// 27 characters per line /// 10 lines /// </remarks> public static void PatchIntroStory(Patch in_Patch, ISeed in_Seed) { const Int32 INTRO_STORY_PAGE1_ADDRESS = 0x036D56; IntroStorySet introStorySet = Properties.Resources.IntroStoryConfig.Deserialize <IntroStorySet>(); IEnumerable <IntroStory> introStories = introStorySet.Where(x => true == x.Enabled); IntroStory introStory = in_Seed.NextElement(introStories); in_Patch.Add( INTRO_STORY_PAGE1_ADDRESS, introStory.GetFormattedString(), $"Intro Text: {introStory.Title}"); }
protected void ChangeMetal(Patch Patch, Random r) { int rInt; double rDbl; // Unused addresses //0x02CC2D - Projectile type //0x02CC29 - Metal Blade sound effect 0x20 // Metalman AI //0x02CC3F - Speed of Metal blade 0x04, do 2 to 9 rInt = r.Next(8) + 2; Patch.Add(0x02CC3F, (byte)rInt, "Metalman Projectile Velocity Integer"); //0x02CC1D - Odd change to attack behavior, 0x06, only if different than 6. Give 25% chance. rDbl = r.NextDouble(); if (rDbl > 0.75) { Patch.Add(0x02CC1D, 0x05, "Metalman Alternate Attack Behavior"); } //0x02CBB5 - Jump Height 1 0x06, do from 03 - 07 ? higher than 7 bonks ceiling //0x02CBB6 - Jump Height 2 0x05 //0x02CBB7 - Jump Height 3 0x04 List <int> jumpHeight = new List <int>() { 0x03, 0x04, 0x05, 0x06, 0x07 }; for (int i = 0; i < 3; i++) { // Pick a height at random and remove from list to get 3 different heights rInt = r.Next(jumpHeight.Count); Patch.Add(0x02CBB5 + i, (byte)jumpHeight[rInt], String.Format("Metalman Jump {0} Y-Velocity Integer", i + 1)); jumpHeight.RemoveAt(rInt); } }