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(); } }
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; } } } }
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); }
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; } }
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(); }
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; } }
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); } }
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(); }
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; } } } }
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(); } }
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 } }); } } }
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; } }
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; } }
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++; } } }
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; } }
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); } } }