Barotrauma Client Doc
BarotraumaShared/SharedSource/Events/Missions/AlienRuinMission.cs
1 using System;
4 using Microsoft.Xna.Framework;
5 using System.Collections.Generic;
6 using System.Linq;
7 
8 namespace Barotrauma
9 {
10  partial class AlienRuinMission : Mission
11  {
12  private readonly Identifier[] targetItemIdentifiers;
13  private readonly Identifier[] targetEnemyIdentifiers;
14  private readonly int minEnemyCount;
15  private readonly HashSet<Entity> existingTargets = new HashSet<Entity>();
16  private readonly HashSet<Character> spawnedTargets = new HashSet<Character>();
17  private readonly HashSet<Entity> allTargets = new HashSet<Entity>();
18 
19  private Ruin TargetRuin { get; set; }
20 
21  public override IEnumerable<(LocalizedString Label, Vector2 Position)> SonarLabels
22  {
23  get
24  {
25  if (State == 0)
26  {
27  return allTargets
28  .Where(t => (t is Item i && !IsItemDestroyed(i)) || (t is Character c && !IsEnemyDefeated(c)))
29  .Select(t => (Prefab.SonarLabel, t.WorldPosition));
30  }
31  else
32  {
33  return Enumerable.Empty<(LocalizedString Label, Vector2 Position)>();
34  }
35  }
36  }
37 
38  public AlienRuinMission(MissionPrefab prefab, Location[] locations, Submarine sub) : base(prefab, locations, sub)
39  {
40  targetItemIdentifiers = prefab.ConfigElement.GetAttributeIdentifierArray("targetitems", Array.Empty<Identifier>());
41  targetEnemyIdentifiers = prefab.ConfigElement.GetAttributeIdentifierArray("targetenemies", Array.Empty<Identifier>());
42  minEnemyCount = prefab.ConfigElement.GetAttributeInt("minenemycount", 0);
43  }
44 
45  protected override void StartMissionSpecific(Level level)
46  {
47  existingTargets.Clear();
48  spawnedTargets.Clear();
49  allTargets.Clear();
50  if (IsClient) { return; }
51  TargetRuin = Level.Loaded?.Ruins?.GetRandom(randSync: Rand.RandSync.ServerAndClient);
52  if (TargetRuin == null)
53  {
54  DebugConsole.ThrowError($"Failed to initialize an Alien Ruin mission (\"{Prefab.Identifier}\"): level contains no alien ruins",
55  contentPackage: Prefab.ContentPackage);
56  return;
57  }
58  if (targetItemIdentifiers.Length < 1 && targetEnemyIdentifiers.Length < 1)
59  {
60  DebugConsole.ThrowError($"Failed to initialize an Alien Ruin mission (\"{Prefab.Identifier}\"): no target identifiers set in the mission definition",
61  contentPackage: Prefab.ContentPackage);
62  return;
63  }
64  foreach (var item in Item.ItemList)
65  {
66  if (!targetItemIdentifiers.Contains(item.Prefab.Identifier)) { continue; }
67  if (item.Submarine != TargetRuin.Submarine) { continue; }
68  existingTargets.Add(item);
69  allTargets.Add(item);
70  }
71  int existingEnemyCount = 0;
72  foreach (var character in Character.CharacterList)
73  {
74  if (character.SpeciesName.IsEmpty) { continue; }
75  if (!targetEnemyIdentifiers.Contains(character.SpeciesName)) { continue; }
76  if (character.Submarine != TargetRuin.Submarine) { continue; }
77  existingTargets.Add(character);
78  allTargets.Add(character);
79  existingEnemyCount++;
80  }
81  if (existingEnemyCount < minEnemyCount)
82  {
83  var enemyPrefabs = new HashSet<CharacterPrefab>();
84  foreach (Identifier identifier in targetEnemyIdentifiers)
85  {
86  var prefab = CharacterPrefab.FindBySpeciesName(identifier);
87  if (prefab != null)
88  {
89  enemyPrefabs.Add(prefab);
90  }
91  else
92  {
93  DebugConsole.ThrowError($"Error in an Alien Ruin mission (\"{Prefab.Identifier}\"): could not find a character prefab with the species \"{identifier}\"",
94  contentPackage: Prefab.ContentPackage);
95  }
96  }
97  if (enemyPrefabs.None())
98  {
99  DebugConsole.ThrowError($"Error in an Alien Ruin mission (\"{Prefab.Identifier}\"): no enemy species defined that could be used to spawn more ({minEnemyCount - existingEnemyCount}) enemies",
100  contentPackage: Prefab.ContentPackage);
101  return;
102  }
103  for (int i = 0; i < (minEnemyCount - existingEnemyCount); i++)
104  {
105  var prefab = enemyPrefabs.GetRandomUnsynced();
106  var spawnPos = TargetRuin.Submarine.GetWaypoints(false).GetRandomUnsynced(w => w.CurrentHull != null)?.WorldPosition;
107  if (!spawnPos.HasValue)
108  {
109  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",
110  contentPackage: Prefab.ContentPackage);
111  return;
112  }
113  var newEnemy = Character.Create(prefab.Identifier, spawnPos.Value, ToolBox.RandomSeed(8), createNetworkEvent: false);
114  spawnedTargets.Add(newEnemy);
115  allTargets.Add(newEnemy);
116  }
117  }
118 #if DEBUG
119  DebugConsole.NewMessage("********** CLEAR RUIN MISSION INFO **********");
120  DebugConsole.NewMessage($"Existing item targets: {existingTargets.Count - existingEnemyCount}");
121  DebugConsole.NewMessage($"Existing enemy targets: {existingEnemyCount}");
122  DebugConsole.NewMessage($"Spawned enemy targets: {spawnedTargets.Count}");
123 #endif
124  }
125 
126  protected override void UpdateMissionSpecific(float deltaTime)
127  {
128  if (IsClient) { return; }
129  switch (State)
130  {
131  case 0:
132  if (!AllTargetsEliminated()) { return; }
133  State = 1;
134  break;
135  }
136  }
137 
138  private bool AllTargetsEliminated()
139  {
140  foreach (var target in allTargets)
141  {
142  if (target is Item targetItem)
143  {
144  if (!IsItemDestroyed(targetItem))
145  {
146  return false;
147  }
148  }
149  else if (target is Character targetEnemy)
150  {
151  if (!IsEnemyDefeated(targetEnemy))
152  {
153  return false;
154  }
155  }
156 #if DEBUG
157  else
158  {
159  DebugConsole.ThrowError($"Error in Alien Ruin mission (\"{Prefab.Identifier}\"): unexpected target of type {target?.GetType()?.ToString()}",
160  contentPackage: Prefab.ContentPackage);
161  }
162 #endif
163  }
164  return true;
165  }
166 
167  private static bool IsItemDestroyed(Item item) => item == null || item.Removed || item.Condition <= 0.0f;
168 
169  private static bool IsEnemyDefeated(Character enemy) => enemy == null ||enemy.Removed || enemy.IsDead;
170 
171  protected override bool DetermineCompleted()
172  {
173  bool exitingLevel = GameMain.GameSession?.GameMode is CampaignMode campaign ?
175  Submarine.MainSub is { } sub && sub.AtEitherExit;
176 
177  return State > 0 && exitingLevel;
178  }
179 
180  protected override void EndMissionSpecific(bool completed)
181  {
182  failed = !completed && State > 0;
183  }
184  }
185 }
override IEnumerable<(LocalizedString Label, Vector2 Position)> SonarLabels
AlienRuinMission(MissionPrefab prefab, Location[] locations, Submarine sub)
TransitionType GetAvailableTransition(out LevelData nextLevel, out Submarine leavingSub)
Which type of transition between levels is currently possible (if any)
static Character Create(CharacterInfo characterInfo, Vector2 position, string seed, ushort id=Entity.NullEntityID, bool isRemotePlayer=false, bool hasAi=true, RagdollParams ragdoll=null, bool spawnInitialItems=true)
Create a new character
static CharacterPrefab FindBySpeciesName(Identifier speciesName)
Identifier[] GetAttributeIdentifierArray(Identifier[] def, params string[] keys)
int GetAttributeInt(string key, int def)
static GameSession?? GameSession
Definition: GameMain.cs:88
static readonly List< Item > ItemList
Defines a point in the event that GoTo actions can jump to.
Definition: Label.cs:7
ContentPackage? ContentPackage
Definition: Prefab.cs:37
List< WayPoint > GetWaypoints(bool alsoFromConnectedSubs)