예제 #1
0
        /// <summary>
        /// Read enemylist.csv to construct EnemyInstances.
        /// </summary>
        private void ReadEnemyInstancesFromFile()
        {
            string[] lines = Properties.Resources.enemylist.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);

            foreach (string line in lines)
            {
                if (line.StartsWith("#"))
                {
                    continue;                       // Ignore comment lines
                }
                string[] cols = line.Split(new char[] { ',' });

                EnemyInstance enemy = new EnemyInstance(
                    Convert.ToInt32(cols[0], 16), // Index
                    Convert.ToInt32(cols[1], 16), // StageNum
                    Convert.ToInt32(cols[2], 16), // RoomNum
                    Convert.ToInt32(cols[3], 16), // ScreenNum
                    Convert.ToBoolean(cols[4]),   // IsActive
                    Convert.ToInt32(cols[5], 16), // EnemyID
                    Convert.ToInt32(cols[6], 16), // XPosOriginal
                    Convert.ToInt32(cols[7], 16), // YPosOriginal
                    Convert.ToInt32(cols[8], 16), // YPosAir
                    Convert.ToInt32(cols[9], 16), // YPosGround
                    Convert.ToBoolean(cols[10])); // FaceRight
                EnemyInstances.Add(enemy);
            }
        }
예제 #2
0
        /// <summary>
        /// Read enemylist.csv to construct EnemyInstances.
        /// </summary>
        private void ReadEnemyInstancesFromFile()
        {
            EnemySet enemySet = Properties.Resources.EnemySet.Deserialize <EnemySet>();

            foreach (Enemy enemy in enemySet)
            {
                EnemyInstance enemyInstance = new EnemyInstance(
                    Convert.ToInt32(enemy.Index, 16),
                    Convert.ToInt32(enemy.StageNumber, 16),
                    Convert.ToInt32(enemy.RoomNumber, 16),
                    Convert.ToInt32(enemy.ScreenNumber, 16),
                    enemy.IsActive,
                    Convert.ToInt32(enemy.EnemyId, 16),
                    Convert.ToInt32(enemy.PositionX, 16),
                    Convert.ToInt32(enemy.PositionY, 16),
                    Convert.ToInt32(enemy.PositionAir, 16),
                    Convert.ToInt32(enemy.PositionGround, 16),
                    enemy.FaceRight);

                EnemyInstances.Add(enemyInstance);
            }
        }
예제 #3
0
        private void Execute(Patch Patch, Random r)
        {
            foreach (SpriteBankRoomGroup sbrg in RoomGroups)
            {
                // Skip processing the room if every sprite bank row is taken
                if (sbrg.IsSpriteRestricted && sbrg.SpriteBankRowsRestriction.Count >= 6)
                {
                    continue;
                }

                // Create valid random combination of enemies to place
                List <EnemyType> newEnemies = GenerateEnemyCombinations(sbrg, r);

                // No enemy can fit in this room for some reason, skip this room (GFX will be glitched)
                if (newEnemies.Count == 0)
                {
                    continue;
                }

                // For each enemy ID (in the room, in the room-group), change to a random enemy from the new set
                for (int i = 0; i < sbrg.Rooms.Count; i++)
                {
                    Room room = sbrg.Rooms[i];
                    for (int j = 0; j < room.EnemyInstances.Count; j++)
                    {
                        EnemyInstance instance = room.EnemyInstances[j];

                        int       randomIndex  = r.Next(newEnemies.Count);
                        EnemyType newEnemyType = newEnemies[randomIndex];
                        byte      newId        = (byte)newEnemyType.ID;

                        // When placing the last enemy, If room contains an activator, manually change the last spawn in the room to be its deactivator
                        if (j == room.EnemyInstances.Count - 1)
                        {
                            EEnemyID?activator = room.GetActivatorIfOneHasBeenAdded();
                            if (activator != null)
                            {
                                newId = (byte)EnemyType.GetCorrespondingDeactivator((EEnemyID)activator);
                            }

                            // Also, if this last instance is an activator, try to replace it
                            if (EnemyType.CheckIsActivator(newId))
                            {
                                newId = TryReplaceActivator(newEnemies, newId);

                                // Update the new enemy type because it may require different graphics
                                if (!EnemyType.CheckIsDeactivator(newId))
                                {
                                    newEnemyType = newEnemies.Where(x => (byte)x.ID == newId).First();
                                }
                            }
                        }

                        sbrg.NewEnemyTypes.Add(newEnemyType); // TODO: This all should be refactored. Use a hashtable of EnemyTypes and abolish "EnemyID".

                        // If room contains only this one enemy and it is an activator
                        // TODO: How does Clash stage work with the Pipis? They don't break normally.
                        if ((room.EnemyInstances.Count == 1 && instance.HasNewActivator()))
                        {
                            // Try to replace it with a non-activator enemy
                            //newId = TryReplaceActivator(newEnemies, newId);
                        }

                        // Last-minute adjustments to certain enemy spawns
                        switch ((EEnemyID)newId)
                        {
                        case EEnemyID.Shrink:
                            double randomSpawner = r.NextDouble();
                            if (randomSpawner < CHANCE_SHRINKSPAWNER)
                            {
                                newId = (byte)EEnemyID.Shrink_Spawner;
                            }
                            break;

                        case EEnemyID.Shotman_Left:
                            if (instance.IsFaceRight)
                            {
                                newId = (byte)EEnemyID.Shotman_Right;
                            }
                            break;

                        default: break;
                        }

                        // Update object with new ID for future use
                        room.EnemyInstances[j].EnemyID = newId;

                        // Change the enemy ID in the ROM
                        int IDposition = Stage0EnemyIDAddress +
                                         instance.StageNum * StageLength +
                                         instance.Offset;

                        Patch.Add(IDposition, newId, $"{sbrg.Stage.ToString("G")} Stage Enemy #{instance.Offset} ID (Room {instance.RoomNum}) {((EEnemyID)instance.EnemyID).ToString("G")}");

                        // Change the enemy Y pos based on Air or Ground category
                        int newY = newEnemyType.YAdjust;
                        newY      += (newEnemyType.IsYPosAir) ? instance.YAir : instance.YGround;
                        IDposition = Stage0EnemyYAddress +
                                     instance.StageNum * StageLength +
                                     instance.Offset;
                        Patch.Add(IDposition, (byte)newY, $"{sbrg.Stage.ToString("G")} Stage Enemy #{instance.Offset} Y (Room {instance.RoomNum}) {((EEnemyID)instance.EnemyID).ToString("G")}");
                    }
                }

                // Change sprite banks for the room
                foreach (EnemyType e in sbrg.NewEnemyTypes)
                {
                    for (int i = 0; i < e.SpriteBankRows.Count; i++)
                    {
                        int rowInSlotAddress = sbrg.PatternAddressStart + e.SpriteBankRows[i] * 2;
                        int patternTblPtr1   = e.PatternTableAddresses[2 * i];
                        int patternTblPtr2   = e.PatternTableAddresses[2 * i + 1];

                        Patch.Add(rowInSlotAddress, (byte)patternTblPtr1, $"{sbrg.Stage.ToString("G")} Stage Sprite Bank Slot ? Row {e.SpriteBankRows[i]} Indirect Address 1");
                        Patch.Add(rowInSlotAddress + 1, (byte)patternTblPtr2, $"{sbrg.Stage.ToString("G")} Stage Sprite Bank Slot ? Row {e.SpriteBankRows[i]} Indirect Address 2");
                    }
                }
            } // end foreach sbrg
        }
예제 #4
0
        private void InitializeRooms()
        {
            // First, create a list of every room-group that refers to a specifc Sprite Bank Slot.

            // Heatman & Wily 1 stage enemies
            // NOTE: Can only use sprite banks 0-5
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.HeatW1, 0x003470, new int[] { 0, 12 }));       // Bank 0
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.HeatW1, 0x003482, new int[] { 3, 8, 9, 10 })); // Bank 1
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.HeatW1, 0x003494, new int[] { 1, 2 },          // Bank 2
                                                   new int[] { 3 }, new byte[] { 0x97, 0x03 }));           // Force Yoku blocks
            // Heat Bank 3 - Heat fight 0x0034A6
            // Heat Bank 4 - Dragon fight
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.HeatW1, 0x0034ca, new int[] { 7 })); // Bank 5

            // Airman & Wily 2 stage enemies
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.AirW2, 0x007470, new int[] { 0 })); // Bank 0 - Lightning Goro room
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.AirW2, 0x007482, new int[] { 2 })); // Bank 1
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.AirW2, 0x007494, new int[] { 1 })); // Bank 2
            // Air Bank 3 - Air fight 0x0074A6
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.AirW2, 0x0074b8, new int[] { 5 })); // Bank 4
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.AirW2, 0x0074ca, new int[] { 7 })); // Bank 5
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.AirW2, 0x0074dc, new int[] { 9 })); // Bank 6
            // Air Bank 7 - Picopico-kun fight

            // Woodman & Wily 3 stage enemies
            // NOTE: Access to sprite banks 0-7, plus extra banks 0x90 and 0xA2
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.WoodW3, 0x00b470, new int[] { 10, 22 })); // Bank 0; Moved Room 10 from bank 3
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.WoodW3, 0x00B482, new int[] { 1, 6 }));   // Bank 1
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.WoodW3, 0x00B494, new int[] { 7 }));      // Bank 2
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.WoodW3, 0x00B4A6, new int[] { 0 }));      // Bank 3
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.WoodW3, 0x00B4B8, new int[] { 11 }));     // Bank 4
            // Rooms.Add(new EnemyRoom(EStageID.WoodW3, 0x00B4CA, new int[] { 2, 3, 4 })); // Bank 5 - Friender rooms
            // Wood Bank 6 - Wood fight 0x00B4DC
            // Wood Bank 7 - Gutsdozer fight
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.WoodW3, 0x00b500, new int[] { 8, 16 })); // Bank ? (0x90); Moved Room 8 from bank 3
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.WoodW3, 0x00b512, new int[] { 9, 17 })); // Bank ? (0xA2); Moved Room 9 from bank 3

            // Bubbleman & Wily 4 stage enemies
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.BubbleW4, 0x00F470, new int[] { 0, 5 },             // Bank 0
                                                   new int[] { 2 }, new byte[] { 0x9D, 0x02 }));                // Falling platform sprite
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.BubbleW4, 0x00F482, new int[] { 1, 2, 3 }));        // Bank 1
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.BubbleW4, 0x00F494, new int[] { 4 },                // Bank 2
                                                   new int[] { 0, 1 }, new byte[] { 0x9E, 0x02, 0x9F, 0x02 })); // Shrimp sprites
            // Bubble Bank 3 - Bubbleman fight 0x00F4A6
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.BubbleW4, 0x00f4b8, new int[] { 9, 10, 13 }));      // Bank 4
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.BubbleW4, 0x00f4ca, new int[] { 15, 17 },           // Bank 5
                                                   new int[] { 3 }, new byte[] { 0x95, 0x03 }));                // Moving platform sprite
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.BubbleW4, 0x00f4dc, new int[] { 19 }));             // Bank 6

            // Quick
            // Quick Bank 0 - Used in empty room only
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.QuickW5, 0x013482, new int[] { 7 }));                                 // Bank 1
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.QuickW5, 0x013494, new int[] { 15 }));                                // Bank 2
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.QuickW5, 0x0134A6, new int[] { 3, 4, 5, 8, 9, 10, 11, 12, 13, 14 })); // Bank 3
            // Quick Bank 4 - Quick fight // 0x0134B8
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.QuickW5, 0x0134CA, new int[] { 1, 2 }));                              // Bank 5
            // Quick Bank 6 - W5 Teleporters
            // Quick Bank 7 - Wily Machine

            // Flash
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.FlashW6, 0x017470, new int[] { 0, 3, 5 })); // Bank 0
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.FlashW6, 0x017482, new int[] { 1, 6, 7 })); // Bank 1
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.FlashW6, 0x017494, new int[] { 2, 4 }));    // Bank 2; Moved room 2 from bank 0
            // Flash Bank 3: Flashman fight 0x0174A6
            // Flash Bank 4: W6 Alien fight
            // Flash Bank 5: Wily defeated cutscene?
            // Flash Bank 6: Droplets

            // Metal
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.Metal, 0x01B470, new int[] { 0, 1 }));
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.Metal, 0x01B482, new int[] { 2 }));
            // Metal fight 0x01B494

            // Clash
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.Clash, 0x01f494, new int[] { 0, 3, 4, 5 },
                                                   new int[] { 3 }, new byte[] { 0x95, 0x03 })); // Moving platform sprites
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.Clash, 0x01f482, new int[] { 2, 8, 9 }));
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.Clash, 0x01f4a6, new int[] { 6, 7 }));
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.Clash, 0x01f470, new int[] { 10, 11, 12 }));
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.Clash, 0x01f4b8, new int[] { 1 })); // Slot 4, changed from empty room 13 to room 1
            RoomGroups.Add(new SpriteBankRoomGroup(EStageID.Clash, 0x01f4ca, new int[] { 14 }));
            // Clash fight 0x01F4DC

            // Get copy of enemy spawn list to save time
            List <EnemyInstance> usedInstances = new List <EnemyInstance>(EnemyInstances);

            // First, loop back through entire list of room-groups. For each, loop through entire list of
            // enemies, match them and assign them to their room-group. Assigned enemies are removed from
            // the list, reducing the search time on each loop. For now, completely discard all enemy spawns
            // that are reserved (i.e. Yoku blocks in Heat, beams in Quick).
            foreach (SpriteBankRoomGroup sbrg in RoomGroups)
            {
                int stageNum = (int)sbrg.Stage;

                // Find index of first enemy instance in the stage
                int i = 0;
                for (i = 0; i < usedInstances.Count; i++)
                {
                    if (usedInstances.ElementAt(i).StageNum == stageNum)
                    {
                        break;
                    }
                }

                // Check each applicable room number for this room group
                for (int roomIndex = 0; roomIndex < sbrg.Rooms.Count; roomIndex++)
                {
                    Room room = sbrg.Rooms[roomIndex];

                    // Find first occurrence of this room num in enemy list (starting at this stage)
                    int j = 0;
                    while (i + j < usedInstances.Count)
                    {
                        EnemyInstance checkEnemy = usedInstances.ElementAt(i + j);

                        // Only add non-required enemy instances to the randomizer
                        if (checkEnemy.IsActive)
                        {
                            // Enemy instance stage/room num match one described in a SpriteBankRoomGroup
                            if (checkEnemy.RoomNum == room.RoomNum && checkEnemy.StageNum == stageNum)
                            {
                                // Add enemy to room group
                                room.EnemyInstances.Add(checkEnemy);

                                // Remove enemy from temporary list
                                usedInstances.RemoveAt(i + j);
                            }
                            else
                            {
                                // Only inc j here since an element hasn't been removed
                                j++;
                            }
                        }
                        else
                        {
                            // Remove unrandomizable enemy from temporary list
                            usedInstances.RemoveAt(i + j);
                        }
                    } // end while
                }     // end foreach roomNum in sbrg
            }         // end foreach sbrg
        }
예제 #5
0
        /// <summary>
        /// Read enemylist.csv to construct EnemyInstances.
        /// </summary>
        private void ReadEnemyInstancesFromFile()
        {
            using (StreamReader sr = new StreamReader("enemylist.csv"))
            {
                while (!sr.EndOfStream)
                {
                    string line = sr.ReadLine();
                    if (line.StartsWith("#")) continue; // Ignore comment lines

                    string[] cols = line.Split(new char[] { ',' });

                    EnemyInstance enemy = new EnemyInstance(
                        Convert.ToInt32(cols[0], 16), // Index
                        Convert.ToInt32(cols[1], 16), // StageNum
                        Convert.ToInt32(cols[2], 16), // RoomNum
                        Convert.ToInt32(cols[3], 16), // ScreenNum
                        Convert.ToBoolean(cols[4]),   // IsActive
                        Convert.ToInt32(cols[5], 16), // EnemyID
                        Convert.ToInt32(cols[6], 16), // XPosOriginal
                        Convert.ToInt32(cols[7], 16), // YPosOriginal
                        Convert.ToInt32(cols[8], 16), // YPosAir
                        Convert.ToInt32(cols[9], 16), // YPosGround
                        Convert.ToBoolean(cols[10])); // FaceRight
                    EnemyInstances.Add(enemy);
                }
            }
        }