3 using Microsoft.Xna.Framework;
5 using System.Collections.Generic;
6 using System.Collections.Immutable;
14 private readonly Dictionary<Identifier, int> resourceAmounts =
new Dictionary<Identifier, int>();
15 private readonly Dictionary<Identifier, List<Item>> spawnedResources =
new Dictionary<Identifier, List<Item>>();
16 private readonly Dictionary<Identifier, Item[]> relevantLevelResources =
new Dictionary<Identifier, Item[]>();
17 private readonly List<(Identifier Identifier, Vector2 Position)> missionClusterPositions =
new List<(Identifier Identifier, Vector2 Position)>();
21 private readonly PositionType positionType = PositionType.
Cave;
30 PositionType.SidePath,
31 PositionType.MainPath,
32 PositionType.AbyssCave,
38 private readonly
float resourceHandoverAmount;
44 return missionClusterPositions
45 .Where(p => spawnedResources.ContainsKey(p.Identifier) && AnyAreUncollected(spawnedResources[p.Identifier]))
57 var positionType = prefab.
ConfigElement.GetAttributeEnum(
"PositionType", in this.positionType);
60 this.positionType = positionType;
64 resourceHandoverAmount = Math.Clamp(handoverAmount, 0.0f, 1.0f);
67 foreach (var c
in configElement.GetChildElements(
"Item"))
70 if (identifier.IsEmpty) {
continue; }
71 if (resourceAmounts.ContainsKey(identifier))
73 resourceAmounts[identifier]++;
77 resourceAmounts.Add(identifier, 1);
84 if (spawnedResources.Any())
87 throw new Exception($
"SpawnedResources.Count > 0 ({spawnedResources.Count})");
89 DebugConsole.AddWarning(
"Spawned resources list was not empty at the start of a mineral mission. The mission instance may not have been ended correctly on previous rounds.");
90 spawnedResources.Clear();
94 if (relevantLevelResources.Any())
97 throw new Exception($
"RelevantLevelResources.Count > 0 ({relevantLevelResources.Count})");
99 DebugConsole.AddWarning(
"Relevant level resources list was not empty at the start of a mineral mission. The mission instance may not have been ended correctly on previous rounds.");
100 relevantLevelResources.Clear();
104 if (missionClusterPositions.Any())
107 throw new Exception($
"MissionClusterPositions.Count > 0 ({missionClusterPositions.Count})");
109 DebugConsole.AddWarning(
"Mission cluster positions list was not empty at the start of a mineral mission. The mission instance may not have been ended correctly on previous rounds.");
110 missionClusterPositions.Clear();
118 foreach ((Identifier identifier,
int amount) in resourceAmounts)
122 DebugConsole.ThrowError($
"Error in MineralMission: couldn't find an item prefab (identifier: \"{identifier}\")",
128 if (spawnedResources.Count < amount)
130 DebugConsole.ThrowError($
"Error in MineralMission: spawned only {spawnedResources.Count}/{amount} of {prefab.Name}",
134 if (spawnedResources.None()) {
continue; }
136 this.spawnedResources.Add(identifier, spawnedResources);
140 foreach (var spawnedResource
in spawnedResources)
142 if (cave.Area.Contains(spawnedResource.WorldPosition))
144 cave.MissionsToDisplayOnSonar.Add(
this);
152 CalculateMissionClusterPositions();
153 FindRelevantLevelResources();
162 if (!EnoughHaveBeenCollected()) {
return; }
174 return EnoughHaveBeenCollected();
185 var handoverResources =
new List<Item>();
186 foreach (Identifier identifier
in resourceAmounts.Keys)
188 if (relevantLevelResources.TryGetValue(identifier, out var availableResources))
190 var collectedResources = availableResources.Where(HasBeenCollected);
191 if (!collectedResources.Any()) {
continue; }
192 int handoverCount = (int)MathF.Round(resourceHandoverAmount * collectedResources.Count());
193 for (
int i = 0; i < handoverCount; i++)
195 handoverResources.Add(collectedResources.ElementAt(i));
199 foreach (var resource
in handoverResources)
205 foreach (var kvp
in spawnedResources)
207 foreach (var i
in kvp.Value)
209 if (i !=
null && !i.Removed && !HasBeenCollected(i))
215 spawnedResources.Clear();
216 relevantLevelResources.Clear();
217 missionClusterPositions.Clear();
220 private void FindRelevantLevelResources()
222 relevantLevelResources.Clear();
223 foreach (var identifier
in resourceAmounts.Keys)
225 var items =
Item.
ItemList.Where(i => i.Prefab.Identifier == identifier &&
226 i.Submarine ==
null && i.ParentInventory ==
null &&
227 (i.GetComponent<
Holdable>() is not
Holdable h || (h.Attachable && h.Attached)))
229 relevantLevelResources.Add(identifier, items);
233 private bool EnoughHaveBeenCollected()
235 foreach (var kvp
in resourceAmounts)
237 if (relevantLevelResources.TryGetValue(kvp.Key, out var availableResources))
239 var collected = availableResources.Count(HasBeenCollected);
240 var needed = kvp.Value;
241 if (collected < needed) {
return false; }
251 private bool HasBeenCollected(Item item)
253 if (item ==
null) {
return false; }
254 if (item.Removed) {
return false; }
255 var owner = item.GetRootInventoryOwner();
256 if (owner.Submarine !=
null && owner.Submarine.Info.Type ==
SubmarineType.Player)
260 else if (owner is Character c)
262 return c.Info !=
null && GameMain.GameSession.CrewManager.GetCharacterInfos().Contains(c.Info);
267 private bool AnyAreUncollected(IEnumerable<Item> items)
268 => items.Any(i => !HasBeenCollected(i));
270 private void CalculateMissionClusterPositions()
272 missionClusterPositions.Clear();
273 foreach (var kvp
in spawnedResources)
275 if (kvp.Value.None()) {
continue; }
276 var pos = Vector2.Zero;
278 foreach (var i
in kvp.Value.Where(i => i !=
null && !i.Removed))
280 pos += i.WorldPosition;
284 missionClusterPositions.Add((kvp.Key, pos));
291 foreach ((Identifier identifier,
int amount) in resourceAmounts)
294 Replace($
"[resourcequantity{i}]", amount.ToString());
297 Replace(
"[handoverpercentage]", ToolBox.GetFormattedPercentage(resourceHandoverAmount));
300 void Replace(
string find,
string replace)
304 replace = $
"‖color:gui.orange‖{replace}‖end‖";
306 message = message.
Replace(find, replace);
float GetAttributeFloat(string key, float def)
ContentXElement? GetChildElement(string name)
Identifier GetAttributeIdentifier(string key, string def)
static readonly List< Item > ItemList
Defines a point in the event that GoTo actions can jump to.
Cave(CaveGenerationParams caveGenerationParams, Rectangle area, Point startPos, Point endPos)
List< Item > GenerateMissionResources(ItemPrefab prefab, int requiredAmount, PositionType positionType, IEnumerable< Cave > targetCaves=null)
Used by clients to set the rotation for the resources
LocalizedString Replace(Identifier find, LocalizedString replace, StringComparison stringComparison=StringComparison.Ordinal)
static MapEntityPrefab FindByIdentifier(Identifier identifier)
abstract LocalizedString Name
static readonly ImmutableArray< PositionType > ValidPositionTypes
override LocalizedString ModifyMessage(LocalizedString message, bool color=true)
override LocalizedString FailureMessage
override LocalizedString SuccessMessage
override IEnumerable<(LocalizedString Label, Vector2 Position)> SonarLabels
override void UpdateMissionSpecific(float deltaTime)
override void StartMissionSpecific(Level level)
override LocalizedString Description
override void EndMissionSpecific(bool completed)
override LocalizedString Name
MineralMission(MissionPrefab prefab, Location[] locations, Submarine sub)
override bool DetermineCompleted()
LocalizedString description
readonly ContentXElement ConfigElement
ContentPackage? ContentPackage