private IEnumerable <object> CreateAIHusk() { character.Enabled = false; Entity.Spawner.AddToRemoveQueue(character); string huskedSpeciesName = GetHuskedSpeciesName(character.SpeciesName, Prefab as AfflictionPrefabHusk); CharacterPrefab prefab = CharacterPrefab.FindBySpeciesName(huskedSpeciesName); if (prefab == null) { DebugConsole.ThrowError("Failed to turn character \"" + character.Name + "\" into a husk - husk config file not found."); yield return(CoroutineStatus.Success); } var husk = Character.Create(huskedSpeciesName, character.WorldPosition, ToolBox.RandomSeed(8), character.Info, isRemotePlayer: false, hasAi: true); foreach (Limb limb in husk.AnimController.Limbs) { if (limb.type == LimbType.None) { limb.body.SetTransform(character.SimPosition, 0.0f); continue; } var matchingLimb = character.AnimController.GetLimb(limb.type); if (matchingLimb?.body != null) { limb.body.SetTransform(matchingLimb.SimPosition, matchingLimb.Rotation); limb.body.LinearVelocity = matchingLimb.LinearVelocity; limb.body.AngularVelocity = matchingLimb.body.AngularVelocity; } } if (character.Inventory != null && husk.Inventory != null) { if (character.Inventory.Items.Length != husk.Inventory.Items.Length) { string errorMsg = "Failed to move items from the source character's inventory into a husk's inventory (inventory sizes don't match)"; DebugConsole.ThrowError(errorMsg); GameAnalyticsManager.AddErrorEventOnce("AfflictionHusk.CreateAIHusk:InventoryMismatch", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); yield return(CoroutineStatus.Success); } for (int i = 0; i < character.Inventory.Items.Length && i < husk.Inventory.Items.Length; i++) { if (character.Inventory.Items[i] == null) { continue; } husk.Inventory.TryPutItem(character.Inventory.Items[i], i, true, false, null); } } husk.SetStun(5); yield return(new WaitForSeconds(5, false)); #if CLIENT husk.PlaySound(CharacterSound.SoundType.Idle); #endif yield return(CoroutineStatus.Success); }
private void InitCharacters(Submarine submarine) { characters.Clear(); characterItems.Clear(); if (characterConfig == null) { return; } foreach (XElement element in characterConfig.Elements()) { if (GameMain.NetworkMember == null && element.GetAttributeBool("multiplayeronly", false)) { continue; } int defaultCount = element.GetAttributeInt("count", -1); if (defaultCount < 0) { defaultCount = element.GetAttributeInt("amount", 1); } int min = Math.Min(element.GetAttributeInt("min", defaultCount), 255); int max = Math.Min(Math.Max(min, element.GetAttributeInt("max", defaultCount)), 255); int count = Rand.Range(min, max + 1); if (element.Attribute("identifier") != null && element.Attribute("from") != null) { string characterIdentifier = element.GetAttributeString("identifier", ""); string characterFrom = element.GetAttributeString("from", ""); HumanPrefab humanPrefab = NPCSet.Get(characterFrom, characterIdentifier); if (humanPrefab == null) { DebugConsole.ThrowError("Couldn't spawn a character for abandoned outpost mission: character prefab \"" + characterIdentifier + "\" not found"); continue; } for (int i = 0; i < count; i++) { LoadHuman(humanPrefab, element, submarine); } } else { string speciesName = element.GetAttributeString("character", element.GetAttributeString("identifier", "")); var characterPrefab = CharacterPrefab.FindBySpeciesName(speciesName); if (characterPrefab == null) { DebugConsole.ThrowError("Couldn't spawn a character for abandoned outpost mission: character prefab \"" + speciesName + "\" not found"); continue; } for (int i = 0; i < count; i++) { LoadMonster(characterPrefab, element, submarine); } } } }
public static Character Spawn(string name, Vector2 worldPos) { Character spawnedCharacter = null; Vector2 spawnPosition = worldPos; string characterLowerCase = name.ToLowerInvariant(); JobPrefab job = null; if (!JobPrefab.Prefabs.ContainsKey(characterLowerCase)) { job = JobPrefab.Prefabs.Find(jp => jp.Name != null && jp.Name.Equals(characterLowerCase, StringComparison.OrdinalIgnoreCase)); } else { job = JobPrefab.Prefabs[characterLowerCase]; } bool human = job != null || characterLowerCase == CharacterPrefab.HumanSpeciesName; if (string.IsNullOrWhiteSpace(name)) { return(null); } if (human) { var variant = job != null?Rand.Range(0, job.Variants, Rand.RandSync.Server) : 0; CharacterInfo characterInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: job, variant: variant); spawnedCharacter = Character.Create(characterInfo, spawnPosition, ToolBox.RandomSeed(8)); if (GameMain.GameSession != null) { //TODO: a way to select which team to spawn to? spawnedCharacter.TeamID = Character.Controlled != null ? Character.Controlled.TeamID : CharacterTeamType.Team1; #if CLIENT GameMain.GameSession.CrewManager.AddCharacter(spawnedCharacter); #endif } spawnedCharacter.GiveJobItems(null); spawnedCharacter.Info.StartItemsGiven = true; } else { if (CharacterPrefab.FindBySpeciesName(name) != null) { spawnedCharacter = Character.Create(name, spawnPosition, ToolBox.RandomSeed(8)); } } return(spawnedCharacter); }
public override IEnumerable <ContentFile> GetFilesToPreload() { string path = CharacterPrefab.FindBySpeciesName(speciesName)?.FilePath; if (string.IsNullOrWhiteSpace(path)) { DebugConsole.ThrowError($"Failed to find config file for species \"{speciesName}\""); yield break; } else { yield return(new ContentFile(path, ContentType.Character)); } }
public MonsterMission(MissionPrefab prefab, Location[] locations) : base(prefab, locations) { string speciesName = prefab.ConfigElement.GetAttributeString("monsterfile", null); if (!string.IsNullOrEmpty(speciesName)) { var characterPrefab = CharacterPrefab.FindBySpeciesName(speciesName); if (characterPrefab != null) { int monsterCount = Math.Min(prefab.ConfigElement.GetAttributeInt("monstercount", 1), 255); monsterPrefabs.Add(new Tuple <CharacterPrefab, Point>(characterPrefab, new Point(monsterCount))); } else { DebugConsole.ThrowError($"Error in monster mission \"{prefab.Identifier}\". Could not find a character prefab with the name \"{speciesName}\"."); } } maxSonarMarkerDistance = prefab.ConfigElement.GetAttributeFloat("maxsonarmarkerdistance", 10000.0f); foreach (var monsterElement in prefab.ConfigElement.GetChildElements("monster")) { speciesName = monsterElement.GetAttributeString("character", string.Empty); int defaultCount = monsterElement.GetAttributeInt("count", -1); if (defaultCount < 0) { defaultCount = monsterElement.GetAttributeInt("amount", 1); } int min = Math.Min(monsterElement.GetAttributeInt("min", defaultCount), 255); int max = Math.Min(Math.Max(min, monsterElement.GetAttributeInt("max", defaultCount)), 255); var characterPrefab = CharacterPrefab.FindBySpeciesName(speciesName); if (characterPrefab != null) { monsterPrefabs.Add(new Tuple <CharacterPrefab, Point>(characterPrefab, new Point(min, max))); } else { DebugConsole.ThrowError($"Error in monster mission \"{prefab.Identifier}\". Could not find a character prefab with the name \"{speciesName}\"."); } } if (monsterPrefabs.Any()) { var characterParams = new CharacterParams(monsterPrefabs.First().Item1.FilePath); description = description.Replace("[monster]", TextManager.Get("character." + characterParams.SpeciesTranslationOverride, returnNull: true) ?? TextManager.Get("character." + characterParams.SpeciesName)); } }
public NestMission(MissionPrefab prefab, Location[] locations, Submarine sub) : base(prefab, locations, sub) { itemConfig = prefab.ConfigElement.Element("Items"); itemSpawnRadius = prefab.ConfigElement.GetAttributeFloat("itemspawnradius", 800.0f); approachItemsRadius = prefab.ConfigElement.GetAttributeFloat("approachitemsradius", itemSpawnRadius * 2.0f); monsterSpawnRadius = prefab.ConfigElement.GetAttributeFloat("monsterspawnradius", approachItemsRadius * 2.0f); nestObjectRadius = prefab.ConfigElement.GetAttributeFloat("nestobjectradius", itemSpawnRadius * 2.0f); nestObjectAmount = prefab.ConfigElement.GetAttributeInt("nestobjectamount", 10); requireDelivery = prefab.ConfigElement.GetAttributeBool("requiredelivery", false); string spawnPositionTypeStr = prefab.ConfigElement.GetAttributeString("spawntype", ""); if (string.IsNullOrWhiteSpace(spawnPositionTypeStr) || !Enum.TryParse(spawnPositionTypeStr, true, out spawnPositionType)) { spawnPositionType = Level.PositionType.Cave | Level.PositionType.Ruin; } foreach (var monsterElement in prefab.ConfigElement.GetChildElements("monster")) { string speciesName = monsterElement.GetAttributeString("character", string.Empty); int defaultCount = monsterElement.GetAttributeInt("count", -1); if (defaultCount < 0) { defaultCount = monsterElement.GetAttributeInt("amount", 1); } int min = Math.Min(monsterElement.GetAttributeInt("min", defaultCount), 255); int max = Math.Min(Math.Max(min, monsterElement.GetAttributeInt("max", defaultCount)), 255); var characterPrefab = CharacterPrefab.FindBySpeciesName(speciesName); if (characterPrefab != null) { monsterPrefabs.Add(new Tuple <CharacterPrefab, Point>(characterPrefab, new Point(min, max))); } else { DebugConsole.ThrowError($"Error in monster mission \"{prefab.Identifier}\". Could not find a character prefab with the name \"{speciesName}\"."); } } }
public bool Load() { bool success = base.Load(File); if (doc.Root.IsCharacterVariant()) { VariantFile = doc; var original = CharacterPrefab.FindBySpeciesName(doc.Root.GetAttributeString("inherit", string.Empty)); success = Load(original.FilePath); CreateSubParams(); TryLoadOverride(this, VariantFile.Root, SerializableProperties); foreach (XElement subElement in VariantFile.Root.Elements()) { var matchingParams = SubParams.FirstOrDefault(p => p.Name.Equals(subElement.Name.ToString(), StringComparison.OrdinalIgnoreCase)); if (matchingParams != null) { TryLoadOverride(matchingParams, subElement, matchingParams.SerializableProperties); // TODO: Make recursive? In practice we don't have to go deeper than this, but the implementation would be a lot cleaner with recursion. foreach (XElement subSubElement in subElement.Elements()) { if (subSubElement.Name.ToString().Equals("item", StringComparison.OrdinalIgnoreCase)) { continue; } var matchingSubParams = matchingParams.SubParams.FirstOrDefault(p => p.Name.Equals(subSubElement.Name.ToString(), StringComparison.OrdinalIgnoreCase)); if (matchingSubParams != null) { TryLoadOverride(matchingSubParams, subSubElement, matchingSubParams.SerializableProperties); } } } } return(success); } if (string.IsNullOrEmpty(SpeciesName) && MainElement != null) { //backwards compatibility SpeciesName = MainElement.GetAttributeString("name", ""); } CreateSubParams(); return(success); }
public void PreloadContent(IEnumerable <ContentFile> contentFiles) { var filesToPreload = new List <ContentFile>(contentFiles); foreach (Submarine sub in Submarine.Loaded) { if (sub.WreckAI == null) { continue; } if (!string.IsNullOrEmpty(sub.WreckAI.Config.DefensiveAgent)) { var prefab = CharacterPrefab.FindBySpeciesName(sub.WreckAI.Config.DefensiveAgent); if (prefab != null && !filesToPreload.Any(f => f.Path == prefab.FilePath)) { filesToPreload.Add(new ContentFile(prefab.FilePath, ContentType.Character)); } } foreach (Item item in Item.ItemList) { if (item.Submarine != sub) { continue; } foreach (Items.Components.ItemComponent component in item.Components) { if (component.statusEffectLists == null) { continue; } foreach (var statusEffectList in component.statusEffectLists.Values) { foreach (StatusEffect statusEffect in statusEffectList) { foreach (var spawnInfo in statusEffect.SpawnCharacters) { var prefab = CharacterPrefab.FindBySpeciesName(spawnInfo.SpeciesName); if (prefab != null && !filesToPreload.Any(f => f.Path == prefab.FilePath)) { filesToPreload.Add(new ContentFile(prefab.FilePath, ContentType.Character)); } } } } } } } foreach (ContentFile file in filesToPreload) { switch (file.Type) { case ContentType.Character: #if CLIENT CharacterPrefab characterPrefab = CharacterPrefab.FindByFilePath(file.Path); if (characterPrefab?.XDocument == null) { throw new Exception($"Failed to load the character config file from {file.Path}!"); } var doc = characterPrefab.XDocument; var rootElement = doc.Root; var mainElement = rootElement.IsOverride() ? rootElement.FirstElement() : rootElement; foreach (var soundElement in mainElement.GetChildElements("sound")) { var sound = Submarine.LoadRoundSound(soundElement); } string speciesName = mainElement.GetAttributeString("speciesname", null); if (string.IsNullOrWhiteSpace(speciesName)) { speciesName = mainElement.GetAttributeString("name", null); if (!string.IsNullOrWhiteSpace(speciesName)) { DebugConsole.NewMessage($"Error in {file.Path}: 'name' is deprecated! Use 'speciesname' instead.", Color.Orange); } else { throw new Exception($"Species name null in {file.Path}"); } } bool humanoid = mainElement.GetAttributeBool("humanoid", false); RagdollParams ragdollParams; if (humanoid) { ragdollParams = RagdollParams.GetRagdollParams <HumanRagdollParams>(speciesName); } else { ragdollParams = RagdollParams.GetRagdollParams <FishRagdollParams>(speciesName); } if (ragdollParams != null) { HashSet <string> texturePaths = new HashSet <string> { ragdollParams.Texture }; foreach (RagdollParams.LimbParams limb in ragdollParams.Limbs) { if (!string.IsNullOrEmpty(limb.normalSpriteParams?.Texture)) { texturePaths.Add(limb.normalSpriteParams.Texture); } if (!string.IsNullOrEmpty(limb.deformSpriteParams?.Texture)) { texturePaths.Add(limb.deformSpriteParams.Texture); } if (!string.IsNullOrEmpty(limb.damagedSpriteParams?.Texture)) { texturePaths.Add(limb.damagedSpriteParams.Texture); } foreach (var decorativeSprite in limb.decorativeSpriteParams) { if (!string.IsNullOrEmpty(decorativeSprite.Texture)) { texturePaths.Add(decorativeSprite.Texture); } } } foreach (string texturePath in texturePaths) { preloadedSprites.Add(new Sprite(texturePath, Vector2.Zero)); } } #endif break; } } }
public void PreloadContent(IEnumerable <ContentFile> contentFiles) { var filesToPreload = new List <ContentFile>(contentFiles); foreach (Submarine sub in Submarine.Loaded) { if (sub.WreckAI == null) { continue; } if (!string.IsNullOrEmpty(sub.WreckAI.Config.DefensiveAgent)) { var prefab = CharacterPrefab.FindBySpeciesName(sub.WreckAI.Config.DefensiveAgent); if (prefab != null && !filesToPreload.Any(f => f.Path == prefab.FilePath)) { filesToPreload.Add(new ContentFile(prefab.FilePath, ContentType.Character)); } } foreach (Item item in Item.ItemList) { if (item.Submarine != sub) { continue; } foreach (Items.Components.ItemComponent component in item.Components) { if (component.statusEffectLists == null) { continue; } foreach (var statusEffectList in component.statusEffectLists.Values) { foreach (StatusEffect statusEffect in statusEffectList) { foreach (var spawnInfo in statusEffect.SpawnCharacters) { var prefab = CharacterPrefab.FindBySpeciesName(spawnInfo.SpeciesName); if (prefab != null && !filesToPreload.Any(f => f.Path == prefab.FilePath)) { filesToPreload.Add(new ContentFile(prefab.FilePath, ContentType.Character)); } } } } } } } foreach (ContentFile file in filesToPreload) { switch (file.Type) { case ContentType.Character: #if CLIENT CharacterPrefab characterPrefab = CharacterPrefab.FindByFilePath(file.Path); if (characterPrefab?.XDocument == null) { throw new Exception($"Failed to load the character config file from {file.Path}!"); } var doc = characterPrefab.XDocument; var rootElement = doc.Root; var mainElement = rootElement.IsOverride() ? rootElement.FirstElement() : rootElement; mainElement.GetChildElements("sound").ForEach(e => Submarine.LoadRoundSound(e)); if (!CharacterPrefab.CheckSpeciesName(mainElement, file.Path, out string speciesName)) { continue; } bool humanoid = mainElement.GetAttributeBool("humanoid", false); CharacterPrefab originalCharacter; if (characterPrefab.VariantOf != null) { originalCharacter = CharacterPrefab.FindBySpeciesName(characterPrefab.VariantOf); var originalRoot = originalCharacter.XDocument.Root; var originalMainElement = originalRoot.IsOverride() ? originalRoot.FirstElement() : originalRoot; originalMainElement.GetChildElements("sound").ForEach(e => Submarine.LoadRoundSound(e)); if (!CharacterPrefab.CheckSpeciesName(mainElement, file.Path, out string name)) { continue; } speciesName = name; if (mainElement.Attribute("humanoid") == null) { humanoid = originalMainElement.GetAttributeBool("humanoid", false); } } RagdollParams ragdollParams; try { if (humanoid) { ragdollParams = RagdollParams.GetRagdollParams <HumanRagdollParams>(characterPrefab.VariantOf ?? speciesName); } else { ragdollParams = RagdollParams.GetRagdollParams <FishRagdollParams>(characterPrefab.VariantOf ?? speciesName); } } catch (Exception e) { DebugConsole.ThrowError($"Failed to preload a ragdoll file for the character \"{characterPrefab.Name}\"", e); continue; } if (ragdollParams != null) { HashSet <string> texturePaths = new HashSet <string> { ragdollParams.Texture }; foreach (RagdollParams.LimbParams limb in ragdollParams.Limbs) { if (!string.IsNullOrEmpty(limb.normalSpriteParams?.Texture)) { texturePaths.Add(limb.normalSpriteParams.Texture); } if (!string.IsNullOrEmpty(limb.deformSpriteParams?.Texture)) { texturePaths.Add(limb.deformSpriteParams.Texture); } if (!string.IsNullOrEmpty(limb.damagedSpriteParams?.Texture)) { texturePaths.Add(limb.damagedSpriteParams.Texture); } foreach (var decorativeSprite in limb.decorativeSpriteParams) { if (!string.IsNullOrEmpty(decorativeSprite.Texture)) { texturePaths.Add(decorativeSprite.Texture); } } } foreach (string texturePath in texturePaths) { preloadedSprites.Add(new Sprite(texturePath, Vector2.Zero)); } } #endif break; } } }
public static List <Limb> AttachHuskAppendage(Character character, string afflictionIdentifier, XElement appendageDefinition = null, Ragdoll ragdoll = null) { var appendage = new List <Limb>(); if (!(AfflictionPrefab.List.FirstOrDefault(ap => ap.Identifier == afflictionIdentifier) is AfflictionPrefabHusk matchingAffliction)) { DebugConsole.ThrowError($"Could not find an affliction of type 'huskinfection' that matches the affliction '{afflictionIdentifier}'!"); return(appendage); } string nonhuskedSpeciesName = GetNonHuskedSpeciesName(character.SpeciesName, matchingAffliction); string huskedSpeciesName = GetHuskedSpeciesName(nonhuskedSpeciesName, matchingAffliction); CharacterPrefab huskPrefab = CharacterPrefab.FindBySpeciesName(huskedSpeciesName); if (huskPrefab?.XDocument == null) { DebugConsole.ThrowError($"Failed to find the config file for the husk infected species with the species name '{huskedSpeciesName}'!"); return(appendage); } var mainElement = huskPrefab.XDocument.Root.IsOverride() ? huskPrefab.XDocument.Root.FirstElement() : huskPrefab.XDocument.Root; var element = appendageDefinition; if (element == null) { element = mainElement.GetChildElements("huskappendage").FirstOrDefault(e => e.GetAttributeString("affliction", string.Empty).Equals(afflictionIdentifier)); } if (element == null) { DebugConsole.ThrowError($"Error in '{huskPrefab.FilePath}': Failed to find a huskappendage that matches the affliction with an identifier '{afflictionIdentifier}'!"); return(appendage); } string pathToAppendage = element.GetAttributeString("path", string.Empty); XDocument doc = XMLExtensions.TryLoadXml(pathToAppendage); if (doc == null) { return(appendage); } if (ragdoll == null) { ragdoll = character.AnimController; } if (ragdoll.Dir < 1.0f) { ragdoll.Flip(); } var limbElements = doc.Root.Elements("limb").ToDictionary(e => e.GetAttributeString("id", null), e => e); foreach (var jointElement in doc.Root.Elements("joint")) { if (limbElements.TryGetValue(jointElement.GetAttributeString("limb2", null), out XElement limbElement)) { var jointParams = new RagdollParams.JointParams(jointElement, ragdoll.RagdollParams); Limb attachLimb = null; if (matchingAffliction.AttachLimbId > -1) { attachLimb = ragdoll.Limbs.FirstOrDefault(l => !l.IsSevered && l.Params.ID == matchingAffliction.AttachLimbId); } else if (matchingAffliction.AttachLimbName != null) { attachLimb = ragdoll.Limbs.FirstOrDefault(l => !l.IsSevered && l.Name == matchingAffliction.AttachLimbName); } else if (matchingAffliction.AttachLimbType != LimbType.None) { attachLimb = ragdoll.Limbs.FirstOrDefault(l => !l.IsSevered && l.type == matchingAffliction.AttachLimbType); } if (attachLimb == null) { attachLimb = ragdoll.Limbs.FirstOrDefault(l => !l.IsSevered && l.Params.ID == jointParams.Limb1); } if (attachLimb != null) { jointParams.Limb1 = attachLimb.Params.ID; var appendageLimbParams = new RagdollParams.LimbParams(limbElement, ragdoll.RagdollParams) { // Ensure that we have a valid id for the new limb ID = ragdoll.Limbs.Length }; jointParams.Limb2 = appendageLimbParams.ID; Limb huskAppendage = new Limb(ragdoll, character, appendageLimbParams); huskAppendage.body.Submarine = character.Submarine; huskAppendage.body.SetTransform(attachLimb.SimPosition, attachLimb.Rotation); ragdoll.AddLimb(huskAppendage); ragdoll.AddJoint(jointParams); appendage.Add(huskAppendage); } } } return(appendage); }
protected override void StartMissionSpecific(Level level) { existingTargets.Clear(); spawnedTargets.Clear(); allTargets.Clear(); if (IsClient) { return; } TargetRuin = Level.Loaded?.Ruins?.GetRandom(randSync: Rand.RandSync.Server); if (TargetRuin == null) { DebugConsole.ThrowError($"Failed to initialize an Alien Ruin mission (\"{Prefab.Identifier}\"): level contains no alien ruins"); return; } if (targetItemIdentifiers.Length < 1 && targetEnemyIdentifiers.Length < 1) { DebugConsole.ThrowError($"Failed to initialize an Alien Ruin mission (\"{Prefab.Identifier}\"): no target identifiers set in the mission definition"); return; } foreach (var item in Item.ItemList) { if (!targetItemIdentifiers.Contains(item.Prefab.Identifier)) { continue; } if (item.Submarine != TargetRuin.Submarine) { continue; } existingTargets.Add(item); allTargets.Add(item); } int existingEnemyCount = 0; foreach (var character in Character.CharacterList) { if (string.IsNullOrEmpty(character.SpeciesName)) { continue; } if (!targetEnemyIdentifiers.Contains(character.SpeciesName.ToLowerInvariant())) { continue; } if (character.Submarine != TargetRuin.Submarine) { continue; } existingTargets.Add(character); allTargets.Add(character); existingEnemyCount++; } if (existingEnemyCount < minEnemyCount) { var enemyPrefabs = new HashSet <CharacterPrefab>(); foreach (string identifier in targetEnemyIdentifiers) { var prefab = CharacterPrefab.FindBySpeciesName(identifier); if (prefab != null) { enemyPrefabs.Add(prefab); } else { DebugConsole.ThrowError($"Error in an Alien Ruin mission (\"{Prefab.Identifier}\"): could not find a character prefab with the species \"{identifier}\""); } } if (enemyPrefabs.None()) { DebugConsole.ThrowError($"Error in an Alien Ruin mission (\"{Prefab.Identifier}\"): no enemy species defined that could be used to spawn more ({minEnemyCount - existingEnemyCount}) enemies"); return; } for (int i = 0; i < (minEnemyCount - existingEnemyCount); i++) { var prefab = enemyPrefabs.GetRandom(); var spawnPos = TargetRuin.Submarine.GetWaypoints(false).GetRandom(w => w.CurrentHull != null)?.WorldPosition; if (!spawnPos.HasValue) { DebugConsole.ThrowError($"Error in an Alien Ruin mission (\"{Prefab.Identifier}\"): no valid spawn positions could be found for the additional ({minEnemyCount - existingEnemyCount}) enemies to be spawned"); return; } var newEnemy = Character.Create(prefab.Identifier, spawnPos.Value, ToolBox.RandomSeed(8), createNetworkEvent: false); spawnedTargets.Add(newEnemy); allTargets.Add(newEnemy); } } #if DEBUG DebugConsole.NewMessage("********** CLEAR RUIN MISSION INFO **********"); DebugConsole.NewMessage($"Existing item targets: {existingTargets.Count - existingEnemyCount}"); DebugConsole.NewMessage($"Existing enemy targets: {existingEnemyCount}"); DebugConsole.NewMessage($"Spawned enemy targets: {spawnedTargets.Count}"); #endif }