Пример #1
0
        private void RepositionItems(List <Location> ItemLocs)
        {
            if (ItemLocs.Count > 0)
            {
                //We are currently looking guns + ammo
                List <TR2Entities> targetents = TR2EntityUtilities.GetListOfGunTypes();
                targetents.AddRange(TR2EntityUtilities.GetListOfAmmoTypes());

                for (int i = 0; i < _levelInstance.Entities.Count(); i++)
                {
                    if (targetents.Contains((TR2Entities)_levelInstance.Entities[i].TypeID) && (i != _planeCargoWeaponIndex))
                    {
                        Location RandomLocation = ItemLocs[_generator.Next(0, ItemLocs.Count)];

                        Location GlobalizedRandomLocation = SpatialConverters.TransformToLevelSpace(RandomLocation, _levelInstance.Rooms[RandomLocation.Room].Info);

                        _levelInstance.Entities[i].Room       = Convert.ToInt16(GlobalizedRandomLocation.Room);
                        _levelInstance.Entities[i].X          = GlobalizedRandomLocation.X;
                        _levelInstance.Entities[i].Y          = GlobalizedRandomLocation.Y;
                        _levelInstance.Entities[i].Z          = GlobalizedRandomLocation.Z;
                        _levelInstance.Entities[i].Intensity1 = -1;
                        _levelInstance.Entities[i].Intensity2 = -1;
                    }
                }
            }
        }
Пример #2
0
            protected override void StartImpl()
            {
                // Make the outfit selection outwith the processing thread to ensure consistent RNG.
                // We select all potential Laras including the default for the level as there are
                // only 4 to choose from (there is also Assault Course Lara, but when holstering pistols
                // her trousers disappear, so she is excluded for the time being...).
                List <TR2Entities>      allLaras = TR2EntityUtilities.GetLaraTypes();
                List <TR2CombinedLevel> levels   = new List <TR2CombinedLevel>(_outfitAllocations.Keys);

                foreach (TR2CombinedLevel level in levels)
                {
                    // Add the persistent outfit first, but we will populate the candidate
                    // list regardless in case a level cannot support this choice.
                    if (_outer.PersistOutfits)
                    {
                        _outfitAllocations[level].Add(_outer._persistentOutfit);
                    }

                    while (_outfitAllocations[level].Count < allLaras.Count)
                    {
                        TR2Entities nextLara = allLaras[_outer._generator.Next(0, allLaras.Count)];
                        if (!_outfitAllocations[level].Contains(nextLara))
                        {
                            _outfitAllocations[level].Add(nextLara);
                        }
                    }
                }
            }
Пример #3
0
        private void AdjustEntities()
        {
            // If an entity is marked to be removed but is also in the list
            // to import, don't remove it in the first place.
            List <TR2Entities> cleanedEntities = new List <TR2Entities>();

            foreach (TR2Entities entity in EntitiesToRemove)
            {
                if (_aliasMap.ContainsKey(entity))
                {
                    // Check if we have another alias in the import list different from any
                    // in the current level
                    TR2Entities alias       = TR2EntityUtilities.GetAliasForLevel(LevelName, entity);
                    TR2Entities importAlias = TR2Entities.Lara;
                    foreach (TR2Entities a in _aliasMap[entity])
                    {
                        if (EntitiesToImport.Contains(a))
                        {
                            importAlias = a;
                            break;
                        }
                    }

                    if (alias != importAlias)
                    {
                        cleanedEntities.Add(entity);
                    }
                }
                else if (!EntitiesToImport.Contains(entity))
                {
                    cleanedEntities.Add(entity);
                }
            }
            EntitiesToRemove = cleanedEntities;
        }
Пример #4
0
        private void RandomizeEnemiesNatively(TR2CombinedLevel level)
        {
            // For the assault course, nothing will be changed for the time being
            if (level.IsAssault)
            {
                return;
            }

            List <TR2Entities> availableEnemyTypes = TR2EntityUtilities.GetEnemyTypeDictionary()[level.Name];
            List <TR2Entities> droppableEnemies    = TR2EntityUtilities.DroppableEnemyTypes()[level.Name];
            List <TR2Entities> waterEnemies        = TR2EntityUtilities.FilterWaterEnemies(availableEnemyTypes);

            if (DocileBirdMonsters && level.Is(LevelNames.CHICKEN))
            {
                DisguiseEntity(level, TR2Entities.MaskedGoon1, TR2Entities.BirdMonster);
            }

            RandomizeEnemies(level, new EnemyRandomizationCollection
            {
                Available         = availableEnemyTypes,
                Droppable         = droppableEnemies,
                Water             = waterEnemies,
                BirdMonsterGuiser = TR2Entities.MaskedGoon1 // If randomizing natively, this will only apply to Ice Palace
            });
        }
Пример #5
0
 private void SetPersistentOutfit()
 {
     if (PersistOutfits)
     {
         List <TR2Entities> allLaras = TR2EntityUtilities.GetLaraTypes();
         _persistentOutfit = allLaras[_generator.Next(0, allLaras.Count)];
     }
 }
Пример #6
0
            // This is triggered synchronously after the import work to ensure the RNG remains consistent
            internal void ApplyRandomization()
            {
                foreach (TR2CombinedLevel level in _enemyMapping.Keys)
                {
                    if (!level.IsAssault)
                    {
                        EnemyTransportCollection importedCollection = null;
                        foreach (EnemyTransportCollection enemies in _enemyMapping[level])
                        {
                            if (enemies.ImportResult)
                            {
                                importedCollection = enemies;
                                break;
                            }
                        }

                        if (importedCollection == null)
                        {
                            // Cross-level was not possible with the enemy combinations. This could be due to either
                            // a lack of space for texture packing, or the max ObjectTexture count (2048) was reached.
                            _outer.TextureMonitor.RemoveMonitor(level.Name);

                            // And just randomize normally
                            // TODO: maybe trigger a warning to display at the end of randomizing to say that cross-
                            // level was not possible?
                            _outer.RandomizeEnemiesNatively(level);
                            //System.Diagnostics.Debug.WriteLine(level.Name + ": Native enemies");
                        }
                        else
                        {
                            // The import worked, so randomize the entities based on what we now have in place.
                            //System.Diagnostics.Debug.WriteLine(level.Name + ": " + string.Join(", ", importedCollection.EntitiesToImport));
                            EnemyRandomizationCollection enemies = new EnemyRandomizationCollection
                            {
                                Available = importedCollection.EntitiesToImport,
                                Droppable = TR2EntityUtilities.FilterDroppableEnemies(importedCollection.EntitiesToImport, !_outer.ProtectMonks),
                                Water     = TR2EntityUtilities.FilterWaterEnemies(importedCollection.EntitiesToImport)
                            };

                            if (_outer.DocileBirdMonsters && importedCollection.BirdMonsterGuiser != TR2Entities.BirdMonster)
                            {
                                _outer.DisguiseEntity(level, importedCollection.BirdMonsterGuiser, TR2Entities.BirdMonster);
                                enemies.BirdMonsterGuiser = importedCollection.BirdMonsterGuiser;
                            }

                            _outer.RandomizeEnemies(level, enemies);
                        }

                        _outer.SaveLevel(level);
                    }

                    if (!_outer.TriggerProgress())
                    {
                        break;
                    }
                }
            }
Пример #7
0
        private void BuildDefinitionList(List <TRModelDefinition> standardModelDefinitions, List <TRModelDefinition> soundModelDefinitions, List <TR2Entities> modelEntities, TR2Entities nextEntity, bool isDependency)
        {
            if (modelEntities.Contains(nextEntity))
            {
                // If the model already in the list is a dependency only, but the new one to add isn't, switch it
                TRModelDefinition definition = standardModelDefinitions.Find(m => m.Alias == nextEntity);
                if (definition != null && definition.IsDependencyOnly && !isDependency)
                {
                    definition.IsDependencyOnly = false;
                }
                return;
            }

            TRModelDefinition nextDefinition = LoadDefinition(nextEntity);

            nextDefinition.IsDependencyOnly = isDependency;
            modelEntities.Add(nextEntity);

            // Add dependencies first
            foreach (TR2Entities dependency in nextDefinition.Dependencies)
            {
                // If it's a non-graphics dependency, but we are importing another alias
                // for it, or the level already contains the dependency, we don't need it.
                bool        nonGraphics = _noGraphicsEntityDependencies.Contains(dependency);
                TR2Entities aliasFor    = TR2EntityUtilities.TranslateEntityAlias(dependency);
                if (aliasFor != dependency && nonGraphics)
                {
                    bool required = true;
                    // #139 check entire model list for instances where alias and dependencies cause clashes
                    foreach (TR2Entities entity in modelEntities)
                    {
                        // If this entity and the dependency are in the same family
                        if (aliasFor == TR2EntityUtilities.TranslateEntityAlias(entity))
                        {
                            // Skip it
                            required = false;
                            break;
                        }
                    }

                    if (!required)
                    {
                        // We don't need the graphics, but do we need hardcoded sound?
                        if (_soundOnlyDependencies.Contains(dependency) && standardModelDefinitions.Find(m => m.Alias == dependency) == null)
                        {
                            soundModelDefinitions.Add(LoadDefinition(dependency));
                        }

                        continue;
                    }
                }

                BuildDefinitionList(standardModelDefinitions, soundModelDefinitions, modelEntities, dependency, nonGraphics);
            }

            standardModelDefinitions.Add(nextDefinition);
        }
Пример #8
0
        public static bool IsEnemySupported(string lvlName, TR2Entities entity)
        {
            if (_unsupportedEnemies.ContainsKey(lvlName))
            {
                return(!_unsupportedEnemies[lvlName].Contains(TR2EntityUtilities.TranslateEntityAlias(entity)));
            }

            return(true);
        }
Пример #9
0
        private void CleanAliases()
        {
            List <TR2Entities> cleanedEntities = new List <TR2Entities>();

            // Do we have any aliases?
            foreach (TR2Entities importEntity in EntitiesToImport)
            {
                if (_aliasMap.ContainsKey(importEntity))
                {
                    throw new TransportException(string.Format
                                                 (
                                                     "Cannot import ambiguous entity {0} - choose an alias from [{1}].",
                                                     importEntity.ToString(),
                                                     string.Join(", ", _aliasMap[importEntity])
                                                 ));
                }

                bool entityIsValid = true;
                if (_entityAliases.ContainsKey(importEntity))
                {
                    TR2Entities existingEntity = TR2EntityUtilities.GetAliasForLevel(LevelName, _entityAliases[importEntity]);
                    // This entity is only valid if the alias it's for is not already there
                    entityIsValid = importEntity != existingEntity;
                }

                if (entityIsValid)
                {
                    cleanedEntities.Add(importEntity);
                }
            }

            // #139 Ensure that aliases are added last so to avoid dependency issues
            cleanedEntities.Sort(delegate(TR2Entities e1, TR2Entities e2)
            {
                return(((short)TR2EntityUtilities.TranslateEntityAlias(e1)).CompareTo((short)TR2EntityUtilities.TranslateEntityAlias(e2)));
            });

            // For some reason, if the Barracuda is added before the shark, there is a slight animation
            // corruption on the shark's mouth. I can't find the reason. The patch is to ensure the
            // Barracuda is added last.
            // #137 Reason found - animated textures were not being reindexed following deduplication. See
            // TRModelExtensions.ReindexTextures and #137.

            /*int barracudaIndex = cleanedEntities.FindIndex(e => _aliasMap[TR2Entities.Barracuda].Contains(e));
             * if (barracudaIndex != -1)
             * {
             *  TR2Entities barracuda = cleanedEntities[barracudaIndex];
             *  cleanedEntities.RemoveAt(barracudaIndex);
             *  cleanedEntities.Add(barracuda);
             * }*/

            EntitiesToImport = cleanedEntities;
        }
Пример #10
0
        private Dictionary <int, TR2Entity> GetSecretItems(TR2Level level)
        {
            Dictionary <int, TR2Entity> entities = new Dictionary <int, TR2Entity>();

            for (int i = 0; i < level.NumEntities; i++)
            {
                if (TR2EntityUtilities.IsSecretType((TR2Entities)level.Entities[i].TypeID))
                {
                    entities[i] = level.Entities[i];
                }
            }

            return(entities);
        }
Пример #11
0
        private void SetNightMode(TR2CombinedLevel level)
        {
            foreach (TR2Room room in level.Data.Rooms)
            {
                room.Darken();
            }

            // Replace any entities that don't "make sense" at night
            List <TR2Entity> entities = level.Data.Entities.ToList();

            // A list of item locations to choose from
            List <TR2Entity> items = entities.Where
                                     (
                e =>
                TR2EntityUtilities.IsAmmoType((TR2Entities)e.TypeID) ||
                TR2EntityUtilities.IsGunType((TR2Entities)e.TypeID) ||
                TR2EntityUtilities.IsUtilityType((TR2Entities)e.TypeID)
                                     ).ToList();

            foreach (TR2Entities entityToReplace in _entitiesToReplace.Keys)
            {
                IEnumerable <TR2Entity> ents = entities.Where(e => e.TypeID == (short)entityToReplace);
                foreach (TR2Entity entity in ents)
                {
                    TR2Entity item = items[_generator.Next(0, items.Count)];
                    entity.TypeID     = (short)_entitiesToReplace[entityToReplace];
                    entity.Room       = item.Room;
                    entity.X          = item.X;
                    entity.Y          = item.Y;
                    entity.Z          = item.Z;
                    entity.Intensity1 = item.Intensity1;
                    entity.Intensity2 = item.Intensity2;
                }
            }

            // Hide any static meshes
            if (_staticMeshesToHide.ContainsKey(level.Name))
            {
                List <TRStaticMesh> staticMeshes = level.Data.StaticMeshes.ToList();
                foreach (uint meshID in _staticMeshesToHide[level.Name])
                {
                    TRStaticMesh mesh = staticMeshes.Find(m => m.ID == meshID);
                    if (mesh != null)
                    {
                        mesh.NonCollidable = true;
                        mesh.Visible       = false;
                    }
                }
            }
        }
Пример #12
0
        public int Compare(TR2CombinedLevel lvl1, TR2CombinedLevel lvl2)
        {
            int enemyCount1 = TR2EntityUtilities.GetEnemyTypeDictionary()[lvl1.Name].Count;
            int enemyCount2 = TR2EntityUtilities.GetEnemyTypeDictionary()[lvl2.Name].Count;

            int freeTiles1 = 16 - (int)lvl1.Data.NumImages;
            int freeTiles2 = 16 - (int)lvl2.Data.NumImages;

            if (freeTiles1 == freeTiles2)
            {
                return(enemyCount2.CompareTo(enemyCount1));
            }

            return((enemyCount1 * freeTiles1).CompareTo(enemyCount2 * freeTiles2));
        }
Пример #13
0
        public List <TR2Entity> GetEnemyEntities()
        {
            List <TR2Entities> allEnemies    = TR2EntityUtilities.GetFullListOfEnemies();
            List <TR2Entity>   levelEntities = new List <TR2Entity>();

            for (int i = 0; i < Data.NumEntities; i++)
            {
                TR2Entity entity = Data.Entities[i];
                if (allEnemies.Contains((TR2Entities)entity.TypeID))
                {
                    levelEntities.Add(entity);
                }
            }
            return(levelEntities);
        }
        internal List <TR2Entities> GetIgnoredEntities(string lvlName)
        {
            TexturePositionMonitor monitor = GetMonitor(lvlName);

            if (monitor != null && monitor.RemovedTextures != null)
            {
                List <TR2Entities> entities = new List <TR2Entities>();
                foreach (TR2Entities entity in monitor.RemovedTextures)
                {
                    entities.Add(TR2EntityUtilities.GetAliasForLevel(lvlName, entity));
                }
                return(entities);
            }
            return(null);
        }
Пример #15
0
        private void CleanPlaneCargo()
        {
            //In relation to #66 to ensure successive randomizations don't pollute the entity list
            List <TR2Entity> Entities = _levelInstance.Entities.ToList();

            int index = Entities.FindIndex(e => (e.Room == 1 && TR2EntityUtilities.IsAmmoType((TR2Entities)e.TypeID)));

            while (index != -1 && index < Entities.Count)
            {
                Entities.RemoveAt(index);
                _levelInstance.NumEntities--;
                index = Entities.FindIndex(e => (e.Room == 1 && TR2EntityUtilities.IsAmmoType((TR2Entities)e.TypeID)));
            }

            _levelInstance.Entities = Entities.ToArray();
        }
Пример #16
0
 public static bool IsWaterEnemyRequired(TR2CombinedLevel level)
 {
     foreach (TR2Entity entityInstance in level.Data.Entities)
     {
         TR2Entities entity = (TR2Entities)entityInstance.TypeID;
         if (TR2EntityUtilities.IsWaterCreature(entity))
         {
             if (!level.CanPerformDraining(entityInstance.Room))
             {
                 // Draining cannot be performed so we need to ensure we get at least one water enemy
                 return(true);
             }
         }
     }
     return(false);
 }
Пример #17
0
        private void RandomizeORPistol()
        {
            //Is there something in the plane cargo?
            if (_planeCargoWeaponIndex != -1)
            {
                List <TR2Entities> ReplacementWeapons = TR2EntityUtilities.GetListOfGunTypes();
                ReplacementWeapons.Add(TR2Entities.Pistols_S_P);

                TR2Entities Weap = ReplacementWeapons[_generator.Next(0, ReplacementWeapons.Count)];

                TR2Entity CargoWeapon = _levelInstance.Entities[_planeCargoWeaponIndex];

                //#68 - Provide some additional ammo for a weapon if not pistols
                switch (Weap)
                {
                case TR2Entities.Shotgun_S_P:
                    AddORAmmo(TR2Entities.ShotgunAmmo_S_P, 8, CargoWeapon);
                    break;

                case TR2Entities.Automags_S_P:
                    AddORAmmo(TR2Entities.AutoAmmo_S_P, 4, CargoWeapon);
                    break;

                case TR2Entities.Uzi_S_P:
                    AddORAmmo(TR2Entities.UziAmmo_S_P, 4, CargoWeapon);
                    break;

                case TR2Entities.Harpoon_S_P:
                    AddORAmmo(TR2Entities.HarpoonAmmo_S_P, 10, CargoWeapon);
                    break;

                case TR2Entities.M16_S_P:
                    AddORAmmo(TR2Entities.M16Ammo_S_P, 2, CargoWeapon);
                    break;

                case TR2Entities.GrenadeLauncher_S_P:
                    AddORAmmo(TR2Entities.GrenadeLauncher_S_P, 4, CargoWeapon);
                    break;

                default:
                    break;
                }

                CargoWeapon.TypeID = (short)Weap;
            }
        }
Пример #18
0
        private void PopulateHSHCloset()
        {
            List <TR2Entities> replacementWeapons = TR2EntityUtilities.GetListOfGunTypes();

            if (_levelInstance.Script.RemovesWeapons)
            {
                replacementWeapons.Add(TR2Entities.Pistols_S_P);
            }

            // Pick a new weapon, but exclude the grenade launcher because it affects the kill
            // count. Also exclude the harpoon as neither it nor the grenade launcher can break
            // Lara's bedroom window, and the enemy there may have been randomized to one without
            // a gun. Probably not a softlock scenario but safer to exclude for now.
            TR2Entities replacementWeapon;

            do
            {
                replacementWeapon = replacementWeapons[_generator.Next(0, replacementWeapons.Count)];
            }while (replacementWeapon == TR2Entities.GrenadeLauncher_S_P || replacementWeapon == TR2Entities.Harpoon_S_P);

            TR2Entities replacementAmmo = GetWeaponAmmo(replacementWeapon);

            List <TR2Entity> ents = _levelInstance.Data.Entities.ToList();

            foreach (TR2Entity entity in ents)
            {
                if (entity.Room != 57)
                {
                    continue;
                }

                TR2Entities entityType = (TR2Entities)entity.TypeID;
                if (TR2EntityUtilities.IsGunType(entityType))
                {
                    entity.TypeID = (short)replacementWeapon;
                }
                else if (TR2EntityUtilities.IsAmmoType(entityType) && replacementWeapon != TR2Entities.Pistols_S_P)
                {
                    entity.TypeID = (short)replacementAmmo;
                }
            }
        }
Пример #19
0
        private void RandomizeORPistol()
        {
            //#44 - Agreed to keep it there but randomize its type.
            _planeCargoWeaponIndex = Array.FindIndex(_levelInstance.Entities,
                                                     e => (e.TypeID == (int)TR2Entities.Pistols_S_P ||
                                                           e.TypeID == (int)TR2Entities.Shotgun_S_P ||
                                                           e.TypeID == (int)TR2Entities.Automags_S_P ||
                                                           e.TypeID == (int)TR2Entities.Uzi_S_P ||
                                                           e.TypeID == (int)TR2Entities.Harpoon_S_P ||
                                                           e.TypeID == (int)TR2Entities.M16_S_P ||
                                                           e.TypeID == (int)TR2Entities.GrenadeLauncher_S_P) && (e.Room == 1));

            //Is there something in the plane cargo?
            if (_planeCargoWeaponIndex != -1)
            {
                List <TR2Entities> ReplacementWeapons = TR2EntityUtilities.GetListOfGunTypes();
                ReplacementWeapons.Add(TR2Entities.Pistols_S_P);

                _levelInstance.Entities[_planeCargoWeaponIndex].TypeID = (short)ReplacementWeapons[_generator.Next(0, ReplacementWeapons.Count)];
            }
        }
Пример #20
0
        private void CleanUnarmedPistolLocation(Location levelPistolLocation)
        {
            //In relation to #66 to ensure successive randomizations don't pollute the entity list
            List <TR2Entity> entities = _levelInstance.Entities.ToList();

            // We need to ensure x,y,z also match rather than just the room as TRGE could have added pistols
            // (which may then have been randomized and/or ammo added) to a room that already had other ammo pickups.
            IEnumerable <TR2Entity> existingInjections = entities.Where
                                                         (
                e =>
                TR2EntityUtilities.IsAmmoType((TR2Entities)e.TypeID) &&
                e.Room == levelPistolLocation.Room &&
                e.X == levelPistolLocation.X &&
                e.Y == levelPistolLocation.Y &&
                e.Z == levelPistolLocation.Z
                                                         );

            if (existingInjections.Count() > 0)
            {
                // For Rig, if it's no longer unarmed, TRGE will have added UZI clips where the pistols normally
                // would be. This is to preserve item indices as the pistols have index 4 - to remove them completely
                // would mean anything that points to higher item indices (triggers etc) would need to change. The clips
                // can be safely randomized - it's just the index that needs to remain the same.
                if (_scriptedLevelInstance.Is(LevelNames.RIG))
                {
                    TR2Entity cargoEntity = existingInjections.FirstOrDefault();
                    entities.RemoveAll(e => existingInjections.Contains(e) && e != cargoEntity);
                }
                else
                {
                    entities.RemoveAll(e => existingInjections.Contains(e));
                }
                _levelInstance.NumEntities = (uint)entities.Count;
                _levelInstance.Entities    = entities.ToArray();
            }
        }
Пример #21
0
        public static bool IsDroppableEnemyRequired(TR2CombinedLevel level)
        {
            foreach (TR2Entity entityInstance in level.Data.Entities)
            {
                List <TR2Entity> sharedItems = new List <TR2Entity>(Array.FindAll
                                                                    (
                                                                        level.Data.Entities,
                                                                        e =>
                                                                        (
                                                                            e.X == entityInstance.X &&
                                                                            e.Y == entityInstance.Y &&
                                                                            e.Z == entityInstance.Z
                                                                        )
                                                                    ));
                if (sharedItems.Count > 1)
                {
                    // Are any entities that are sharing a location a droppable pickup?
                    foreach (TR2Entity ent in sharedItems)
                    {
                        TR2Entities EntType = (TR2Entities)ent.TypeID;

                        if
                        (
                            TR2EntityUtilities.IsUtilityType(EntType) ||
                            TR2EntityUtilities.IsGunType(EntType) ||
                            TR2EntityUtilities.IsKeyItemType(EntType)
                        )
                        {
                            return(true);
                        }
                    }
                }
            }

            return(false);
        }
Пример #22
0
        private void RepositionItems(List <Location> ItemLocs)
        {
            if (IsDevelopmentModeOn)
            {
                PlaceAllItems(ItemLocs);
                return;
            }

            if (ItemLocs.Count > 0)
            {
                //We are currently looking guns + ammo
                List <TR2Entities> targetents = TR2EntityUtilities.GetListOfGunTypes();
                targetents.AddRange(TR2EntityUtilities.GetListOfAmmoTypes());

                //And also key items...
                if (IncludeKeyItems)
                {
                    targetents.AddRange(TR2EntityUtilities.GetListOfKeyItemTypes());
                }

                //It's important to now start zoning key items as softlocks must be avoided.
                ZonedLocationCollection ZonedLocations = new ZonedLocationCollection();
                ZonedLocations.PopulateZones(_levelInstance.Name, ItemLocs, ZonePopulationMethod.KeyPuzzleQuestOnly);

                for (int i = 0; i < _levelInstance.Data.Entities.Count(); i++)
                {
                    if (targetents.Contains((TR2Entities)_levelInstance.Data.Entities[i].TypeID) && (i != _unarmedLevelPistolIndex))
                    {
                        Location RandomLocation        = new Location();
                        bool     FoundPossibleLocation = false;

                        if (TR2EntityUtilities.IsKeyItemType((TR2Entities)_levelInstance.Data.Entities[i].TypeID))
                        {
                            TR2Entities type = (TR2Entities)_levelInstance.Data.Entities[i].TypeID;

                            // Apply zoning for key items
                            switch (type)
                            {
                            case TR2Entities.Puzzle1_S_P:
                                if (ZonedLocations.Puzzle1Zone.Count > 0)
                                {
                                    if (_levelInstance.Name == LevelNames.DA)
                                    {
                                        int burnerChipID  = 120;
                                        int consoleChipID = 7;

                                        RandomLocation = ZonedLocations.Puzzle1Zone[_generator.Next(0, ZonedLocations.Puzzle1Zone.Count)];

                                        //Special case - multiple chips
                                        if (i == burnerChipID)
                                        {
                                            //Burner Chip
                                            List <int> AllowedBurnerRooms = new List <int>()
                                            {
                                                13, 14, 15, 16, 21, 22, 23, 24, 25, 26, 29, 30, 32, 34, 75, 80, 83, 84, 85, 86, 87, 88, 89
                                            };

                                            while (!AllowedBurnerRooms.Contains(RandomLocation.Room))
                                            {
                                                RandomLocation = ZonedLocations.Puzzle1Zone[_generator.Next(0, ZonedLocations.Puzzle1Zone.Count)];
                                            }

                                            FoundPossibleLocation = true;
                                        }
                                        else if (i == consoleChipID)
                                        {
                                            //Center Console Chip
                                            List <int> AllowedConsoleRooms = new List <int>()
                                            {
                                                2, 12, 13, 14, 15, 16, 17, 21, 22, 23, 24, 25, 26, 29, 30, 32, 34, 35, 64, 65, 66, 68, 69, 70, 75, 80, 82, 83, 84, 85, 86, 87, 88, 89
                                            };

                                            while (!AllowedConsoleRooms.Contains(RandomLocation.Room))
                                            {
                                                RandomLocation = ZonedLocations.Puzzle1Zone[_generator.Next(0, ZonedLocations.Puzzle1Zone.Count)];
                                            }

                                            FoundPossibleLocation = true;
                                        }
                                        else
                                        {
                                            RandomLocation        = ZonedLocations.Puzzle1Zone[_generator.Next(0, ZonedLocations.Puzzle1Zone.Count)];
                                            FoundPossibleLocation = true;
                                        }
                                    }
                                    else
                                    {
                                        RandomLocation        = ZonedLocations.Puzzle1Zone[_generator.Next(0, ZonedLocations.Puzzle1Zone.Count)];
                                        FoundPossibleLocation = true;
                                    }
                                }
                                break;

                            case TR2Entities.Puzzle2_S_P:
                                if (ZonedLocations.Puzzle2Zone.Count > 0)
                                {
                                    RandomLocation        = ZonedLocations.Puzzle2Zone[_generator.Next(0, ZonedLocations.Puzzle2Zone.Count)];
                                    FoundPossibleLocation = true;
                                }
                                break;

                            case TR2Entities.Puzzle3_S_P:
                                if (ZonedLocations.Puzzle3Zone.Count > 0)
                                {
                                    RandomLocation        = ZonedLocations.Puzzle3Zone[_generator.Next(0, ZonedLocations.Puzzle3Zone.Count)];
                                    FoundPossibleLocation = true;
                                }
                                break;

                            case TR2Entities.Puzzle4_S_P:
                                if (ZonedLocations.Puzzle4Zone.Count > 0)
                                {
                                    RandomLocation        = ZonedLocations.Puzzle4Zone[_generator.Next(0, ZonedLocations.Puzzle4Zone.Count)];
                                    FoundPossibleLocation = true;
                                }
                                break;

                            case TR2Entities.Key1_S_P:
                                if (ZonedLocations.Key1Zone.Count > 0)
                                {
                                    if (_levelInstance.Name == LevelNames.OPERA)
                                    {
                                        int startKeyID = 172;
                                        int fanKeyID   = 118;

                                        //Special case - multiple keys
                                        if (i == startKeyID)
                                        {
                                            //Start key
                                            List <int> AllowedStartRooms = new List <int>()
                                            {
                                                10, 23, 25, 27, 29, 30, 31, 32, 33, 35, 127, 162, 163
                                            };

                                            while (!AllowedStartRooms.Contains(RandomLocation.Room))
                                            {
                                                RandomLocation = ZonedLocations.Key1Zone[_generator.Next(0, ZonedLocations.Key1Zone.Count)];
                                            }

                                            FoundPossibleLocation = true;
                                        }
                                        else if (i == fanKeyID)
                                        {
                                            //Fan area key
                                            List <int> AllowedFanRooms = new List <int>()
                                            {
                                                1, 5, 8, 16, 37, 38, 44, 46, 47, 48, 49, 50, 52, 53, 55, 57, 59, 60, 63, 65, 66, 67, 68, 69, 70, 71, 72, 75, 76, 77, 78, 82, 83, 86, 87, 88, 89, 90, 93, 95, 96, 100, 102, 103, 105, 107, 109, 111, 120, 132, 139, 141, 143, 144, 151, 153, 154, 155, 156, 158, 159, 161, 174, 176, 177, 178, 179, 183, 185, 187, 188, 189
                                            };

                                            while (!AllowedFanRooms.Contains(RandomLocation.Room))
                                            {
                                                RandomLocation = ZonedLocations.Key1Zone[_generator.Next(0, ZonedLocations.Key1Zone.Count)];
                                            }

                                            FoundPossibleLocation = true;
                                        }
                                        else
                                        {
                                            RandomLocation        = ZonedLocations.Key1Zone[_generator.Next(0, ZonedLocations.Key1Zone.Count)];
                                            FoundPossibleLocation = true;
                                        }
                                    }
                                    else
                                    {
                                        RandomLocation        = ZonedLocations.Key1Zone[_generator.Next(0, ZonedLocations.Key1Zone.Count)];
                                        FoundPossibleLocation = true;
                                    }
                                }
                                break;

                            case TR2Entities.Key2_S_P:
                                if (ZonedLocations.Key2Zone.Count > 0)
                                {
                                    RandomLocation        = ZonedLocations.Key2Zone[_generator.Next(0, ZonedLocations.Key2Zone.Count)];
                                    FoundPossibleLocation = true;
                                }
                                break;

                            case TR2Entities.Key3_S_P:
                                if (ZonedLocations.Key3Zone.Count > 0)
                                {
                                    RandomLocation        = ZonedLocations.Key3Zone[_generator.Next(0, ZonedLocations.Key3Zone.Count)];
                                    FoundPossibleLocation = true;
                                }
                                break;

                            case TR2Entities.Key4_S_P:
                                if (ZonedLocations.Key4Zone.Count > 0)
                                {
                                    RandomLocation        = ZonedLocations.Key4Zone[_generator.Next(0, ZonedLocations.Key4Zone.Count)];
                                    FoundPossibleLocation = true;
                                }
                                break;

                            case TR2Entities.Quest1_S_P:
                                if (ZonedLocations.Quest1Zone.Count > 0)
                                {
                                    RandomLocation        = ZonedLocations.Quest1Zone[_generator.Next(0, ZonedLocations.Quest1Zone.Count)];
                                    FoundPossibleLocation = true;
                                }
                                break;

                            case TR2Entities.Quest2_S_P:
                                if (ZonedLocations.Quest2Zone.Count > 0)
                                {
                                    RandomLocation        = ZonedLocations.Quest2Zone[_generator.Next(0, ZonedLocations.Quest2Zone.Count)];
                                    FoundPossibleLocation = true;
                                }
                                break;

                            default:
                                break;
                            }
                        }
                        else
                        {
                            //Place standard items as normal for now
                            RandomLocation        = ItemLocs[_generator.Next(0, ItemLocs.Count)];
                            FoundPossibleLocation = true;
                        }

                        if (FoundPossibleLocation)
                        {
                            Location GlobalizedRandomLocation = SpatialConverters.TransformToLevelSpace(RandomLocation, _levelInstance.Data.Rooms[RandomLocation.Room].Info);

                            _levelInstance.Data.Entities[i].Room       = Convert.ToInt16(GlobalizedRandomLocation.Room);
                            _levelInstance.Data.Entities[i].X          = GlobalizedRandomLocation.X;
                            _levelInstance.Data.Entities[i].Y          = GlobalizedRandomLocation.Y;
                            _levelInstance.Data.Entities[i].Z          = GlobalizedRandomLocation.Z;
                            _levelInstance.Data.Entities[i].Intensity1 = -1;
                            _levelInstance.Data.Entities[i].Intensity2 = -1;
                        }
                    }
                }
            }
        }
Пример #23
0
        private void RandomizeORPistol()
        {
            //Is there something in the unarmed level pistol location?
            if (_unarmedLevelPistolIndex != -1)
            {
                List <TR2Entities> ReplacementWeapons = TR2EntityUtilities.GetListOfGunTypes();
                ReplacementWeapons.Add(TR2Entities.Pistols_S_P);

                TR2Entities Weap = ReplacementWeapons[_generator.Next(0, ReplacementWeapons.Count)];
                if (_levelInstance.Is(LevelNames.CHICKEN))
                {
                    // Grenade Launcher and Harpoon cannot trigger the bells in Ice Palace
                    while (Weap.Equals(TR2Entities.GrenadeLauncher_S_P) || Weap.Equals(TR2Entities.Harpoon_S_P))
                    {
                        Weap = ReplacementWeapons[_generator.Next(0, ReplacementWeapons.Count)];
                    }
                }

                TR2Entity unarmedLevelWeapons = _levelInstance.Data.Entities[_unarmedLevelPistolIndex];

                uint ammoToGive      = 0;
                bool addPistols      = false;
                uint smallMediToGive = 0;
                uint largeMediToGive = 0;

                if (_startingAmmoToGive.ContainsKey(Weap))
                {
                    ammoToGive = _startingAmmoToGive[Weap];
                    if (PerformEnemyWeighting)
                    {
                        // Create a score based on each type of enemy in this level and increase the ammo count based on this
                        EnemyDifficulty difficulty = EnemyUtilities.GetEnemyDifficulty(_levelInstance.GetEnemyEntities());
                        ammoToGive *= (uint)difficulty;

                        // Depending on how difficult the enemy combination is, allocate some extra helpers.
                        addPistols = difficulty > EnemyDifficulty.Easy;

                        if (difficulty == EnemyDifficulty.Medium || difficulty == EnemyDifficulty.Hard)
                        {
                            smallMediToGive++;
                        }
                        if (difficulty > EnemyDifficulty.Medium)
                        {
                            largeMediToGive++;
                        }
                        if (difficulty == EnemyDifficulty.VeryHard)
                        {
                            largeMediToGive++;
                        }
                    }
                    else if (_levelInstance.Is(LevelNames.LAIR))
                    {
                        ammoToGive *= 6;
                    }
                }

                //#68 - Provide some additional ammo for a weapon if not pistols
                if (Weap != TR2Entities.Pistols_S_P)
                {
                    AddORAmmo(GetWeaponAmmo(Weap), ammoToGive, unarmedLevelWeapons);
                }

                unarmedLevelWeapons.TypeID = (short)Weap;

                if (Weap != TR2Entities.Pistols_S_P)
                {
                    // If we haven't decided to add the pistols (i.e. for enemy difficulty)
                    // add a 1/3 chance of getting them anyway. #149 If the harpoon is being
                    // given, the pistols will be included.
                    if (addPistols || Weap == TR2Entities.Harpoon_S_P || _generator.Next(0, 3) == 0)
                    {
                        CopyEntity(unarmedLevelWeapons, TR2Entities.Pistols_S_P);
                    }
                }

                for (int i = 0; i < smallMediToGive; i++)
                {
                    CopyEntity(unarmedLevelWeapons, TR2Entities.SmallMed_S_P);
                }
                for (int i = 0; i < largeMediToGive; i++)
                {
                    CopyEntity(unarmedLevelWeapons, TR2Entities.LargeMed_S_P);
                }
            }
        }
Пример #24
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
            });
        }
Пример #25
0
        private void RandomizeEnemies(TR2CombinedLevel level, EnemyRandomizationCollection enemies)
        {
            bool shotgunGoonSeen = level.Is(LevelNames.HOME); // 1 ShotgunGoon in HSH only
            bool dragonSeen      = level.Is(LevelNames.LAIR); // 1 Marco in DL only

            // Get a list of current enemy entities
            List <TR2Entity> enemyEntities = level.GetEnemyEntities();

            // Keep track of any new entities added (e.g. Skidoo)
            List <TR2Entity> newEntities = new List <TR2Entity>();

            // #148 If it's HSH and we have been able to import cross-level, we will add 15
            // dogs outside the gate to ensure the kill counter works. Dogs, Goon1 and
            // StickGoons will have been excluded from the cross-level pool for simplicity
            // Their textures will have been removed but they won't spawn anyway as we aren't
            // defining triggers - the game only needs them to be present in the entity list.
            if (level.Is(LevelNames.HOME) && !enemies.Available.Contains(TR2Entities.Doberman))
            {
                for (int i = 0; i < 15; i++)
                {
                    newEntities.Add(new TR2Entity
                    {
                        TypeID     = (short)TR2Entities.Doberman,
                        Room       = 85,
                        X          = 61919,
                        Y          = 2560,
                        Z          = 74222,
                        Angle      = 16384,
                        Flags      = 0,
                        Intensity1 = -1,
                        Intensity2 = -1
                    });
                }
            }

            // First iterate through any enemies that are restricted by room
            Dictionary <TR2Entities, List <int> > enemyRooms = EnemyUtilities.GetRestrictedEnemyRooms(level.Name);

            if (enemyRooms != null)
            {
                foreach (TR2Entities entity in enemyRooms.Keys)
                {
                    if (!enemies.Available.Contains(entity))
                    {
                        continue;
                    }

                    List <int> rooms          = enemyRooms[entity];
                    int        maxEntityCount = EnemyUtilities.GetRestrictedEnemyLevelCount(entity);
                    if (maxEntityCount == -1)
                    {
                        // We are allowed any number, but this can't be more than the number of unique rooms,
                        // so we will assume 1 per room as these restricted enemies are likely to be tanky.
                        maxEntityCount = rooms.Count;
                    }
                    else
                    {
                        maxEntityCount = Math.Min(maxEntityCount, rooms.Count);
                    }

                    // Pick an actual count
                    int enemyCount = _generator.Next(1, maxEntityCount + 1);
                    for (int i = 0; i < enemyCount; i++)
                    {
                        // Find an entity in one of the rooms that the new enemy is restricted to
                        TR2Entity targetEntity = null;
                        do
                        {
                            int room = enemyRooms[entity][_generator.Next(0, enemyRooms[entity].Count)];
                            targetEntity = enemyEntities.Find(e => e.Room == room);
                        }while (targetEntity == null);

                        targetEntity.TypeID = (short)TR2EntityUtilities.TranslateEntityAlias(entity);

                        // #146 Ensure OneShot triggers are set for this enemy if needed
                        EnemyUtilities.SetEntityTriggers(level.Data, targetEntity);

                        // Remove the target entity so it doesn't get replaced
                        enemyEntities.Remove(targetEntity);
                    }

                    // Remove this entity type from the available rando pool
                    enemies.Available.Remove(entity);
                }
            }

            foreach (TR2Entity currentEntity in enemyEntities)
            {
                TR2Entities currentEntityType = (TR2Entities)currentEntity.TypeID;
                TR2Entities newEntityType     = currentEntityType;

                // If it's an existing enemy that has to remain in the same spot, skip it
                if (EnemyUtilities.IsEnemyRequired(level.Name, currentEntityType))
                {
                    continue;
                }

                //#45 - Check to see if any items are at the same location as the enemy.
                //If there are we need to ensure that the new random enemy type is one that can drop items.
                List <TR2Entity> sharedItems = new List <TR2Entity>(Array.FindAll
                                                                    (
                                                                        level.Data.Entities,
                                                                        e =>
                                                                        (
                                                                            e.X == currentEntity.X &&
                                                                            e.Y == currentEntity.Y &&
                                                                            e.Z == currentEntity.Z
                                                                        )
                                                                    ));

                //Do multiple entities share one location?
                bool isPickupItem = false;
                if (sharedItems.Count > 1 && enemies.Droppable.Count != 0)
                {
                    //Are any entities sharing a location a droppable pickup?

                    foreach (TR2Entity ent in sharedItems)
                    {
                        TR2Entities entType = (TR2Entities)ent.TypeID;

                        isPickupItem = TR2EntityUtilities.IsUtilityType(entType) ||
                                       TR2EntityUtilities.IsGunType(entType) ||
                                       TR2EntityUtilities.IsKeyItemType(entType);

                        if (isPickupItem)
                        {
                            break;
                        }
                    }

                    //Generate a new type
                    newEntityType = enemies.Available[_generator.Next(0, enemies.Available.Count)];

                    //Do we need to ensure the enemy can drop the item on the same tile?
                    if (!TR2EntityUtilities.CanDropPickups(newEntityType, !ProtectMonks) && isPickupItem)
                    {
                        //Ensure the new random entity can drop pickups
                        newEntityType = enemies.Droppable[_generator.Next(0, enemies.Droppable.Count)];
                    }
                }
                else
                {
                    //Generate a new type
                    newEntityType = enemies.Available[_generator.Next(0, enemies.Available.Count)];
                }

                short   roomIndex = currentEntity.Room;
                TR2Room room      = level.Data.Rooms[roomIndex];

                if (level.Is(LevelNames.DA) && roomIndex == 77)
                {
                    // Make sure the end level trigger isn't blocked by an unkillable enemy
                    while (TR2EntityUtilities.IsHazardCreature(newEntityType) || (ProtectMonks && TR2EntityUtilities.IsMonk(newEntityType)))
                    {
                        newEntityType = enemies.Available[_generator.Next(0, enemies.Available.Count)];
                    }
                }

                if (TR2EntityUtilities.IsWaterCreature(currentEntityType) && !TR2EntityUtilities.IsWaterCreature(newEntityType))
                {
                    // Check alternate rooms too - e.g. rooms 74/48 in 40 Fathoms
                    short roomDrainIndex = -1;
                    if (room.ContainsWater)
                    {
                        roomDrainIndex = roomIndex;
                    }
                    else if (room.AlternateRoom != -1 && level.Data.Rooms[room.AlternateRoom].ContainsWater)
                    {
                        roomDrainIndex = room.AlternateRoom;
                    }

                    if (roomDrainIndex != -1 && !level.PerformDraining(roomDrainIndex))
                    {
                        // Draining cannot be performed so make the entity a water creature.
                        // The list of provided water creatures will either be those native
                        // to this level, or if randomizing cross-level, a pre-check will
                        // have already been performed on draining so if it's not possible,
                        // at least one water creature will be available.
                        newEntityType = enemies.Water[_generator.Next(0, enemies.Water.Count)];
                    }
                }

                // Ensure that if we have to pick a different enemy at this point that we still
                // honour any pickups in the same spot.
                List <TR2Entities> enemyPool = isPickupItem ? enemies.Droppable : enemies.Available;

                if (newEntityType == TR2Entities.ShotgunGoon && shotgunGoonSeen) // HSH only
                {
                    while (newEntityType == TR2Entities.ShotgunGoon)
                    {
                        newEntityType = enemyPool[_generator.Next(0, enemyPool.Count)];
                    }
                }

                if (newEntityType == TR2Entities.MarcoBartoli && dragonSeen) // DL only, other levels use quasi-zoning for the dragon
                {
                    while (newEntityType == TR2Entities.MarcoBartoli)
                    {
                        newEntityType = enemyPool[_generator.Next(0, enemyPool.Count)];
                    }
                }

                // If we are restricting count per level for this enemy and have reached that count, pick
                // someting else. This applies when we are restricting by in-level count, but not by room
                // (e.g. Winston).
                int maxEntityCount = EnemyUtilities.GetRestrictedEnemyLevelCount(newEntityType);
                if (maxEntityCount != -1)
                {
                    if (level.Data.Entities.ToList().FindAll(e => e.TypeID == (short)newEntityType).Count == maxEntityCount)
                    {
                        TR2Entities tmp = newEntityType;
                        while (newEntityType == tmp)
                        {
                            newEntityType = enemyPool[_generator.Next(0, enemyPool.Count)];
                        }
                    }
                }

                // #158 Several entity freezing issues have been found with various enemy
                // combinations in Barkhang, so for now all Mercenary1 and MonkWithLongStick
                // entities must remain in place, and no additional ones should be added.
                if (level.Is(LevelNames.MONASTERY))
                {
                    while (EnemyUtilities.IsEnemyRequired(level.Name, newEntityType))
                    {
                        newEntityType = enemyPool[_generator.Next(0, enemyPool.Count)];
                    }
                }

                // #144 Disguise something as the Chicken. Pre-checks will have been done to ensure
                // the guiser is suitable for the level.
                if (DocileBirdMonsters && newEntityType == TR2Entities.BirdMonster)
                {
                    newEntityType = enemies.BirdMonsterGuiser;
                }

                // Make sure to convert BengalTiger, StickWieldingGoonBandana etc back to their actual types
                currentEntity.TypeID = (short)TR2EntityUtilities.TranslateEntityAlias(newEntityType);

                // #146 Ensure OneShot triggers are set for this enemy if needed. This currently only applies
                // to the dragon, which will be handled above in defined rooms, but the check should be made
                // here in case this needs to be extended later.
                EnemyUtilities.SetEntityTriggers(level.Data, currentEntity);
            }

            // MercSnowMobDriver relies on RedSnowmobile so it will be available in the model list
            if (!level.Is(LevelNames.TIBET))
            {
                TR2Entity mercDriver = level.Data.Entities.ToList().Find(e => e.TypeID == (short)TR2Entities.MercSnowmobDriver);
                if (mercDriver != null)
                {
                    short room, angle;
                    int   x, y, z;

                    // we will only spawn one skidoo, so only need one random location
                    Location randomLocation = VehicleUtilities.GetRandomLocation(level.Name, TR2Entities.RedSnowmobile, _generator);
                    if (randomLocation != null)
                    {
                        room  = (short)randomLocation.Room;
                        x     = randomLocation.X;
                        y     = randomLocation.Y;
                        z     = randomLocation.Z;
                        angle = randomLocation.Angle;
                    }
                    else
                    {
                        // if the level does not have skidoo locations for some reason, just spawn it on the MercSnowMobDriver
                        room  = mercDriver.Room;
                        x     = mercDriver.X;
                        y     = mercDriver.Y;
                        z     = mercDriver.Z;
                        angle = mercDriver.Angle;
                    }

                    newEntities.Add(new TR2Entity
                    {
                        TypeID     = (short)TR2Entities.RedSnowmobile,
                        Room       = room,
                        X          = x,
                        Y          = y,
                        Z          = z,
                        Angle      = angle,
                        Flags      = 0,
                        Intensity1 = -1,
                        Intensity2 = -1
                    });
                }
            }

            // Did we add any new entities?
            if (newEntities.Count > 0)
            {
                List <TR2Entity> levelEntities = level.Data.Entities.ToList();
                levelEntities.AddRange(newEntities);
                level.Data.Entities    = levelEntities.ToArray();
                level.Data.NumEntities = (uint)levelEntities.Count;
            }
        }
Пример #26
0
        private void RandomizeStartPosition(TR2CombinedLevel level)
        {
            if (level.Script.HasStartAnimation)
            {
                // Don't change either the position or angle in Rig or HSH as the start cutscene looks odd and
                // for HSH Lara doesn't end up on the trigger for the enemies.
                return;
            }

            List <TR2Entity> entities = level.Data.Entities.ToList();
            TR2Entity        lara     = entities.Find(e => e.TypeID == (short)TR2Entities.Lara);

            if (!RotateOnly)
            {
                // We only change position if there is not a secret in the same room as Lara, This is just in case it ends up
                // where she starts on a slope (GW or Opera House for example), as its X,Y,Z values may not be identical to Lara's.
                if (entities.Find(e => e.Room == lara.Room && TR2EntityUtilities.IsSecretType((TR2Entities)e.TypeID)) == null)
                {
                    // If we haven't defined anything for a level, Lara will just be rotated. This is most likely where there are
                    // triggers just after Lara's starting spot, so we just skip them here.
                    if (_startLocations.ContainsKey(level.Name))
                    {
                        List <Location> locations = _startLocations[level.Name];
                        if (DevelopmentMode)
                        {
                            foreach (Location loc in locations)
                            {
                                entities.Add(new TR2Entity
                                {
                                    TypeID     = (short)TR2Entities.Lara,
                                    Room       = Convert.ToInt16(loc.Room),
                                    X          = loc.X,
                                    Y          = loc.Y,
                                    Z          = loc.Z,
                                    Angle      = 0,
                                    Intensity1 = -1,
                                    Intensity2 = -1,
                                    Flags      = 0
                                });
                            }
                        }
                        else
                        {
                            Location location = locations[_generator.Next(0, locations.Count)];
                            lara.Room  = (short)location.Room;
                            lara.X     = location.X;
                            lara.Y     = location.Y;
                            lara.Z     = location.Z;
                            lara.Angle = location.Angle;
                        }
                    }
                }
            }

            RotateLara(lara, level);

            if (DevelopmentMode)
            {
                level.Data.Entities    = entities.ToArray();
                level.Data.NumEntities = (uint)entities.Count;
            }
        }
Пример #27
0
        private void RandomizeORPistol()
        {
            //Is there something in the unarmed level pistol location?
            if (_unarmedLevelPistolIndex != -1)
            {
                List <TR2Entities> ReplacementWeapons = TR2EntityUtilities.GetListOfGunTypes();
                ReplacementWeapons.Add(TR2Entities.Pistols_S_P);

                TR2Entities Weap = ReplacementWeapons[_generator.Next(0, ReplacementWeapons.Count)];
                if (_scriptedLevelInstance.Is(LevelNames.CHICKEN))
                {
                    // Grenade Launcher and Harpoon cannot trigger the bells in Ice Palace
                    while (Weap.Equals(TR2Entities.GrenadeLauncher_S_P) || Weap.Equals(TR2Entities.Harpoon_S_P))
                    {
                        Weap = ReplacementWeapons[_generator.Next(0, ReplacementWeapons.Count)];
                    }
                }

                TR2Entity unarmedLevelWeapons = _levelInstance.Entities[_unarmedLevelPistolIndex];

                uint ammoToGive = 0;
                if (_startingAmmoToGive.ContainsKey(Weap))
                {
                    ammoToGive = _startingAmmoToGive[Weap];
                    if (_scriptedLevelInstance.Is(LevelNames.LAIR))
                    {
                        ammoToGive *= 6;
                    }
                }

                //#68 - Provide some additional ammo for a weapon if not pistols
                switch (Weap)
                {
                case TR2Entities.Shotgun_S_P:
                    AddORAmmo(TR2Entities.ShotgunAmmo_S_P, ammoToGive, unarmedLevelWeapons);
                    break;

                case TR2Entities.Automags_S_P:
                    AddORAmmo(TR2Entities.AutoAmmo_S_P, ammoToGive, unarmedLevelWeapons);
                    break;

                case TR2Entities.Uzi_S_P:
                    AddORAmmo(TR2Entities.UziAmmo_S_P, ammoToGive, unarmedLevelWeapons);
                    break;

                case TR2Entities.Harpoon_S_P:
                    AddORAmmo(TR2Entities.HarpoonAmmo_S_P, ammoToGive, unarmedLevelWeapons);
                    break;

                case TR2Entities.M16_S_P:
                    AddORAmmo(TR2Entities.M16Ammo_S_P, ammoToGive, unarmedLevelWeapons);
                    break;

                case TR2Entities.GrenadeLauncher_S_P:
                    AddORAmmo(TR2Entities.Grenades_S_P, ammoToGive, unarmedLevelWeapons);
                    break;

                default:
                    break;
                }

                unarmedLevelWeapons.TypeID = (short)Weap;
            }
        }
Пример #28
0
        private void RepositionItems(List <Location> ItemLocs, string lvl)
        {
            if (IsDevelopmentModeOn)
            {
                PlaceAllItems(ItemLocs);
                return;
            }

            if (ItemLocs.Count > 0)
            {
                //We are currently looking guns + ammo
                List <TR2Entities> targetents = TR2EntityUtilities.GetListOfGunTypes();
                targetents.AddRange(TR2EntityUtilities.GetListOfAmmoTypes());

                //And also key items...
                if (IncludeKeyItems)
                {
                    targetents.AddRange(TR2EntityUtilities.GetListOfKeyItemTypes());
                }

                //It's important to now start zoning key items as softlocks must be avoided.
                ZonedLocationCollection ZonedLocations = new ZonedLocationCollection();
                ZonedLocations.PopulateZones(lvl, ItemLocs, ZonePopulationMethod.KeyPuzzleQuestOnly);

                for (int i = 0; i < _levelInstance.Entities.Count(); i++)
                {
                    if (targetents.Contains((TR2Entities)_levelInstance.Entities[i].TypeID) && (i != _unarmedLevelPistolIndex))
                    {
                        Location RandomLocation        = new Location();
                        bool     FoundPossibleLocation = false;

                        if (TR2EntityUtilities.IsKeyItemType((TR2Entities)_levelInstance.Entities[i].TypeID))
                        {
                            TR2Entities type = (TR2Entities)_levelInstance.Entities[i].TypeID;

                            // Apply zoning for key items
                            switch (type)
                            {
                            case TR2Entities.Puzzle1_S_P:
                                if (ZonedLocations.Puzzle1Zone.Count > 0)
                                {
                                    RandomLocation        = ZonedLocations.Puzzle1Zone[_generator.Next(0, ZonedLocations.Puzzle1Zone.Count)];
                                    FoundPossibleLocation = true;
                                }
                                break;

                            case TR2Entities.Puzzle2_S_P:
                                if (ZonedLocations.Puzzle2Zone.Count > 0)
                                {
                                    RandomLocation        = ZonedLocations.Puzzle2Zone[_generator.Next(0, ZonedLocations.Puzzle2Zone.Count)];
                                    FoundPossibleLocation = true;
                                }
                                break;

                            case TR2Entities.Puzzle3_S_P:
                                if (ZonedLocations.Puzzle3Zone.Count > 0)
                                {
                                    RandomLocation        = ZonedLocations.Puzzle3Zone[_generator.Next(0, ZonedLocations.Puzzle3Zone.Count)];
                                    FoundPossibleLocation = true;
                                }
                                break;

                            case TR2Entities.Puzzle4_S_P:
                                if (ZonedLocations.Puzzle4Zone.Count > 0)
                                {
                                    RandomLocation        = ZonedLocations.Puzzle4Zone[_generator.Next(0, ZonedLocations.Puzzle4Zone.Count)];
                                    FoundPossibleLocation = true;
                                }
                                break;

                            case TR2Entities.Key1_S_P:
                                if (ZonedLocations.Key1Zone.Count > 0)
                                {
                                    RandomLocation        = ZonedLocations.Key1Zone[_generator.Next(0, ZonedLocations.Key1Zone.Count)];
                                    FoundPossibleLocation = true;
                                }
                                break;

                            case TR2Entities.Key2_S_P:
                                if (ZonedLocations.Key2Zone.Count > 0)
                                {
                                    RandomLocation        = ZonedLocations.Key2Zone[_generator.Next(0, ZonedLocations.Key2Zone.Count)];
                                    FoundPossibleLocation = true;
                                }
                                break;

                            case TR2Entities.Key3_S_P:
                                if (ZonedLocations.Key3Zone.Count > 0)
                                {
                                    RandomLocation        = ZonedLocations.Key3Zone[_generator.Next(0, ZonedLocations.Key3Zone.Count)];
                                    FoundPossibleLocation = true;
                                }
                                break;

                            case TR2Entities.Key4_S_P:
                                if (ZonedLocations.Key4Zone.Count > 0)
                                {
                                    RandomLocation        = ZonedLocations.Key4Zone[_generator.Next(0, ZonedLocations.Key4Zone.Count)];
                                    FoundPossibleLocation = true;
                                }
                                break;

                            case TR2Entities.Quest1_S_P:
                                if (ZonedLocations.Quest1Zone.Count > 0)
                                {
                                    RandomLocation        = ZonedLocations.Quest1Zone[_generator.Next(0, ZonedLocations.Quest1Zone.Count)];
                                    FoundPossibleLocation = true;
                                }
                                break;

                            case TR2Entities.Quest2_S_P:
                                if (ZonedLocations.Quest2Zone.Count > 0)
                                {
                                    RandomLocation        = ZonedLocations.Quest2Zone[_generator.Next(0, ZonedLocations.Quest2Zone.Count)];
                                    FoundPossibleLocation = true;
                                }
                                break;

                            default:
                                break;
                            }
                        }
                        else
                        {
                            //Place standard items as normal for now
                            RandomLocation        = ItemLocs[_generator.Next(0, ItemLocs.Count)];
                            FoundPossibleLocation = true;
                        }

                        if (FoundPossibleLocation)
                        {
                            Location GlobalizedRandomLocation = SpatialConverters.TransformToLevelSpace(RandomLocation, _levelInstance.Rooms[RandomLocation.Room].Info);

                            _levelInstance.Entities[i].Room       = Convert.ToInt16(GlobalizedRandomLocation.Room);
                            _levelInstance.Entities[i].X          = GlobalizedRandomLocation.X;
                            _levelInstance.Entities[i].Y          = GlobalizedRandomLocation.Y;
                            _levelInstance.Entities[i].Z          = GlobalizedRandomLocation.Z;
                            _levelInstance.Entities[i].Intensity1 = -1;
                            _levelInstance.Entities[i].Intensity2 = -1;
                        }
                    }
                }
            }
        }
Пример #29
0
        private void RandomizeEnemyTypes(string lvl)
        {
            List <TR2Entities> EnemyTypes = TR2EntityUtilities.GetEnemyTypeDictionary()[lvl];

            for (int i = 0; i < _levelInstance.Entities.Count(); i++)
            {
                if (lvl == LevelNames.CHICKEN &&
                    _levelInstance.Entities[i].Room == 143 &&
                    _levelInstance.Entities[i].TypeID == (int)TR2Entities.BirdMonster)
                {
                    //#60 - Ice Palace - Room 143 chicken man must remain to avoid softlock.
                    continue;
                }
                else if (lvl == LevelNames.HOME && _levelInstance.Entities[i].TypeID == (int)TR2Entities.ShotgunGoon)
                {
                    //#62 - Avoid randomizing shotgun goon in HSH
                    continue;
                }

                //#45 - Check to see if any items are at the same location as the enemy.
                //If there are we need to ensure that the new random enemy type is one that can drop items.
                List <TR2Entity> SharedItems = new List <TR2Entity>(Array.FindAll(_levelInstance.Entities, e => ((e.X == _levelInstance.Entities[i].X) &&
                                                                                                                 (e.Y == _levelInstance.Entities[i].Y) && (e.Z == _levelInstance.Entities[i].Z))));

                List <TR2Entities> DroppableEnemies = TR2EntityUtilities.DroppableEnemyTypes()[lvl];

                //Is it an entity we are keen on replacing?
                if (EnemyTypes.Contains((TR2Entities)_levelInstance.Entities[i].TypeID))
                {
                    //Do multiple entities share one location?
                    if ((SharedItems.Count > 1) && (DroppableEnemies.Count != 0))
                    {
                        //Are any entities sharing a location a droppable pickup?
                        bool IsPickupItem = false;

                        foreach (TR2Entity ent in SharedItems)
                        {
                            TR2Entities EntType = (TR2Entities)ent.TypeID;

                            IsPickupItem = (TR2EntityUtilities.IsUtilityType(EntType)) ||
                                           (TR2EntityUtilities.IsGunType(EntType)) ||
                                           (TR2EntityUtilities.IsKeyItemType(EntType));

                            if (IsPickupItem)
                            {
                                break;
                            }
                        }

                        //Generate a location
                        _levelInstance.Entities[i].TypeID = (short)EnemyTypes[_generator.Next(0, EnemyTypes.Count)];

                        //Do we need to ensure the enemy can drop the item on the same tile?
                        if (!TR2EntityUtilities.CanDropPickups((TR2Entities)_levelInstance.Entities[i].TypeID, false) && IsPickupItem)
                        {
                            //Ensure the new random entity can drop pickups
                            _levelInstance.Entities[i].TypeID = (short)DroppableEnemies[_generator.Next(0, DroppableEnemies.Count)];
                        }
                    }
                    else
                    {
                        _levelInstance.Entities[i].TypeID = (short)EnemyTypes[_generator.Next(0, EnemyTypes.Count)];
                    }

                    short room = _levelInstance.Entities[i].Room;

                    if (!TR2EntityUtilities.IsWaterCreature((TR2Entities)_levelInstance.Entities[i].TypeID) &&
                        _levelInstance.Rooms[room].ContainsWater)
                    {
                        if (!PerformDraining(lvl, room))
                        {
                            //Draining cannot be performed so make the entity a water creature.
                            TR2Entities ent;

                            //Make sure water creature can appear on level
                            do
                            {
                                ent = TR2EntityUtilities.WaterCreatures()[_generator.Next(0, TR2EntityUtilities.WaterCreatures().Count)];
                            } while (!EnemyTypes.Contains(ent));

                            _levelInstance.Entities[i].TypeID = (short)ent;
                        }
                    }
                }
            }
        }
Пример #30
0
        private void RandomizeEnemyTypes(string lvl)
        {
            List <TR2Entities> EnemyTypes = TR2EntityUtilities.GetEnemyTypeDictionary()[lvl];

            for (int i = 0; i < _levelInstance.Entities.Count(); i++)
            {
                //#60 - Ice Palace - Room 143 chicken man must remain to avoid softlock.
                if (lvl == LevelNames.CHICKEN &&
                    _levelInstance.Entities[i].Room == 143 &&
                    _levelInstance.Entities[i].TypeID == (int)TR2Entities.BirdMonster)
                {
                    continue;
                }

                //#45 - Check to see if any items are at the same location as the enemy.
                //If there are we need to ensure that the new random enemy type is one that can drop items.
                List <TR2Entity> SharedItems = new List <TR2Entity>(Array.FindAll(_levelInstance.Entities, e => ((e.X == _levelInstance.Entities[i].X) &&
                                                                                                                 (e.Y == _levelInstance.Entities[i].Y) && (e.Z == _levelInstance.Entities[i].Z))));

                List <TR2Entities> DroppableEnemies = TR2EntityUtilities.DroppableEnemyTypes()[lvl];

                //Is it an entity we are keen on replacing?
                if (EnemyTypes.Contains((TR2Entities)_levelInstance.Entities[i].TypeID))
                {
                    //Do multiple entities share one location?
                    if ((SharedItems.Count > 1) && (DroppableEnemies.Count != 0))
                    {
                        //Are any entities sharing a location a droppable pickup?
                        bool IsPickupItem = false;

                        foreach (TR2Entity ent in SharedItems)
                        {
                            TR2Entities EntType = (TR2Entities)ent.TypeID;

                            IsPickupItem = (TR2EntityUtilities.IsAmmoType(EntType)) ||
                                           (TR2EntityUtilities.IsGunType(EntType)) ||
                                           (TR2EntityUtilities.IsKeyItemType(EntType));

                            if (IsPickupItem)
                            {
                                break;
                            }
                        }

                        //Generate a location
                        _levelInstance.Entities[i].TypeID = (short)EnemyTypes[_generator.Next(0, EnemyTypes.Count)];

                        //Do we need to ensure the enemy can drop the item on the same tile?
                        if (!TR2EntityUtilities.CanDropPickups((TR2Entities)_levelInstance.Entities[i].TypeID) && IsPickupItem)
                        {
                            //Ensure the new random entity can drop pickups
                            _levelInstance.Entities[i].TypeID = (short)DroppableEnemies[_generator.Next(0, DroppableEnemies.Count)];
                        }
                    }
                    else
                    {
                        _levelInstance.Entities[i].TypeID = (short)EnemyTypes[_generator.Next(0, EnemyTypes.Count)];
                    }
                }
            }
        }