private void RepositionItems(List <Location> ItemLocs) { if (ItemLocs.Count > 0) { //We are currently looking guns + ammo List <TR2Entities> targetents = TR2EntityUtilities.GetListOfGunTypes(); targetents.AddRange(TR2EntityUtilities.GetListOfAmmoTypes()); for (int i = 0; i < _levelInstance.Entities.Count(); i++) { if (targetents.Contains((TR2Entities)_levelInstance.Entities[i].TypeID) && (i != _planeCargoWeaponIndex)) { Location RandomLocation = ItemLocs[_generator.Next(0, ItemLocs.Count)]; Location GlobalizedRandomLocation = SpatialConverters.TransformToLevelSpace(RandomLocation, _levelInstance.Rooms[RandomLocation.Room].Info); _levelInstance.Entities[i].Room = Convert.ToInt16(GlobalizedRandomLocation.Room); _levelInstance.Entities[i].X = GlobalizedRandomLocation.X; _levelInstance.Entities[i].Y = GlobalizedRandomLocation.Y; _levelInstance.Entities[i].Z = GlobalizedRandomLocation.Z; _levelInstance.Entities[i].Intensity1 = -1; _levelInstance.Entities[i].Intensity2 = -1; } } } }
protected override void StartImpl() { // Make the outfit selection outwith the processing thread to ensure consistent RNG. // We select all potential Laras including the default for the level as there are // only 4 to choose from (there is also Assault Course Lara, but when holstering pistols // her trousers disappear, so she is excluded for the time being...). List <TR2Entities> allLaras = TR2EntityUtilities.GetLaraTypes(); List <TR2CombinedLevel> levels = new List <TR2CombinedLevel>(_outfitAllocations.Keys); foreach (TR2CombinedLevel level in levels) { // Add the persistent outfit first, but we will populate the candidate // list regardless in case a level cannot support this choice. if (_outer.PersistOutfits) { _outfitAllocations[level].Add(_outer._persistentOutfit); } while (_outfitAllocations[level].Count < allLaras.Count) { TR2Entities nextLara = allLaras[_outer._generator.Next(0, allLaras.Count)]; if (!_outfitAllocations[level].Contains(nextLara)) { _outfitAllocations[level].Add(nextLara); } } } }
private void AdjustEntities() { // If an entity is marked to be removed but is also in the list // to import, don't remove it in the first place. List <TR2Entities> cleanedEntities = new List <TR2Entities>(); foreach (TR2Entities entity in EntitiesToRemove) { if (_aliasMap.ContainsKey(entity)) { // Check if we have another alias in the import list different from any // in the current level TR2Entities alias = TR2EntityUtilities.GetAliasForLevel(LevelName, entity); TR2Entities importAlias = TR2Entities.Lara; foreach (TR2Entities a in _aliasMap[entity]) { if (EntitiesToImport.Contains(a)) { importAlias = a; break; } } if (alias != importAlias) { cleanedEntities.Add(entity); } } else if (!EntitiesToImport.Contains(entity)) { cleanedEntities.Add(entity); } } EntitiesToRemove = cleanedEntities; }
private void RandomizeEnemiesNatively(TR2CombinedLevel level) { // For the assault course, nothing will be changed for the time being if (level.IsAssault) { return; } List <TR2Entities> availableEnemyTypes = TR2EntityUtilities.GetEnemyTypeDictionary()[level.Name]; List <TR2Entities> droppableEnemies = TR2EntityUtilities.DroppableEnemyTypes()[level.Name]; List <TR2Entities> waterEnemies = TR2EntityUtilities.FilterWaterEnemies(availableEnemyTypes); if (DocileBirdMonsters && level.Is(LevelNames.CHICKEN)) { DisguiseEntity(level, TR2Entities.MaskedGoon1, TR2Entities.BirdMonster); } RandomizeEnemies(level, new EnemyRandomizationCollection { Available = availableEnemyTypes, Droppable = droppableEnemies, Water = waterEnemies, BirdMonsterGuiser = TR2Entities.MaskedGoon1 // If randomizing natively, this will only apply to Ice Palace }); }
private void SetPersistentOutfit() { if (PersistOutfits) { List <TR2Entities> allLaras = TR2EntityUtilities.GetLaraTypes(); _persistentOutfit = allLaras[_generator.Next(0, allLaras.Count)]; } }
// This is triggered synchronously after the import work to ensure the RNG remains consistent internal void ApplyRandomization() { foreach (TR2CombinedLevel level in _enemyMapping.Keys) { if (!level.IsAssault) { EnemyTransportCollection importedCollection = null; foreach (EnemyTransportCollection enemies in _enemyMapping[level]) { if (enemies.ImportResult) { importedCollection = enemies; break; } } if (importedCollection == null) { // Cross-level was not possible with the enemy combinations. This could be due to either // a lack of space for texture packing, or the max ObjectTexture count (2048) was reached. _outer.TextureMonitor.RemoveMonitor(level.Name); // And just randomize normally // TODO: maybe trigger a warning to display at the end of randomizing to say that cross- // level was not possible? _outer.RandomizeEnemiesNatively(level); //System.Diagnostics.Debug.WriteLine(level.Name + ": Native enemies"); } else { // The import worked, so randomize the entities based on what we now have in place. //System.Diagnostics.Debug.WriteLine(level.Name + ": " + string.Join(", ", importedCollection.EntitiesToImport)); EnemyRandomizationCollection enemies = new EnemyRandomizationCollection { Available = importedCollection.EntitiesToImport, Droppable = TR2EntityUtilities.FilterDroppableEnemies(importedCollection.EntitiesToImport, !_outer.ProtectMonks), Water = TR2EntityUtilities.FilterWaterEnemies(importedCollection.EntitiesToImport) }; if (_outer.DocileBirdMonsters && importedCollection.BirdMonsterGuiser != TR2Entities.BirdMonster) { _outer.DisguiseEntity(level, importedCollection.BirdMonsterGuiser, TR2Entities.BirdMonster); enemies.BirdMonsterGuiser = importedCollection.BirdMonsterGuiser; } _outer.RandomizeEnemies(level, enemies); } _outer.SaveLevel(level); } if (!_outer.TriggerProgress()) { break; } } }
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); }
public static bool IsEnemySupported(string lvlName, TR2Entities entity) { if (_unsupportedEnemies.ContainsKey(lvlName)) { return(!_unsupportedEnemies[lvlName].Contains(TR2EntityUtilities.TranslateEntityAlias(entity))); } return(true); }
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; }
private Dictionary <int, TR2Entity> GetSecretItems(TR2Level level) { Dictionary <int, TR2Entity> entities = new Dictionary <int, TR2Entity>(); for (int i = 0; i < level.NumEntities; i++) { if (TR2EntityUtilities.IsSecretType((TR2Entities)level.Entities[i].TypeID)) { entities[i] = level.Entities[i]; } } return(entities); }
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 int Compare(TR2CombinedLevel lvl1, TR2CombinedLevel lvl2) { int enemyCount1 = TR2EntityUtilities.GetEnemyTypeDictionary()[lvl1.Name].Count; int enemyCount2 = TR2EntityUtilities.GetEnemyTypeDictionary()[lvl2.Name].Count; int freeTiles1 = 16 - (int)lvl1.Data.NumImages; int freeTiles2 = 16 - (int)lvl2.Data.NumImages; if (freeTiles1 == freeTiles2) { return(enemyCount2.CompareTo(enemyCount1)); } return((enemyCount1 * freeTiles1).CompareTo(enemyCount2 * freeTiles2)); }
public List <TR2Entity> GetEnemyEntities() { List <TR2Entities> allEnemies = TR2EntityUtilities.GetFullListOfEnemies(); List <TR2Entity> levelEntities = new List <TR2Entity>(); for (int i = 0; i < Data.NumEntities; i++) { TR2Entity entity = Data.Entities[i]; if (allEnemies.Contains((TR2Entities)entity.TypeID)) { levelEntities.Add(entity); } } return(levelEntities); }
internal List <TR2Entities> GetIgnoredEntities(string lvlName) { TexturePositionMonitor monitor = GetMonitor(lvlName); if (monitor != null && monitor.RemovedTextures != null) { List <TR2Entities> entities = new List <TR2Entities>(); foreach (TR2Entities entity in monitor.RemovedTextures) { entities.Add(TR2EntityUtilities.GetAliasForLevel(lvlName, entity)); } return(entities); } return(null); }
private void CleanPlaneCargo() { //In relation to #66 to ensure successive randomizations don't pollute the entity list List <TR2Entity> Entities = _levelInstance.Entities.ToList(); int index = Entities.FindIndex(e => (e.Room == 1 && TR2EntityUtilities.IsAmmoType((TR2Entities)e.TypeID))); while (index != -1 && index < Entities.Count) { Entities.RemoveAt(index); _levelInstance.NumEntities--; index = Entities.FindIndex(e => (e.Room == 1 && TR2EntityUtilities.IsAmmoType((TR2Entities)e.TypeID))); } _levelInstance.Entities = Entities.ToArray(); }
public static bool IsWaterEnemyRequired(TR2CombinedLevel level) { foreach (TR2Entity entityInstance in level.Data.Entities) { TR2Entities entity = (TR2Entities)entityInstance.TypeID; if (TR2EntityUtilities.IsWaterCreature(entity)) { if (!level.CanPerformDraining(entityInstance.Room)) { // Draining cannot be performed so we need to ensure we get at least one water enemy return(true); } } } return(false); }
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; } }
private void PopulateHSHCloset() { List <TR2Entities> replacementWeapons = TR2EntityUtilities.GetListOfGunTypes(); if (_levelInstance.Script.RemovesWeapons) { replacementWeapons.Add(TR2Entities.Pistols_S_P); } // Pick a new weapon, but exclude the grenade launcher because it affects the kill // count. Also exclude the harpoon as neither it nor the grenade launcher can break // Lara's bedroom window, and the enemy there may have been randomized to one without // a gun. Probably not a softlock scenario but safer to exclude for now. TR2Entities replacementWeapon; do { replacementWeapon = replacementWeapons[_generator.Next(0, replacementWeapons.Count)]; }while (replacementWeapon == TR2Entities.GrenadeLauncher_S_P || replacementWeapon == TR2Entities.Harpoon_S_P); TR2Entities replacementAmmo = GetWeaponAmmo(replacementWeapon); List <TR2Entity> ents = _levelInstance.Data.Entities.ToList(); foreach (TR2Entity entity in ents) { if (entity.Room != 57) { continue; } TR2Entities entityType = (TR2Entities)entity.TypeID; if (TR2EntityUtilities.IsGunType(entityType)) { entity.TypeID = (short)replacementWeapon; } else if (TR2EntityUtilities.IsAmmoType(entityType) && replacementWeapon != TR2Entities.Pistols_S_P) { entity.TypeID = (short)replacementAmmo; } } }
private void RandomizeORPistol() { //#44 - Agreed to keep it there but randomize its type. _planeCargoWeaponIndex = Array.FindIndex(_levelInstance.Entities, e => (e.TypeID == (int)TR2Entities.Pistols_S_P || e.TypeID == (int)TR2Entities.Shotgun_S_P || e.TypeID == (int)TR2Entities.Automags_S_P || e.TypeID == (int)TR2Entities.Uzi_S_P || e.TypeID == (int)TR2Entities.Harpoon_S_P || e.TypeID == (int)TR2Entities.M16_S_P || e.TypeID == (int)TR2Entities.GrenadeLauncher_S_P) && (e.Room == 1)); //Is there something in the plane cargo? if (_planeCargoWeaponIndex != -1) { List <TR2Entities> ReplacementWeapons = TR2EntityUtilities.GetListOfGunTypes(); ReplacementWeapons.Add(TR2Entities.Pistols_S_P); _levelInstance.Entities[_planeCargoWeaponIndex].TypeID = (short)ReplacementWeapons[_generator.Next(0, ReplacementWeapons.Count)]; } }
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(); } }
public static bool IsDroppableEnemyRequired(TR2CombinedLevel level) { foreach (TR2Entity entityInstance in level.Data.Entities) { List <TR2Entity> sharedItems = new List <TR2Entity>(Array.FindAll ( level.Data.Entities, e => ( e.X == entityInstance.X && e.Y == entityInstance.Y && e.Z == entityInstance.Z ) )); if (sharedItems.Count > 1) { // Are any entities that are sharing a location a droppable pickup? foreach (TR2Entity ent in sharedItems) { TR2Entities EntType = (TR2Entities)ent.TypeID; if ( TR2EntityUtilities.IsUtilityType(EntType) || TR2EntityUtilities.IsGunType(EntType) || TR2EntityUtilities.IsKeyItemType(EntType) ) { return(true); } } } } return(false); }
private void RepositionItems(List <Location> ItemLocs) { if (IsDevelopmentModeOn) { PlaceAllItems(ItemLocs); return; } if (ItemLocs.Count > 0) { //We are currently looking guns + ammo List <TR2Entities> targetents = TR2EntityUtilities.GetListOfGunTypes(); targetents.AddRange(TR2EntityUtilities.GetListOfAmmoTypes()); //And also key items... if (IncludeKeyItems) { targetents.AddRange(TR2EntityUtilities.GetListOfKeyItemTypes()); } //It's important to now start zoning key items as softlocks must be avoided. ZonedLocationCollection ZonedLocations = new ZonedLocationCollection(); ZonedLocations.PopulateZones(_levelInstance.Name, ItemLocs, ZonePopulationMethod.KeyPuzzleQuestOnly); for (int i = 0; i < _levelInstance.Data.Entities.Count(); i++) { if (targetents.Contains((TR2Entities)_levelInstance.Data.Entities[i].TypeID) && (i != _unarmedLevelPistolIndex)) { Location RandomLocation = new Location(); bool FoundPossibleLocation = false; if (TR2EntityUtilities.IsKeyItemType((TR2Entities)_levelInstance.Data.Entities[i].TypeID)) { TR2Entities type = (TR2Entities)_levelInstance.Data.Entities[i].TypeID; // Apply zoning for key items switch (type) { case TR2Entities.Puzzle1_S_P: if (ZonedLocations.Puzzle1Zone.Count > 0) { if (_levelInstance.Name == LevelNames.DA) { int burnerChipID = 120; int consoleChipID = 7; RandomLocation = ZonedLocations.Puzzle1Zone[_generator.Next(0, ZonedLocations.Puzzle1Zone.Count)]; //Special case - multiple chips if (i == burnerChipID) { //Burner Chip List <int> AllowedBurnerRooms = new List <int>() { 13, 14, 15, 16, 21, 22, 23, 24, 25, 26, 29, 30, 32, 34, 75, 80, 83, 84, 85, 86, 87, 88, 89 }; while (!AllowedBurnerRooms.Contains(RandomLocation.Room)) { RandomLocation = ZonedLocations.Puzzle1Zone[_generator.Next(0, ZonedLocations.Puzzle1Zone.Count)]; } FoundPossibleLocation = true; } else if (i == consoleChipID) { //Center Console Chip List <int> AllowedConsoleRooms = new List <int>() { 2, 12, 13, 14, 15, 16, 17, 21, 22, 23, 24, 25, 26, 29, 30, 32, 34, 35, 64, 65, 66, 68, 69, 70, 75, 80, 82, 83, 84, 85, 86, 87, 88, 89 }; while (!AllowedConsoleRooms.Contains(RandomLocation.Room)) { RandomLocation = ZonedLocations.Puzzle1Zone[_generator.Next(0, ZonedLocations.Puzzle1Zone.Count)]; } FoundPossibleLocation = true; } else { RandomLocation = ZonedLocations.Puzzle1Zone[_generator.Next(0, ZonedLocations.Puzzle1Zone.Count)]; FoundPossibleLocation = true; } } else { RandomLocation = ZonedLocations.Puzzle1Zone[_generator.Next(0, ZonedLocations.Puzzle1Zone.Count)]; FoundPossibleLocation = true; } } break; case TR2Entities.Puzzle2_S_P: if (ZonedLocations.Puzzle2Zone.Count > 0) { RandomLocation = ZonedLocations.Puzzle2Zone[_generator.Next(0, ZonedLocations.Puzzle2Zone.Count)]; FoundPossibleLocation = true; } break; case TR2Entities.Puzzle3_S_P: if (ZonedLocations.Puzzle3Zone.Count > 0) { RandomLocation = ZonedLocations.Puzzle3Zone[_generator.Next(0, ZonedLocations.Puzzle3Zone.Count)]; FoundPossibleLocation = true; } break; case TR2Entities.Puzzle4_S_P: if (ZonedLocations.Puzzle4Zone.Count > 0) { RandomLocation = ZonedLocations.Puzzle4Zone[_generator.Next(0, ZonedLocations.Puzzle4Zone.Count)]; FoundPossibleLocation = true; } break; case TR2Entities.Key1_S_P: if (ZonedLocations.Key1Zone.Count > 0) { if (_levelInstance.Name == LevelNames.OPERA) { int startKeyID = 172; int fanKeyID = 118; //Special case - multiple keys if (i == startKeyID) { //Start key List <int> AllowedStartRooms = new List <int>() { 10, 23, 25, 27, 29, 30, 31, 32, 33, 35, 127, 162, 163 }; while (!AllowedStartRooms.Contains(RandomLocation.Room)) { RandomLocation = ZonedLocations.Key1Zone[_generator.Next(0, ZonedLocations.Key1Zone.Count)]; } FoundPossibleLocation = true; } else if (i == fanKeyID) { //Fan area key List <int> AllowedFanRooms = new List <int>() { 1, 5, 8, 16, 37, 38, 44, 46, 47, 48, 49, 50, 52, 53, 55, 57, 59, 60, 63, 65, 66, 67, 68, 69, 70, 71, 72, 75, 76, 77, 78, 82, 83, 86, 87, 88, 89, 90, 93, 95, 96, 100, 102, 103, 105, 107, 109, 111, 120, 132, 139, 141, 143, 144, 151, 153, 154, 155, 156, 158, 159, 161, 174, 176, 177, 178, 179, 183, 185, 187, 188, 189 }; while (!AllowedFanRooms.Contains(RandomLocation.Room)) { RandomLocation = ZonedLocations.Key1Zone[_generator.Next(0, ZonedLocations.Key1Zone.Count)]; } FoundPossibleLocation = true; } else { RandomLocation = ZonedLocations.Key1Zone[_generator.Next(0, ZonedLocations.Key1Zone.Count)]; FoundPossibleLocation = true; } } else { RandomLocation = ZonedLocations.Key1Zone[_generator.Next(0, ZonedLocations.Key1Zone.Count)]; FoundPossibleLocation = true; } } break; case TR2Entities.Key2_S_P: if (ZonedLocations.Key2Zone.Count > 0) { RandomLocation = ZonedLocations.Key2Zone[_generator.Next(0, ZonedLocations.Key2Zone.Count)]; FoundPossibleLocation = true; } break; case TR2Entities.Key3_S_P: if (ZonedLocations.Key3Zone.Count > 0) { RandomLocation = ZonedLocations.Key3Zone[_generator.Next(0, ZonedLocations.Key3Zone.Count)]; FoundPossibleLocation = true; } break; case TR2Entities.Key4_S_P: if (ZonedLocations.Key4Zone.Count > 0) { RandomLocation = ZonedLocations.Key4Zone[_generator.Next(0, ZonedLocations.Key4Zone.Count)]; FoundPossibleLocation = true; } break; case TR2Entities.Quest1_S_P: if (ZonedLocations.Quest1Zone.Count > 0) { RandomLocation = ZonedLocations.Quest1Zone[_generator.Next(0, ZonedLocations.Quest1Zone.Count)]; FoundPossibleLocation = true; } break; case TR2Entities.Quest2_S_P: if (ZonedLocations.Quest2Zone.Count > 0) { RandomLocation = ZonedLocations.Quest2Zone[_generator.Next(0, ZonedLocations.Quest2Zone.Count)]; FoundPossibleLocation = true; } break; default: break; } } else { //Place standard items as normal for now RandomLocation = ItemLocs[_generator.Next(0, ItemLocs.Count)]; FoundPossibleLocation = true; } if (FoundPossibleLocation) { Location GlobalizedRandomLocation = SpatialConverters.TransformToLevelSpace(RandomLocation, _levelInstance.Data.Rooms[RandomLocation.Room].Info); _levelInstance.Data.Entities[i].Room = Convert.ToInt16(GlobalizedRandomLocation.Room); _levelInstance.Data.Entities[i].X = GlobalizedRandomLocation.X; _levelInstance.Data.Entities[i].Y = GlobalizedRandomLocation.Y; _levelInstance.Data.Entities[i].Z = GlobalizedRandomLocation.Z; _levelInstance.Data.Entities[i].Intensity1 = -1; _levelInstance.Data.Entities[i].Intensity2 = -1; } } } } }
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); } } }
private EnemyTransportCollection SelectCrossLevelEnemies(TR2CombinedLevel level, int reduceEnemyCountBy = 0) { // For the assault course, nothing will be imported for the time being if (level.IsAssault) { return(null); } // Get the list of enemy types currently in the level List <TR2Entities> oldEntities = TR2EntityUtilities.GetEnemyTypeDictionary()[level.Name]; // Work out how many we can support int enemyCount = oldEntities.Count - reduceEnemyCountBy + EnemyUtilities.GetEnemyAdjustmentCount(level.Name); List <TR2Entities> newEntities = new List <TR2Entities>(enemyCount); List <TR2Entities> chickenGuisers = EnemyUtilities.GetEnemyGuisers(TR2Entities.BirdMonster); TR2Entities chickenGuiser = TR2Entities.BirdMonster; // #148 For HSH, we lock the enemies that are required for the kill counter to work outside // the gate, which means the game still has the correct target kill count, while allowing // us to randomize the ones inside the gate (except the final shotgun goon). // If however, we are on the final packing attempt, we will just change the stick goon // alias and add docile bird monsters (if selected) as this is known to be supported. if (level.Is(LevelNames.HOME) && reduceEnemyCountBy > 0) { TR2Entities newGoon = TR2Entities.StickWieldingGoon1BlackJacket; List <TR2Entities> goonies = TR2EntityUtilities.GetEntityFamily(newGoon); do { newGoon = goonies[_generator.Next(0, goonies.Count)]; }while (newGoon == TR2Entities.StickWieldingGoon1BlackJacket); newEntities.AddRange(oldEntities); newEntities.Remove(TR2Entities.StickWieldingGoon1); newEntities.Add(newGoon); if (DocileBirdMonsters) { newEntities.Remove(TR2Entities.MaskedGoon1); newEntities.Add(TR2Entities.BirdMonster); chickenGuiser = TR2Entities.MaskedGoon1; } } else { // Do we need at least one water creature? bool waterEnemyRequired = EnemyUtilities.IsWaterEnemyRequired(level); // Do we need at least one enemy that can drop? bool droppableEnemyRequired = EnemyUtilities.IsDroppableEnemyRequired(level); // Let's try to populate the list. Start by adding one water enemy // and one droppable enemy if they are needed. if (waterEnemyRequired) { List <TR2Entities> waterEnemies = TR2EntityUtilities.KillableWaterCreatures(); TR2Entities entity; do { entity = waterEnemies[_generator.Next(0, waterEnemies.Count)]; }while (!EnemyUtilities.IsEnemySupported(level.Name, entity)); newEntities.Add(entity); } if (droppableEnemyRequired) { List <TR2Entities> droppableEnemies = TR2EntityUtilities.GetCrossLevelDroppableEnemies(!ProtectMonks); TR2Entities entity; do { entity = droppableEnemies[_generator.Next(0, droppableEnemies.Count)]; }while (!EnemyUtilities.IsEnemySupported(level.Name, entity)); newEntities.Add(entity); } // Are there any other types we need to retain? foreach (TR2Entities entity in EnemyUtilities.GetRequiredEnemies(level.Name)) { if (!newEntities.Contains(entity)) { newEntities.Add(entity); } } // Get all other candidate enemies and fill the list List <TR2Entities> allEnemies = TR2EntityUtilities.GetCandidateCrossLevelEnemies(); while (newEntities.Count < newEntities.Capacity) { TR2Entities entity = allEnemies[_generator.Next(0, allEnemies.Count)]; // Make sure this isn't known to be unsupported in the level if (!EnemyUtilities.IsEnemySupported(level.Name, entity)) { continue; } // If it's the chicken in HSH but we're not using docile, we don't want it ending the level if (!DocileBirdMonsters && entity == TR2Entities.BirdMonster && level.Is(LevelNames.HOME)) { continue; } // If it's a docile chicken in Barkhang, it won't work because we can't disguise monks in this level. if (DocileBirdMonsters && entity == TR2Entities.BirdMonster && level.Is(LevelNames.MONASTERY)) { continue; } // If this is a tracked enemy throughout the game, we only allow it if the number // of unique levels is within the limit. Bear in mind we are collecting more than // one group of enemies per level. if (_gameEnemyTracker.ContainsKey(entity) && !_gameEnemyTracker[entity].Contains(level.Name)) { if (_gameEnemyTracker[entity].Count < _gameEnemyTracker[entity].Capacity) { // The entity is allowed, so store the fact that this level will have it _gameEnemyTracker[entity].Add(level.Name); } else { // Otherwise, pick something else continue; } } // GetEntityFamily returns all aliases for the likes of the tigers, but if an entity // doesn't have any, the returned list just contains the entity itself. This means // we can avoid duplicating standard enemies as well as avoiding alias-clashing. List <TR2Entities> family = TR2EntityUtilities.GetEntityFamily(entity); if (!newEntities.Any(e1 => family.Any(e2 => e1 == e2))) { // #144 We can include docile chickens provided we aren't including everything // that can be disguised as a chicken. if (DocileBirdMonsters) { bool guisersAvailable = !chickenGuisers.All(g => newEntities.Contains(g)); // If the selected entity is the chicken, it can be added provided there are // available guisers. if (!guisersAvailable && entity == TR2Entities.BirdMonster) { continue; } // If the selected entity is a potential guiser, it can only be added if it's not // the last available guiser. Otherwise, it will become the guiser. if (chickenGuisers.Contains(entity) && newEntities.Contains(TR2Entities.BirdMonster)) { if (newEntities.FindAll(e => chickenGuisers.Contains(e)).Count == chickenGuisers.Count - 1) { continue; } } } newEntities.Add(entity); } } } // #144 Decide at this point who will be guising unless it has already been decided above (e.g. HSH) if (DocileBirdMonsters && newEntities.Contains(TR2Entities.BirdMonster) && chickenGuiser == TR2Entities.BirdMonster) { int guiserIndex = chickenGuisers.FindIndex(g => !newEntities.Contains(g)); if (guiserIndex != -1) { chickenGuiser = chickenGuisers[guiserIndex]; } } return(new EnemyTransportCollection { EntitiesToImport = newEntities, EntitiesToRemove = oldEntities, BirdMonsterGuiser = chickenGuiser }); }
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 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 RepositionItems(List <Location> ItemLocs, string lvl) { if (IsDevelopmentModeOn) { PlaceAllItems(ItemLocs); return; } if (ItemLocs.Count > 0) { //We are currently looking guns + ammo List <TR2Entities> targetents = TR2EntityUtilities.GetListOfGunTypes(); targetents.AddRange(TR2EntityUtilities.GetListOfAmmoTypes()); //And also key items... if (IncludeKeyItems) { targetents.AddRange(TR2EntityUtilities.GetListOfKeyItemTypes()); } //It's important to now start zoning key items as softlocks must be avoided. ZonedLocationCollection ZonedLocations = new ZonedLocationCollection(); ZonedLocations.PopulateZones(lvl, ItemLocs, ZonePopulationMethod.KeyPuzzleQuestOnly); for (int i = 0; i < _levelInstance.Entities.Count(); i++) { if (targetents.Contains((TR2Entities)_levelInstance.Entities[i].TypeID) && (i != _unarmedLevelPistolIndex)) { Location RandomLocation = new Location(); bool FoundPossibleLocation = false; if (TR2EntityUtilities.IsKeyItemType((TR2Entities)_levelInstance.Entities[i].TypeID)) { TR2Entities type = (TR2Entities)_levelInstance.Entities[i].TypeID; // Apply zoning for key items switch (type) { case TR2Entities.Puzzle1_S_P: if (ZonedLocations.Puzzle1Zone.Count > 0) { RandomLocation = ZonedLocations.Puzzle1Zone[_generator.Next(0, ZonedLocations.Puzzle1Zone.Count)]; FoundPossibleLocation = true; } break; case TR2Entities.Puzzle2_S_P: if (ZonedLocations.Puzzle2Zone.Count > 0) { RandomLocation = ZonedLocations.Puzzle2Zone[_generator.Next(0, ZonedLocations.Puzzle2Zone.Count)]; FoundPossibleLocation = true; } break; case TR2Entities.Puzzle3_S_P: if (ZonedLocations.Puzzle3Zone.Count > 0) { RandomLocation = ZonedLocations.Puzzle3Zone[_generator.Next(0, ZonedLocations.Puzzle3Zone.Count)]; FoundPossibleLocation = true; } break; case TR2Entities.Puzzle4_S_P: if (ZonedLocations.Puzzle4Zone.Count > 0) { RandomLocation = ZonedLocations.Puzzle4Zone[_generator.Next(0, ZonedLocations.Puzzle4Zone.Count)]; FoundPossibleLocation = true; } break; case TR2Entities.Key1_S_P: if (ZonedLocations.Key1Zone.Count > 0) { RandomLocation = ZonedLocations.Key1Zone[_generator.Next(0, ZonedLocations.Key1Zone.Count)]; FoundPossibleLocation = true; } break; case TR2Entities.Key2_S_P: if (ZonedLocations.Key2Zone.Count > 0) { RandomLocation = ZonedLocations.Key2Zone[_generator.Next(0, ZonedLocations.Key2Zone.Count)]; FoundPossibleLocation = true; } break; case TR2Entities.Key3_S_P: if (ZonedLocations.Key3Zone.Count > 0) { RandomLocation = ZonedLocations.Key3Zone[_generator.Next(0, ZonedLocations.Key3Zone.Count)]; FoundPossibleLocation = true; } break; case TR2Entities.Key4_S_P: if (ZonedLocations.Key4Zone.Count > 0) { RandomLocation = ZonedLocations.Key4Zone[_generator.Next(0, ZonedLocations.Key4Zone.Count)]; FoundPossibleLocation = true; } break; case TR2Entities.Quest1_S_P: if (ZonedLocations.Quest1Zone.Count > 0) { RandomLocation = ZonedLocations.Quest1Zone[_generator.Next(0, ZonedLocations.Quest1Zone.Count)]; FoundPossibleLocation = true; } break; case TR2Entities.Quest2_S_P: if (ZonedLocations.Quest2Zone.Count > 0) { RandomLocation = ZonedLocations.Quest2Zone[_generator.Next(0, ZonedLocations.Quest2Zone.Count)]; FoundPossibleLocation = true; } break; default: break; } } else { //Place standard items as normal for now RandomLocation = ItemLocs[_generator.Next(0, ItemLocs.Count)]; FoundPossibleLocation = true; } if (FoundPossibleLocation) { Location GlobalizedRandomLocation = SpatialConverters.TransformToLevelSpace(RandomLocation, _levelInstance.Rooms[RandomLocation.Room].Info); _levelInstance.Entities[i].Room = Convert.ToInt16(GlobalizedRandomLocation.Room); _levelInstance.Entities[i].X = GlobalizedRandomLocation.X; _levelInstance.Entities[i].Y = GlobalizedRandomLocation.Y; _levelInstance.Entities[i].Z = GlobalizedRandomLocation.Z; _levelInstance.Entities[i].Intensity1 = -1; _levelInstance.Entities[i].Intensity2 = -1; } } } } }
private void RandomizeEnemyTypes(string lvl) { List <TR2Entities> EnemyTypes = TR2EntityUtilities.GetEnemyTypeDictionary()[lvl]; for (int i = 0; i < _levelInstance.Entities.Count(); i++) { if (lvl == LevelNames.CHICKEN && _levelInstance.Entities[i].Room == 143 && _levelInstance.Entities[i].TypeID == (int)TR2Entities.BirdMonster) { //#60 - Ice Palace - Room 143 chicken man must remain to avoid softlock. continue; } else if (lvl == LevelNames.HOME && _levelInstance.Entities[i].TypeID == (int)TR2Entities.ShotgunGoon) { //#62 - Avoid randomizing shotgun goon in HSH continue; } //#45 - Check to see if any items are at the same location as the enemy. //If there are we need to ensure that the new random enemy type is one that can drop items. List <TR2Entity> SharedItems = new List <TR2Entity>(Array.FindAll(_levelInstance.Entities, e => ((e.X == _levelInstance.Entities[i].X) && (e.Y == _levelInstance.Entities[i].Y) && (e.Z == _levelInstance.Entities[i].Z)))); List <TR2Entities> DroppableEnemies = TR2EntityUtilities.DroppableEnemyTypes()[lvl]; //Is it an entity we are keen on replacing? if (EnemyTypes.Contains((TR2Entities)_levelInstance.Entities[i].TypeID)) { //Do multiple entities share one location? if ((SharedItems.Count > 1) && (DroppableEnemies.Count != 0)) { //Are any entities sharing a location a droppable pickup? bool IsPickupItem = false; foreach (TR2Entity ent in SharedItems) { TR2Entities EntType = (TR2Entities)ent.TypeID; IsPickupItem = (TR2EntityUtilities.IsUtilityType(EntType)) || (TR2EntityUtilities.IsGunType(EntType)) || (TR2EntityUtilities.IsKeyItemType(EntType)); if (IsPickupItem) { break; } } //Generate a location _levelInstance.Entities[i].TypeID = (short)EnemyTypes[_generator.Next(0, EnemyTypes.Count)]; //Do we need to ensure the enemy can drop the item on the same tile? if (!TR2EntityUtilities.CanDropPickups((TR2Entities)_levelInstance.Entities[i].TypeID, false) && IsPickupItem) { //Ensure the new random entity can drop pickups _levelInstance.Entities[i].TypeID = (short)DroppableEnemies[_generator.Next(0, DroppableEnemies.Count)]; } } else { _levelInstance.Entities[i].TypeID = (short)EnemyTypes[_generator.Next(0, EnemyTypes.Count)]; } short room = _levelInstance.Entities[i].Room; if (!TR2EntityUtilities.IsWaterCreature((TR2Entities)_levelInstance.Entities[i].TypeID) && _levelInstance.Rooms[room].ContainsWater) { if (!PerformDraining(lvl, room)) { //Draining cannot be performed so make the entity a water creature. TR2Entities ent; //Make sure water creature can appear on level do { ent = TR2EntityUtilities.WaterCreatures()[_generator.Next(0, TR2EntityUtilities.WaterCreatures().Count)]; } while (!EnemyTypes.Contains(ent)); _levelInstance.Entities[i].TypeID = (short)ent; } } } } }
private void RandomizeEnemyTypes(string lvl) { List <TR2Entities> EnemyTypes = TR2EntityUtilities.GetEnemyTypeDictionary()[lvl]; for (int i = 0; i < _levelInstance.Entities.Count(); i++) { //#60 - Ice Palace - Room 143 chicken man must remain to avoid softlock. if (lvl == LevelNames.CHICKEN && _levelInstance.Entities[i].Room == 143 && _levelInstance.Entities[i].TypeID == (int)TR2Entities.BirdMonster) { continue; } //#45 - Check to see if any items are at the same location as the enemy. //If there are we need to ensure that the new random enemy type is one that can drop items. List <TR2Entity> SharedItems = new List <TR2Entity>(Array.FindAll(_levelInstance.Entities, e => ((e.X == _levelInstance.Entities[i].X) && (e.Y == _levelInstance.Entities[i].Y) && (e.Z == _levelInstance.Entities[i].Z)))); List <TR2Entities> DroppableEnemies = TR2EntityUtilities.DroppableEnemyTypes()[lvl]; //Is it an entity we are keen on replacing? if (EnemyTypes.Contains((TR2Entities)_levelInstance.Entities[i].TypeID)) { //Do multiple entities share one location? if ((SharedItems.Count > 1) && (DroppableEnemies.Count != 0)) { //Are any entities sharing a location a droppable pickup? bool IsPickupItem = false; foreach (TR2Entity ent in SharedItems) { TR2Entities EntType = (TR2Entities)ent.TypeID; IsPickupItem = (TR2EntityUtilities.IsAmmoType(EntType)) || (TR2EntityUtilities.IsGunType(EntType)) || (TR2EntityUtilities.IsKeyItemType(EntType)); if (IsPickupItem) { break; } } //Generate a location _levelInstance.Entities[i].TypeID = (short)EnemyTypes[_generator.Next(0, EnemyTypes.Count)]; //Do we need to ensure the enemy can drop the item on the same tile? if (!TR2EntityUtilities.CanDropPickups((TR2Entities)_levelInstance.Entities[i].TypeID) && IsPickupItem) { //Ensure the new random entity can drop pickups _levelInstance.Entities[i].TypeID = (short)DroppableEnemies[_generator.Next(0, DroppableEnemies.Count)]; } } else { _levelInstance.Entities[i].TypeID = (short)EnemyTypes[_generator.Next(0, EnemyTypes.Count)]; } } } }