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>
        /// Get a random unique sound for a weapon to use
        /// </summary>
        /// <param name="r"></param>
        /// <returns>Sound ID Byte</returns>
        private ESoundID GetRandomSound(ISeed in_Seed)
        {
            Int32    i     = in_Seed.NextInt32(sounds.Count);
            ESoundID sound = sounds.ElementAt(i);

            sounds.RemoveAt(i);

            // Pick a random charge level if charge sound is chosen
            if (sound == ESoundID.WeaponH_Charge0)
            {
                i     = in_Seed.NextInt32(3);
                sound = ESoundID.WeaponH_Charge0 + i;
            }
            return(sound);
        }
Esempio n. 3
0
        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));
            }
        }
        protected void ChangeItem1(Patch in_Patch, ISeed in_Seed)
        {
            Int32 rInt;

            //0x03E1A0(0F:E190) - Item 1 Update Subroutine

            //0x03E1AC - Delay before Item 1 starts flashing 0xBB (make 0x00 for infinite) (do from 0x30 to 0xFF)
            rInt = in_Seed.NextInt32(0xFF - 0x30 + 1) + 0x30;
            in_Patch.Add(0x03E1AC, (Byte)rInt, "(1) | Delay Before Flashing");

            //0x03E1BF - Delay before Item 1 disappears after flashing 0x3E (make 0x00 for infinite) (do from 0x20 to 0x70)
            rInt = in_Seed.NextInt32(0x70 - 0x20 + 1) + 0x20;
            in_Patch.Add(0x03E1BF, (Byte)rInt, "(1) | Delay Before Despawn");

            //0x03E1E2 - y-pos offset for Mega Man once standing on Item 1(0x04)
            //0x03E1E7 - width of Item 1 surface for Mega Man to stand on(0x14)
            //0x03E1F0 - Distance above Item to check for despawning(0x1D)

            //0x03D4C2 - y-vel fraction(0x41)
            Int32[] rXVelFracs = new Int32[] { 0x00, 0x20, 0x41, 0x50 };
            rInt = in_Seed.NextInt32(rXVelFracs.Length);
            in_Patch.Add(0x03D4C2, (Byte)rXVelFracs[rInt], "(1) | Y-Velocity (Fraction)");
        }
Esempio n. 5
0
        /*
         * private void ReadLevelComponentJSON(Patch p, Random r)
         * {
         *  ComponentManager component = JsonConvert.DeserializeObject<ComponentManager>(Properties.Resources.level_components);
         *
         *  foreach (var levelComponent in component.LevelComponents)
         *  {
         *      // Get a random variation
         *      var variation = levelComponent.Variations[r.Next(levelComponent.Variations.Count)];
         *
         *      // Add patch for each element of the tsamap
         *      Int32 startAddress = Convert.ToInt32(levelComponent.StartAddress, 16);
         *      for (Int32 i = 0; i < variation.TsaMap.Length; i++)
         *      {
         *          // Parse hex String
         *          Byte tsaVal = Convert.ToByte(variation.TsaMap[i], 16);
         *
         *          p.Add(startAddress + i, tsaVal, $"Tilemap data for {levelComponent.Name} variation \"{variation.Name}\"");
         *      }
         *  }
         *
         *  Console.WriteLine(component.LevelComponents);
         * }
         */

        private static void ChangeW4FloorsBeforeSpikes(Patch in_Patch, ISeed in_Seed)
        {
            // Choose 2 of the 5 32x32 tiles to be fake
            Int32 tileA = in_Seed.NextInt32(5);
            Int32 tileB = in_Seed.NextInt32(4);

            // Make sure 2nd tile chosen is different
            if (tileB == tileA)
            {
                tileB++;
            }

            for (Int32 i = 0; i < 5; i++)
            {
                if (i == tileA || i == tileB)
                {
                    in_Patch.Add(0x00CB5C + i * 8, 0x94, String.Format("Wily 4 Room 4 Tile {0} (fake)", i));
                }
                else
                {
                    in_Patch.Add(0x00CB5C + i * 8, 0x85, String.Format("Wily 4 Room 4 Tile {0} (solid)", i));
                }
            }
        }
Esempio n. 6
0
        private static void ChangeW4FloorsSpikePit(Patch in_Patch, ISeed in_Seed)
        {
            // 5 tiles, but since two adjacent must construct a gap, 4 possible gaps.  Choose 1 random gap.
            Int32 gap = in_Seed.NextInt32(4);

            for (Int32 i = 0; i < 4; i++)
            {
                if (i == gap)
                {
                    in_Patch.Add(0x00CB9A + i * 8, 0x9B, String.Format("Wily 4 Room 5 Tile {0} (gap on right)", i));
                    in_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
                {
                    in_Patch.Add(0x00CB9A + i * 8, 0x9D, String.Format("Wily 4 Room 5 Tile {0} (solid)", i));
                }
            }
        }
        protected void ChangeMetal(Patch in_Patch, ISeed in_Seed)
        {
            //0x03DBB6 - M max shots (04) (change to 0x02-0x05, or 1-4)
            Int32 maxShots = in_Seed.NextInt32(0x04) + 0x02;

            in_Patch.Add(0x03DBB6, (Byte)maxShots, "(M) | Max Shots");

            //0x03DBC9 - M sound effect(23)
            ESoundID sound = this.GetRandomSound(in_Seed);

            in_Patch.Add(0x03DBC9, (Byte)sound, "(M) | Sound");

            //0x03DBD2 - M shots per ammo tick(04) (change to 1-5)
            Int32 magSize = in_Seed.NextInt32(0x05) + 0x01;

            in_Patch.Add(0x03DBD2, (Byte)magSize, "(M) | Shots Per Ammo Tick");
            AmmoUsage.Add(1d / magSize);

            // Speeds.  Change each to be 2-7.  Diagonal will be half each, rounded up.
            Int32 velX  = in_Seed.NextInt32(0x06) + 0x02;
            Int32 velY  = in_Seed.NextInt32(0x06) + 0x02;
            Int32 halfY = (Int32)Math.Ceiling((Double)velY / 2d);
            Int32 halfX = (Int32)Math.Ceiling((Double)velX / 2d);

            //0x03DC12 - M y - speed, holding up(04)
            in_Patch.Add(0x03DC12, (Byte)velY, "(M) | Y-Velocity Up");

            //0x03DC31 - M x - speed, no direction(04)
            in_Patch.Add(0x03DC31, (Byte)velX, "(M) | X-Velocity Neutral");

            //0x03DC35 - M x - speed, holding left(04)
            in_Patch.Add(0x03DC35, (Byte)velX, "(M) | X-Velocity Left");

            //0x03DC39 - M x - speed, holding right(04)
            in_Patch.Add(0x03DC39, (Byte)velX, "(M) | X-Velocity Right");

            //0x03DC13 - M y - speed, holding down(FC)
            in_Patch.Add(0x03DC13, (Byte)(0x00 - (Byte)velY), "(M) | Y-Velocity Down");

            //0x03DC16 - M y - speed, holding up + left(02)
            in_Patch.Add(0x03DC16, (Byte)halfY, "(M) | Y-Velocity Up+Left");

            //0x03DC17 - M y - speed, holding down + left(FD)
            in_Patch.Add(0x03DC17, (Byte)(0x00 - (Byte)halfY), "(M) | Y-Velocity Down+Left");

            //0x03DC1A - M y - speed, holding up + right(02)
            in_Patch.Add(0x03DC1A, (Byte)halfY, "(M) | Y-Velocity Up+Right");

            //0x03DC1B - M y - speed, holding down + right(FD)
            in_Patch.Add(0x03DC1B, (Byte)(0x00 - (Byte)halfY), "(M) | Y-Velocity Down+Right");

            //0x03DC36 - M x - speed, holding up + left(02)
            in_Patch.Add(0x03DC36, (Byte)halfX, "(M) | X-Velocity Up+Left");

            //0x03DC37 - M x - speed, holding down + left(02)
            in_Patch.Add(0x03DC37, (Byte)halfX, "(M) | X-Velocity Down+Left");

            //0x03DC3A - M x - speed, holding up + right(02)
            in_Patch.Add(0x03DC3A, (Byte)halfX, "(M) | X-Velocity Up+Right");

            //0x03DC3B - M x - speed, holding down + right(02)
            in_Patch.Add(0x03DC3B, (Byte)halfX, "(M) | X-Velocity Down+Right");
        }
        protected void ChangeFlash(Patch in_Patch, ISeed in_Seed)
        {
            //0x03DC59 - F sound (21)
            ESoundID sound = this.GetRandomSound(in_Seed);

            in_Patch.Add(0x03DC59, (Byte)sound, "(F) | Sound");

            //0x03E172 - F custom subroutine for reusable weapon
            // 75% chance to occur
            Double rTestFChange = in_Seed.NextDouble();

            if (rTestFChange > 0.25)
            {
                // 0x03E175 - New ammo-usage address.
                Byte[] ammos  = new Byte[] { 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
                Int32  rIndex = in_Seed.NextInt32(7);
                Byte   ammo   = ammos[rIndex];

                // Change ammo-tick subroutine into one that resumes time
                Byte[] sub = new Byte[]
                {
                    0xA5, 0xA1,         // LDA ammoFlash = #$0A
                    0xE9, ammo,         // SBC {ammo use}
                    0x85, 0xA1,         // STA ammoFlash = #$0A
                    0x5E, 0x20, 0x04,   // LSR megaXDir, X @ megaXDir = #$C0
                    0xA9, 0x00,         // LDA #$00
                    0x85, 0xAA,         // STA $00AA = #$00
                    0x85, 0x50,         // STA $0050 = #$00 ; we erased LDA #$01, but doesn't seem to hurt
                };
                for (Int32 i = 0; i < sub.Length; i++)
                {
                    in_Patch.Add(0x03E172 + i, sub[i], "(F) | Reusable Time-Stopper Subroutine");
                }

                // 0x03E16E and 0x03D49D is the new duration, in frames. They must both be the same value.
                Int32 duration = 0x01;
                switch (ammo)
                {
                case 0x02:
                    duration = in_Seed.NextInt32(70) + 20;
                    break;

                case 0x03:
                    duration = in_Seed.NextInt32(60) + 40;
                    break;

                case 0x04:
                    duration = in_Seed.NextInt32(60) + 60;
                    break;

                case 0x05:
                    duration = in_Seed.NextInt32(60) + 80;
                    break;

                case 0x06:
                    duration = in_Seed.NextInt32(60) + 100;
                    break;

                case 0x07:
                    duration = in_Seed.NextInt32(85) + 120;
                    break;

                case 0x08:
                    duration = in_Seed.NextInt32(115) + 140;
                    break;

                default: break;
                }
                in_Patch.Add(0x03E16E, (Byte)duration, "(F) | Freeze Duration (1)");
                in_Patch.Add(0x03D49D, (Byte)duration, "(F) | Freeze Duration (2)");

                // Finally, a fix is needed to prevent ammo underflow
                // 0x03DC41 - WpnMove_FStart
                sub = new Byte[]
                {
                    0xA2, 0x02,         // LDX #$02
                    0xAD, 0x22, 0x04,   // LDA $0422 = #$61
                    0x30, 0x1E,         // BMI WpnMove_Done
                    0xA5, 0xA1,         // LDA ammoFlash = #$02
                    0xC9, 0x07,         // CMP #$07
                    0x90, 0x18,         // BCC WpnMove_Done
                };
                for (Int32 i = 0; i < sub.Length; i++)
                {
                    in_Patch.Add(0x03DC41 + i, sub[i], "(F) | Reusable Time-Stopper Ammo-Underflow Fix");
                }
            }
            else
            {
                // Standard Time Stopper, but modify the tick frequency
                // Default 0x0F. For 28 ticks, that's 7 seconds. Modify to be 4-10 seconds, which is about 0x09 to 0x16
                Int32 tickDelay = in_Seed.NextInt32(0x13) + 9;
                in_Patch.Add(0x03E16E, (Byte)tickDelay, "(F) | Time-Stopper Ammo Tick Delay (1)");
                in_Patch.Add(0x03D49D, (Byte)tickDelay, "(F) | Time-Stopper Ammo Tick Delay (2)");
            }
        }
        protected void ChangeQuick(Patch in_Patch, ISeed in_Seed)
        {
            // Q autofire delay, default 0x0B
            //    Do from 0x05 to 0x12
            Int32 autoFireDelay = in_Seed.NextInt32(0x0D) + 0x05;

            in_Patch.Add(0x03DB54, (Byte)autoFireDelay, "(Q) | Autofire Delay");

            // Q max shots, default 0x05
            //    Do from 0x03 to 0x07(2 to 6 shots)
            Int32 maxShots = in_Seed.NextInt32(0x04) + 0x03;

            in_Patch.Add(0x03DB5C, (Byte)maxShots, "(Q) | Max Shots");

            // 0x03DB6F - Q sound effect
            ESoundID sound = this.GetRandomSound(in_Seed);

            in_Patch.Add(0x03DB6F, (Byte)sound, "(Q) | Sound");

            // Q shots per ammo tick, default 0x08
            //    Do from 0x04 to 0x0A ?
            Int32 magSize = in_Seed.NextInt32(0x06) + 0x04;

            in_Patch.Add(0x03DB78, (Byte)magSize, "(Q) | Shots Per Ammo Tick");
            AmmoUsage.Add(1d / (Double)magSize);

            // Q behavior, distance, default 0x12
            //    Do from 0x0A to 0x20 ?
            Int32 distance = in_Seed.NextInt32(0x16) + 0x0A;

            in_Patch.Add(0x03DFE2, (Byte)distance, "(Q) | Travel Distance");

            // Q behavior, initial angle, default 0x4B
            //    Do from 0x00 to 0x60 ?
            Int32 angle1 = in_Seed.NextInt32(0x60);

            in_Patch.Add(0x03DFEA, (Byte)angle1, "(Q) | Initial Angle");

            //0x03DFF2 - Q behavior, weird, default 0x00
            //    Don't use, but change to 0x01 for dumb effect

            // Q behavior, angle, default 0x40
            //    0x40 - (GOOD)Normal
            //    0x80 - (GOOD / HARD) Disappears(doesn't return)
            //    0x00 - (GOOD)Sine wave
            //    0x03 - (GOOD)Float downwards(interesting behavior when changing other Byte)
            //    0x04, 05 - Float downwards(short, not different enough from 03)
            //    0x06 - Float downwards(faster)
            Int32[] angle2s = new Int32[] { 0x40, 0x80, 0x00, 0x03 };
            Int32   rIndex  = in_Seed.NextInt32(angle2s.Length);
            Int32   angle2  = angle2s[rIndex];

            in_Patch.Add(0x03DFFF, (Byte)angle2, "(Q) | Secondary Angle");

            // Q behavior, time before disappearing on return, default 0x23
            //    Do from 0x1E to 0x30
            Int32 despawnDelay = in_Seed.NextInt32(0x12) + 0x1E;

            in_Patch.Add(0x03E007, (Byte)despawnDelay, "(Q) | Despawn Delay");

            // Q behavior, return angle, default 0x4B
            //    Do from 0x00 to 0x90
            Int32 angle3 = in_Seed.NextInt32(0x90);

            in_Patch.Add(0x03E013, (Byte)angle3, "(Q) | Return Angle");

            //0x03E01B - Q behavior, weird, default 0x00
            //    Change to 0x01 for interesting effects
        }
        protected void ChangeBubble(Patch in_Patch, ISeed in_Seed)
        {
            //0x03D4AB - B x - speed on shoot (0x01) (do 1-3)
            Int32 xVelShoot = in_Seed.NextInt32(0x03) + 0x01;

            in_Patch.Add(0x03D4AB, (Byte)xVelShoot, "(B) | X-Velocity Shoot (Integer)");

            //0x03D4CF - B y - speed on shoot(0x02) (do 0-6)
            Int32 yVelShoot = in_Seed.NextInt32(0x06);

            in_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.
            Int32 maxShots = in_Seed.NextInt32(0x04) + 0x02;

            in_Patch.Add(0x03DB21, (Byte)maxShots, "(B) | Max Shots");

            //0x03DB2F - B weapon type(0x04)
            // Don't do

            //0x03DB34 - B sound effect
            ESoundID sound = this.GetRandomSound(in_Seed);

            in_Patch.Add(0x03DB34, (Byte)sound, "(B) | Sound");

            //0x03DB3D - B shots per ammo tick (0x02) (do 1-4)
            Int32 magSize = in_Seed.NextInt32(0x04) + 0x01;

            in_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 ?
            Int32 xVelRoll = in_Seed.NextInt32(0x04) + 0x01;

            in_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
            Int32  xVelFall        = 0x00;
            Double rTestXFallSpeed = in_Seed.NextDouble();

            if (rTestXFallSpeed > 0.5)
            {
                xVelFall = in_Seed.NextInt32(0x05) + 0x01;
            }

            in_Patch.Add(0x03DFC0, (Byte)xVelFall, "(B) | X-Velocity Fall (Integer)");

            //0x03DFC8 - B y - speed after falling(0xFE)
            //      Either 0xFA - 0xFF or 0x01 - 0x06
            Int32[] yFallVels = new Int32[] { 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
            Int32   rIndex    = in_Seed.NextInt32(yFallVels.Length);
            Int32   yFallVel  = yFallVels[rIndex];

            in_Patch.Add(0x03DFC8, (Byte)yFallVel, "(B) | Y-Velocity Fall (Integer)");
        }
        protected void ChangeWood(Patch in_Patch, ISeed in_Seed)
        {
            //0x03DEDA - W deploy time (0C)
            //    Can change from 06 to 12
            //    Note: Shield glitches on odd numbers.  Use evens only.
            Int32[] deployDelays = new Int32[] {
                0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x10, 0x14, 0x1C, 0x22
            };
            Int32 rIndex      = in_Seed.NextInt32(deployDelays.Length);
            Int32 deployDelay = deployDelays[rIndex];

            in_Patch.Add(0x03DEDA, (Byte)deployDelay, "(W) | Deploy Delay");

            //0x03DF0D - W spin animation? (01)

            //0x03DF1B - W delay between sounds(07)
            // TODO

            //0x03DF1F - W deploy sound effect
            ESoundID sound = this.GetRandomSound(in_Seed);

            in_Patch.Add(0x03DF1F, (Byte)sound, "(W) | Deploy Sound");

            //0x03DF41 - W which directions the shield is allowed to launch in (F0)
            //    Can prevent launching left / right, up / down, etc

            //0x03DF4D - W launch directions (C0)
            //    Don't use this one

            //0x03DF50 - W launch x - direction subroutine
            //    50% chance to have inverted x controls
            //    Change "LSR AND #40" (4A 29 40) to simply "AND #40" to implement
            Double rTestReverseX = in_Seed.NextDouble();

            if (rTestReverseX > 0.5)
            {
                in_Patch.Add(0x03DF50, 0x29, "(W) | Reverse X-Direction Code AND");
                in_Patch.Add(0x03DF51, 0x40, "(W) | Reverse X-Direction Code #$40");
                in_Patch.Add(0x03DF52, 0xEA, "(W) | Reverse X-Direction Code NOP"); // (best thing i could find to simply "skip" a line)
            }

            //0x03DF59 - W x - speed(04) (do 0x02-0x08)
            Int32 launchVel = in_Seed.NextInt32(0x06) + 0x02;

            in_Patch.Add(0x03DF59, (Byte)launchVel, "(W) | Launch X-Velocity Integer");

            //0x03DF64 - W launch y - direction(10)
            //  Change to 0x20 to reverse, 50% chance
            Int32  reverseY      = 0x10;
            Double rTestReverseY = in_Seed.NextDouble();

            if (rTestReverseY > 0.5)
            {
                reverseY = 0x20;
            }

            in_Patch.Add(0x03DF64, (Byte)reverseY, "(W) | Launch Y-Direction");

            //0x03DF72 - W ammo usage (3) (do from 1 to 3)
            Int32 ammoUse = in_Seed.NextInt32(0x03) + 0x01;

            in_Patch.Add(0x03DF72, (Byte)ammoUse, "(W) | Ammo Usage");
            AmmoUsage.Add(ammoUse);

            //0x03DF7D - W y - speed(04)
            in_Patch.Add(0x03DF7D, (Byte)launchVel, "(W) | Launch Y-Velocity Integer");
        }
        protected void ChangeAir(Patch in_Patch, ISeed in_Seed)
        {
            //0x03DAD6 - A num projectiles, default 0x04
            //  Values 0x02 and 0x03 work, but larger values behave strangely
            Int32  numProjectiles      = 0x04;
            Double rTestNumProjectiles = in_Seed.NextDouble();

            if (rTestNumProjectiles > 0.80)
            {
                numProjectiles = 0x03;
            }
            else if (rTestNumProjectiles > 0.60)
            {
                numProjectiles = 0x02;
            }

            in_Patch.Add(0x03DAD6, (Byte)numProjectiles, "(A) | Number of Projectiles");

            //0x03DADA - A projectile type, default 0x02
            //  Can use this to change behavior completely!Buggy though.

            //0x03DAE6 - A sound effect (0x3F)
            ESoundID sound = this.GetRandomSound(in_Seed);

            in_Patch.Add(0x03DAE6, (Byte)sound, "(A) | Sound");

            //0x03DAEE - A ammo used(0x02)
            Int32 ammoUse = in_Seed.NextInt32(0x02) + 0x01;

            in_Patch.Add(0x03DAEE, (Byte)ammoUse, "(A) | Ammo Use");
            AmmoUsage.Add(ammoUse);

            //0x03DE6E - A projectile y-acceleration fraction(10)
            // Do 0x02 to 0x32, where values above 0x10 are less common
            Int32 yAccFrac = in_Seed.NextInt32(0x17) + 0x02;

            if (yAccFrac > 0x10)
            {
                // Double the addition of any acceleration value chosen above 0x10
                yAccFrac += (yAccFrac - 0x10) * 2;
            }
            in_Patch.Add(0x03DE6E, (Byte)yAccFrac, "(A) | Y-Acceleration (fraction)");

            //0x03DE76 - A projectile y-acceleration integer(00)
            // 15% chance to be significantly faster
            Int32  yAccInt = 0x00;
            Double rYAcc   = in_Seed.NextDouble();

            if (rYAcc > 0.85)
            {
                yAccInt = 0x01;
            }

            in_Patch.Add(0x03DE76, (Byte)yAccInt, "(A) | Y-Acceleration (integer)");

            //0x03DE7E - A x - speed fraction projectile 1(19)
            //0x03DE7F - A x - speed fraction projectile 2(99)
            //0x03DE80 - A x - speed fraction projectile 3(33)
            for (Int32 i = 0; i < 3; i++)
            {
                Int32 xFracSpeed = in_Seed.NextInt32(0xFF) + 0x01;
                in_Patch.Add(0x03DE7E + i, (Byte)xFracSpeed, String.Format("(A) | Projectile {0} X-Velocity (fraction)", i + 1));
            }

            //0x03DE81 - A x - speed integer projectile 1(01)
            //0x03DE82 - A x - speed integer projectile 2(01)
            //0x03DE83 - A x - speed integer projectile 3(02)
            Int32[] xIntSpeeds = new Int32[] {
                0x00, 0x01, 0x02, 0x04, 0x06
            };
            Int32 rIndex    = 0;
            Int32 xIntSpeed = 0;

            for (Int32 i = 0; i < 3; i++)
            {
                rIndex    = in_Seed.NextInt32(xIntSpeeds.Length);
                xIntSpeed = xIntSpeeds[rIndex];
                in_Patch.Add(0x03DE81 + i, (Byte)xIntSpeed, String.Format("(A) | Projectile {0} X-Velocity (integer)", i + 1));
            }
        }
        protected void ChangeHeat(Patch in_Patch, ISeed in_Seed)
        {
            //0x03DE55 - H L1 Ammo use(01)
            //0x03DE56 - H L2 Ammo use(06)
            //0x03DE57 - H L3 Ammo use(0A)
            // 90% chance for L1 to be free, else cost 1 ammo
            // L2 ammo cost = L1 ammo cost
            // L3 will cost 1-4 ammo
            Double rTestL1Ammo = in_Seed.NextDouble();
            Byte   L1Ammo      = (rTestL1Ammo > 0.1) ? (Byte)0 : (Byte)1;

            in_Patch.Add(0x03DE55, L1Ammo, $"(H) | Shot L1 Ammo Cost: {L1Ammo}");
            in_Patch.Add(0x03DE56, L1Ammo, $"(H) | Shot L2 Ammo Cost: {L1Ammo}");

            Byte[] bytes = new Byte[] { 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, };
            Int32  rInt  = in_Seed.NextInt32(bytes.Length);

            in_Patch.Add(0x03DE57, bytes[rInt], $"(H) | Shot L3 Ammo Cost: {bytes[rInt]}");
            AmmoUsage.Add(bytes[rInt]);

            // Charge Behavior
            // 20% to have old charge behavior, 40% to skip 1 level, 40% to shoot L3 immediately
            Double rTestChargeType = in_Seed.NextDouble();

            //0x03DD66 - H change these 4 bytes to 0xEA to skip L2 charge
            if (rTestChargeType < 0.8)
            {
                for (Int32 i = 0; i < 4; i++)
                {
                    in_Patch.Add(0x03DD66 + i, 0xEA, $"(H) | Skip L2 Charge");
                }
            }
            //0x03DD62 - H after doing the previous, change these 2 bytes to 0xEA to skip charge altogether
            if (rTestChargeType < 0.4)
            {
                in_Patch.Add(0x03DD62, 0xEA, $"(H) | Skip L1 Charge");
                in_Patch.Add(0x03DD63, 0xEA, $"(H) | Skip L1 Charge");
                //0x03DD95 - H change this from 0x0D to 0x14 to fire the charge on press instead of on release
                in_Patch.Add(0x03DD95, 0x14, $"(H) | Fire Shot On Press");
            }

            // Charge delay for L2 will be between 0x10 and 0x80 frames
            //0x03DD61 - H L2 charge delay (0x7D)
            Int32 chargeDelayL2 = in_Seed.NextInt32(0x70) + 0x09;

            in_Patch.Add(0x03DD61, (Byte)chargeDelayL2, "(H) | L2 Charge Delay");

            // Charge delay for L3 will L2 plus a value between 0x10 and 0x40 frames
            //0x03DD67 - H L3 charge delay(0xBB)
            Int32 chargeDelayL3 = in_Seed.NextInt32(0x30) + 0x09 + chargeDelayL2;

            if (rTestChargeType >= 0.8)
            {
                in_Patch.Add(0x03DD67, (Byte)chargeDelayL3, "(H) | L3 Charge Delay");
            }

            //0x03DDEC - H shot sound effect(38)
            ESoundID sound = this.GetRandomSound(in_Seed);

            in_Patch.Add(0x03DDEC, (Byte)sound, "(H) | Shot Sound");

            //0x03DDF1 - H x - speed (04, all levels)
            //    Do from 02 to 08
            Int32 xVel = in_Seed.NextInt32(0x07) + 0x02;

            in_Patch.Add(0x03DDF1, (Byte)xVel, "(H) | X-Velocity");

            //0x03DE45 - H charge sound 1(35) Unused
            //0x03DE46 - H charge sound 2(35)
            sound = this.GetRandomSound(in_Seed);
            in_Patch.Add(0x03DE46, (Byte)sound, "(H) | L1 Sound");

            //0x03DE47 - H charge sound 3(36)
            sound = this.GetRandomSound(in_Seed);
            in_Patch.Add(0x03DE47, (Byte)sound, "(H) | L2 Sound");

            //0x03DE48 - H charge sound 4(37)
            sound = this.GetRandomSound(in_Seed);
            in_Patch.Add(0x03DE48, (Byte)sound, "(H) | L3 Sound");
        }
Esempio n. 14
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="p"></param>
        /// <param name="r"></param>
        public void ImportMusic(Patch in_Patch, ISeed in_Seed)
        {
            List <Song> songs      = new List <Song>();
            List <Song> stageSongs = new List <Song>();

            SoundTrackSet soundTrackSet = Properties.Resources.SoundTrackConfiguration.Deserialize <SoundTrackSet>();

            // Read songs from file, parse and add to list
            foreach (SoundTrack soundTrack in soundTrackSet)
            {
                if (true == soundTrack.Enabled)
                {
                    songs.Add(new Song(soundTrack.Title, soundTrack.StartAddress, soundTrack.TrackData));
                }
            }

            debug.AppendLine($"{songs.Count} stage songs loaded.");

            // Create a shuffled list of songs
            Boolean     checkBytes = true;
            const Int32 numTracks  = 11;

            while (checkBytes)
            {
                // Shuffle and get first 11 songs
                songs      = in_Seed.Shuffle(songs).ToList();
                stageSongs = songs.GetRange(0, numTracks);

                // Count bytes
                Int32 totalBytes = 0;
                Int32 i          = 0;

                foreach (Song song in stageSongs)
                {
                    totalBytes += song.SongHeader.Count;
                    totalBytes += song.SongData.Count;
                    i++;
                }

                // Break if within limit (Redo shuffle if over limit)
                // DEBUG DEBUG
                if (totalBytes <= StageSongsSize)
                {
                    checkBytes = false;
                    debug.AppendLine($"{numTracks} songs selected. {totalBytes} bytes used out of {StageSongsSize} limit.");
                }
                else
                {
                    debug.AppendLine($"{numTracks} songs selected. {totalBytes} bytes, greater than {StageSongsSize} limit. Reshuffled songs.");
                }
            }

            // Write the songs and song info
            Int32 songStartRom = 0x030AE6;
            Int32 TblPtrOffset = 0x30A60; // Start of address pairs which point to song locations

            for (Int32 k = 0; k < stageSongs.Count; k++)
            {
                Song song = stageSongs[k];

                // Calculate start addresses from song lengths and write to address table
                Int32  addressTwoBytes = songStartRom - 0x30010 + 0x8000;
                String addressHex      = addressTwoBytes.ToString("X");
                song.SongStartPtr1stByte = Byte.Parse(addressHex.Substring(0, 2), NumberStyles.HexNumber);
                song.SongStartPtr2ndByte = Byte.Parse(addressHex.Substring(2, 2), NumberStyles.HexNumber);

                if (k >= 10) // Must use a different location for the 11th track; use Stage Select at 0x30A78
                {
                    in_Patch.Add(0x30A78, song.SongStartPtr2ndByte, $"Song {k} Pointer Offset Byte 1");
                    in_Patch.Add(0x30A79, song.SongStartPtr1stByte, $"Song {k} Pointer Offset Byte 2");

                    Byte songIndex = 0x0C; // Wily 5 will play the song at the Stage Select address
                    in_Patch.Add(0x0381EC, songIndex, $"Wily 5 Song");
                    debug.AppendLine($"{Enum.GetName(typeof(EMusicID), (EMusicID)songIndex)} stage song: {song.SongName}, {song.OriginalStartAddress}");
                }
                else
                {
                    in_Patch.Add(TblPtrOffset++, song.SongStartPtr2ndByte, $"Song {k} Pointer Offset Byte 1");
                    in_Patch.Add(TblPtrOffset++, song.SongStartPtr1stByte, $"Song {k} Pointer Offset Byte 2");
                    debug.AppendLine($"{Enum.GetName(typeof(EMusicID), (EMusicID)k)} stage song: {song.SongName}, {song.OriginalStartAddress}");
                }

                // Header: Calculate 4 channel addresses and vibrato address
                Byte  origChannel1ByteSmall = song.SongHeader[1];
                Byte  origChannel1ByteLarge = song.SongHeader[2];
                Int32 origChannel1Offset    = origChannel1ByteSmall + (origChannel1ByteLarge * 256);
                Int32 relChannel1Offset     = origChannel1Offset - song.OriginalStartAddressInt;
                Int32 newChannel1Offset     = addressTwoBytes + relChannel1Offset;
                song.SongHeader[1] = (Byte)(newChannel1Offset % 256);
                song.SongHeader[2] = (Byte)(newChannel1Offset / 256);

                Byte  origChannel2ByteSmall = song.SongHeader[3];
                Byte  origChannel2ByteLarge = song.SongHeader[4];
                Int32 origChannel2Offset    = origChannel2ByteSmall + (origChannel2ByteLarge * 256);
                Int32 relChannel2Offset     = origChannel2Offset - song.OriginalStartAddressInt;
                Int32 newChannel2Offset     = addressTwoBytes + relChannel2Offset;
                song.SongHeader[3] = (Byte)(newChannel2Offset % 256);
                song.SongHeader[4] = (Byte)(newChannel2Offset / 256);

                Byte  origChannel3ByteSmall = song.SongHeader[5];
                Byte  origChannel3ByteLarge = song.SongHeader[6];
                Int32 origChannel3Offset    = origChannel3ByteSmall + (origChannel3ByteLarge * 256);
                Int32 relChannel3Offset     = origChannel3Offset - song.OriginalStartAddressInt;
                Int32 newChannel3Offset     = addressTwoBytes + relChannel3Offset;
                song.SongHeader[5] = (Byte)(newChannel3Offset % 256);
                song.SongHeader[6] = (Byte)(newChannel3Offset / 256);

                Byte origChannel4ByteSmall = song.SongHeader[7];
                Byte origChannel4ByteLarge = song.SongHeader[8];

                if (origChannel4ByteSmall > 0 || origChannel4ByteLarge > 0)
                {
                    Int32 origChannel4Offset = origChannel4ByteSmall + (origChannel4ByteLarge * 256);
                    Int32 relChannel4Offset  = origChannel4Offset - song.OriginalStartAddressInt;
                    Int32 newChannel4Offset  = addressTwoBytes + relChannel4Offset;
                    song.SongHeader[7] = (Byte)(newChannel4Offset % 256);
                    song.SongHeader[8] = (Byte)(newChannel4Offset / 256);

                    if (relChannel4Offset > song.TotalLength || relChannel4Offset < 0)
                    {
                        debug.AppendLine($"WARNING: Song {song.SongName} channel 4 points to a shared location.");
                    }
                }

                Byte  origVibratoByteSmall = song.SongHeader[9];
                Byte  origVibratoByteLarge = song.SongHeader[10];
                Int32 origVibratoOffset    = origVibratoByteSmall + (origVibratoByteLarge * 256);
                Int32 relVibratoOffset     = origVibratoOffset - song.OriginalStartAddressInt;
                Int32 newVibratoOffset     = addressTwoBytes + relVibratoOffset;
                song.SongHeader[9]  = (Byte)(newVibratoOffset % 256);
                song.SongHeader[10] = (Byte)(newVibratoOffset / 256);

                if (relChannel1Offset > song.TotalLength || relChannel1Offset < 0)
                {
                    debug.AppendLine($"WARNING: Song {song.SongName} channel 1 points to a shared location.");
                }

                if (relChannel2Offset > song.TotalLength || relChannel2Offset < 0)
                {
                    debug.AppendLine($"WARNING: Song {song.SongName} channel 2 points to a shared location.");
                }

                if (relChannel3Offset > song.TotalLength || relChannel3Offset < 0)
                {
                    debug.AppendLine($"WARNING: Song {song.SongName} channel 3 points to a shared location.");
                }

                if (relVibratoOffset > song.TotalLength || relVibratoOffset < 0)
                {
                    debug.AppendLine($"WARNING: Song {song.SongName} vibrato points to a shared location.");
                }


                // Write song header
                foreach (Byte b in song.SongHeader)
                {
                    in_Patch.Add(songStartRom, b, $"Song Header Byte for {song.SongName}");
                    songStartRom++;
                }

                // Song Data: Traverse stream and change loop pointers
                for (Int32 i = 0; i < song.SongData.Count; i++)
                {
                    // Do not parse loop pointers for vibrato
                    // TODO: Check the length of the vibrato String, or even better, use separate lists for each channel!
                    if (i >= song.VibratoIndex && i < song.VibratoLength)
                    {
                        continue;
                    }

                    Byte b0 = song.SongData[i];

                    // Bisqwit is awesome.
                    // http://www.romhacking.net/forum/index.php?topic=16383.0
                    // http://bisqwit.iki.fi/jutut/megamansource/mm2music.txt
                    switch (b0)
                    {
                    // Two-Byte encoding $00 n.  Song speed is set as n
                    // frames per tick.
                    case 0x00:
                    {
                        i += 1;
                        break;
                    }

                    // Two-Byte encoding $01 n. Adjusts vibrato parameters
                    // by n. Affects all following notes.
                    case 0x01:
                    {
                        i += 1;
                        break;
                    }

                    // Two-Byte encoding $02 n. Selects duty cycle settings.
                    // Valid values for n: $00,$40,$80,$C0. Only applicable
                    // for squarewave channels.
                    case 0x02:
                    {
                        i += 1;
                        break;
                    }

                    // Two-Byte encoding $03 n. Selects volume and envelope
                    // settings. Value n is passed directly to the soundchip;
                    // Affects all following notes.
                    case 0x03:
                    {
                        i += 1;
                        break;
                    }

                    // Four-Byte encoding $04 n w. Ends a loop. If n=0, loop is
                    // infinite. Otherwise the marked section plays for n+1 times.
                    // w is a 16-bit pointer to the beginning of the loop.
                    // Finite loops cannot be nested.
                    case 0x04:
                    {
                        Byte origLoopPtrSmall = song.SongData[i + 2];
                        Byte origLoopPtrLarge = song.SongData[i + 3];

                        // Get the loop destination pointer by converting the two bytes to a 16-bit Int32
                        Int32 origLoopOffset = origLoopPtrSmall + (origLoopPtrLarge * 256);
                        // Find index of destination of the loop with respect to the start of the song
                        Int32 relLoopOffset = origLoopOffset - song.OriginalStartAddressInt;
                        // Make new loop destination with respect to the new starting location of this song
                        Int32 newLoopOffset = addressTwoBytes + relLoopOffset;

                        // Put new hex bytes back into song data array
                        song.SongData[i + 2] = (Byte)(newLoopOffset % 256);
                        song.SongData[i + 3] = (Byte)(newLoopOffset / 256);

                        // Validation check when testing out newly ripped songs to make sure I didn't miss any loops
                        if (relLoopOffset > song.TotalLength || relLoopOffset < 0)
                        {
                            debug.AppendLine($"WARNING: Song {song.SongName} has external loop point.");
                        }

                        i += 3;

                        break;
                    }

                    // Two-Byte encoding $05 n. Sets note base to n. Value
                    // n is added to the note index for any notes
                    // (excluding pauses) played on this channel from now.
                    case 0x05:
                    {
                        i += 1;
                        break;
                    }

                    // One-Byte encoding $06. Dotted note: The next note will
                    // be played 50% longer than otherwise, i.e. 3/2 of its
                    // stated duration.
                    case 0x06:
                    {
                        break;
                    }

                    // Three-Byte encoding $07 n m. Sets volume curve settings.
                    // Byte n controls the attack, and Byte m controls the decay.
                    // Affects all following notes.
                    case 0x07:
                    {
                        i += 2;
                        break;
                    }

                    // Two-Byte encoding $08 n. Select vibrato entry n from the
                    // vibrato table referred to by the song header. Affects all
                    // following notes.
                    case 0x08:
                    {
                        i += 1;
                        break;
                    }

                    // One-Byte encoding $09. Ends the track. Can be omitted if
                    // the track ends in an infinite loop instead.
                    case 0x09:
                    {
                        break;
                    }

                    // One - Byte encoding $20 + n.Note delay(n = 0 - 7):
                    //      Delays the next note by n ticks, without affecting
                    //      its overall timing. (I.e.plays silence for the
                    //      first n ticks of the note.)
                    //
                    // One - Byte encoding $30.Triplet:
                    //      The next note will be played at 2 / 3 of its
                    //      stated duration.
                    //
                    // One - Byte encoding:
                    //      m * 0x20 + n.Play note(m = 2..7).If n = 0, plays pause.
                    //      Otherwise, plays note n(note base is added to n). The
                    //      lowest note that can be played is C - 0(n + base = 0).
                    //      Note or pause length is 2m−1 ticks, possibly altered by
                    //      the triplet / dotted modifiers.The next event will be
                    //      read only after this note/pause is done playing.
                    default:
                    {
                        break;
                    }
                    }
                }

                // Write song data
                foreach (Byte b in song.SongData)
                {
                    in_Patch.Add(songStartRom, b, $"Song Data Byte for {song.SongName}");
                    songStartRom++;
                }
            }

            // Play a random song during wily 6
            Int32 w6song = in_Seed.NextInt32(11);

            if (w6song == 10)
            {
                // Wily 5 song was saved to 0x0C, don't use 0x0A
                in_Patch.Add(0x0381ED, 0x0C, $"Random Wily 6 Song: Song #{w6song}, {stageSongs[w6song].SongName}");
            }
            else
            {
                in_Patch.Add(0x0381ED, (Byte)w6song, $"Random Wily 6 Song: Song #{w6song}, {stageSongs[w6song].SongName}");
            }
            debug.AppendLine($"Wily 6 stage song: {stageSongs[w6song].SongName} (#{w6song})");

            // Play a random stage song during the credits
            Song creditsSong = in_Seed.NextElement(stageSongs);

            in_Patch.Add(0x30A88, creditsSong.SongStartPtr2ndByte, $"Song Credits 2 Byte 0 ({creditsSong.SongName})");
            in_Patch.Add(0x30A89, creditsSong.SongStartPtr1stByte, $"Song Credits 2 Byte 1 ({creditsSong.SongName})");
            debug.AppendLine($"Credits song: {creditsSong.SongName}");
        }
Esempio n. 15
0
        /// <summary>
        /// TODO
        /// </summary>
        private void RandomizeWilyUJ(Patch in_Patch, ISeed in_Seed)
        {
            // List of special weapon damage tables for enemies
            List <EDmgVsEnemy> dmgPtrEnemies = EDmgVsEnemy.GetTables(false);
            EDmgVsEnemy        enemyWeak1;
            EDmgVsEnemy        enemyWeak2;
            EDmgVsEnemy        enemyWeak3;

            // List of special weapon damage tables for bosses (no flash or buster)
            List <EDmgVsBoss> dmgPtrBosses = EDmgVsBoss.GetTables(false, false);
            EDmgVsBoss        bossWeak1;
            EDmgVsBoss        bossWeak2;
            EDmgVsBoss        bossWeak3;
            EDmgVsBoss        bossWeak4;

            #region Dragon

            // Dragon
            // 25% chance to have a buster vulnerability
            Double rBuster   = in_Seed.NextDouble();
            Byte   busterDmg = 0x00;

            if (rBuster > 0.75)
            {
                busterDmg = 0x01;
            }

            in_Patch.Add(EDmgVsBoss.U_DamageP + EDmgVsBoss.Offset.Dragon, busterDmg, "Buster Damage to Dragon");
            WilyWeaknesses[0, 0] = busterDmg;

            // Choose 2 special weapon weaknesses
            List <EDmgVsBoss> dragon = new List <EDmgVsBoss>(dmgPtrBosses);
            Int32             rInt   = in_Seed.NextInt32(dragon.Count);
            bossWeak1 = dragon[rInt];
            dragon.RemoveAt(rInt);
            rInt      = in_Seed.NextInt32(dragon.Count);
            bossWeak2 = dragon[rInt];

            // For each weapon, apply the weaknesses and immunities
            for (Int32 i = 0; i < dmgPtrBosses.Count; i++)
            {
                EDmgVsBoss weapon = dmgPtrBosses[i];

                // Dragon weak
                if (weapon == bossWeak1 || weapon == bossWeak2)
                {
                    // Deal 1 damage with weapons that cost 1 or less ammo
                    Byte damage = 0x01;

                    // Deal damage = ammoUsage - 1, minimum 2 damage
                    if (RWeaponBehavior.AmmoUsage[i + 1] > 1)
                    {
                        Int32 tryDamage = (Int32)RWeaponBehavior.AmmoUsage[i + 1] - 0x01;
                        damage = (tryDamage < 2) ? (Byte)0x02 : (Byte)tryDamage;
                    }
                    in_Patch.Add(weapon + EDmgVsBoss.Offset.Dragon, damage, String.Format("{0} Damage to Dragon", weapon.WeaponName));
                    WilyWeaknesses[0, i + 1] = damage;
                }
                // Dragon immune
                else
                {
                    in_Patch.Add(weapon + EDmgVsBoss.Offset.Dragon, 0x00, String.Format("{0} Damage to Dragon", weapon.WeaponName));
                    WilyWeaknesses[0, i + 1] = 0x00;
                }
            }

            #endregion

            #region Picopico-kun

            // Picopico-kun
            // 20 HP each
            // 25% chance for buster to deal 3-7 damage
            rBuster   = in_Seed.NextDouble();
            busterDmg = 0x00;
            if (rBuster > 0.75)
            {
                busterDmg = (Byte)(in_Seed.NextInt32(5) + 3);
            }
            in_Patch.Add(EDmgVsEnemy.DamageP + EDmgVsEnemy.Offset.PicopicoKun, busterDmg, String.Format("Buster Damage to Picopico-Kun"));
            WilyWeaknesses[1, 0] = busterDmg;

            // Deal ammoUse x 10 for the main weakness
            // Deal ammoUse x 6 for another
            // Deal ammoUse x 3 for another
            List <EDmgVsEnemy> pico = new List <EDmgVsEnemy>(dmgPtrEnemies);
            rInt       = in_Seed.NextInt32(pico.Count);
            enemyWeak1 = pico[rInt];
            pico.RemoveAt(rInt);
            rInt       = in_Seed.NextInt32(pico.Count);
            enemyWeak2 = pico[rInt];
            pico.RemoveAt(rInt);
            rInt       = in_Seed.NextInt32(pico.Count);
            enemyWeak3 = pico[rInt];
            for (Int32 i = 0; i < dmgPtrEnemies.Count; i++)
            {
                EDmgVsEnemy weapon = dmgPtrEnemies[i];
                Byte        damage = 0x00;
                Char        level  = ' ';

                if (weapon == enemyWeak1)
                {
                    damage = (Byte)(RWeaponBehavior.AmmoUsage[i + 1] * 10);

                    if (damage < 2)
                    {
                        damage = 3;
                    }

                    level = '^';
                }
                else if (weapon == enemyWeak2)
                {
                    damage = (Byte)(RWeaponBehavior.AmmoUsage[i + 1] * 6);

                    if (damage < 2)
                    {
                        damage = 2;
                    }

                    level = '*';
                }
                else if (weapon == enemyWeak3)
                {
                    damage = (Byte)(RWeaponBehavior.AmmoUsage[i + 1] * 3);

                    if (damage < 2)
                    {
                        damage = 2;
                    }
                }

                // If any weakness is Atomic Fire, deal 20 damage
                //if (weapon == EDmgVsEnemy.DamageH && (enemyWeak1 == weapon || enemyWeak2 == weapon || enemyWeak3 == weapon))
                //{
                //    damage = 20;
                //}

                // Bump up already high damage values to 20
                if (damage >= 14)
                {
                    damage = 20;
                }
                in_Patch.Add(weapon + EDmgVsEnemy.Offset.PicopicoKun, damage, String.Format("{0} Damage to Picopico-Kun{1}", weapon.WeaponName, level));
                WilyWeaknesses[1, i + 1]   = damage;
                WilyWeaknessInfo[1, i + 1] = level;
            }

            #endregion

            #region Guts

            // Guts
            // 25% chance to have a buster vulnerability
            rBuster   = in_Seed.NextDouble();
            busterDmg = 0x00;

            if (rBuster > 0.75)
            {
                busterDmg = 0x01;
            }

            in_Patch.Add(EDmgVsBoss.U_DamageP + EDmgVsBoss.Offset.Guts, busterDmg, String.Format("Buster Damage to Guts Tank"));
            WilyWeaknesses[2, 0] = busterDmg;

            // Choose 2 special weapon weaknesses
            List <EDmgVsBoss> guts = new List <EDmgVsBoss>(dmgPtrBosses);
            rInt      = in_Seed.NextInt32(guts.Count);
            bossWeak1 = guts[rInt];
            guts.RemoveAt(rInt);
            rInt      = in_Seed.NextInt32(guts.Count);
            bossWeak2 = guts[rInt];

            for (Int32 i = 0; i < dmgPtrBosses.Count; i++)
            {
                EDmgVsBoss weapon = dmgPtrBosses[i];

                // Guts weak
                if (weapon == bossWeak1 || weapon == bossWeak2)
                {
                    // Deal 1 damage with weapons that cost 1 or less ammo
                    Byte damage = 0x01;

                    // Deal damage = ammoUsage - 1, minimum 2 damage
                    if (RWeaponBehavior.AmmoUsage[i + 1] > 1)
                    {
                        Int32 tryDamage = (Int32)RWeaponBehavior.AmmoUsage[i + 1] - 0x01;
                        damage = (tryDamage < 2) ? (Byte)0x02 : (Byte)tryDamage;
                    }
                    in_Patch.Add(weapon + EDmgVsBoss.Offset.Guts, damage, String.Format("{0} Damage to Guts Tank", weapon.WeaponName));
                    WilyWeaknesses[2, i + 1] = damage;
                }
                // Guts immune
                else
                {
                    in_Patch.Add(weapon + EDmgVsBoss.Offset.Guts, 0x00, String.Format("{0} Damage to Guts Tank", weapon.WeaponName));
                    WilyWeaknesses[2, i + 1] = 0x00;
                }
            }

            #endregion

            #region Buebeam Trap

            // Buebeam
            // 5 Orbs + 3 Required Barriers (5 total barriers, 4 in speedrun route)
            // Choose a weakness for both Barriers and Orbs, scale damage to have plenty of energy
            // If the same weakness for both, scale damage further
            // If any weakness is Atomic Fire, ensure that there's enough ammo

            // Randomize Crash Barrier weakness
            List <EDmgVsEnemy> dmgBarrierList = EDmgVsEnemy.GetTables(true);

            // Remove Heat as possibility if it costs too much ammo
            if (RWeaponBehavior.AmmoUsage[1] > 5)
            {
                dmgBarrierList.RemoveAt(1);
                WilyWeaknesses[3, 1] = 0;
            }

            // Get Barrier weakness
            Int32       rBarrierWeakness = in_Seed.NextInt32(dmgBarrierList.Count);
            EDmgVsEnemy wpnBarrier       = dmgBarrierList[rBarrierWeakness];

            // Scale damage to be slightly more capable than killing 5 barriers at full ammo
            Int32 dmgW4 = 0x01;
            if (wpnBarrier != EDmgVsEnemy.DamageP)
            {
                Int32 totalShots        = (Int32)(28 / RWeaponBehavior.GetAmmoUsage(wpnBarrier));
                Int32 numHitsPerBarrier = (Int32)(totalShots / 5);

                if (numHitsPerBarrier > 1)
                {
                    numHitsPerBarrier--;
                }

                if (numHitsPerBarrier > 8)
                {
                    numHitsPerBarrier = 8;
                }

                dmgW4 = (Int32)Math.Ceiling(20d / numHitsPerBarrier);
            }
            for (Int32 i = 0; i < dmgBarrierList.Count; i++)
            {
                // Deal damage with weakness, and 0 for everything else
                Byte        damage = (Byte)dmgW4;
                EDmgVsEnemy wpn    = dmgBarrierList[i];
                if (wpn != wpnBarrier)
                {
                    damage = 0;
                }
                in_Patch.Add(wpn.Address + EDmgVsEnemy.Offset.ClashBarrier_W4, damage, String.Format("{0} Damage to Clash Barrier 1", wpn.WeaponName));
                in_Patch.Add(wpn.Address + EDmgVsEnemy.Offset.ClashBarrier_Other, damage, String.Format("{0} Damage to Clash Barrier 2", wpn.WeaponName));
            }

            // Remove Barrier weakness from list first (therefore, different Buebeam weakness)
            dmgBarrierList.RemoveAt(rBarrierWeakness);

            // Get Buebeam weakness
            rInt = in_Seed.NextInt32(dmgBarrierList.Count);
            EDmgVsEnemy wpnBuebeam = dmgBarrierList[rInt];

            // Add Barrier weakness back to list for counting later
            dmgBarrierList.Insert(rBarrierWeakness, wpnBarrier);

            // Scale damage to be slightly more capable than killing 5 buebeams at full ammo
            dmgW4 = 0x01;
            if (wpnBuebeam != EDmgVsEnemy.DamageP)
            {
                Int32 totalShots        = (Int32)(28 / RWeaponBehavior.GetAmmoUsage(wpnBuebeam));
                Int32 numHitsPerBuebeam = (Int32)(totalShots / 5);

                if (numHitsPerBuebeam > 1)
                {
                    numHitsPerBuebeam--;
                }

                if (numHitsPerBuebeam > 8)
                {
                    numHitsPerBuebeam = 8;
                }

                dmgW4 = (Int32)Math.Ceiling(20d / numHitsPerBuebeam);
            }

            // Add Buebeam damage values to patch, as well as array for use by Text and other modules later
            for (Int32 i = 0; i < dmgBarrierList.Count; i++)
            {
                Byte        damage = (Byte)dmgW4;
                EDmgVsEnemy wpn    = dmgBarrierList[i];
                if (wpn != wpnBuebeam)
                {
                    damage = 0;
                }
                in_Patch.Add(wpn.Address + EDmgVsEnemy.Offset.Buebeam, damage, String.Format("{0} Damage to Buebeam Trap", wpnBuebeam.WeaponName));

                // Add to damage table (skipping heat if necessary)
                if (RWeaponBehavior.AmmoUsage[1] > 5 && i >= 1)
                {
                    WilyWeaknesses[3, i + 1] = damage;
                }
                else
                {
                    WilyWeaknesses[3, i] = damage;
                }
            }

            #endregion

            #region Wily Machine

            // Machine
            // Will have 4 weaknesses and potentially a Buster weakness
            // Phase 1 will disable 2 of the weaknesses, taking no damage
            // Phase 2 will re-enable them, but disable 1 other weakness
            // Mega Man 2 behaves in a similar fashion, disabling Q and A in phase 1, but only disabling H in phase 2

            // 75% chance to have a buster vulnerability
            rBuster   = in_Seed.NextDouble();
            busterDmg = 0x00;

            if (rBuster > 0.25)
            {
                busterDmg = 0x01;
            }

            in_Patch.Add(EDmgVsBoss.U_DamageP + EDmgVsBoss.Offset.Machine, busterDmg, String.Format("Buster Damage to Wily Machine"));
            WilyWeaknesses[4, 0] = busterDmg;

            // Choose 4 special weapon weaknesses
            List <EDmgVsBoss> machine = new List <EDmgVsBoss>(dmgPtrBosses);
            rInt      = in_Seed.NextInt32(machine.Count);
            bossWeak1 = machine[rInt];
            machine.RemoveAt(rInt);
            rInt      = in_Seed.NextInt32(machine.Count);
            bossWeak2 = machine[rInt];
            machine.RemoveAt(rInt);
            rInt      = in_Seed.NextInt32(machine.Count);
            bossWeak3 = machine[rInt];
            machine.RemoveAt(rInt);
            rInt      = in_Seed.NextInt32(machine.Count);
            bossWeak4 = machine[rInt];

            for (Int32 i = 0; i < dmgPtrBosses.Count; i++)
            {
                EDmgVsBoss weapon = dmgPtrBosses[i];

                // Machine weak
                if (weapon == bossWeak1 || weapon == bossWeak2 || weapon == bossWeak3 || weapon == bossWeak4)
                {
                    // Deal 1 damage with weapons that cost 1 or less ammo
                    Byte damage = 0x01;

                    // Deal damage = ammoUsage
                    if (RWeaponBehavior.AmmoUsage[i + 1] > 1)
                    {
                        damage = (Byte)RWeaponBehavior.AmmoUsage[i + 1];
                    }
                    in_Patch.Add(weapon + EDmgVsBoss.Offset.Machine, damage, String.Format("{0} Damage to Wily Machine", weapon.WeaponName));
                    WilyWeaknesses[4, i + 1] = damage;
                }
                // Machine immune
                else
                {
                    in_Patch.Add(weapon + EDmgVsBoss.Offset.Machine, 0x00, String.Format("{0} Damage to Wily Machine", weapon.WeaponName));
                    WilyWeaknesses[4, i + 1] = 0x00;
                }

                // Get index of this weapon out of all weapons 0-8;
                Byte wIndex = (Byte)(i + 1);

                if (weapon == EDmgVsBoss.ClashBomber || weapon == EDmgVsBoss.MetalBlade)
                {
                    wIndex++;
                }

                // Disable weakness 1 and 2 on Wily Machine Phase 1
                if (weapon == bossWeak1)
                {
                    in_Patch.Add(0x02DA2E, wIndex, String.Format("Wily Machine Phase 1 Resistance 1 ({0})", weapon.WeaponName));
                }
                if (weapon == bossWeak2)
                {
                    in_Patch.Add(0x02DA32, wIndex, String.Format("Wily Machine Phase 1 Resistance 2 ({0})", weapon.WeaponName));
                }
                // Disable weakness 3 on Wily Machine Phase 2
                if (weapon == bossWeak3)
                {
                    in_Patch.Add(0x02DA3A, wIndex, String.Format("Wily Machine Phase 2 Resistance ({0})", weapon.WeaponName));
                }
            }

            #endregion

            #region Alien

            // Alien
            // Buster Heat Air Wood Bubble Quick Clash Metal
            Byte alienDamage = 1;
            List <EDmgVsBoss> alienWeapons = EDmgVsBoss.GetTables(true, false);
            Int32             rWeaponIndex = in_Seed.NextInt32(alienWeapons.Count);

            // Deal two damage for 1-ammo weapons (or buster)
            if (RWeaponBehavior.AmmoUsage[rWeaponIndex] == 1)
            {
                alienDamage = 2;
            }
            // For 2+ ammo use weapons, deal 20% more than that in damage, rounded up
            else if (RWeaponBehavior.AmmoUsage[rWeaponIndex] > 1)
            {
                alienDamage = (Byte)Math.Ceiling(RWeaponBehavior.AmmoUsage[rWeaponIndex] * 1.2);
            }

            // Apply weakness and erase others (flash will remain 0xFF)
            for (Int32 i = 0; i < alienWeapons.Count; i++)
            {
                EDmgVsBoss weapon = alienWeapons[i];

                if (i == rWeaponIndex)
                {
                    in_Patch.Add(weapon + EDmgVsBoss.Offset.Alien, alienDamage, String.Format("{0} Damage to Alien", weapon.WeaponName));
                    WilyWeaknesses[5, i] = alienDamage;
                }
                else
                {
                    in_Patch.Add(weapon + EDmgVsBoss.Offset.Alien, 0xFF, String.Format("{0} Damage to Alien", weapon.WeaponName));
                    WilyWeaknesses[5, i] = 0xFF;
                }
            }

            #endregion

            debug.AppendLine("Wily Boss Weaknesses:");
            debug.AppendLine("P\tH\tA\tW\tB\tQ\tF\tM\tC:");
            debug.AppendLine("--------------------------------------------");
            for (Int32 i = 0; i < WilyWeaknesses.GetLength(0); i++)
            {
                for (Int32 j = 0; j < WilyWeaknesses.GetLength(1); j++)
                {
                    debug.Append(String.Format("{0}\t", WilyWeaknesses[i, j]));

                    if (j == 5)
                    {
                        debug.Append("X\t");     // skip flash
                    }
                }
                String bossName = "";
                switch (i)
                {
                case 0:
                    bossName = "dragon";
                    break;

                case 1:
                    bossName = "picopico-kun";
                    break;

                case 2:
                    bossName = "guts";
                    break;

                case 3:
                    bossName = "boobeam";
                    break;

                case 4:
                    bossName = "machine";
                    break;

                case 5:
                    bossName = "alien";
                    break;

                default: break;
                }
                debug.AppendLine("< " + bossName);
            }
            debug.Append(Environment.NewLine);
        } // End method RandomizeWilyUJ