2 using Microsoft.Xna.Framework;
4 using System.Collections.Generic;
13 private readonly List<Item> items =
new List<Item>();
14 private readonly Dictionary<Item, UInt16> parentInventoryIDs =
new Dictionary<Item, UInt16>();
15 private readonly Dictionary<Item, int> inventorySlotIndices =
new Dictionary<Item, int>();
16 private readonly Dictionary<Item, byte> parentItemContainerIndices =
new Dictionary<Item, byte>();
21 private float requiredDeliveryAmount;
24 private int? rewardPerCrate;
25 private int calculatedReward;
26 private int maxItemCount;
31 private readonly List<CargoMission> previouslySelectedMissions =
new List<CargoMission>();
39 string rewardText = $
"‖color:gui.orange‖{string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0:N0}
", GetReward(Submarine.MainSub))}‖end‖";
47 : base(prefab, locations, sub)
49 this.currentSub = sub;
50 this.nextRoundSubInfo = sub?.
Info;
62 private void DetermineCargo()
64 if (this.currentSub ==
null || itemConfig ==
null)
66 calculatedReward =
Prefab.Reward;
73 foreach (var subElement
in itemConfig.Elements())
75 int maxCount = subElement.GetAttributeInt(
"maxcount", 10);
76 maxItemCount += maxCount;
79 var pendingSubInfo = GameMain.GameSession?.Campaign?.PendingSubmarineSwitch;
80 if (pendingSubInfo !=
null && pendingSubInfo != currentSub.
Info)
85 maxItemCount = Math.Min(maxItemCount, pendingSubInfo.CargoCapacity);
86 previouslySelectedMissions.Clear();
87 if (GameMain.GameSession?.StartLocation?.SelectedMissions !=
null)
89 bool isPriorMission =
true;
90 foreach (
Mission mission
in GameMain.GameSession.StartLocation.SelectedMissions)
92 if (mission is not
CargoMission otherMission) {
continue; }
93 if (mission ==
this) { isPriorMission =
false; }
94 previouslySelectedMissions.Add(otherMission);
95 if (!isPriorMission) {
continue; }
96 maxItemCount -= otherMission.itemsToSpawn.Count;
99 for (
int i = 0; i < maxItemCount; i++)
101 foreach (var subElement
in itemConfig.Elements())
103 int maxCount = subElement.GetAttributeInt(
"maxcount", 10);
104 if (itemsToSpawn.Count(it => it.element == subElement) >= maxCount) {
continue; }
107 while (itemsToSpawn.Count < maxItemCount)
109 itemsToSpawn.Add((subElement,
null));
110 if (itemsToSpawn.Count(it => it.element == subElement) >= maxCount) {
break; }
114 maxItemCount = Math.Max(0, maxItemCount);
115 nextRoundSubInfo = pendingSubInfo;
120 containers.Sort((c1, c2) => {
return c2.container.Capacity.CompareTo(c1.container.Capacity); });
122 previouslySelectedMissions.Clear();
123 if (GameMain.GameSession?.StartLocation?.SelectedMissions !=
null)
125 bool isPriorMission =
true;
126 foreach (
Mission mission
in GameMain.GameSession.StartLocation.SelectedMissions)
128 if (mission is not
CargoMission otherMission) {
continue; }
129 if (mission ==
this) { isPriorMission =
false; }
130 previouslySelectedMissions.Add(otherMission);
131 if (!isPriorMission) {
continue; }
132 foreach (var (element, container) in otherMission.itemsToSpawn)
134 for (
int i = 0; i < containers.Count; i++)
136 if (containers[i].container == container)
138 containers[i] = (containers[i].container, containers[i].freeSlots - 1);
146 for (
int i = 0; i < containers.Count; i++)
148 foreach (var subElement
in itemConfig.Elements())
150 int maxCount = subElement.GetAttributeInt(
"maxcount", 10);
151 if (itemsToSpawn.Count(it => it.element == subElement) >= maxCount) {
continue; }
153 while (containers[i].freeSlots > 0 && containers[i].container.Inventory.CanBePut(itemPrefab))
155 containers[i] = (containers[i].container, containers[i].freeSlots - 1);
156 itemsToSpawn.Add((subElement, containers[i].container));
157 if (itemsToSpawn.Count(it => it.element == subElement) >= maxCount) {
break; }
163 if (!itemsToSpawn.Any())
165 itemsToSpawn.Add((itemConfig.Elements().First(),
null));
170 calculatedReward = 0;
171 bool crateValuesUniform =
true;
172 int? prevCrateReward =
null;
173 foreach (var (element, container) in itemsToSpawn)
175 int currentCrateReward = element.GetAttributeInt(
"reward", 0);
176 calculatedReward += currentCrateReward;
180 if (crateValuesUniform)
182 if (prevCrateReward.HasValue)
184 if (prevCrateReward.Value != currentCrateReward)
186 crateValuesUniform =
false;
189 prevCrateReward = currentCrateReward;
193 if (crateValuesUniform)
196 rewardPerCrate = calculatedReward / itemsToSpawn.Count;
201 rewardPerCrate =
null;
205 if (GameMain.GameSession?.Campaign is CampaignMode campaign && rewardPerCrate is
int confirmedRewardPerCrate)
207 rewardPerCrate = (int)Math.Round(confirmedRewardPerCrate * campaign.Settings.MissionRewardMultiplier);
210 string rewardText = $
"‖color:gui.orange‖{string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0:N0}
", GetReward(currentSub))}‖end‖";
219 return calculatedReward;
222 bool missionsChanged =
false;
226 if (currentMissions.Count != previouslySelectedMissions.Count)
228 missionsChanged =
true;
232 for (
int i = 0; i < previouslySelectedMissions.Count; i++)
234 if (previouslySelectedMissions[i] != currentMissions[i])
236 missionsChanged =
true;
244 if (pendingSubInfo !=
null && nextRoundSubInfo != pendingSubInfo)
246 this.nextRoundSubInfo = pendingSubInfo;
249 else if (sub != this.currentSub || missionsChanged)
251 this.currentSub = sub;
252 this.nextRoundSubInfo = sub?.
Info;
256 return calculatedReward;
259 private void InitItems()
265 parentInventoryIDs.Clear();
266 parentItemContainerIndices.Clear();
267 inventorySlotIndices.Clear();
269 if (itemConfig ==
null)
271 DebugConsole.ThrowError(
"Failed to initialize items for cargo mission (itemConfig == null)",
276 foreach (var (element, container) in itemsToSpawn)
278 LoadItemAsChild(element, container?.Item);
281 if (requiredDeliveryAmount <= 0.0f) { requiredDeliveryAmount = 1.0f; }
284 private void LoadItemAsChild(ContentXElement element, Item parent)
289 if (!position.HasValue) {
return; }
291 var item =
new Item(itemPrefab, position.Value, cargoRoomSub)
293 SpawnedInCurrentOutpost =
true,
294 AllowStealing =
false
296 item.AddTag(Tags.CargoMissionItem);
305 if (parent?.GetComponent<ItemContainer>() !=
null)
307 parentInventoryIDs.Add(item, parent.ID);
308 parentItemContainerIndices.Add(item, (
byte)parent.GetComponentIndex(parent.GetComponent<
ItemContainer>()));
309 parent.Combine(item, user:
null);
310 inventorySlotIndices.Add(item, item.ParentInventory?.FindIndex(item) ?? -1);
313 foreach (var subElement
in element.Elements())
315 int amount = subElement.GetAttributeInt(
"amount", 1);
316 for (
int i = 0; i < amount; i++)
318 LoadItemAsChild(subElement, item);
326 parentInventoryIDs.Clear();
338 int deliveredItemCount = items.Count(it => IsItemDelivered(it));
339 if (deliveredItemCount / (
float)items.Count >= requiredDeliveryAmount)
349 foreach (
Item item
in items)
357 private static bool IsItemDelivered(
Item item)
361 return submarine == Submarine.MainSub || Submarine.MainSub.GetConnectedSubs().Contains(submarine);
SubmarineInfo PendingSubmarineSwitch
override void StartMissionSpecific(Level level)
override bool DetermineCompleted()
CargoMission(MissionPrefab prefab, Location[] locations, Submarine sub)
override LocalizedString????? Description
override void EndMissionSpecific(bool completed)
override int GetBaseReward(Submarine sub)
Calculates the base reward, can be overridden for different mission types
float GetAttributeFloat(string key, float def)
ContentXElement? GetChildElement(string name)
static GameSession?? GameSession
LocalizedString Replace(Identifier find, LocalizedString replace, StringComparison stringComparison=StringComparison.Ordinal)
IEnumerable< Mission > SelectedMissions
readonly MissionPrefab Prefab
LocalizedString descriptionWithoutReward
LocalizedString description
Mission(MissionPrefab prefab, Location[] locations, Submarine sub)
Vector2? GetCargoSpawnPosition(ItemPrefab itemPrefab, out Submarine cargoRoomSub)
ItemPrefab FindItemPrefab(XElement element)
readonly Location[] Locations
readonly ImmutableHashSet< Identifier > Tags
readonly ContentXElement ConfigElement
ContentPackage? ContentPackage
readonly Identifier Identifier
static List< Submarine > Loaded
List<(ItemContainer container, int freeSlots)> GetCargoContainers()