3 using Microsoft.Xna.Framework;
5 using System.Collections.Generic;
12 partial class MultiPlayerCampaign : CampaignMode
18 get {
return ForceMapUI || CoroutineManager.IsCoroutineRunning(
"LevelTransition"); }
21 private UInt16 pendingSaveID = 1;
30 pendingSaveID = value;
33 if (pendingSaveID == 0) { pendingSaveID++; }
59 parent.Visible =
true;
68 var buttonContainer =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, 0.07f), layout.RectTransform) { RelativeOffset = new Vector2(0.0f, 0.1f) }, isHorizontal:
true)
71 RelativeSpacing = 0.02f
74 var campaignContainer =
new GUIFrame(
new RectTransform(
new Vector2(1.0f, 0.9f), layout.RectTransform,
Anchor.BottomLeft), style:
"GUIFrameListBox")
79 var newCampaignContainer =
new GUIFrame(
new RectTransform(
new Vector2(0.95f, 0.95f), campaignContainer.RectTransform,
Anchor.Center), style:
null);
80 var loadCampaignContainer =
new GUIFrame(
new RectTransform(
new Vector2(0.95f, 0.95f), campaignContainer.RectTransform,
Anchor.Center), style:
null);
84 var newCampaignButton =
new GUIButton(
new RectTransform(
new Vector2(0.5f, 1.0f), buttonContainer.RectTransform),
85 TextManager.Get(
"NewCampaign"), style:
"GUITabButton")
90 var loadCampaignButton =
new GUIButton(
new RectTransform(
new Vector2(0.5f, 1.00f), buttonContainer.RectTransform),
91 TextManager.Get(
"LoadCampaign"), style:
"GUITabButton");
93 newCampaignButton.OnClicked = (btn, obj) =>
95 newCampaignButton.Selected =
true;
96 loadCampaignButton.Selected =
false;
97 newCampaignContainer.Visible =
true;
98 loadCampaignContainer.Visible =
false;
102 loadCampaignButton.OnClicked = (btn, obj) =>
104 newCampaignButton.Selected =
false;
105 loadCampaignButton.Selected =
true;
106 newCampaignContainer.Visible =
false;
107 loadCampaignContainer.Visible =
true;
111 loadCampaignContainer.Visible =
false;
119 partial
void InitProjSpecific()
129 private void CreateButtons()
136 onReturnToMapScreen: () =>
138 ShowCampaignUI = true;
139 if (CampaignUI == null) { InitCampaignUI(); }
145 int readyButtonWidth = (int)(GUI.Scale * 50 * (GUI.IsUltrawide ? 3.0f : 1.0f));
146 int readyButtonHeight = (int)(GUI.Scale * 40);
147 int readyButtonCenter = readyButtonHeight / 2,
148 screenMiddle = GameMain.GraphicsWidth / 2;
149 ReadyCheckButton =
new GUIButton(HUDLayoutSettings.ToRectTransform(
new Rectangle(screenMiddle + (
endRoundButton.
Rect.Width / 2) + GUI.IntScale(16), HUDLayoutSettings.ButtonAreaTop.Center.Y - readyButtonCenter, readyButtonWidth, readyButtonHeight), GUI.Canvas),
150 style:
"RepairBuyButton")
152 ToolTip = TextManager.Get(
"ReadyCheck.Tooltip"),
157 ReadyCheck.CreateReadyCheck();
161 UserData =
"ReadyCheckButton"
167 campaignUIContainer =
new GUIFrame(
new RectTransform(Vector2.One, GUI.Canvas,
Anchor.Center), style:
"InnerGlow", color: Color.Black);
180 CoroutineManager.StartCoroutine(DoInitialCameraTransition(),
"MultiplayerCampaign.DoInitialCameraTransition");
186 throw new InvalidOperationException(
"");
190 private IEnumerable<CoroutineStatus> DoInitialCameraTransition()
197 if (GameMain.Client ==
null)
199 yield
return CoroutineStatus.
Success;
202 if (GameMain.Client.LateCampaignJoin)
204 GameMain.Client.LateCampaignJoin =
false;
205 yield
return CoroutineStatus.
Success;
209 GUI.DisableHUD =
true;
212 if (SlideshowPrefab.Prefabs.TryGet(
"campaignstart".ToIdentifier(), out var slideshow))
214 SlideshowPlayer =
new SlideshowPlayer(GUICanvas.Instance, slideshow);
218 prevControlled?.ClearInputs();
220 overlayColor = Color.LightGray;
221 overlaySprite = Map.CurrentLocation.Type.GetPortrait(Map.CurrentLocation.PortraitId);
223 var outpost = GameMain.GameSession.Level.StartOutpost;
224 var borders = outpost.GetDockedBorders();
225 borders.Location += outpost.WorldPosition.ToPoint();
226 GameMain.GameScreen.Cam.Position =
new Vector2(borders.X + borders.Width / 2, borders.Y - borders.Height / 2);
227 float startZoom = 0.8f /
228 ((float)Math.Max(borders.Width, borders.Height) / (float)GameMain.GameScreen.Cam.Resolution.X);
229 GameMain.GameScreen.Cam.Zoom = GameMain.GameScreen.Cam.MinZoom = Math.Min(startZoom, GameMain.GameScreen.Cam.MinZoom);
230 while (SlideshowPlayer !=
null && !SlideshowPlayer.LastTextShown)
232 GUI.PreventPauseMenuToggle =
true;
233 yield
return CoroutineStatus.Running;
235 GUI.PreventPauseMenuToggle =
false;
237 GameMain.LightManager.LosAlpha = 0.0f;
238 var transition =
new CameraTransition(prevControlled, GameMain.GameScreen.Cam,
244 startZoom: startZoom, endZoom: 1.0f)
246 AllowInterrupt =
true,
247 RemoveControlFromCharacter =
false
249 overlayColor = Color.Transparent;
250 while (transition.Running)
252 yield
return CoroutineStatus.Running;
255 if (prevControlled !=
null)
262 var transition =
new CameraTransition(
Submarine.MainSub, GameMain.GameScreen.Cam,
267 startZoom: 0.5f, endZoom: 1.0f)
269 AllowInterrupt =
true,
270 RemoveControlFromCharacter =
true
272 while (transition.Running)
274 yield
return CoroutineStatus.Running;
278 if (prevControlled !=
null)
280 prevControlled.SelectedItem = prevControlled.SelectedSecondaryItem =
null;
281 if (prevControlled.AIController !=
null)
283 prevControlled.AIController.Enabled =
true;
286 GUI.DisableHUD =
false;
287 yield
return CoroutineStatus.Success;
295 private IEnumerable<CoroutineStatus> DoLevelTransition()
297 SoundPlayer.OverrideMusicType = (
CrewManager.
GetCharacters().Any(c => !c.IsDead) ?
"endround" :
"crewdead").ToIdentifier();
298 SoundPlayer.OverrideMusicDuration = 18.0f;
306 if (continueButton !=
null)
308 continueButton.
Visible =
false;
311 Character.Controlled =
null;
313 yield
return new WaitForSeconds(0.1f);
315 GameMain.Client.EndCinematic?.Stop();
316 var endTransition =
new CameraTransition(Submarine.MainSub, GameMain.GameScreen.Cam,
null,
319 panDuration: EndTransitionDuration);
320 GameMain.Client.EndCinematic = endTransition;
322 Location portraitLocation = Map?.SelectedLocation ?? Map?.CurrentLocation ?? Level.Loaded?.StartLocation;
323 if (portraitLocation !=
null)
325 overlaySprite = portraitLocation.Type.GetPortrait(portraitLocation.PortraitId);
327 float fadeOutDuration = endTransition.PanDuration;
329 while (t < fadeOutDuration || endTransition.Running)
331 t += CoroutineManager.DeltaTime;
332 overlayColor = Color.Lerp(Color.Transparent, Color.White, t / fadeOutDuration);
333 yield
return CoroutineStatus.Running;
335 overlayColor = Color.White;
336 yield
return CoroutineStatus.Running;
342 while (Level.Loaded == prevLevel || Level.Loaded ==
null)
344 if (DateTime.Now > timeOut || Screen.Selected != GameMain.GameScreen) {
break; }
345 yield
return CoroutineStatus.Running;
348 endTransition.Stop();
349 overlayColor = Color.Transparent;
351 if (DateTime.Now > timeOut)
353 DebugConsole.ThrowError(
"Failed to start the round. Timed out while waiting for the level transition to finish.");
354 GameMain.NetLobbyScreen.Select();
356 if (Screen.Selected is not RoundSummaryScreen)
358 if (continueButton !=
null)
360 continueButton.Visible =
true;
364 GUI.SetSavingIndicatorState(
false);
365 yield
return CoroutineStatus.Success;
368 public override void Update(
float deltaTime)
370 if (CoroutineManager.IsCoroutineRunning(
"LevelTransition") ||
Level.
Loaded ==
null) {
return; }
372 if (ShowCampaignUI || ForceMapUI)
378 base.Update(deltaTime);
385 ShowCampaignUI =
false;
388 roundSummary.ContinueButton.
Visible)
394 if (!GUI.DisableHUD && !GUI.DisableUpperHUD)
396 endRoundButton.UpdateManually(deltaTime);
397 ReadyCheckButton?.UpdateManually(deltaTime);
398 if (CoroutineManager.IsCoroutineRunning(
"LevelTransition") || ForceMapUI) {
return; }
432 var transitionType = GetAvailableTransition(out _, out _);
435 ShowCampaignUI =
false;
437 HintManager.OnAvailableTransition(transitionType);
448 base.End(transitionType);
449 ForceMapUI = ShowCampaignUI =
false;
457 if (ReadyCheck.IsReadyCheck(mb) || mb.UserData is
Pair<string, ushort> pair && pair.
First.Equals(
"conversationaction", StringComparison.OrdinalIgnoreCase))
470 IsFirstRound =
false;
471 CoroutineManager.StartCoroutine(DoLevelTransition(),
"LevelTransition");
482 GUI.DisableHUD =
false;
492 System.Diagnostics.Debug.Assert(map.Locations.Count < UInt16.MaxValue);
494 msg.
WriteUInt16(map.CurrentLocationIndex == -1 ? UInt16.MaxValue : (UInt16)map.CurrentLocationIndex);
495 msg.
WriteUInt16(map.SelectedLocationIndex == -1 ? UInt16.MaxValue : (UInt16)map.SelectedLocationIndex);
497 var selectedMissionIndices = map.GetSelectedMissionIndices();
498 msg.
WriteByte((
byte)selectedMissionIndices.Count());
499 foreach (
int selectedMissionIndex
in selectedMissionIndices)
501 msg.WriteByte((
byte)selectedMissionIndex);
503 msg.WriteBoolean(PurchasedHullRepairs);
504 msg.WriteBoolean(PurchasedItemRepairs);
505 msg.WriteBoolean(PurchasedLostShuttles);
515 msg.WriteIdentifier(prefab.Identifier);
516 msg.WriteIdentifier(category.Identifier);
517 msg.WriteByte((
byte)level);
523 msg.WriteUInt16(itemSwap.ItemToRemove.ID);
524 msg.WriteIdentifier(itemSwap.ItemToInstall?.Identifier ?? Identifier.Empty);
538 bool refreshCampaignUI =
false;
542 string savePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Multiplayer);
546 campaign.CampaignID = campaignID;
551 if (NetIdUtils.IdMoreRecent(saveID, campaign.PendingSaveID)) { campaign.PendingSaveID = saveID; }
552 campaign.IsFirstRound = isFirstRound;
554 if (requiredFlags.HasFlag(
NetFlags.Misc))
556 DebugConsole.Log(
"Received campaign update (Misc)");
561 if (ShouldApply(
NetFlags.Misc,
id, requireUpToDateSave:
false))
563 refreshCampaignUI = campaign.PurchasedHullRepairs != purchasedHullRepairs ||
564 campaign.PurchasedItemRepairs != purchasedItemRepairs ||
565 campaign.PurchasedLostShuttles != purchasedLostShuttles;
566 campaign.PurchasedHullRepairs = purchasedHullRepairs;
567 campaign.PurchasedItemRepairs = purchasedItemRepairs;
568 campaign.PurchasedLostShuttles = purchasedLostShuttles;
572 if (requiredFlags.HasFlag(
NetFlags.MapAndMissions))
574 DebugConsole.Log(
"Received campaign update (MapAndMissions)");
582 var availableMissions =
new List<(Identifier Identifier, byte ConnectionIndex)>();
583 for (
int i = 0; i < missionCount; i++)
586 byte connectionIndex = msg.
ReadByte();
587 availableMissions.Add((missionIdentifier, connectionIndex));
590 byte selectedMissionCount = msg.
ReadByte();
591 List<int> selectedMissionIndices =
new List<int>();
592 for (
int i = 0; i < selectedMissionCount; i++)
594 selectedMissionIndices.Add(msg.
ReadByte());
597 if (ShouldApply(
NetFlags.MapAndMissions,
id, requireUpToDateSave:
true))
599 campaign.ForceMapUI = forceMapUI;
600 campaign.Map.AllowDebugTeleport = allowDebugTeleport;
601 campaign.Map.SetLocation(currentLocIndex == UInt16.MaxValue ? -1 : currentLocIndex);
602 campaign.Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex);
603 foreach (var availableMission
in availableMissions)
606 if (missionPrefab ==
null)
608 DebugConsole.ThrowError($
"Error when receiving campaign data from the server: mission prefab \"{availableMission.Identifier}\" not found.");
611 if (availableMission.ConnectionIndex == 255)
613 campaign.Map.CurrentLocation.UnlockMission(missionPrefab);
617 if (availableMission.ConnectionIndex < 0 || availableMission.ConnectionIndex >= campaign.Map.CurrentLocation.Connections.Count)
619 DebugConsole.ThrowError($
"Error when receiving campaign data from the server: connection index for mission \"{availableMission.Identifier}\" out of range (index: {availableMission.ConnectionIndex}, current location: {campaign.Map.CurrentLocation.DisplayName}, connections: {campaign.Map.CurrentLocation.Connections.Count}).");
622 LocationConnection connection = campaign.Map.CurrentLocation.Connections[availableMission.ConnectionIndex];
623 campaign.Map.CurrentLocation.UnlockMission(missionPrefab, connection);
626 campaign.Map.SelectMission(selectedMissionIndices);
627 ReadStores(msg, apply:
true);
631 ReadStores(msg, apply:
false);
635 if (requiredFlags.HasFlag(
NetFlags.SubList))
637 DebugConsole.Log(
"Received campaign update (SubList)");
640 List<ushort> ownedSubIndices =
new List<ushort>();
641 for (
int i = 0; i < ownedSubCount; i++)
646 if (ShouldApply(
NetFlags.SubList,
id, requireUpToDateSave:
false))
648 foreach (ushort ownedSubIndex
in ownedSubIndices)
655 errorMsg = $
"Error in {nameof(MultiPlayerCampaign.ClientRead)}. Owned submarine index was out of bounds (list of server submarines is empty).";
659 errorMsg = $
"Error in {nameof(MultiPlayerCampaign.ClientRead)}. Owned submarine index was out of bounds. Index: {ownedSubIndex}, submarines: {string.Join(",
", GameMain.Client.ServerSubmarines.Select(s => s.Name))}";
661 DebugConsole.ThrowError(errorMsg);
662 GameAnalyticsManager.AddErrorEventOnce(
663 "MultiPlayerCampaign.ClientRead.OwnerSubIndexOutOfBounds" + ownedSubIndex,
664 GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
680 if (requiredFlags.HasFlag(
NetFlags.UpgradeManager))
682 DebugConsole.Log(
"Received campaign update (UpgradeManager)");
685 ushort pendingUpgradeCount = msg.
ReadUInt16();
686 List<PurchasedUpgrade> pendingUpgrades =
new List<PurchasedUpgrade>();
687 for (
int i = 0; i < pendingUpgradeCount; i++)
690 UpgradePrefab prefab = UpgradePrefab.Find(upgradeIdentifier);
692 UpgradeCategory category = UpgradeCategory.Find(categoryIdentifier);
694 if (prefab ==
null || category ==
null) {
continue; }
695 pendingUpgrades.Add(
new PurchasedUpgrade(prefab, category, upgradeLevel));
698 ushort purchasedItemSwapCount = msg.
ReadUInt16();
699 List<PurchasedItemSwap> purchasedItemSwaps =
new List<PurchasedItemSwap>();
700 for (
int i = 0; i < purchasedItemSwapCount; i++)
704 ItemPrefab itemToInstall = itemToInstallIdentifier.IsEmpty ? null :
ItemPrefab.
Find(
string.Empty, itemToInstallIdentifier);
706 purchasedItemSwaps.Add(
new PurchasedItemSwap(itemToRemove, itemToInstall));
710 ShouldApply(
NetFlags.UpgradeManager,
id, requireUpToDateSave:
true))
712 UpgradeStore.WaitForServerUpdate =
false;
713 campaign.UpgradeManager.SetPendingUpgrades(pendingUpgrades);
714 campaign.UpgradeManager.PurchasedUpgrades.Clear();
715 foreach (var purchasedItemSwap
in purchasedItemSwaps)
717 if (purchasedItemSwap.ItemToInstall ==
null)
719 campaign.UpgradeManager.CancelItemSwap(purchasedItemSwap.ItemToRemove, force:
true);
723 campaign.UpgradeManager.PurchaseItemSwap(purchasedItemSwap.ItemToRemove, purchasedItemSwap.ItemToInstall, isNetworkMessage:
true);
728 if (item.
PendingItemSwap !=
null && !purchasedItemSwaps.Any(it => it.ItemToRemove == item))
733 campaign.CampaignUI?.UpgradeStore?.RequestRefresh();
738 if (requiredFlags.HasFlag(
NetFlags.ItemsInBuyCrate))
740 DebugConsole.Log(
"Received campaign update (ItemsInBuyCrate)");
742 var buyCrateItems = ReadPurchasedItems(msg, sender:
null);
743 if (ShouldApply(
NetFlags.ItemsInBuyCrate,
id, requireUpToDateSave:
true))
745 campaign.CargoManager.SetItemsInBuyCrate(buyCrateItems);
746 campaign.SetLastUpdateIdForFlag(
NetFlags.ItemsInBuyCrate,
id);
747 ReadStores(msg, apply:
true);
751 ReadStores(msg, apply:
false);
754 if (requiredFlags.HasFlag(
NetFlags.ItemsInSellFromSubCrate))
756 DebugConsole.Log(
"Received campaign update (ItemsInSellFromSubCrate)");
758 var subSellCrateItems = ReadPurchasedItems(msg, sender:
null);
759 if (ShouldApply(
NetFlags.ItemsInSellFromSubCrate,
id, requireUpToDateSave:
true))
761 campaign.CargoManager.SetItemsInSubSellCrate(subSellCrateItems);
762 campaign.SetLastUpdateIdForFlag(
NetFlags.ItemsInSellFromSubCrate,
id);
763 ReadStores(msg, apply:
true);
767 ReadStores(msg, apply:
false);
770 if (requiredFlags.HasFlag(
NetFlags.PurchasedItems))
772 DebugConsole.Log(
"Received campaign update (PuchasedItems)");
774 var purchasedItems = ReadPurchasedItems(msg, sender:
null);
775 if (ShouldApply(
NetFlags.PurchasedItems,
id, requireUpToDateSave:
true))
777 campaign.CargoManager.SetPurchasedItems(purchasedItems);
778 campaign.SetLastUpdateIdForFlag(
NetFlags.PurchasedItems,
id);
779 ReadStores(msg, apply:
true);
783 ReadStores(msg, apply:
false);
786 if (requiredFlags.HasFlag(
NetFlags.SoldItems))
788 DebugConsole.Log(
"Received campaign update (SoldItems)");
790 var soldItems = ReadSoldItems(msg);
791 if (ShouldApply(
NetFlags.SoldItems,
id, requireUpToDateSave:
true))
793 campaign.CargoManager.SetSoldItems(soldItems);
794 campaign.SetLastUpdateIdForFlag(
NetFlags.SoldItems,
id);
795 ReadStores(msg, apply:
true);
799 ReadStores(msg, apply:
false);
802 if (requiredFlags.HasFlag(
NetFlags.Reputation))
804 DebugConsole.Log(
"Received campaign update (Reputation)");
806 Dictionary<Identifier, float> factionReps =
new Dictionary<Identifier, float>();
807 byte factionsCount = msg.
ReadByte();
808 for (
int i = 0; i < factionsCount; i++)
812 if (ShouldApply(
NetFlags.Reputation,
id, requireUpToDateSave:
true))
814 foreach (var (identifier, rep) in factionReps)
816 Faction faction = campaign.Factions.FirstOrDefault(f => f.Prefab.Identifier == identifier);
823 DebugConsole.ThrowError($
"Received an update for a faction that doesn't exist \"{identifier}\".");
826 campaign?.CampaignUI?.UpgradeStore?.RequestRefresh();
829 if (requiredFlags.HasFlag(
NetFlags.CharacterInfo))
831 DebugConsole.Log(
"Received campaign update (CharacterInfo)");
836 if (hasCharacterData)
840 if (!waitForModsDownloaded && ShouldApply(
NetFlags.CharacterInfo,
id, requireUpToDateSave:
true))
842 if (myCharacterInfo !=
null)
854 campaign.SuppressStateSending =
true;
856 if (campaign.LastSaveID == saveID)
860 if (refreshCampaignUI)
862 campaign?.CampaignUI?.UpgradeStore?.RequestRefresh();
864 campaign.SuppressStateSending =
false;
866 bool ShouldApply(
NetFlags flag, UInt16
id,
bool requireUpToDateSave)
868 if (NetIdUtils.IdMoreRecent(
id, campaign.GetLastUpdateIdForFlag(flag)) &&
869 (!requireUpToDateSave || saveID == campaign.LastSaveID))
871 campaign.SetLastUpdateIdForFlag(flag,
id);
882 var storeBalances =
new Dictionary<Identifier, UInt16>();
886 for (
int i = 0; i < storeCount; i++)
890 storeBalances.Add(identifier, storeBalance);
895 foreach (var balance
in storeBalances)
897 if (campaign.Map?.CurrentLocation?.GetStore(balance.Key) is { } store)
899 store.Balance = balance.Value;
911 ushort availableHireLength = msg.
ReadUInt16();
912 List<CharacterInfo> availableHires =
new List<CharacterInfo>();
913 for (
int i = 0; i < availableHireLength; i++)
917 availableHires.Add(hire);
921 List<UInt16> pendingHires =
new List<UInt16>();
922 for (
int i = 0; i < pendingHireLength; i++)
928 List<CharacterInfo> hiredCharacters =
new List<CharacterInfo>();
929 for (
int i = 0; i < hiredLength; i++)
933 hiredCharacters.Add(hired);
937 if (renameCrewMember)
942 if (renamedCharacter !=
null)
950 DebugConsole.ThrowError($
"Could not find a character to rename with the ID {renamedIdentifier}.");
966 if (!NetIdUtils.IdMoreRecent(pendingSaveID, LastSaveID))
979 CurrentLocation?.ForceHireableCharacters(availableHires);
986 NetWalletUpdate update = INetSerializableStruct.Read<NetWalletUpdate>(inc);
987 foreach (NetWalletTransaction transaction
in update.Transactions)
989 WalletInfo info = transaction.Info;
990 if (transaction.CharacterID.TryUnwrap(out var charID))
993 if (targetCharacter is
null) {
break; }
994 Wallet wallet = targetCharacter.
Wallet;
996 wallet.Balance = info.Balance;
997 wallet.RewardDistribution = info.RewardDistribution;
998 TryInvokeEvent(wallet, transaction.ChangedData, info);
1002 Bank.Balance = info.Balance;
1003 Bank.RewardDistribution = info.RewardDistribution;
1004 TryInvokeEvent(Bank, transaction.ChangedData, info);
1008 void TryInvokeEvent(Wallet wallet, WalletChangedData data, WalletInfo info)
1010 if (data.BalanceChanged.IsSome() || data.RewardDistributionChanged.IsSome())
1012 OnMoneyChanged.Invoke(
new WalletChangedEvent(wallet, data, info));
1026 return PersonalWallet.TryDeduct(price);
1029 int balance = PersonalWallet.Balance;
1031 if (balance >= price)
1033 return PersonalWallet.TryDeduct(price);
1036 if (balance + Bank.Balance >= price)
1038 int remainder = price - balance;
1039 if (balance > 0) { PersonalWallet.Deduct(balance); }
1040 Bank.Deduct(remainder);
1047 public override void Save(XElement element)
1054 DebugConsole.Log($
"Loading save file for an existing game session ({filePath})");
1055 SaveUtil.DecompressToDirectory(filePath, SaveUtil.TempPath);
1057 string gamesessionDocPath = Path.Combine(SaveUtil.TempPath, SaveUtil.GameSessionFileName);
1058 XDocument doc = XMLExtensions.TryLoadXml(gamesessionDocPath);
1061 DebugConsole.ThrowError($
"Failed to load the state of a multiplayer campaign. Could not open the file \"{gamesessionDocPath}\".");
1064 Load(doc.Root.Element(
"MultiPlayerCampaign"));
static bool AllowedToManageWallets()
GUIButton ReadyCheckButton
GUIButton CreateEndRoundButton()
void TryEndRoundWithFuelCheck(Action onConfirm, Action onReturnToMapScreen)
Action< string > LoadGame
Action< SubmarineInfo, string, string, CampaignSettings > StartNewGame
CampaignMode.InteractionType SelectedTab
void SelectTab(CampaignMode.InteractionType tab, Character npc=null)
Dictionary< Identifier, List< PurchasedItem > > PurchasedItems
Dictionary< Identifier, List< PurchasedItem > > ItemsInSellFromSubCrate
Dictionary< Identifier, List< PurchasedItem > > ItemsInBuyCrate
Dictionary< Identifier, List< SoldItem > > SoldItems
static readonly List< Character > CharacterList
static bool DisableControls
static Character? Controlled
Stores information about the Character that is needed between rounds in the menu etc....
static CharacterInfo ClientRead(Identifier speciesName, IReadMessage inc, bool requireJobPrefabFound=true)
static readonly Identifier HumanSpeciesName
static CoroutineStatus Running
static CoroutineStatus Success
Responsible for keeping track of the characters in the player crew, saving and loading their orders,...
IEnumerable< Character > GetCharacters()
IEnumerable< CharacterInfo > GetCharacterInfos()
Note: this only returns AI characters' infos in multiplayer. The infos are used to manage hiring/firi...
void FireCharacter(CharacterInfo characterInfo)
ReadyCheck ActiveReadyCheck
void RenameCharacter(CharacterInfo characterInfo, string newName)
static Entity FindEntityByID(ushort ID)
Find an entity based on the ID
GUIComponent GetChildByUserData(object obj)
virtual void ClearChildren()
int GetChildIndex(GUIComponent child)
void UpdateManually(float deltaTime, bool alsoChildren=false, bool recursive=true)
By default, all the gui elements are updated automatically in the same order they appear on the updat...
GUIFrame Content
A frame that contains the contents of the listbox. The frame itself is not rendered.
static readonly List< GUIComponent > MessageBoxes
static GUIComponent VisibleBox
static void AutoScaleAndNormalize(params GUITextBlock[] textBlocks)
Set the text scale of the GUITextBlocks so that they all use the same scale and can fit the text with...
static GameSession?? GameSession
static NetLobbyScreen NetLobbyScreen
static CampaignEndScreen CampaignEndScreen
static GameModePreset MultiPlayerCampaign
List< SubmarineInfo > OwnedSubmarines
RoundSummary RoundSummary
SubmarineInfo SubmarineInfo
The "HR manager" UI, which is used to hire/fire characters and rename crewmates.
void SetPendingHires(List< UInt16 > characterInfos, Location location)
bool ValidateHires(List< CharacterInfo > hires, bool takeMoney=true, bool createNetworkEvent=false, bool createNotification=true)
void SetHireables(Location location, List< CharacterInfo > availableHires)
ItemPrefab PendingItemSwap
static readonly List< Item > ItemList
static ItemPrefab Find(string name, Identifier identifier)
static readonly PrefabCollection< MissionPrefab > Prefabs
override bool TryPurchase(Client client, int price)
override void HUDScaleChanged()
void ClientReadMoney(IReadMessage inc)
override IEnumerable< CoroutineStatus > DoLevelTransition(TransitionType transitionType, LevelData newLevel, Submarine leavingSub, bool mirror)
override void Update(float deltaTime)
void ClientReadCrew(IReadMessage msg)
override void End(TransitionType transitionType=TransitionType.None)
override int GetBalance(Client client=null)
override Wallet GetWallet(Client client=null)
override void EndCampaignProjSpecific()
bool SuppressStateSending
override void LoadInitialLevel()
Load the first level and start the round after loading a save file
override void Save(XElement element)
void LoadState(string filePath)
static void ClientRead(IReadMessage msg)
override void UpdateWhilePaused(float deltaTime)
static void StartCampaignSetup(List< SaveInfo > saveFiles)
void ClientWrite(IWriteMessage msg)
void ToggleCampaignMode(bool enabled)
void HighlightMode(int modeIndex)
GUIButton QuitCampaignButton
GUIFrame CampaignSetupFrame
MultiPlayerCampaignSetupUI CampaignSetupUI
void RefreshStartButtonVisibility()
void SetCampaignCharacterInfo(CharacterInfo newCampaignCharacterInfo)
bool CheckIfCampaignSubMatches(SubmarineInfo serverSubmarine, SubmarineDeliveryData deliveryData)
static readonly TimeSpan LevelTransitionTimeOut
CharacterInfo CharacterInfo
readonly List< SubmarineInfo > ServerSubmarines
void SetupNewCampaign(SubmarineInfo sub, string saveName, string mapSeed, CampaignSettings settings)
void SetupLoadCampaign(string saveName)
void RequestStartRound(bool continueCampaign=false)
Tell the server to start the round (permission required)
void SetReputation(float newReputation)
IEnumerable< Submarine > GetConnectedSubs()
Returns a list of all submarines that are connected to this one via docking ports,...
This class handles all upgrade logic. Storing, applying, checking and validation of upgrades.
readonly List< PurchasedItemSwap > PurchasedItemSwaps
readonly List< PurchasedUpgrade > PurchasedUpgrades
This is used by the client to notify the server which upgrades are yet to be paid for.
Identifier ReadIdentifier()
void WriteUInt16(UInt16 val)