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 }); }
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)); }
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)]; } } } }
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 }); }