Client LuaCsForBarotrauma
BarotraumaShared/SharedSource/Events/Missions/EliminateTargetsMission.cs
1 using System;
4 using Microsoft.Xna.Framework;
5 using System.Collections.Generic;
6 using System.Linq;
7 
8 namespace Barotrauma
9 {
10  [TypePreviouslyKnownAs("AlienRuinMission")]
12  {
13  private readonly Identifier[] targetItemIdentifiers;
14  private readonly Identifier[] targetEnemyIdentifiers;
15  private readonly int minEnemyCount;
16  private readonly HashSet<Entity> existingTargets = new HashSet<Entity>();
17  private readonly HashSet<Character> spawnedTargets = new HashSet<Character>();
18  private readonly HashSet<Entity> allTargets = new HashSet<Entity>();
19 
20  public readonly SubmarineType TargetSubType;
21  public readonly bool PrioritizeThalamus;
22 
23  private Submarine TargetSub { get; set; }
24 
25  public override IEnumerable<(LocalizedString Label, Vector2 Position)> SonarLabels
26  {
27  get
28  {
29  if (State == 0)
30  {
31  return allTargets
32  .Where(t => (t is Item i && !IsItemDestroyed(i)) || (t is Character c && !IsEnemyDefeated(c)))
33  .Select(t => (Prefab.SonarLabel, t.WorldPosition));
34  }
35  else
36  {
37  return Enumerable.Empty<(LocalizedString Label, Vector2 Position)>();
38  }
39  }
40  }
41 
42  public EliminateTargetsMission(MissionPrefab prefab, Location[] locations, Submarine sub) : base(prefab, locations, sub)
43  {
44  targetItemIdentifiers = prefab.ConfigElement.GetAttributeIdentifierArray("targetitems", Array.Empty<Identifier>());
45  targetEnemyIdentifiers = prefab.ConfigElement.GetAttributeIdentifierArray("targetenemies", Array.Empty<Identifier>());
46  minEnemyCount = prefab.ConfigElement.GetAttributeInt("minenemycount", 0);
47  TargetSubType = prefab.ConfigElement.GetAttributeEnum("targetsub", SubmarineType.Ruin);
48  PrioritizeThalamus = prefab.RequireThalamusWreck;
49  }
50 
51  protected override void StartMissionSpecific(Level level)
52  {
53  existingTargets.Clear();
54  spawnedTargets.Clear();
55  allTargets.Clear();
56  if (IsClient) { return; }
57 
58  TargetSub = TargetSubType switch
59  {
60  SubmarineType.Wreck => FindWreck(),
61  SubmarineType.Ruin => Level.Loaded?.Ruins?.GetRandom(Rand.RandSync.ServerAndClient).Submarine,
62  SubmarineType.BeaconStation => Level.Loaded?.BeaconStation,
63  _ => null
64  };
65 
66  Submarine FindWreck()
67  {
68  var wrecks = Level.Loaded?.Wrecks;
69  if (wrecks == null || wrecks.None()) { return null; }
70 
72  {
73  var thalamusWrecks = wrecks.Where(w => w.WreckAI != null).ToArray();
74  if (thalamusWrecks.Any())
75  {
76  return thalamusWrecks.GetRandom(Rand.RandSync.ServerAndClient);
77  }
78  }
79 
80  return wrecks.GetRandom(Rand.RandSync.ServerAndClient);
81  }
82 
83  if (TargetSub == null)
84  {
85  DebugConsole.ThrowError($"Failed to initialize an {nameof(EliminateTargetsMission)} mission (\"{Prefab.Identifier}\"): level contains no submarines",
86  contentPackage: Prefab.ContentPackage);
87  return;
88  }
89  if (targetItemIdentifiers.Length < 1 && targetEnemyIdentifiers.Length < 1)
90  {
91  DebugConsole.ThrowError($"Failed to initialize an {nameof(EliminateTargetsMission)} mission (\"{Prefab.Identifier}\"): no target identifiers set in the mission definition",
92  contentPackage: Prefab.ContentPackage);
93  return;
94  }
95  foreach (var item in Item.ItemList)
96  {
97  if (!targetItemIdentifiers.Contains(item.Prefab.Identifier)) { continue; }
98  if (item.Submarine != TargetSub) { continue; }
99  existingTargets.Add(item);
100  allTargets.Add(item);
101  }
102  int existingEnemyCount = 0;
103  foreach (var character in Character.CharacterList)
104  {
105  if (character.SpeciesName.IsEmpty) { continue; }
106  if (!targetEnemyIdentifiers.Contains(character.SpeciesName)) { continue; }
107  if (character.Submarine != TargetSub) { continue; }
108  existingTargets.Add(character);
109  allTargets.Add(character);
110  existingEnemyCount++;
111  }
112  if (existingEnemyCount < minEnemyCount)
113  {
114  var enemyPrefabs = new HashSet<CharacterPrefab>();
115  foreach (Identifier identifier in targetEnemyIdentifiers)
116  {
117  var prefab = CharacterPrefab.FindBySpeciesName(identifier);
118  if (prefab != null)
119  {
120  enemyPrefabs.Add(prefab);
121  }
122  else
123  {
124  DebugConsole.ThrowError($"Error in an Alien Ruin mission (\"{Prefab.Identifier}\"): could not find a character prefab with the species \"{identifier}\"",
125  contentPackage: Prefab.ContentPackage);
126  }
127  }
128  if (enemyPrefabs.None())
129  {
130  DebugConsole.ThrowError($"Error in an Alien Ruin mission (\"{Prefab.Identifier}\"): no enemy species defined that could be used to spawn more ({minEnemyCount - existingEnemyCount}) enemies",
131  contentPackage: Prefab.ContentPackage);
132  return;
133  }
134  for (int i = 0; i < (minEnemyCount - existingEnemyCount); i++)
135  {
136  var prefab = enemyPrefabs.GetRandomUnsynced();
137  var spawnPos = TargetSub.GetWaypoints(false).GetRandomUnsynced(w => w.CurrentHull != null)?.WorldPosition;
138  if (!spawnPos.HasValue)
139  {
140  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",
141  contentPackage: Prefab.ContentPackage);
142  return;
143  }
144  var newEnemy = Character.Create(prefab.Identifier, spawnPos.Value, ToolBox.RandomSeed(8), createNetworkEvent: false);
145  spawnedTargets.Add(newEnemy);
146  allTargets.Add(newEnemy);
147  }
148  }
149 #if DEBUG
150  DebugConsole.NewMessage("********** CLEAR RUIN MISSION INFO **********");
151  DebugConsole.NewMessage($"Existing item targets: {existingTargets.Count - existingEnemyCount}");
152  DebugConsole.NewMessage($"Existing enemy targets: {existingEnemyCount}");
153  DebugConsole.NewMessage($"Spawned enemy targets: {spawnedTargets.Count}");
154 #endif
155  }
156 
157  protected override void UpdateMissionSpecific(float deltaTime)
158  {
159  if (IsClient) { return; }
160  switch (State)
161  {
162  case 0:
163  if (!AllTargetsEliminated()) { return; }
164  State = 1;
165  break;
166  }
167  }
168 
169  private bool AllTargetsEliminated()
170  {
171  foreach (var target in allTargets)
172  {
173  if (target is Item targetItem)
174  {
175  if (!IsItemDestroyed(targetItem))
176  {
177  return false;
178  }
179  }
180  else if (target is Character targetEnemy)
181  {
182  if (!IsEnemyDefeated(targetEnemy))
183  {
184  return false;
185  }
186  }
187 #if DEBUG
188  else
189  {
190  DebugConsole.ThrowError($"Error in Alien Ruin mission (\"{Prefab.Identifier}\"): unexpected target of type {target?.GetType()?.ToString()}",
191  contentPackage: Prefab.ContentPackage);
192  }
193 #endif
194  }
195  return true;
196  }
197 
198  private static bool IsItemDestroyed(Item item) => item == null || item.Removed || item.Condition <= 0.0f;
199 
200  private static bool IsEnemyDefeated(Character enemy) => enemy == null ||enemy.Removed || enemy.IsDead;
201 
202  protected override bool DetermineCompleted()
203  {
204  bool exitingLevel = GameMain.GameSession?.GameMode is CampaignMode campaign ?
206  Submarine.MainSub is { } sub && sub.AtEitherExit;
207 
208  return State > 0 && exitingLevel;
209  }
210 
211  protected override void EndMissionSpecific(bool completed)
212  {
213  failed = !completed && State > 0;
214  }
215  }
216 }
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)
override IEnumerable<(LocalizedString Label, Vector2 Position)> SonarLabels
EliminateTargetsMission(MissionPrefab prefab, Location[] locations, Submarine sub)
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)