Client LuaCsForBarotrauma
BarotraumaShared/SharedSource/Events/Missions/AbandonedOutpostMission.cs
2 using Microsoft.Xna.Framework;
3 using System;
4 using System.Collections.Generic;
5 using System.Linq;
6 using System.Xml.Linq;
7 
8 namespace Barotrauma
9 {
11  {
12  private readonly XElement characterConfig;
13 
14  protected readonly List<Character> characters = new List<Character>();
15  private readonly Dictionary<Character, List<Item>> characterItems = new Dictionary<Character, List<Item>>();
16  protected readonly HashSet<Character> requireKill = new HashSet<Character>();
17  protected readonly HashSet<Character> requireRescue = new HashSet<Character>();
18 
19  private readonly Identifier itemTag;
20  private readonly XElement itemConfig;
21  private readonly List<Item> items = new List<Item>();
22 
23  protected const int HostagesKilledState = 5;
24 
25  private readonly LocalizedString hostagesKilledMessage;
26 
27  private const float EndDelay = 5.0f;
28  private float endTimer;
29 
30  private bool allowOrderingRescuees;
31 
32  public override bool AllowRespawn => false;
33 
34  public override bool AllowUndocking
35  {
36  get
37  {
38  if (GameMain.GameSession.GameMode is CampaignMode) { return true; }
39  return state > 0;
40  }
41  }
42 
43  public override IEnumerable<(LocalizedString Label, Vector2 Position)> SonarLabels
44  {
45  get
46  {
47  if (State == 0)
48  {
49  return Targets.Select(t => (Prefab.SonarLabel, t.WorldPosition));
50  }
51  else
52  {
53  return Enumerable.Empty<(LocalizedString Label, Vector2 Position)>();
54  }
55  }
56  }
57 
58  private IEnumerable<Entity> Targets
59  {
60  get
61  {
62  if (State > 0)
63  {
64  return Enumerable.Empty<Entity>();
65  }
66  else
67  {
68  if (items.Any())
69  {
70  return items.Where(it => !it.Removed && it.Condition > 0.0f).Cast<Entity>().Concat(requireKill.Where(c => !c.Removed && !c.IsDead)).Concat(requireRescue);
71  }
72  else
73  {
74  return requireKill.Concat(requireRescue);
75  }
76  }
77  }
78  }
79 
80  protected bool wasDocked;
81 
82  public AbandonedOutpostMission(MissionPrefab prefab, Location[] locations, Submarine sub) :
83  base(prefab, locations, sub)
84  {
85  characterConfig = prefab.ConfigElement.GetChildElement("Characters");
86 
87  allowOrderingRescuees = prefab.ConfigElement.GetAttributeBool(nameof(allowOrderingRescuees), true);
88 
89  string msgTag = prefab.ConfigElement.GetAttributeString("hostageskilledmessage", "");
90  hostagesKilledMessage = TextManager.Get(msgTag).Fallback(msgTag);
91 
92  itemConfig = prefab.ConfigElement.GetChildElement("Items");
93  itemTag = prefab.ConfigElement.GetAttributeIdentifier("targetitem", Identifier.Empty);
94  }
95 
96  protected override void StartMissionSpecific(Level level)
97  {
98  failed = false;
99  endTimer = 0.0f;
100  characters.Clear();
101  characterItems.Clear();
102  requireKill.Clear();
103  requireRescue.Clear();
104  items.Clear();
105 #if SERVER
106  spawnedItems.Clear();
107 #endif
108 
109  var submarine = Submarine.Loaded.Find(s => s.Info.Type == SubmarineType.Outpost) ?? Submarine.MainSub;
110  InitItems(submarine);
111  if (!IsClient)
112  {
113  InitCharacters(submarine);
114  }
115 
117  }
118 
119  private void InitItems(Submarine submarine)
120  {
121  if (!itemTag.IsEmpty)
122  {
123  var itemsToDestroy = Item.ItemList.FindAll(it => it.Submarine?.Info.Type != SubmarineType.Player && it.HasTag(itemTag));
124  if (!itemsToDestroy.Any())
125  {
126  DebugConsole.ThrowError($"Error in mission \"{Prefab.Identifier}\". Could not find an item with the tag \"{itemTag}\".",
127  contentPackage: Prefab.ContentPackage);
128  }
129  else
130  {
131  items.AddRange(itemsToDestroy);
132  }
133  }
134 
135  if (itemConfig != null && !IsClient)
136  {
137  foreach (XElement element in itemConfig.Elements())
138  {
139  Identifier itemIdentifier = element.GetAttributeIdentifier("identifier", Identifier.Empty);
140  if (MapEntityPrefab.FindByIdentifier(itemIdentifier) is not ItemPrefab itemPrefab)
141  {
142  DebugConsole.ThrowError("Couldn't spawn item for outpost destroy mission: item prefab \"" + itemIdentifier + "\" not found",
143  contentPackage: Prefab.ContentPackage);
144  continue;
145  }
146 
147  Identifier[] moduleFlags = element.GetAttributeIdentifierArray("moduleflags", null);
148  Identifier[] spawnPointTags = element.GetAttributeIdentifierArray("spawnpointtags", null);
149  ISpatialEntity spawnPoint = SpawnAction.GetSpawnPos(
150  SpawnAction.SpawnLocationType.Outpost, SpawnType.Human | SpawnType.Enemy,
151  moduleFlags, spawnPointTags, element.GetAttributeBool("asfaraspossible", false));
152  spawnPoint ??= submarine.GetHulls(alsoFromConnectedSubs: false).GetRandomUnsynced();
153  Vector2 spawnPos = spawnPoint.WorldPosition;
154  if (spawnPoint is WayPoint wp && wp.CurrentHull != null && wp.CurrentHull.Rect.Width > 100)
155  {
156  spawnPos = new Vector2(
157  MathHelper.Clamp(wp.WorldPosition.X + Rand.Range(-200, 201), wp.CurrentHull.WorldRect.X + 50, wp.CurrentHull.WorldRect.Right - 50),
158  wp.CurrentHull.WorldRect.Y - wp.CurrentHull.Rect.Height + 16.0f);
159  }
160  var item = new Item(itemPrefab, spawnPos, null);
161  items.Add(item);
162 #if SERVER
163  spawnedItems.Add(item);
164 #endif
165  }
166  }
167  }
168 
169  private void InitCharacters(Submarine submarine)
170  {
171  characters.Clear();
172  characterItems.Clear();
173 
174  if (characterConfig != null)
175  {
176  foreach (XElement element in characterConfig.Elements())
177  {
178  if (GameMain.NetworkMember == null && element.GetAttributeBool("multiplayeronly", false)) { continue; }
179 
180  int defaultCount = element.GetAttributeInt("count", -1);
181  if (defaultCount < 0)
182  {
183  defaultCount = element.GetAttributeInt("amount", 1);
184  }
185  int min = Math.Min(element.GetAttributeInt("min", defaultCount), 255);
186  int max = Math.Min(Math.Max(min, element.GetAttributeInt("max", defaultCount)), 255);
187  int count = Rand.Range(min, max + 1);
188 
189  if (element.Attribute("identifier") != null && element.Attribute("from") != null)
190  {
191  HumanPrefab humanPrefab = GetHumanPrefabFromElement(element);
192  if (humanPrefab == null)
193  {
194  DebugConsole.ThrowError($"Couldn't spawn a human character for abandoned outpost mission: human prefab \"{element.GetAttributeString("identifier", string.Empty)}\" not found",
195  contentPackage: Prefab.ContentPackage);
196  continue;
197  }
198  for (int i = 0; i < count; i++)
199  {
200  LoadHuman(humanPrefab, element, submarine);
201  }
202  }
203  else
204  {
205  Identifier speciesName = element.GetAttributeIdentifier("character", element.GetAttributeIdentifier("identifier", Identifier.Empty));
206  var characterPrefab = CharacterPrefab.FindBySpeciesName(speciesName);
207  if (characterPrefab == null)
208  {
209  DebugConsole.ThrowError($"Couldn't spawn a character for abandoned outpost mission: character prefab \"{speciesName}\" not found",
210  contentPackage: Prefab.ContentPackage);
211  continue;
212  }
213  for (int i = 0; i < count; i++)
214  {
215  LoadMonster(characterPrefab, element, submarine);
216  }
217  }
218  }
219  }
220  }
221 
222  private void LoadHuman(HumanPrefab humanPrefab, XElement element, Submarine submarine)
223  {
224  Identifier[] moduleFlags = element.GetAttributeIdentifierArray("moduleflags", null);
225  Identifier[] spawnPointTags = element.GetAttributeIdentifierArray("spawnpointtags", null);
226  var spawnPointType = element.GetAttributeEnum("spawnpointtype", SpawnType.Human);
227  ISpatialEntity spawnPos = SpawnAction.GetSpawnPos(
228  SpawnAction.SpawnLocationType.Outpost, spawnPointType,
229  moduleFlags ?? humanPrefab.GetModuleFlags(),
230  spawnPointTags ?? humanPrefab.GetSpawnPointTags(),
231  element.GetAttributeBool("asfaraspossible", false));
232  spawnPos ??= submarine.GetHulls(alsoFromConnectedSubs: false).GetRandomUnsynced();
233 
234  bool requiresRescue = element.GetAttributeBool("requirerescue", false);
235  var teamId = element.GetAttributeEnum("teamid", requiresRescue ? CharacterTeamType.FriendlyNPC : CharacterTeamType.None);
236  Character spawnedCharacter = CreateHuman(humanPrefab, characters, characterItems, submarine, teamId, spawnPos);
237  if (Level.Loaded?.StartOutpost?.Info is { } outPostInfo)
238  {
239  outPostInfo.AddOutpostNPCIdentifierOrTag(spawnedCharacter, humanPrefab.Identifier);
240  foreach (Identifier tag in humanPrefab.GetTags())
241  {
242  outPostInfo.AddOutpostNPCIdentifierOrTag(spawnedCharacter, tag);
243  }
244  }
245 
246  if (spawnPos is WayPoint wp)
247  {
248  spawnedCharacter.GiveIdCardTags(wp);
249  }
250 
251  if (requiresRescue)
252  {
253  requireRescue.Add(spawnedCharacter);
254 #if CLIENT
255  if (allowOrderingRescuees)
256  {
257  GameMain.GameSession.CrewManager.AddCharacterToCrewList(spawnedCharacter);
258  }
259 #endif
260  }
261  else if (TimesAttempted > 0 && spawnedCharacter.AIController is HumanAIController humanAi)
262  {
263  var order = OrderPrefab.Prefabs["fightintruders"]
264  .CreateInstance(OrderPrefab.OrderTargetType.Entity, orderGiver: spawnedCharacter)
265  .WithManualPriority(CharacterInfo.HighestManualOrderPriority);
266  spawnedCharacter.SetOrder(order, isNewOrder: true, speak: false);
267  }
268 
269  if (element.GetAttributeBool("requirekill", false))
270  {
271  requireKill.Add(spawnedCharacter);
272  }
273  }
274 
275  private void LoadMonster(CharacterPrefab monsterPrefab, XElement element, Submarine submarine)
276  {
277  Identifier[] moduleFlags = element.GetAttributeIdentifierArray("moduleflags", null);
278  Identifier[] spawnPointTags = element.GetAttributeIdentifierArray("spawnpointtags", null);
279  ISpatialEntity spawnPos = SpawnAction.GetSpawnPos(SpawnAction.SpawnLocationType.Outpost, SpawnType.Enemy, moduleFlags, spawnPointTags, element.GetAttributeBool("asfaraspossible", false));
280  spawnPos ??= submarine.GetHulls(alsoFromConnectedSubs: false).GetRandomUnsynced();
281  Character spawnedCharacter = Character.Create(monsterPrefab.Identifier, spawnPos.WorldPosition, ToolBox.RandomSeed(8), createNetworkEvent: false);
282  characters.Add(spawnedCharacter);
283  if (element.GetAttributeBool("requirekill", false))
284  {
285  requireKill.Add(spawnedCharacter);
286  }
287  if (spawnedCharacter.Inventory != null)
288  {
289  characterItems.Add(spawnedCharacter, spawnedCharacter.Inventory.FindAllItems(recursive: true));
290  }
291  if (submarine != null && spawnedCharacter.AIController is EnemyAIController enemyAi)
292  {
293  enemyAi.UnattackableSubmarines.Add(submarine);
294  enemyAi.UnattackableSubmarines.Add(Submarine.MainSub);
295  foreach (Submarine sub in Submarine.MainSub.DockedTo)
296  {
297  enemyAi.UnattackableSubmarines.Add(sub);
298  }
299  }
300  }
301 
302 
303  protected override void UpdateMissionSpecific(float deltaTime)
304  {
305  if (State != HostagesKilledState)
306  {
307  if (requireRescue.Any(r => r.Removed || r.IsDead))
308  {
310  return;
311  }
312  }
313  else
314  {
315  endTimer += deltaTime;
316  if (endTimer > EndDelay)
317  {
318 #if SERVER
319  if (!(GameMain.GameSession.GameMode is CampaignMode) && GameMain.Server != null)
320  {
321  GameMain.Server.EndGame();
322  }
323 #endif
324  }
325  }
326 
327  switch (state)
328  {
329  case 0:
330 
331  if (items.All(it => it.Removed || it.Condition <= 0.0f) &&
332  requireKill.All(c => c.Removed || c.IsDead || (c.LockHands && c.Submarine == Submarine.MainSub)) &&
333  requireRescue.All(c => c.Submarine?.Info.Type == SubmarineType.Player))
334  {
335  State = 1;
336  }
337  break;
338 #if SERVER
339  case 1:
340  if (!(GameMain.GameSession.GameMode is CampaignMode) && GameMain.Server != null)
341  {
343  {
344  GameMain.Server.EndGame();
345  State = 2;
346  }
347  }
348  break;
349 #endif
350  }
351 
352  }
353 
354  protected override bool DetermineCompleted()
355  {
356  return State > 0 && State != HostagesKilledState;
357  }
358 
359  protected override void EndMissionSpecific(bool completed)
360  {
361  failed = !completed && requireRescue.Any(r => r.Removed || r.IsDead);
362  }
363  }
364 }
override IEnumerable<(LocalizedString Label, Vector2 Position)> SonarLabels
AbandonedOutpostMission(MissionPrefab prefab, Location[] locations, Submarine sub)
string? GetAttributeString(string key, string? def)
ContentXElement? GetChildElement(string name)
bool GetAttributeBool(string key, bool def)
Identifier GetAttributeIdentifier(string key, string 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
static Character CreateHuman(HumanPrefab humanPrefab, List< Character > characters, Dictionary< Character, List< Item >> characterItems, Submarine submarine, CharacterTeamType teamType, ISpatialEntity positionToStayIn=null, Rand.RandSync humanPrefabRandSync=Rand.RandSync.ServerAndClient)
ContentPackage? ContentPackage
Definition: Prefab.cs:37
List< Hull > GetHulls(bool alsoFromConnectedSubs)