예제 #1
0
        private void CopyEntity(TR2Entity entity, TR2Entities newType)
        {
            List <TR2Entity> ents = _levelInstance.Data.Entities.ToList();

            if (ents.Count < _levelInstance.GetMaximumEntityLimit())
            {
                TR2Entity copy = entity.Clone();
                copy.TypeID = (short)newType;
                ents.Add(copy);
                _levelInstance.Data.NumEntities++;
                _levelInstance.Data.Entities = ents.ToArray();
            }
        }
예제 #2
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;
                    }
                }
            }
        }
예제 #3
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);
        }
예제 #4
0
        private void RotateLara(TR2Entity lara, TR2CombinedLevel level)
        {
            short currentAngle = lara.Angle;

            while (lara.Angle == currentAngle)
            {
                int degrees = 45 * _generator.Next(0, 8);
                lara.Angle = (short)(degrees * 16384 / -90);
            }

            // Spin the boat around too
            if (level.Is(LevelNames.BARTOLI))
            {
                TR2Entity boat = level.Data.Entities.ToList().Find(e => e.TypeID == (short)TR2Entities.Boat);
                boat.Angle = lara.Angle;
            }
        }
예제 #5
0
        private void AddORAmmo(TR2Entities ammoType, uint count, TR2Entity weapon)
        {
            List <TR2Entity> ents = _levelInstance.Entities.ToList();

            for (uint i = 0; i < count; i++)
            {
                TR2Entity ammo = weapon.Clone();

                ammo.TypeID = (short)ammoType;

                ents.Add(ammo);
            }
            ;

            _levelInstance.NumEntities += count;
            _levelInstance.Entities     = ents.ToArray();
        }
예제 #6
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;
            }
        }
예제 #7
0
        public static void SetEntityTriggers(TR2Level level, TR2Entity entity)
        {
            if (_oneShotEnemies.Contains((TR2Entities)entity.TypeID))
            {
                int entityID = level.Entities.ToList().IndexOf(entity);

                FDControl fdControl = new FDControl();
                fdControl.ParseFromLevel(level);

                List <FDTriggerEntry> triggers = FDUtilities.GetEntityTriggers(fdControl, entityID);
                foreach (FDTriggerEntry trigger in triggers)
                {
                    trigger.TrigSetup.SetOneShot();
                }

                fdControl.WriteToLevel(level);
            }
        }
예제 #8
0
        private void AddORAmmo(TR2Entities ammoType, uint count, TR2Entity weapon)
        {
            List <TR2Entity> ents = _levelInstance.Data.Entities.ToList();

            int entityLimit = _levelInstance.GetMaximumEntityLimit();

            for (uint i = 0; i < count && ents.Count < entityLimit; i++)
            {
                TR2Entity ammo = weapon.Clone();

                ammo.TypeID = (short)ammoType;

                ents.Add(ammo);
            }
            ;

            _levelInstance.Data.NumEntities = (uint)ents.Count;
            _levelInstance.Data.Entities    = ents.ToArray();
        }
예제 #9
0
        private void FindUnarmedPistolsLocation()
        {
            // #66 - checks were previously performed to clean locations from previous
            // randomization sessions to avoid item pollution. This is no longer required
            // as randomization is now always performed on the original level files.

            // #124 Default pistol locations are no longer limited to one per level.

            _unarmedLevelPistolIndex = -1;

            if (_levelInstance.Script.RemovesWeapons && _pistolLocations.ContainsKey(_levelInstance.Name))
            {
                short pistolID    = (short)TR2Entities.Pistols_S_P;
                int   pistolIndex = _levelInstance.Data.Entities.ToList().FindIndex(e => e.TypeID == pistolID);
                if (pistolIndex != -1)
                {
                    // Sanity check that the location is one that we expect
                    TR2Entity pistols        = _levelInstance.Data.Entities[pistolIndex];
                    Location  pistolLocation = new Location
                    {
                        X    = pistols.X,
                        Y    = pistols.Y,
                        Z    = pistols.Z,
                        Room = pistols.Room
                    };

                    int match = _pistolLocations[_levelInstance.Name].FindIndex
                                (
                        location =>
                        location.X == pistolLocation.X &&
                        location.Y == pistolLocation.Y &&
                        location.Z == pistolLocation.Z &&
                        location.Room == pistolLocation.Room
                                );

                    if (match != -1)
                    {
                        _unarmedLevelPistolIndex = pistolIndex;
                    }
                }
            }
        }
예제 #10
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();
            }
        }
예제 #11
0
        private void RandomizeSecretTracks(TR2Level level, FDControl floorData)
        {
            // Generate new triggers for secrets to allow different sounds for each one
            List <TRAudioTrack>         secretTracks = _tracks[TRAudioCategory.Secret];
            Dictionary <int, TR2Entity> secrets      = GetSecretItems(level);

            foreach (int entityIndex in secrets.Keys)
            {
                TR2Entity    secret = secrets[entityIndex];
                TRRoomSector sector = FDUtilities.GetRoomSector(secret.X, secret.Y, secret.Z, secret.Room, level, floorData);
                if (sector.FDIndex == 0)
                {
                    // The secret is positioned on a tile that currently has no FD, so create it
                    floorData.CreateFloorData(sector);
                }

                List <FDEntry> entries = floorData.Entries[sector.FDIndex];
                FDTriggerEntry existingTriggerEntry = entries.Find(e => e is FDTriggerEntry) as FDTriggerEntry;
                bool           existingEntityPickup = false;
                if (existingTriggerEntry != null)
                {
                    if (existingTriggerEntry.TrigType == FDTrigType.Pickup && existingTriggerEntry.TrigActionList[0].Parameter == entityIndex)
                    {
                        // GW gold secret (default location) already has a pickup trigger to spawn the
                        // TRex (or whatever enemy) so we'll just append to that item list here
                        existingEntityPickup = true;
                    }
                    else
                    {
                        // There is already a non-pickup trigger for this sector so while nothing is wrong with
                        // adding a pickup trigger, the game ignores it. So in this instance, the sound that
                        // plays will just be whatever is set in the script.
                        continue;
                    }
                }

                // Generate a new music action
                FDActionListItem musicAction = new FDActionListItem
                {
                    TrigAction = FDTrigAction.PlaySoundtrack,
                    Parameter  = secretTracks[_generator.Next(0, secretTracks.Count)].ID
                };

                // For GW default gold, just append it
                if (existingEntityPickup)
                {
                    existingTriggerEntry.TrigActionList.Add(musicAction);
                }
                else
                {
                    entries.Add(new FDTriggerEntry
                    {
                        // The values here are replicated from Trigger#112 (in trview) in GW.
                        // The first action list must be the entity being picked up and so
                        // remaining action list items are actioned on pick up.
                        Setup = new FDSetup {
                            Value = 1028
                        },
                        TrigSetup = new FDTrigSetup {
                            Value = 15872
                        },
                        TrigActionList = new List <FDActionListItem>
                        {
                            new FDActionListItem
                            {
                                TrigAction = FDTrigAction.Object,
                                Parameter  = (ushort)entityIndex
                            },
                            musicAction
                        }
                    });
                }
            }
        }
예제 #12
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;
            }
        }
예제 #13
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;
            }
        }
예제 #14
0
        private void RandomizeSecrets(List <Location> LevelLocations, TR23ScriptedLevel lvl)
        {
            if (LevelLocations.Count > 2)
            {
                if (IsDevelopmentModeOn)
                {
                    PlaceAllSecrets(lvl, LevelLocations);
                    return;
                }

                //Apply zoning to the locations to ensure they are spread out.
                ZonedLocationCollection ZonedLocations = new ZonedLocationCollection();

                ZonedLocations.PopulateZones(lvl.LevelFileBaseName.ToUpper(), LevelLocations, ZonePopulationMethod.SecretsOnly);

                Location GoldSecret;
                Location JadeSecret;
                Location StoneSecret;

                //Find suitable locations, ensuring they are zoned, do not share a room and difficulty.
                //Location = ZoneLocations[ZoneGroup][LocationInZoneGroup]
                do
                {
                    GoldSecret = ZonedLocations.GoldZone[_generator.Next(0, ZonedLocations.GoldZone.Count)];
                } while (GoldSecret.Difficulty == Difficulty.Hard && AllowHard == false);


                do
                {
                    JadeSecret = ZonedLocations.JadeZone[_generator.Next(0, ZonedLocations.JadeZone.Count)];
                } while ((JadeSecret.Room == GoldSecret.Room) ||
                         (JadeSecret.Difficulty == Difficulty.Hard && AllowHard == false));

                do
                {
                    StoneSecret = ZonedLocations.StoneZone[_generator.Next(0, ZonedLocations.StoneZone.Count)];
                } while ((StoneSecret.Room == GoldSecret.Room) ||
                         (StoneSecret.Room == JadeSecret.Room) ||
                         (StoneSecret.Difficulty == Difficulty.Hard && AllowHard == false));

                //Due to TRMod only accepting room space coords entities are actually stored in level space. So include some
                //calls to support a transformation of any locations that are specified in room space to maintain backwards compatbility
                //with older locations and support locations that are specified in both level or room space.
                GoldSecret  = SpatialConverters.TransformToLevelSpace(GoldSecret, _levelInstance.Rooms[GoldSecret.Room].Info);
                JadeSecret  = SpatialConverters.TransformToLevelSpace(JadeSecret, _levelInstance.Rooms[JadeSecret.Room].Info);
                StoneSecret = SpatialConverters.TransformToLevelSpace(StoneSecret, _levelInstance.Rooms[StoneSecret.Room].Info);

                //Does the level contain the entities?
                int GoldIndex  = Array.FindIndex(_levelInstance.Entities, ent => (ent.TypeID == (short)TR2Entities.GoldSecret_S_P));
                int JadeIndex  = Array.FindIndex(_levelInstance.Entities, ent => (ent.TypeID == (short)TR2Entities.JadeSecret_S_P));
                int StoneIndex = Array.FindIndex(_levelInstance.Entities, ent => (ent.TypeID == (short)TR2Entities.StoneSecret_S_P));

                //Check if we could find instances of the secret entities, if not, we need to add not edit.
                if (GoldIndex != -1)
                {
                    _levelInstance.Entities[GoldIndex].Room = Convert.ToInt16(GoldSecret.Room);
                    _levelInstance.Entities[GoldIndex].X    = GoldSecret.X;
                    _levelInstance.Entities[GoldIndex].Y    = GoldSecret.Y;
                    _levelInstance.Entities[GoldIndex].Z    = GoldSecret.Z;
                }
                else
                {
                    TR2Entity GoldEntity = new TR2Entity
                    {
                        TypeID     = (int)TR2Entities.GoldSecret_S_P,
                        Room       = Convert.ToInt16(GoldSecret.Room),
                        X          = GoldSecret.X,
                        Y          = GoldSecret.Y,
                        Z          = GoldSecret.Z,
                        Angle      = 0,
                        Intensity1 = -1,
                        Intensity2 = -1,
                        Flags      = 0
                    };

                    List <TR2Entity> ents = _levelInstance.Entities.ToList();
                    ents.Add(GoldEntity);
                    _levelInstance.Entities = ents.ToArray();
                    _levelInstance.NumEntities++;
                }

                if (JadeIndex != -1)
                {
                    _levelInstance.Entities[JadeIndex].Room = Convert.ToInt16(JadeSecret.Room);
                    _levelInstance.Entities[JadeIndex].X    = JadeSecret.X;
                    _levelInstance.Entities[JadeIndex].Y    = JadeSecret.Y;
                    _levelInstance.Entities[JadeIndex].Z    = JadeSecret.Z;
                }
                else
                {
                    TR2Entity JadeEntity = new TR2Entity
                    {
                        TypeID     = (int)TR2Entities.JadeSecret_S_P,
                        Room       = Convert.ToInt16(JadeSecret.Room),
                        X          = JadeSecret.X,
                        Y          = JadeSecret.Y,
                        Z          = JadeSecret.Z,
                        Angle      = 0,
                        Intensity1 = -1,
                        Intensity2 = -1,
                        Flags      = 0
                    };

                    List <TR2Entity> ents = _levelInstance.Entities.ToList();
                    ents.Add(JadeEntity);
                    _levelInstance.Entities = ents.ToArray();
                    _levelInstance.NumEntities++;
                }

                if (StoneIndex != -1)
                {
                    _levelInstance.Entities[StoneIndex].Room = Convert.ToInt16(StoneSecret.Room);
                    _levelInstance.Entities[StoneIndex].X    = StoneSecret.X;
                    _levelInstance.Entities[StoneIndex].Y    = StoneSecret.Y;
                    _levelInstance.Entities[StoneIndex].Z    = StoneSecret.Z;
                }
                else
                {
                    TR2Entity StoneEntity = new TR2Entity
                    {
                        TypeID     = (int)TR2Entities.StoneSecret_S_P,
                        Room       = Convert.ToInt16(StoneSecret.Room),
                        X          = StoneSecret.X,
                        Y          = StoneSecret.Y,
                        Z          = StoneSecret.Z,
                        Angle      = 0,
                        Intensity1 = -1,
                        Intensity2 = -1,
                        Flags      = 0
                    };

                    List <TR2Entity> ents = _levelInstance.Entities.ToList();
                    ents.Add(StoneEntity);
                    _levelInstance.Entities = ents.ToArray();
                    _levelInstance.NumEntities++;
                }
            }
        }
예제 #15
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;
            }
        }
예제 #16
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);
                }
            }
        }