Client LuaCsForBarotrauma
BarotraumaShared/SharedSource/Events/Missions/BeaconMission.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 {
10  partial class BeaconMission : Mission
11  {
12  private class MonsterSet
13  {
14  public readonly HashSet<(CharacterPrefab character, Point amountRange)> MonsterPrefabs = new HashSet<(CharacterPrefab character, Point amountRange)>();
15  public float Commonness;
16 
17  public MonsterSet(XElement element)
18  {
19  Commonness = element.GetAttributeFloat("commonness", 100.0f);
20  }
21  }
22 
23  private bool swarmSpawned;
24  private readonly List<MonsterSet> monsterSets = new List<MonsterSet>();
25  private readonly LocalizedString sonarLabel;
26 
27  public BeaconMission(MissionPrefab prefab, Location[] locations, Submarine sub) : base(prefab, locations, sub)
28  {
29  swarmSpawned = false;
30 
31  foreach (var monsterElement in prefab.ConfigElement.GetChildElements("monster"))
32  {
33  if (!monsterSets.Any())
34  {
35  monsterSets.Add(new MonsterSet(monsterElement));
36  }
37  LoadMonsters(monsterElement, monsterSets[0]);
38  }
39  foreach (var monsterSetElement in prefab.ConfigElement.GetChildElements("monsters"))
40  {
41  monsterSets.Add(new MonsterSet(monsterSetElement));
42  foreach (var monsterElement in monsterSetElement.GetChildElements("monster"))
43  {
44  LoadMonsters(monsterElement, monsterSets.Last());
45  }
46  }
47 
48  sonarLabel = TextManager.Get("beaconstationsonarlabel");
49  DebugConsole.NewMessage("Initialized beacon mission: " + prefab.Identifier, Color.LightSkyBlue, debugOnly: true);
50  }
51 
52  private void LoadMonsters(XElement monsterElement, MonsterSet set)
53  {
54  Identifier speciesName = monsterElement.GetAttributeIdentifier("character", Identifier.Empty);
55  int defaultCount = monsterElement.GetAttributeInt("count", -1);
56  if (defaultCount < 0)
57  {
58  defaultCount = monsterElement.GetAttributeInt("amount", 1);
59  }
60  int min = Math.Min(monsterElement.GetAttributeInt("min", defaultCount), 255);
61  int max = Math.Min(Math.Max(min, monsterElement.GetAttributeInt("max", defaultCount)), 255);
62  var characterPrefab = CharacterPrefab.FindBySpeciesName(speciesName);
63  if (characterPrefab != null)
64  {
65  set.MonsterPrefabs.Add((characterPrefab, new Point(min, max)));
66  }
67  else
68  {
69  DebugConsole.ThrowError($"Error in beacon mission \"{Prefab.Identifier}\". Could not find a character prefab with the name \"{speciesName}\".",
70  contentPackage: Prefab.ContentPackage);
71  }
72  }
73 
74  public override IEnumerable<(LocalizedString Label, Vector2 Position)> SonarLabels
75  {
76  get
77  {
78  if (level.BeaconStation == null || state > 0)
79  {
80  yield break;
81  }
82  else
83  {
84  yield return (
85  Prefab.SonarLabel.IsNullOrEmpty() ? sonarLabel : Prefab.SonarLabel,
87  }
88  }
89  }
90 
91  protected override void UpdateMissionSpecific(float deltaTime)
92  {
93  if (IsClient) { return; }
94  if (!swarmSpawned && level.CheckBeaconActive())
95  {
96  IEnumerable<Submarine> connectedSubs = level.BeaconStation.GetConnectedSubs();
97  foreach (Item item in Item.ItemList)
98  {
99  if (!connectedSubs.Contains(item.Submarine) || item.Submarine?.Info is { IsPlayer: true }) { continue; }
100  bool isReactor = item.GetComponent<Reactor>() != null;
101  if ((isReactor && GameMain.GameSession is not { TraitorsEnabled: true }) ||
102  item.GetComponent<PowerTransfer>() != null ||
103  item.GetComponent<PowerContainer>() != null ||
104  item.GetComponent<Sonar>() != null)
105  {
106  item.InvulnerableToDamage = true;
107  }
108  }
109 
110  State = 1;
111 
112  Vector2 spawnPos = level.BeaconStation.WorldPosition;
113  spawnPos.Y += level.BeaconStation.GetDockedBorders().Height * 1.5f;
114 
115  var availablePositions = Level.Loaded.PositionsOfInterest.FindAll(p =>
116  p.PositionType == Level.PositionType.MainPath ||
117  p.PositionType == Level.PositionType.SidePath);
118  availablePositions.RemoveAll(p => Level.Loaded.ExtraWalls.Any(w => w.IsPointInside(p.Position.ToVector2())));
119  availablePositions.RemoveAll(p => Submarine.FindContaining(p.Position.ToVector2()) != null);
120 
121  if (availablePositions.Any())
122  {
123  Level.InterestingPosition? closestPos = null;
124  float closestDist = float.PositiveInfinity;
125  foreach (var pos in availablePositions)
126  {
127  float dist = Vector2.DistanceSquared(pos.Position.ToVector2(), level.BeaconStation.WorldPosition);
128  if (dist < closestDist)
129  {
130  closestDist = dist;
131  closestPos = pos;
132  }
133  }
134  if (closestPos.HasValue)
135  {
136  spawnPos = closestPos.Value.Position.ToVector2();
137  }
138  }
139 
140  if (monsterSets.Any())
141  {
142  var monsterSet = ToolBox.SelectWeightedRandom(monsterSets, m => m.Commonness, Rand.RandSync.Unsynced);
143  foreach ((CharacterPrefab monsterSpecies, Point monsterCountRange) in monsterSet.MonsterPrefabs)
144  {
145  int amount = Rand.Range(monsterCountRange.X, monsterCountRange.Y + 1);
146  for (int i = 0; i < amount; i++)
147  {
148  CoroutineManager.Invoke(() =>
149  {
150  //round ended before the coroutine finished
151  if (GameMain.GameSession == null || Level.Loaded == null) { return; }
152  Entity.Spawner.AddCharacterToSpawnQueue(monsterSpecies.Identifier, spawnPos);
153  }, Rand.Range(0f, amount));
154  }
155  }
156  }
157 
158  swarmSpawned = true;
159  }
160 #if DEBUG || UNSTABLE
161  if (State == 1 && !level.CheckBeaconActive())
162  {
163  DebugConsole.ThrowError("Beacon became inactive!");
164  State = 2;
165  }
166 #endif
167  }
168 
169  protected override bool DetermineCompleted()
170  {
171  return level.CheckBeaconActive();
172  }
173 
174  protected override void EndMissionSpecific(bool completed)
175  {
176  if (completed && level.LevelData != null)
177  {
179  }
180  }
181 
182  public override void AdjustLevelData(LevelData levelData)
183  {
184  levelData.HasBeaconStation = true;
185  levelData.IsBeaconActive = false;
186  }
187  }
188 }
BeaconMission(MissionPrefab prefab, Location[] locations, Submarine sub)
override IEnumerable<(LocalizedString Label, Vector2 Position)>? SonarLabels
static EntitySpawner Spawner
Definition: Entity.cs:31
Submarine Submarine
Definition: Entity.cs:53
void AddCharacterToSpawnQueue(Identifier speciesName, Vector2 worldPosition, Action< Character > onSpawn=null)
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
readonly Identifier Identifier
Definition: Prefab.cs:34
static Submarine FindContaining(Vector2 worldPosition, float inflate=500.0f)
Finds the sub whose world borders contain the position.
IEnumerable< Submarine > GetConnectedSubs()
Returns a list of all submarines that are connected to this one via docking ports,...
Rectangle GetDockedBorders(bool allowDifferentTeam=true)
Returns a rect that contains the borders of this sub and all subs docked to it, excluding outposts