Пример #1
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);
        }
Пример #2
0
        public static bool IsEnemySupported(string lvlName, TR2Entities entity)
        {
            if (_unsupportedEnemies.ContainsKey(lvlName))
            {
                return(!_unsupportedEnemies[lvlName].Contains(TR2EntityUtilities.TranslateEntityAlias(entity)));
            }

            return(true);
        }
Пример #3
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;
        }
Пример #4
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;
            }
        }