using Barotrauma.Tutorials; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using System.Collections.Generic; using System.Linq; using System.Xml.Linq; namespace Barotrauma { class SinglePlayerCampaign : CampaignMode { public ContextualTutorial ContextualTutorial; private GUIButton endRoundButton; private bool crewDead; private float endTimer; private bool savedOnStart; private List subsToLeaveBehind; private Submarine leavingSub; private bool atEndPosition; public SinglePlayerCampaign(GameModePreset preset, object param) : base(preset, param) { int buttonHeight = (int)(HUDLayoutSettings.ButtonAreaTop.Height * 0.7f); endRoundButton = new GUIButton(HUDLayoutSettings.ToRectTransform(new Rectangle(HUDLayoutSettings.ButtonAreaTop.Right - 200, HUDLayoutSettings.ButtonAreaTop.Center.Y - buttonHeight / 2, 200, buttonHeight), GUICanvas.Instance), TextManager.Get("EndRound"), textAlignment: Alignment.Center) { Font = GUI.SmallFont, OnClicked = (btn, userdata) => { TryEndRound(GetLeavingSub()); return true; } }; foreach (JobPrefab jobPrefab in JobPrefab.List) { for (int i = 0; i < jobPrefab.InitialCount; i++) { CrewManager.AddCharacterInfo(new CharacterInfo(Character.HumanConfigFile, "", jobPrefab)); } } ContextualTutorial = Tutorial.Tutorials.Find(t => t is ContextualTutorial) as ContextualTutorial; if (ContextualTutorial.Selected) // Selected when starting a new game -> initialize { ContextualTutorial.Initialize(); } } public override void Start() { base.Start(); CargoManager.CreateItems(); if (!savedOnStart) { SaveUtil.SaveGame(GameMain.GameSession.SavePath); savedOnStart = true; } crewDead = false; endTimer = 5.0f; isRunning = true; CrewManager.InitSinglePlayerRound(); if (ContextualTutorial.Initialized) { ContextualTutorial.Start(); } } public bool TryHireCharacter(Location location, CharacterInfo characterInfo) { if (Money < characterInfo.Salary) { return false; } location.RemoveHireableCharacter(characterInfo); CrewManager.AddCharacterInfo(characterInfo); Money -= characterInfo.Salary; return true; } public void FireCharacter(CharacterInfo characterInfo) { CrewManager.RemoveCharacterInfo(characterInfo); } private Submarine GetLeavingSub() { if (Character.Controlled?.Submarine == null) { return null; } //allow leaving if inside an outpost, and the submarine is either docked to it or close enough return GetLeavingSubAtOutpost(Level.Loaded.StartOutpost) ?? GetLeavingSubAtOutpost(Level.Loaded.EndOutpost); Submarine GetLeavingSubAtOutpost(Submarine outpost) { //controlled character has to be inside the outpost if (Character.Controlled.Submarine != outpost) { return null; } //if there's a sub docked to the outpost, we can leave the level if (outpost.DockedTo.Count > 0) { var dockedSub = outpost.DockedTo.FirstOrDefault(); return dockedSub.DockedTo.Contains(Submarine.MainSub) ? Submarine.MainSub : dockedSub; } //nothing docked, check if there's a sub close enough to the outpost Submarine closestSub = Submarine.FindClosest(outpost.WorldPosition, ignoreOutposts: true); if (closestSub == null) { return null; } if (outpost == Level.Loaded.StartOutpost) { if (!closestSub.AtStartPosition) { return null; } } else if (outpost == Level.Loaded.EndOutpost) { if (!closestSub.AtEndPosition) { return null; } } return closestSub.DockedTo.Contains(Submarine.MainSub) ? Submarine.MainSub : closestSub; } } public override void Draw(SpriteBatch spriteBatch) { if (!isRunning|| GUI.DisableHUD || GUI.DisableUpperHUD) return; if (Submarine.MainSub == null) return; Submarine leavingSub = GetLeavingSub(); if (leavingSub == null) { endRoundButton.Visible = false; } else if (leavingSub.AtEndPosition) { endRoundButton.Text = ToolBox.LimitString(TextManager.Get("EnterLocation").Replace("[locationname]", Map.SelectedLocation.Name), endRoundButton.Font, endRoundButton.Rect.Width - 5); endRoundButton.Visible = true; } else if (leavingSub.AtStartPosition) { endRoundButton.Text = ToolBox.LimitString(TextManager.Get("EnterLocation").Replace("[locationname]", Map.CurrentLocation.Name), endRoundButton.Font, endRoundButton.Rect.Width - 5); endRoundButton.Visible = true; } else { endRoundButton.Visible = false; } endRoundButton.DrawManually(spriteBatch); } public override void AddToGUIUpdateList() { if (!isRunning) return; base.AddToGUIUpdateList(); CrewManager.AddToGUIUpdateList(); endRoundButton.AddToGUIUpdateList(); if (ContextualTutorial.Initialized) { ContextualTutorial.AddToGUIUpdateList(); } } public override void Update(float deltaTime) { if (!isRunning) { return; } base.Update(deltaTime); if (ContextualTutorial.Initialized) { ContextualTutorial.Update(deltaTime); } if (!GUI.DisableHUD && !GUI.DisableUpperHUD) { endRoundButton.UpdateManually(deltaTime); } if (!crewDead) { if (!CrewManager.GetCharacters().Any(c => !c.IsDead)) crewDead = true; } else { endTimer -= deltaTime; if (endTimer <= 0.0f) { EndRound(leavingSub: null); } } } protected override void WatchmanInteract(Character watchman, Character interactor) { Submarine leavingSub = GetLeavingSub(); if (leavingSub == null) { CreateDialog(new List { watchman }, "WatchmanInteractNoLeavingSub", 5.0f); return; } CreateDialog(new List { watchman }, "WatchmanInteract", 1.0f); if (GUIMessageBox.MessageBoxes.Any(mbox => mbox.UserData as string == "watchmanprompt")) { return; } var msgBox = new GUIMessageBox("", TextManager.Get("CampaignEnterOutpostPrompt") .Replace("[locationname]", leavingSub.AtStartPosition ? Map.CurrentLocation.Name : Map.SelectedLocation.Name), new string[] { TextManager.Get("Yes"), TextManager.Get("No") }) { UserData = "watchmanprompt" }; msgBox.Buttons[0].OnClicked = (btn, userdata) => { if (!isRunning) { return true; } TryEndRound(GetLeavingSub()); return true; }; msgBox.Buttons[0].OnClicked += msgBox.Close; msgBox.Buttons[1].OnClicked += msgBox.Close; } public override void End(string endMessage = "") { isRunning = false; bool success = CrewManager.GetCharacters().Any(c => !c.IsDead); crewDead = false; if (success) { if (subsToLeaveBehind == null || leavingSub == null) { DebugConsole.ThrowError("Leaving submarine not selected -> selecting the closest one"); leavingSub = GetLeavingSub(); subsToLeaveBehind = GetSubsToLeaveBehind(leavingSub); } } GameMain.GameSession.EndRound(""); if (success) { if (leavingSub != Submarine.MainSub && !leavingSub.DockedTo.Contains(Submarine.MainSub)) { Submarine.MainSub = leavingSub; GameMain.GameSession.Submarine = leavingSub; foreach (Submarine sub in subsToLeaveBehind) { MapEntity.mapEntityList.RemoveAll(e => e.Submarine == sub && e is LinkedSubmarine); LinkedSubmarine.CreateDummy(leavingSub, sub); } } if (atEndPosition) { Map.MoveToNextLocation(); } else { Map.SelectLocation(-1); } Map.ProgressWorld(); //save and remove all items that are in someone's inventory foreach (Character c in Character.CharacterList) { if (c.Info == null || c.Inventory == null) { continue; } var inventoryElement = new XElement("inventory"); c.SaveInventory(c.Inventory, inventoryElement); c.Info.InventoryData = inventoryElement; c.Inventory?.DeleteAllItems(); } SaveUtil.SaveGame(GameMain.GameSession.SavePath); } if (!success) { var summaryScreen = GUIMessageBox.VisibleBox; if (summaryScreen != null) { summaryScreen = summaryScreen.Children.First(); var buttonArea = summaryScreen.Children.First().FindChild("buttonarea"); buttonArea.ClearChildren(); summaryScreen.RemoveChild(summaryScreen.Children.FirstOrDefault(c => c is GUIButton)); var okButton = new GUIButton(new RectTransform(new Vector2(0.2f, 1.0f), buttonArea.RectTransform), TextManager.Get("LoadGameButton")) { OnClicked = (GUIButton button, object obj) => { GameMain.GameSession.LoadPrevious(); GameMain.LobbyScreen.Select(); GUIMessageBox.MessageBoxes.Remove(GUIMessageBox.VisibleBox); return true; } }; var quitButton = new GUIButton(new RectTransform(new Vector2(0.2f, 1.0f), buttonArea.RectTransform), TextManager.Get("QuitButton")); quitButton.OnClicked += GameMain.LobbyScreen.QuitToMainMenu; quitButton.OnClicked += (GUIButton button, object obj) => { GUIMessageBox.MessageBoxes.Remove(GUIMessageBox.VisibleBox); return true; }; quitButton.OnClicked += (GUIButton button, object obj) => { if (ContextualTutorial.Initialized) ContextualTutorial.Stop(); return true; }; } } CrewManager.EndRound(); for (int i = Character.CharacterList.Count - 1; i >= 0; i--) { Character.CharacterList[i].Remove(); } Submarine.Unload(); GameMain.LobbyScreen.Select(); } private bool TryEndRound(Submarine leavingSub) { if (leavingSub == null) { return false; } this.leavingSub = leavingSub; subsToLeaveBehind = GetSubsToLeaveBehind(leavingSub); atEndPosition = leavingSub.AtEndPosition; if (subsToLeaveBehind.Any()) { string msg = TextManager.Get(subsToLeaveBehind.Count == 1 ? "LeaveSubBehind" : "LeaveSubsBehind"); var msgBox = new GUIMessageBox(TextManager.Get("Warning"), msg, new string[] { TextManager.Get("Yes"), TextManager.Get("No") }); msgBox.Buttons[0].OnClicked += (btn, userdata) => { EndRound(leavingSub); return true; } ; msgBox.Buttons[0].OnClicked += msgBox.Close; msgBox.Buttons[0].UserData = Submarine.Loaded.FindAll(s => !subsToLeaveBehind.Contains(s)); msgBox.Buttons[1].OnClicked += msgBox.Close; } else { EndRound(leavingSub); } return true; } private bool EndRound(Submarine leavingSub) { isRunning = false; //var cinematic = new RoundEndCinematic(leavingSub, GameMain.GameScreen.Cam, 5.0f); SoundPlayer.OverrideMusicType = CrewManager.GetCharacters().Any(c => !c.IsDead) ? "endround" : "crewdead"; SoundPlayer.OverrideMusicDuration = 18.0f; //CoroutineManager.StartCoroutine(EndCinematic(cinematic), "EndCinematic"); End(""); return true; } /*private IEnumerable EndCinematic(RoundEndCinematic cinematic) { while (cinematic.Running) { if (Submarine.MainSub == null) yield return CoroutineStatus.Success; yield return CoroutineStatus.Running; } if (Submarine.MainSub != null) End(""); yield return CoroutineStatus.Success; }*/ public static SinglePlayerCampaign Load(XElement element) { SinglePlayerCampaign campaign = new SinglePlayerCampaign(GameModePreset.List.Find(gm => gm.Identifier == "singleplayercampaign"), null); foreach (XElement subElement in element.Elements()) { switch (subElement.Name.ToString().ToLowerInvariant()) { case "crew": GameMain.GameSession.CrewManager = new CrewManager(subElement, true); break; case "map": campaign.map = Map.LoadNew(subElement); break; case "contextualtutorial": campaign.ContextualTutorial.Initialize(); // Initialize when saved element found campaign.ContextualTutorial.LoadPartiallyComplete(subElement); break; } } campaign.Money = element.GetAttributeInt("money", 0); campaign.CheatsEnabled = element.GetAttributeBool("cheatsenabled", false); if (campaign.CheatsEnabled) { DebugConsole.CheatsEnabled = true; if (GameMain.Config.UseSteam && !SteamAchievementManager.CheatsEnabled) { SteamAchievementManager.CheatsEnabled = true; new GUIMessageBox("Cheats enabled", "Cheat commands have been enabled on the campaign. You will not receive Steam Achievements until you restart the game."); } } //backwards compatibility with older save files if (campaign.map == null) { string mapSeed = element.GetAttributeString("mapseed", "a"); campaign.GenerateMap(mapSeed); campaign.map.SetLocation(element.GetAttributeInt("currentlocation", 0)); } campaign.savedOnStart = true; return campaign; } public override void Save(XElement element) { XElement modeElement = new XElement("SinglePlayerCampaign", new XAttribute("money", Money), new XAttribute("cheatsenabled", CheatsEnabled)); CrewManager.Save(modeElement); Map.Save(modeElement); if (ContextualTutorial.Initialized) { ContextualTutorial.SavePartiallyComplete(modeElement); } element.Add(modeElement); } } }