Esempio n. 1
0
        private EnemyTransportCollection SelectCrossLevelEnemies(TR2CombinedLevel level, int reduceEnemyCountBy = 0)
        {
            // For the assault course, nothing will be imported for the time being
            if (level.IsAssault)
            {
                return(null);
            }

            // Get the list of enemy types currently in the level
            List <TR2Entities> oldEntities = TR2EntityUtilities.GetEnemyTypeDictionary()[level.Name];

            // Work out how many we can support
            int enemyCount = oldEntities.Count - reduceEnemyCountBy + EnemyUtilities.GetEnemyAdjustmentCount(level.Name);
            List <TR2Entities> newEntities = new List <TR2Entities>(enemyCount);

            List <TR2Entities> chickenGuisers = EnemyUtilities.GetEnemyGuisers(TR2Entities.BirdMonster);
            TR2Entities        chickenGuiser  = TR2Entities.BirdMonster;

            // #148 For HSH, we lock the enemies that are required for the kill counter to work outside
            // the gate, which means the game still has the correct target kill count, while allowing
            // us to randomize the ones inside the gate (except the final shotgun goon).
            // If however, we are on the final packing attempt, we will just change the stick goon
            // alias and add docile bird monsters (if selected) as this is known to be supported.
            if (level.Is(LevelNames.HOME) && reduceEnemyCountBy > 0)
            {
                TR2Entities        newGoon = TR2Entities.StickWieldingGoon1BlackJacket;
                List <TR2Entities> goonies = TR2EntityUtilities.GetEntityFamily(newGoon);
                do
                {
                    newGoon = goonies[_generator.Next(0, goonies.Count)];
                }while (newGoon == TR2Entities.StickWieldingGoon1BlackJacket);

                newEntities.AddRange(oldEntities);
                newEntities.Remove(TR2Entities.StickWieldingGoon1);
                newEntities.Add(newGoon);

                if (DocileBirdMonsters)
                {
                    newEntities.Remove(TR2Entities.MaskedGoon1);
                    newEntities.Add(TR2Entities.BirdMonster);
                    chickenGuiser = TR2Entities.MaskedGoon1;
                }
            }
            else
            {
                // Do we need at least one water creature?
                bool waterEnemyRequired = EnemyUtilities.IsWaterEnemyRequired(level);
                // Do we need at least one enemy that can drop?
                bool droppableEnemyRequired = EnemyUtilities.IsDroppableEnemyRequired(level);

                // Let's try to populate the list. Start by adding one water enemy
                // and one droppable enemy if they are needed.
                if (waterEnemyRequired)
                {
                    List <TR2Entities> waterEnemies = TR2EntityUtilities.KillableWaterCreatures();
                    TR2Entities        entity;
                    do
                    {
                        entity = waterEnemies[_generator.Next(0, waterEnemies.Count)];
                    }while (!EnemyUtilities.IsEnemySupported(level.Name, entity));
                    newEntities.Add(entity);
                }

                if (droppableEnemyRequired)
                {
                    List <TR2Entities> droppableEnemies = TR2EntityUtilities.GetCrossLevelDroppableEnemies(!ProtectMonks);
                    TR2Entities        entity;
                    do
                    {
                        entity = droppableEnemies[_generator.Next(0, droppableEnemies.Count)];
                    }while (!EnemyUtilities.IsEnemySupported(level.Name, entity));
                    newEntities.Add(entity);
                }

                // Are there any other types we need to retain?
                foreach (TR2Entities entity in EnemyUtilities.GetRequiredEnemies(level.Name))
                {
                    if (!newEntities.Contains(entity))
                    {
                        newEntities.Add(entity);
                    }
                }

                // Get all other candidate enemies and fill the list
                List <TR2Entities> allEnemies = TR2EntityUtilities.GetCandidateCrossLevelEnemies();

                while (newEntities.Count < newEntities.Capacity)
                {
                    TR2Entities entity = allEnemies[_generator.Next(0, allEnemies.Count)];

                    // Make sure this isn't known to be unsupported in the level
                    if (!EnemyUtilities.IsEnemySupported(level.Name, entity))
                    {
                        continue;
                    }

                    // If it's the chicken in HSH but we're not using docile, we don't want it ending the level
                    if (!DocileBirdMonsters && entity == TR2Entities.BirdMonster && level.Is(LevelNames.HOME))
                    {
                        continue;
                    }

                    // If it's a docile chicken in Barkhang, it won't work because we can't disguise monks in this level.
                    if (DocileBirdMonsters && entity == TR2Entities.BirdMonster && level.Is(LevelNames.MONASTERY))
                    {
                        continue;
                    }

                    // If this is a tracked enemy throughout the game, we only allow it if the number
                    // of unique levels is within the limit. Bear in mind we are collecting more than
                    // one group of enemies per level.
                    if (_gameEnemyTracker.ContainsKey(entity) && !_gameEnemyTracker[entity].Contains(level.Name))
                    {
                        if (_gameEnemyTracker[entity].Count < _gameEnemyTracker[entity].Capacity)
                        {
                            // The entity is allowed, so store the fact that this level will have it
                            _gameEnemyTracker[entity].Add(level.Name);
                        }
                        else
                        {
                            // Otherwise, pick something else
                            continue;
                        }
                    }

                    // GetEntityFamily returns all aliases for the likes of the tigers, but if an entity
                    // doesn't have any, the returned list just contains the entity itself. This means
                    // we can avoid duplicating standard enemies as well as avoiding alias-clashing.
                    List <TR2Entities> family = TR2EntityUtilities.GetEntityFamily(entity);
                    if (!newEntities.Any(e1 => family.Any(e2 => e1 == e2)))
                    {
                        // #144 We can include docile chickens provided we aren't including everything
                        // that can be disguised as a chicken.
                        if (DocileBirdMonsters)
                        {
                            bool guisersAvailable = !chickenGuisers.All(g => newEntities.Contains(g));
                            // If the selected entity is the chicken, it can be added provided there are
                            // available guisers.
                            if (!guisersAvailable && entity == TR2Entities.BirdMonster)
                            {
                                continue;
                            }

                            // If the selected entity is a potential guiser, it can only be added if it's not
                            // the last available guiser. Otherwise, it will become the guiser.
                            if (chickenGuisers.Contains(entity) && newEntities.Contains(TR2Entities.BirdMonster))
                            {
                                if (newEntities.FindAll(e => chickenGuisers.Contains(e)).Count == chickenGuisers.Count - 1)
                                {
                                    continue;
                                }
                            }
                        }

                        newEntities.Add(entity);
                    }
                }
            }

            // #144 Decide at this point who will be guising unless it has already been decided above (e.g. HSH)
            if (DocileBirdMonsters && newEntities.Contains(TR2Entities.BirdMonster) && chickenGuiser == TR2Entities.BirdMonster)
            {
                int guiserIndex = chickenGuisers.FindIndex(g => !newEntities.Contains(g));
                if (guiserIndex != -1)
                {
                    chickenGuiser = chickenGuisers[guiserIndex];
                }
            }

            return(new EnemyTransportCollection
            {
                EntitiesToImport = newEntities,
                EntitiesToRemove = oldEntities,
                BirdMonsterGuiser = chickenGuiser
            });
        }