4 using Microsoft.Xna.Framework;
5 using System.Collections.Generic;
14 private readonly List<Item> startingItems =
new List<Item>();
15 private readonly List<Scanner> scanners =
new List<Scanner>();
16 private readonly Dictionary<Item, ushort> parentInventoryIDs =
new Dictionary<Item, ushort>();
17 private readonly Dictionary<Item, int> inventorySlotIndices =
new Dictionary<Item, int>();
18 private readonly Dictionary<Item, byte> parentItemContainerIndices =
new Dictionary<Item, byte>();
19 private readonly
int targetsToScan;
20 private readonly Dictionary<WayPoint, bool> scanTargets =
new Dictionary<WayPoint, bool>();
21 private readonly HashSet<WayPoint> newTargetsScanned =
new HashSet<WayPoint>();
22 private readonly
float minTargetDistance;
25 private Ruin TargetRuin {
get;
set; }
27 private bool AllTargetsScanned
31 return scanTargets.Any() && scanTargets.All(kvp => kvp.Value);
39 if (
State > 0 || scanTargets.None())
46 .Where(kvp => !kvp.Value)
47 .Select(kvp => (
Prefab.SonarLabel, kvp.Key.WorldPosition));
65 if (itemConfig ==
null)
67 DebugConsole.ThrowError(
"Failed to initialize a Scan mission: item config is not set",
72 foreach (var element
in itemConfig.Elements())
74 LoadItem(element,
null);
78 TargetRuin =
Level.
Loaded?.
Ruins?.GetRandom(randSync: Rand.RandSync.ServerAndClient);
79 if (TargetRuin ==
null)
81 DebugConsole.ThrowError(
"Failed to initialize a Scan mission: level contains no alien ruins",
87 ruinWaypoints.RemoveAll(wp => wp.CurrentHull ==
null);
88 if (ruinWaypoints.Count < targetsToScan)
90 DebugConsole.ThrowError($
"Failed to initialize a Scan mission: target ruin has less waypoints than required as scan targets ({ruinWaypoints.Count} < {targetsToScan})",
94 var availableWaypoints =
new List<WayPoint>();
95 float minTargetDistanceSquared = minTargetDistance * minTargetDistance;
96 for (
int tries = 0; tries < 15; tries++)
99 availableWaypoints.Clear();
100 availableWaypoints.AddRange(ruinWaypoints);
101 for (
int i = 0; i < targetsToScan; i++)
103 var selectedWaypoint = availableWaypoints.GetRandom(randSync: Rand.RandSync.ServerAndClient);
104 scanTargets.Add(selectedWaypoint,
false);
105 availableWaypoints.Remove(selectedWaypoint);
106 if (i < (targetsToScan - 1))
108 availableWaypoints.RemoveAll(wp => wp.CurrentHull == selectedWaypoint.CurrentHull);
109 availableWaypoints.RemoveAll(wp => Vector2.DistanceSquared(wp.WorldPosition, selectedWaypoint.WorldPosition) < minTargetDistanceSquared);
110 if (availableWaypoints.None())
113 DebugConsole.ThrowError($
"Error initializing a Scan mission: not enough targets available on try #{tries + 1} to reach the required scan target count (current targets: {scanTargets.Count}, required targets: {targetsToScan})",
120 if (scanTargets.Count >= targetsToScan)
123 DebugConsole.NewMessage($
"Successfully initialized a Scan mission: targets set on try #{tries + 1}", Color.Green);
127 if ((tries + 1) % 5 == 0)
129 float reducedMinTargetDistance = (1.0f - (((tries + 1) / 5) * 0.1f)) * minTargetDistance;
130 minTargetDistanceSquared = reducedMinTargetDistance * reducedMinTargetDistance;
132 DebugConsole.NewMessage($
"Reducing minimum distance between Scan mission targets (new min: {reducedMinTargetDistance}) to reach the required target count", Color.Yellow);
136 if (scanTargets.Count < targetsToScan)
138 DebugConsole.ThrowError($
"Error initializing a Scan mission: not enough targets (current targets: {scanTargets.Count}, required targets: {targetsToScan})",
145 startingItems.Clear();
146 parentInventoryIDs.Clear();
147 inventorySlotIndices.Clear();
148 parentItemContainerIndices.Clear();
154 private void LoadItem(XElement element, Item parent)
158 if (!position.HasValue) {
return; }
159 var item =
new Item(itemPrefab, position.Value, cargoRoomSub);
161 startingItems.Add(item);
162 if (parent?.GetComponent<ItemContainer>() is
ItemContainer itemContainer)
164 parentInventoryIDs.Add(item, parent.ID);
165 parentItemContainerIndices.Add(item, (
byte)parent.GetComponentIndex(itemContainer));
166 parent.Combine(item, user:
null);
167 inventorySlotIndices.Add(item, item.ParentInventory?.FindIndex(item) ?? -1);
169 foreach (XElement subElement
in element.Elements())
171 int amount = subElement.GetAttributeInt(
"amount", 1);
172 for (
int i = 0; i < amount; i++)
174 LoadItem(subElement, item);
179 private void GetScanners()
181 foreach (var startingItem
in startingItems)
185 scanner.OnScanStarted += OnScanStarted;
188 scanner.OnScanCompleted += OnScanCompleted;
190 scanners.Add(scanner);
195 private void OnScanStarted(
Scanner scanner)
198 foreach (var kvp
in scanTargets)
200 if (!IsValidScanPosition(scanner, kvp, scanRadiusSquared)) {
continue; }
206 private void OnScanCompleted(
Scanner scanner)
209 newTargetsScanned.Clear();
211 foreach (var kvp
in scanTargets)
213 if (!IsValidScanPosition(scanner, kvp, scanRadiusSquared)) {
continue; }
214 newTargetsScanned.Add(kvp.Key);
216 foreach (var wp
in newTargetsScanned)
218 scanTargets[wp] =
true;
222 GameMain.Server?.UpdateMissionState(
this);
226 private static bool IsValidScanPosition(
Scanner scanner, KeyValuePair<WayPoint, bool> scanStatus,
float scanRadiusSquared)
228 if (scanStatus.Value) {
return false; }
229 if (scanStatus.Key.Submarine != scanner.
Item.
Submarine) {
return false; }
230 if (Vector2.DistanceSquared(scanStatus.Key.WorldPosition, scanner.
Item.
WorldPosition) > scanRadiusSquared) {
return false; }
240 if (AllTargetsScanned)
252 foreach (var scanner
in scanners)
254 if (scanner.Item !=
null && !scanner.Item.Removed)
256 scanner.OnScanStarted -= OnScanStarted;
257 scanner.OnScanCompleted -= OnScanCompleted;
258 scanner.Item.Remove();
float GetAttributeFloat(string key, float def)
ContentXElement? GetChildElement(string name)
int GetAttributeInt(string key, int def)
bool DisplayProgressBar
Should the progress bar be displayed. Use when AlwaysDisplayProgressBar is set to false.
Defines a point in the event that GoTo actions can jump to.
Vector2? GetCargoSpawnPosition(ItemPrefab itemPrefab, out Submarine cargoRoomSub)
ItemPrefab FindItemPrefab(XElement element)
readonly ContentXElement ConfigElement
ContentPackage? ContentPackage
ScanMission(MissionPrefab prefab, Location[] locations, Submarine sub)
override bool DetermineCompleted()
override void UpdateMissionSpecific(float deltaTime)
override IEnumerable<(LocalizedString Label, Vector2 Position)> SonarLabels
override void StartMissionSpecific(Level level)
override void EndMissionSpecific(bool completed)
List< WayPoint > GetWaypoints(bool alsoFromConnectedSubs)