(965c31410a) Unstable v0.10.4.0
This commit is contained in:
@@ -1,10 +1,65 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
abstract partial class CampaignMode : GameMode
|
||||
{
|
||||
protected bool crewDead;
|
||||
|
||||
protected Color overlayColor;
|
||||
protected string overlayText, overlayTextBottom;
|
||||
protected Color overlayTextColor;
|
||||
protected Sprite overlaySprite;
|
||||
|
||||
protected GUIButton endRoundButton;
|
||||
|
||||
public GUIButton EndRoundButton => endRoundButton;
|
||||
|
||||
protected GUIFrame campaignUIContainer;
|
||||
public CampaignUI CampaignUI;
|
||||
|
||||
public bool ForceMapUI
|
||||
{
|
||||
get;
|
||||
protected set;
|
||||
}
|
||||
|
||||
public override bool Paused
|
||||
{
|
||||
get { return ForceMapUI || CoroutineManager.IsCoroutineRunning("LevelTransition"); }
|
||||
}
|
||||
|
||||
private bool showCampaignUI;
|
||||
private bool wasChatBoxOpen;
|
||||
public bool ShowCampaignUI
|
||||
{
|
||||
get { return showCampaignUI; }
|
||||
set
|
||||
{
|
||||
if (value == showCampaignUI) { return; }
|
||||
var chatBox = CrewManager?.ChatBox ?? GameMain.Client?.ChatBox;
|
||||
if (value)
|
||||
{
|
||||
if (chatBox != null)
|
||||
{
|
||||
wasChatBoxOpen = chatBox.ToggleOpen;
|
||||
chatBox.ToggleOpen = false;
|
||||
}
|
||||
}
|
||||
else if (chatBox != null)
|
||||
{
|
||||
chatBox.ToggleOpen = wasChatBoxOpen;
|
||||
}
|
||||
showCampaignUI = value;
|
||||
}
|
||||
}
|
||||
|
||||
public override void ShowStartMessage()
|
||||
{
|
||||
if (Mission == null) return;
|
||||
@@ -15,5 +70,216 @@ namespace Barotrauma
|
||||
UserData = "missionstartmessage"
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// There is a server-side implementation of the method in <see cref="MultiPlayerCampaign"/>
|
||||
/// </summary>
|
||||
public bool AllowedToEndRound()
|
||||
{
|
||||
//allow ending the round if the client has permissions, is the owner, the only client in the server
|
||||
//or if no-one has management permissions
|
||||
if (GameMain.Client == null) { return true; }
|
||||
return
|
||||
GameMain.Client.HasPermission(ClientPermissions.ManageRound) ||
|
||||
GameMain.Client.HasPermission(ClientPermissions.ManageCampaign) ||
|
||||
GameMain.Client.ConnectedClients.Count == 1 ||
|
||||
GameMain.Client.IsServerOwner ||
|
||||
GameMain.Client.ConnectedClients.None(c =>
|
||||
c.InGame && (c.IsOwner || c.HasPermission(ClientPermissions.ManageRound) || c.HasPermission(ClientPermissions.ManageCampaign)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// There is a server-side implementation of the method in <see cref="MultiPlayerCampaign"/>
|
||||
/// </summary>
|
||||
public bool AllowedToManageCampaign()
|
||||
{
|
||||
//allow ending the round if the client has permissions, is the owner, the only client in the server,
|
||||
//or if no-one has management permissions
|
||||
if (GameMain.Client == null) { return true; }
|
||||
return
|
||||
GameMain.Client.HasPermission(ClientPermissions.ManageCampaign) ||
|
||||
GameMain.Client.ConnectedClients.Count == 1 ||
|
||||
GameMain.Client.IsServerOwner ||
|
||||
GameMain.Client.ConnectedClients.None(c =>
|
||||
c.InGame && (c.IsOwner || c.HasPermission(ClientPermissions.ManageCampaign)));
|
||||
}
|
||||
|
||||
public override void Draw(SpriteBatch spriteBatch)
|
||||
{
|
||||
if (overlayColor.A > 0)
|
||||
{
|
||||
if (overlaySprite != null)
|
||||
{
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.Black * (overlayColor.A / 255.0f), isFilled: true);
|
||||
float scale = Math.Max(GameMain.GraphicsWidth / overlaySprite.size.X, GameMain.GraphicsHeight / overlaySprite.size.Y);
|
||||
overlaySprite.Draw(spriteBatch, new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight) / 2, overlayColor, overlaySprite.size / 2, scale: scale);
|
||||
}
|
||||
else
|
||||
{
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), overlayColor, isFilled: true);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(overlayText) && overlayTextColor.A > 0)
|
||||
{
|
||||
var backgroundSprite = GUI.Style.GetComponentStyle("CommandBackground").GetDefaultSprite();
|
||||
Vector2 centerPos = new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight) / 2;
|
||||
backgroundSprite.Draw(spriteBatch,
|
||||
centerPos,
|
||||
Color.White * (overlayTextColor.A / 255.0f),
|
||||
origin: backgroundSprite.size / 2,
|
||||
rotate: 0.0f,
|
||||
scale: new Vector2(1.5f, 0.7f) * (GameMain.GraphicsWidth / 3 / backgroundSprite.size.X));
|
||||
|
||||
string wrappedText = ToolBox.WrapText(overlayText, GameMain.GraphicsWidth / 3, GUI.Font);
|
||||
Vector2 textSize = GUI.Font.MeasureString(wrappedText);
|
||||
Vector2 textPos = centerPos - textSize / 2;
|
||||
GUI.DrawString(spriteBatch, textPos + Vector2.One, wrappedText, Color.Black * (overlayTextColor.A / 255.0f));
|
||||
GUI.DrawString(spriteBatch, textPos, wrappedText, overlayTextColor);
|
||||
|
||||
if (!string.IsNullOrEmpty(overlayTextBottom))
|
||||
{
|
||||
Vector2 bottomTextPos = centerPos + new Vector2(0.0f, textSize.Y + 30 * GUI.Scale) - GUI.Font.MeasureString(overlayTextBottom) / 2;
|
||||
GUI.DrawString(spriteBatch, bottomTextPos + Vector2.One, overlayTextBottom, Color.Black * (overlayTextColor.A / 255.0f));
|
||||
GUI.DrawString(spriteBatch, bottomTextPos, overlayTextBottom, overlayTextColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (GUI.DisableHUD || GUI.DisableUpperHUD || ForceMapUI || CoroutineManager.IsCoroutineRunning("LevelTransition"))
|
||||
{
|
||||
endRoundButton.Visible = false;
|
||||
return;
|
||||
}
|
||||
if (Submarine.MainSub == null) { return; }
|
||||
|
||||
endRoundButton.Visible = false;
|
||||
var availableTransition = GetAvailableTransition(out _, out Submarine leavingSub);
|
||||
string buttonText = "";
|
||||
switch (availableTransition)
|
||||
{
|
||||
case TransitionType.ProgressToNextLocation:
|
||||
case TransitionType.ProgressToNextEmptyLocation:
|
||||
if (Level.Loaded.EndOutpost == null || !Level.Loaded.EndOutpost.DockedTo.Contains(leavingSub))
|
||||
{
|
||||
buttonText = TextManager.GetWithVariable("EnterLocation", "[locationname]", Level.Loaded.EndLocation?.Name ?? "[ERROR]");
|
||||
endRoundButton.Visible = !ForceMapUI && !ShowCampaignUI;
|
||||
}
|
||||
break;
|
||||
case TransitionType.LeaveLocation:
|
||||
// not sure why this can happen at an outpost but it apparently can in multiplayer
|
||||
buttonText = TextManager.GetWithVariable("LeaveLocation", "[locationname]", Level.Loaded.StartLocation?.Name ?? "[ERROR]");
|
||||
endRoundButton.Visible = !ForceMapUI && !ShowCampaignUI;
|
||||
break;
|
||||
case TransitionType.ReturnToPreviousLocation:
|
||||
case TransitionType.ReturnToPreviousEmptyLocation:
|
||||
if (Level.Loaded.StartOutpost == null || !Level.Loaded.StartOutpost.DockedTo.Contains(leavingSub))
|
||||
{
|
||||
buttonText = TextManager.GetWithVariable("EnterLocation", "[locationname]", Level.Loaded.StartLocation?.Name ?? "[ERROR]");
|
||||
endRoundButton.Visible = !ForceMapUI && !ShowCampaignUI;
|
||||
}
|
||||
|
||||
break;
|
||||
case TransitionType.None:
|
||||
default:
|
||||
if (Level.Loaded.Type == LevelData.LevelType.Outpost &&
|
||||
(Character.Controlled?.Submarine?.Info.Type == SubmarineType.Player || (Character.Controlled?.CurrentHull?.OutpostModuleTags?.Contains("airlock") ?? false)))
|
||||
{
|
||||
buttonText = TextManager.GetWithVariable("LeaveLocation", "[locationname]", Level.Loaded.StartLocation?.Name ?? "[ERROR]");
|
||||
endRoundButton.Visible = !ForceMapUI && !ShowCampaignUI;
|
||||
}
|
||||
else
|
||||
{
|
||||
endRoundButton.Visible = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (endRoundButton.Visible)
|
||||
{
|
||||
endRoundButton.Text = ToolBox.LimitString(buttonText, endRoundButton.Font, endRoundButton.Rect.Width - 5);
|
||||
if (endRoundButton.Text != buttonText)
|
||||
{
|
||||
endRoundButton.ToolTip = buttonText;
|
||||
}
|
||||
endRoundButton.Enabled = AllowedToEndRound();
|
||||
}
|
||||
|
||||
endRoundButton.DrawManually(spriteBatch);
|
||||
}
|
||||
|
||||
public Task SelectSummaryScreen(RoundSummary roundSummary, LevelData newLevel, bool mirror, Action action)
|
||||
{
|
||||
var roundSummaryScreen = RoundSummaryScreen.Select(overlaySprite, roundSummary);
|
||||
|
||||
GUI.ClearCursorWait();
|
||||
|
||||
var loadTask = Task.Run(async () =>
|
||||
{
|
||||
await Task.Yield();
|
||||
Rand.ThreadId = System.Threading.Thread.CurrentThread.ManagedThreadId;
|
||||
GameMain.GameSession.StartRound(newLevel, mirrorLevel: mirror);
|
||||
Rand.ThreadId = 0;
|
||||
});
|
||||
TaskPool.Add("AsyncCampaignStartRound", loadTask, (t) =>
|
||||
{
|
||||
overlayColor = Color.Transparent;
|
||||
action?.Invoke();
|
||||
});
|
||||
|
||||
return loadTask;
|
||||
}
|
||||
|
||||
partial void NPCInteractProjSpecific(Character npc, Character interactor)
|
||||
{
|
||||
if (npc == null || interactor == null) { return; }
|
||||
|
||||
switch (npc.CampaignInteractionType)
|
||||
{
|
||||
case InteractionType.None:
|
||||
case InteractionType.Talk:
|
||||
return;
|
||||
case InteractionType.Upgrade when !UpgradeManager.CanUpgradeSub():
|
||||
UpgradeManager.CreateUpgradeErrorMessage(TextManager.Get("Dialog.CantUpgrade"), IsSinglePlayer, npc);
|
||||
return;
|
||||
case InteractionType.Crew when GameMain.NetworkMember != null:
|
||||
CampaignUI.CrewManagement.SendCrewState(false);
|
||||
goto default;
|
||||
default:
|
||||
ShowCampaignUI = true;
|
||||
CampaignUI.SelectTab(npc.CampaignInteractionType);
|
||||
CampaignUI.UpgradeStore?.RefreshAll();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void AddToGUIUpdateList()
|
||||
{
|
||||
if (ShowCampaignUI || ForceMapUI)
|
||||
{
|
||||
campaignUIContainer?.AddToGUIUpdateList();
|
||||
if (CampaignUI?.UpgradeStore?.HoveredItem != null)
|
||||
{
|
||||
if (CampaignUI.SelectedTab != InteractionType.Upgrade) { return; }
|
||||
CampaignUI?.UpgradeStore?.ItemInfoFrame.AddToGUIUpdateList(order: 1);
|
||||
}
|
||||
}
|
||||
base.AddToGUIUpdateList();
|
||||
CrewManager.AddToGUIUpdateList();
|
||||
endRoundButton.AddToGUIUpdateList();
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
base.Update(deltaTime);
|
||||
|
||||
if (PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Escape))
|
||||
{
|
||||
GUIMessageBox.MessageBoxes.RemoveAll(mb => mb.UserData is RoundSummary);
|
||||
}
|
||||
|
||||
if (ShowCampaignUI || ForceMapUI)
|
||||
{
|
||||
CampaignUI?.Update(deltaTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
internal partial class CampaignMetadata
|
||||
{
|
||||
private const int MaxDrawnElements = 12;
|
||||
|
||||
public void DebugDraw(SpriteBatch spriteBatch, Vector2 pos, int debugDrawMetadataOffset, string[] ignoredMetadataInfo)
|
||||
{
|
||||
var campaignData = data;
|
||||
foreach (string ignored in ignoredMetadataInfo)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(ignored))
|
||||
{
|
||||
campaignData = campaignData.Where(pair => !pair.Key.StartsWith(ignored)).ToDictionary(i => i.Key, i => i.Value);
|
||||
}
|
||||
}
|
||||
|
||||
int offset = 0;;
|
||||
if (campaignData.Count > 0)
|
||||
{
|
||||
offset = debugDrawMetadataOffset % campaignData.Count;
|
||||
if (offset < 0) { offset += campaignData.Count; }
|
||||
}
|
||||
|
||||
var text = "Campaign metadata:\n";
|
||||
|
||||
int max = 0;
|
||||
for (int i = offset; i < campaignData.Count + offset; i++)
|
||||
{
|
||||
int index = i;
|
||||
if (index >= campaignData.Count) { index -= campaignData.Count; }
|
||||
|
||||
var (key, value) = campaignData.ElementAt(index);
|
||||
|
||||
if (max < MaxDrawnElements)
|
||||
{
|
||||
text += $"{key.ColorizeObject()}: {value.ColorizeObject()}\n";
|
||||
max++;
|
||||
}
|
||||
else
|
||||
{
|
||||
text += "Use arrow keys to scroll";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
text = text.TrimEnd('\n');
|
||||
|
||||
List<RichTextData> richTextDatas = RichTextData.GetRichTextData(text, out text) ?? new List<RichTextData>();
|
||||
|
||||
Vector2 size = GUI.SmallFont.MeasureString(text);
|
||||
Vector2 infoPos = new Vector2(GameMain.GraphicsWidth - size.X - 16, pos.Y + 8);
|
||||
Rectangle infoRect = new Rectangle(infoPos.ToPoint(), size.ToPoint());
|
||||
infoRect.Inflate(8, 8);
|
||||
GUI.DrawRectangle(spriteBatch, infoRect, Color.Black * 0.8f, isFilled: true);
|
||||
GUI.DrawRectangle(spriteBatch, infoRect, Color.White * 0.8f);
|
||||
|
||||
if (richTextDatas.Any())
|
||||
{
|
||||
GUI.DrawStringWithColors(spriteBatch, infoPos, text, Color.White, richTextDatas, font: GUI.SmallFont);
|
||||
}
|
||||
else
|
||||
{
|
||||
GUI.DrawString(spriteBatch, infoPos, text, Color.White, font: GUI.SmallFont);
|
||||
}
|
||||
|
||||
float y = infoRect.Bottom + 16;
|
||||
if (Campaign.Factions != null)
|
||||
{
|
||||
const string factionHeader = "Reputations";
|
||||
Vector2 factionHeaderSize = GUI.SubHeadingFont.MeasureString(factionHeader);
|
||||
Vector2 factionPos = new Vector2(GameMain.GraphicsWidth - (264 / 2) - factionHeaderSize.X / 2, y);
|
||||
|
||||
GUI.DrawString(spriteBatch, factionPos, factionHeader, Color.White, font: GUI.SubHeadingFont);
|
||||
y += factionHeaderSize.Y + 8;
|
||||
|
||||
foreach (Faction faction in Campaign.Factions)
|
||||
{
|
||||
string name = faction.Prefab.Name;
|
||||
Vector2 nameSize = GUI.SmallFont.MeasureString(name);
|
||||
GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - 264, y), name, Color.White, font: GUI.SmallFont);
|
||||
y += nameSize.Y + 5;
|
||||
|
||||
Color color = ToolBox.GradientLerp(faction.Reputation.NormalizedValue, Color.Red, Color.Yellow, Color.LightGreen);
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle(GameMain.GraphicsWidth - 264, (int) y, (int)(faction.Reputation.NormalizedValue * 255), 10), color, isFilled: true);
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle(GameMain.GraphicsWidth - 264, (int) y, 256, 10), Color.White);
|
||||
y += 15;
|
||||
}
|
||||
}
|
||||
|
||||
Location location = Campaign.Map?.CurrentLocation;
|
||||
if (location?.Reputation != null)
|
||||
{
|
||||
string name = Campaign.Map?.CurrentLocation.Name;
|
||||
Vector2 nameSize = GUI.SmallFont.MeasureString(name);
|
||||
GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - 264, y), name, Color.White, font: GUI.SmallFont);
|
||||
y += nameSize.Y + 5;
|
||||
|
||||
float normalizedReputation = MathUtils.InverseLerp(location.Reputation.MinReputation, location.Reputation.MaxReputation, location.Reputation.Value);
|
||||
Color color = ToolBox.GradientLerp(normalizedReputation, Color.Red, Color.Yellow, Color.LightGreen);
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle(GameMain.GraphicsWidth - 264, (int) y, (int)(normalizedReputation * 255), 10), color, isFilled: true);
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle(GameMain.GraphicsWidth - 264, (int) y, 256, 10), Color.White);
|
||||
}
|
||||
|
||||
richTextDatas.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
+603
-68
@@ -1,7 +1,9 @@
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
@@ -11,14 +13,30 @@ namespace Barotrauma
|
||||
{
|
||||
public bool SuppressStateSending = false;
|
||||
|
||||
private UInt16 startWatchmanID, endWatchmanID;
|
||||
private UInt16 pendingSaveID = 1;
|
||||
public UInt16 PendingSaveID
|
||||
{
|
||||
get
|
||||
{
|
||||
return pendingSaveID;
|
||||
}
|
||||
set
|
||||
{
|
||||
pendingSaveID = value;
|
||||
//pending save ID 0 means "no save received yet"
|
||||
//save IDs are always above 0, so we should never be waiting for 0
|
||||
if (pendingSaveID == 0) { pendingSaveID++; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void StartCampaignSetup(IEnumerable<string> saveFiles)
|
||||
{
|
||||
var parent = GameMain.NetLobbyScreen.CampaignSetupFrame;
|
||||
parent.ClearChildren();
|
||||
parent.Visible = true;
|
||||
GameMain.NetLobbyScreen.HighlightMode(2);
|
||||
GameMain.NetLobbyScreen.HighlightMode(
|
||||
GameMain.NetLobbyScreen.ModeList.Content.GetChildIndex(GameMain.NetLobbyScreen.ModeList.Content.GetChildByUserData(GameModePreset.MultiPlayerCampaign)));
|
||||
|
||||
var layout = new GUILayoutGroup(new RectTransform(Vector2.One, parent.RectTransform, Anchor.Center))
|
||||
{
|
||||
@@ -38,7 +56,7 @@ namespace Barotrauma
|
||||
var newCampaignContainer = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.95f), campaignContainer.RectTransform, Anchor.Center), style: null);
|
||||
var loadCampaignContainer = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.95f), campaignContainer.RectTransform, Anchor.Center), style: null);
|
||||
|
||||
var campaignSetupUI = new CampaignSetupUI(true, newCampaignContainer, loadCampaignContainer, null, saveFiles);
|
||||
GameMain.NetLobbyScreen.CampaignSetupUI = new CampaignSetupUI(true, newCampaignContainer, loadCampaignContainer, null, saveFiles);
|
||||
|
||||
var newCampaignButton = new GUIButton(new RectTransform(new Vector2(0.5f, 1.0f), buttonContainer.RectTransform),
|
||||
TextManager.Get("NewCampaign"), style: "GUITabButton")
|
||||
@@ -68,94 +86,488 @@ namespace Barotrauma
|
||||
loadCampaignContainer.Visible = false;
|
||||
|
||||
GUITextBlock.AutoScaleAndNormalize(newCampaignButton.TextBlock, loadCampaignButton.TextBlock);
|
||||
|
||||
GameMain.NetLobbyScreen.CampaignSetupUI.StartNewGame = GameMain.Client.SetupNewCampaign;
|
||||
GameMain.NetLobbyScreen.CampaignSetupUI.LoadGame = GameMain.Client.SetupLoadCampaign;
|
||||
}
|
||||
|
||||
partial void InitProjSpecific()
|
||||
{
|
||||
var buttonContainer = new GUILayoutGroup(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.ButtonAreaTop, GUICanvas.Instance),
|
||||
isHorizontal: true, childAnchor: Anchor.CenterRight)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
campaignSetupUI.StartNewGame = GameMain.Client.SetupNewCampaign;
|
||||
campaignSetupUI.LoadGame = GameMain.Client.SetupLoadCampaign;
|
||||
int buttonHeight = (int)(GUI.Scale * 40);
|
||||
int buttonWidth = GUI.IntScale(200);
|
||||
|
||||
endRoundButton = new GUIButton(HUDLayoutSettings.ToRectTransform(new Rectangle((GameMain.GraphicsWidth / 2) - (buttonWidth / 2), HUDLayoutSettings.ButtonAreaTop.Center.Y - (buttonHeight / 2), buttonWidth, buttonHeight), GUICanvas.Instance),
|
||||
TextManager.Get("EndRound"), textAlignment: Alignment.Center, style: "EndRoundButton")
|
||||
{
|
||||
Pulse = true,
|
||||
TextBlock =
|
||||
{
|
||||
Shadow = true,
|
||||
AutoScaleHorizontal = true
|
||||
},
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
var availableTransition = GetAvailableTransition(out _, out _);
|
||||
if (Character.Controlled != null &&
|
||||
availableTransition == TransitionType.ReturnToPreviousLocation &&
|
||||
Character.Controlled?.Submarine == Level.Loaded?.StartOutpost)
|
||||
{
|
||||
GameMain.Client.RequestStartRound();
|
||||
}
|
||||
else if (Character.Controlled != null &&
|
||||
availableTransition == TransitionType.ProgressToNextLocation &&
|
||||
Character.Controlled?.Submarine == Level.Loaded?.EndOutpost)
|
||||
{
|
||||
GameMain.Client.RequestStartRound();
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowCampaignUI = true;
|
||||
if (CampaignUI == null) { InitCampaignUI(); }
|
||||
CampaignUI.SelectTab(InteractionType.Map);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
buttonContainer.Recalculate();
|
||||
}
|
||||
|
||||
private void InitCampaignUI()
|
||||
{
|
||||
campaignUIContainer = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas, Anchor.Center), style: "InnerGlow", color: Color.Black);
|
||||
CampaignUI = new CampaignUI(this, campaignUIContainer)
|
||||
{
|
||||
StartRound = () =>
|
||||
{
|
||||
GameMain.Client.RequestStartRound();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public override void Start()
|
||||
{
|
||||
base.Start();
|
||||
CoroutineManager.StartCoroutine(DoInitialCameraTransition(), "MultiplayerCampaign.DoInitialCameraTransition");
|
||||
}
|
||||
|
||||
protected override void LoadInitialLevel()
|
||||
{
|
||||
//clients should never call this
|
||||
throw new InvalidOperationException("");
|
||||
}
|
||||
|
||||
|
||||
private IEnumerable<object> DoInitialCameraTransition()
|
||||
{
|
||||
while (GameMain.Instance.LoadingScreenOpen)
|
||||
{
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
|
||||
if (GameMain.Client.LateCampaignJoin)
|
||||
{
|
||||
GameMain.Client.LateCampaignJoin = false;
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
Character prevControlled = Character.Controlled;
|
||||
if (prevControlled?.AIController != null)
|
||||
{
|
||||
prevControlled.AIController.Enabled = false;
|
||||
}
|
||||
GUI.DisableHUD = true;
|
||||
if (IsFirstRound)
|
||||
{
|
||||
Character.Controlled = null;
|
||||
|
||||
if (prevControlled != null)
|
||||
{
|
||||
prevControlled.ClearInputs();
|
||||
}
|
||||
|
||||
overlayColor = Color.LightGray;
|
||||
overlaySprite = Map.CurrentLocation.Type.GetPortrait(Map.CurrentLocation.PortraitId);
|
||||
overlayTextColor = Color.Transparent;
|
||||
overlayText = TextManager.GetWithVariables("campaignstart",
|
||||
new string[] { "xxxx", "yyyy" },
|
||||
new string[] { Map.CurrentLocation.Name, TextManager.Get("submarineclass." + Submarine.MainSub.Info.SubmarineClass) });
|
||||
float fadeInDuration = 1.0f;
|
||||
float textDuration = 10.0f;
|
||||
float timer = 0.0f;
|
||||
while (timer < textDuration)
|
||||
{
|
||||
// Try to grab the controlled here to prevent inputs, assigned late on multiplayer
|
||||
if (Character.Controlled != null)
|
||||
{
|
||||
prevControlled = Character.Controlled;
|
||||
Character.Controlled = null;
|
||||
prevControlled.ClearInputs();
|
||||
}
|
||||
overlayTextColor = Color.Lerp(Color.Transparent, Color.White, (timer - 1.0f) / fadeInDuration);
|
||||
timer = Math.Min(timer + CoroutineManager.DeltaTime, textDuration);
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
var transition = new CameraTransition(prevControlled, GameMain.GameScreen.Cam,
|
||||
null, null,
|
||||
fadeOut: false,
|
||||
duration: 5,
|
||||
startZoom: 1.5f, endZoom: 1.0f)
|
||||
{
|
||||
AllowInterrupt = true,
|
||||
RemoveControlFromCharacter = false
|
||||
};
|
||||
fadeInDuration = 1.0f;
|
||||
timer = 0.0f;
|
||||
overlayTextColor = Color.Transparent;
|
||||
overlayText = "";
|
||||
while (timer < fadeInDuration)
|
||||
{
|
||||
overlayColor = Color.Lerp(Color.LightGray, Color.Transparent, timer / fadeInDuration);
|
||||
timer += CoroutineManager.DeltaTime;
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
overlayColor = Color.Transparent;
|
||||
while (transition.Running)
|
||||
{
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
|
||||
if (prevControlled != null)
|
||||
{
|
||||
Character.Controlled = prevControlled;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var transition = new CameraTransition(Submarine.MainSub, GameMain.GameScreen.Cam,
|
||||
null, null,
|
||||
fadeOut: false,
|
||||
duration: 5,
|
||||
startZoom: 0.5f, endZoom: 1.0f)
|
||||
{
|
||||
AllowInterrupt = true,
|
||||
RemoveControlFromCharacter = true
|
||||
};
|
||||
while (transition.Running)
|
||||
{
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
}
|
||||
|
||||
if (prevControlled != null)
|
||||
{
|
||||
prevControlled.SelectedConstruction = null;
|
||||
if (prevControlled.AIController != null)
|
||||
{
|
||||
prevControlled.AIController.Enabled = true;
|
||||
}
|
||||
}
|
||||
GUI.DisableHUD = false;
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
protected override IEnumerable<object> DoLevelTransition(TransitionType transitionType, LevelData newLevel, Submarine leavingSub, bool mirror, List<TraitorMissionResult> traitorResults = null)
|
||||
{
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
private IEnumerable<object> DoLevelTransition()
|
||||
{
|
||||
SoundPlayer.OverrideMusicType = CrewManager.GetCharacters().Any(c => !c.IsDead) ? "endround" : "crewdead";
|
||||
SoundPlayer.OverrideMusicDuration = 18.0f;
|
||||
|
||||
Level prevLevel = Level.Loaded;
|
||||
|
||||
bool success = CrewManager.GetCharacters().Any(c => !c.IsDead);
|
||||
crewDead = false;
|
||||
|
||||
var continueButton = GameMain.GameSession.RoundSummary?.ContinueButton;
|
||||
if (continueButton != null)
|
||||
{
|
||||
continueButton.Visible = false;
|
||||
}
|
||||
|
||||
Character.Controlled = null;
|
||||
|
||||
yield return new WaitForSeconds(0.1f);
|
||||
|
||||
GameMain.Client.EndCinematic?.Stop();
|
||||
var endTransition = new CameraTransition(Submarine.MainSub, GameMain.GameScreen.Cam, null,
|
||||
Alignment.Center,
|
||||
fadeOut: false,
|
||||
duration: EndTransitionDuration);
|
||||
GameMain.Client.EndCinematic = endTransition;
|
||||
|
||||
Location portraitLocation = Map?.SelectedLocation ?? Map?.CurrentLocation ?? Level.Loaded?.StartLocation;
|
||||
if (portraitLocation != null)
|
||||
{
|
||||
overlaySprite = portraitLocation.Type.GetPortrait(portraitLocation.PortraitId);
|
||||
}
|
||||
float fadeOutDuration = endTransition.Duration;
|
||||
float t = 0.0f;
|
||||
while (t < fadeOutDuration || endTransition.Running)
|
||||
{
|
||||
t += CoroutineManager.UnscaledDeltaTime;
|
||||
overlayColor = Color.Lerp(Color.Transparent, Color.White, t / fadeOutDuration);
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
overlayColor = Color.White;
|
||||
yield return CoroutineStatus.Running;
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
//wait for the new level to be loaded
|
||||
DateTime timeOut = DateTime.Now + new TimeSpan(0, 0, seconds: 30);
|
||||
while (Level.Loaded == prevLevel || Level.Loaded == null)
|
||||
{
|
||||
if (DateTime.Now > timeOut || Screen.Selected != GameMain.GameScreen) { break; }
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
|
||||
endTransition.Stop();
|
||||
overlayColor = Color.Transparent;
|
||||
|
||||
if (DateTime.Now > timeOut) { GameMain.NetLobbyScreen.Select(); }
|
||||
if (!(Screen.Selected is RoundSummaryScreen))
|
||||
{
|
||||
if (continueButton != null)
|
||||
{
|
||||
continueButton.Visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (CoroutineManager.IsCoroutineRunning("LevelTransition") || Level.Loaded == null) { return; }
|
||||
|
||||
if (ShowCampaignUI || ForceMapUI)
|
||||
{
|
||||
if (CampaignUI == null) { InitCampaignUI(); }
|
||||
Character.DisableControls = true;
|
||||
}
|
||||
|
||||
base.Update(deltaTime);
|
||||
|
||||
if (startWatchmanID > 0 && startWatchman == null)
|
||||
if (PlayerInput.RightButtonClicked() ||
|
||||
PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Escape))
|
||||
{
|
||||
startWatchman = Entity.FindEntityByID(startWatchmanID) as Character;
|
||||
if (startWatchman != null) { InitializeWatchman(startWatchman); }
|
||||
ShowCampaignUI = false;
|
||||
if (GUIMessageBox.VisibleBox?.UserData is RoundSummary roundSummary &&
|
||||
roundSummary.ContinueButton != null &&
|
||||
roundSummary.ContinueButton.Visible)
|
||||
{
|
||||
GUIMessageBox.MessageBoxes.Remove(GUIMessageBox.VisibleBox);
|
||||
}
|
||||
}
|
||||
if (endWatchmanID > 0 && endWatchman == null)
|
||||
|
||||
if (!GUI.DisableHUD && !GUI.DisableUpperHUD)
|
||||
{
|
||||
endWatchman = Entity.FindEntityByID(endWatchmanID) as Character;
|
||||
if (endWatchman != null) { InitializeWatchman(endWatchman); }
|
||||
endRoundButton.UpdateManually(deltaTime);
|
||||
if (CoroutineManager.IsCoroutineRunning("LevelTransition") || ForceMapUI) { return; }
|
||||
}
|
||||
|
||||
if (Level.Loaded.Type == LevelData.LevelType.Outpost)
|
||||
{
|
||||
if (wasDocked)
|
||||
{
|
||||
var connectedSubs = Submarine.MainSub.GetConnectedSubs();
|
||||
bool isDocked = Level.Loaded.StartOutpost != null && connectedSubs.Contains(Level.Loaded.StartOutpost);
|
||||
if (!isDocked)
|
||||
{
|
||||
//undocked from outpost, need to choose a destination
|
||||
ForceMapUI = true;
|
||||
if (CampaignUI == null) { InitCampaignUI(); }
|
||||
CampaignUI.SelectTab(InteractionType.Map);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//wasn't initially docked (sub doesn't have a docking port?)
|
||||
// -> choose a destination when the sub is far enough from the start outpost
|
||||
if (!Submarine.MainSub.AtStartPosition)
|
||||
{
|
||||
ForceMapUI = true;
|
||||
if (CampaignUI == null) { InitCampaignUI(); }
|
||||
CampaignUI.SelectTab(InteractionType.Map);
|
||||
}
|
||||
}
|
||||
|
||||
if (CampaignUI == null) { InitCampaignUI(); }
|
||||
}
|
||||
}
|
||||
|
||||
protected override void WatchmanInteract(Character watchman, Character interactor)
|
||||
public override void End(TransitionType transitionType = TransitionType.None)
|
||||
{
|
||||
if ((watchman.Submarine == Level.Loaded.StartOutpost && !Submarine.MainSub.AtStartPosition) ||
|
||||
(watchman.Submarine == Level.Loaded.EndOutpost && !Submarine.MainSub.AtEndPosition))
|
||||
base.End(transitionType);
|
||||
ForceMapUI = ShowCampaignUI = false;
|
||||
UpgradeManager.CanUpgrade = true;
|
||||
|
||||
// remove all event dialogue boxes
|
||||
GUIMessageBox.MessageBoxes.ForEachMod(mb =>
|
||||
{
|
||||
return;
|
||||
if (mb is GUIMessageBox msgBox)
|
||||
{
|
||||
if (mb.UserData is Pair<string, ushort> pair && pair.First.Equals("conversationaction", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
msgBox.Close();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (transitionType == TransitionType.End)
|
||||
{
|
||||
EndCampaign();
|
||||
}
|
||||
else
|
||||
{
|
||||
IsFirstRound = false;
|
||||
CoroutineManager.StartCoroutine(DoLevelTransition(), "LevelTransition");
|
||||
}
|
||||
}
|
||||
|
||||
protected override void EndCampaignProjSpecific()
|
||||
{
|
||||
if (GUIMessageBox.VisibleBox?.UserData is RoundSummary roundSummary)
|
||||
{
|
||||
GUIMessageBox.MessageBoxes.Remove(GUIMessageBox.VisibleBox);
|
||||
}
|
||||
CoroutineManager.StartCoroutine(DoEndCampaignCameraTransition(), "DoEndCampaignCameraTransition");
|
||||
GameMain.CampaignEndScreen.OnFinished = () =>
|
||||
{
|
||||
GameMain.NetLobbyScreen.Select();
|
||||
if (GameMain.NetLobbyScreen.ContinueCampaignButton != null) { GameMain.NetLobbyScreen.ContinueCampaignButton.Enabled = false; }
|
||||
if (GameMain.NetLobbyScreen.QuitCampaignButton != null) { GameMain.NetLobbyScreen.QuitCampaignButton.Enabled = false; }
|
||||
};
|
||||
}
|
||||
|
||||
private IEnumerable<object> DoEndCampaignCameraTransition()
|
||||
{
|
||||
Character controlled = Character.Controlled;
|
||||
if (controlled != null)
|
||||
{
|
||||
controlled.AIController.Enabled = false;
|
||||
}
|
||||
|
||||
if (GUIMessageBox.MessageBoxes.Any(mbox => mbox.UserData as string == "watchmanprompt"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
GUI.DisableHUD = true;
|
||||
ISpatialEntity endObject = Level.Loaded.LevelObjectManager.GetAllObjects().FirstOrDefault(obj => obj.Prefab.SpawnPos == LevelObjectPrefab.SpawnPosType.LevelEnd);
|
||||
var transition = new CameraTransition(endObject ?? Submarine.MainSub, GameMain.GameScreen.Cam,
|
||||
null, Alignment.Center,
|
||||
fadeOut: true,
|
||||
duration: 10,
|
||||
startZoom: null, endZoom: 0.2f);
|
||||
|
||||
if (GameMain.Client != null && interactor == Character.Controlled)
|
||||
while (transition.Running)
|
||||
{
|
||||
var msgBox = new GUIMessageBox("", TextManager.GetWithVariable("CampaignEnterOutpostPrompt", "[locationname]",
|
||||
Submarine.MainSub.AtStartPosition ? Map.CurrentLocation.Name : Map.SelectedLocation.Name),
|
||||
new string[] { TextManager.Get("Yes"), TextManager.Get("No") })
|
||||
{
|
||||
UserData = "watchmanprompt"
|
||||
};
|
||||
msgBox.Buttons[0].OnClicked = (btn, userdata) =>
|
||||
{
|
||||
GameMain.Client.RequestRoundEnd();
|
||||
return true;
|
||||
};
|
||||
msgBox.Buttons[0].OnClicked += msgBox.Close;
|
||||
msgBox.Buttons[1].OnClicked += msgBox.Close;
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
GameMain.CampaignEndScreen.Select();
|
||||
GUI.DisableHUD = false;
|
||||
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
public void ClientWrite(IWriteMessage msg)
|
||||
{
|
||||
System.Diagnostics.Debug.Assert(map.Locations.Count < UInt16.MaxValue);
|
||||
|
||||
msg.Write(map.CurrentLocationIndex == -1 ? UInt16.MaxValue : (UInt16)map.CurrentLocationIndex);
|
||||
msg.Write(map.SelectedLocationIndex == -1 ? UInt16.MaxValue : (UInt16)map.SelectedLocationIndex);
|
||||
msg.Write(map.SelectedMissionIndex == -1 ? byte.MaxValue : (byte)map.SelectedMissionIndex);
|
||||
msg.Write(PurchasedHullRepairs);
|
||||
msg.Write(PurchasedItemRepairs);
|
||||
msg.Write(PurchasedLostShuttles);
|
||||
|
||||
msg.Write((UInt16)CargoManager.ItemsInBuyCrate.Count);
|
||||
foreach (PurchasedItem pi in CargoManager.ItemsInBuyCrate)
|
||||
{
|
||||
msg.Write(pi.ItemPrefab.Identifier);
|
||||
msg.WriteRangedInteger(pi.Quantity, 0, 100);
|
||||
}
|
||||
|
||||
msg.Write((UInt16)CargoManager.PurchasedItems.Count);
|
||||
foreach (PurchasedItem pi in CargoManager.PurchasedItems)
|
||||
{
|
||||
msg.Write(pi.ItemPrefab.Identifier);
|
||||
msg.WriteRangedInteger(pi.Quantity, 0, 100);
|
||||
}
|
||||
|
||||
msg.Write((UInt16)CargoManager.SoldItems.Count);
|
||||
foreach (SoldItem si in CargoManager.SoldItems)
|
||||
{
|
||||
msg.Write(si.ItemPrefab.Identifier);
|
||||
msg.Write((UInt16)si.ID);
|
||||
msg.Write(si.Removed);
|
||||
msg.Write(si.SellerID);
|
||||
}
|
||||
|
||||
msg.Write((ushort)UpgradeManager.PurchasedUpgrades.Count);
|
||||
foreach (var (prefab, category, level) in UpgradeManager.PurchasedUpgrades)
|
||||
{
|
||||
msg.Write(prefab.Identifier);
|
||||
msg.Write(category.Identifier);
|
||||
msg.Write((byte)level);
|
||||
}
|
||||
}
|
||||
|
||||
//static because we may need to instantiate the campaign if it hasn't been done yet
|
||||
public static void ClientRead(IReadMessage msg)
|
||||
{
|
||||
byte campaignID = msg.ReadByte();
|
||||
UInt16 updateID = msg.ReadUInt16();
|
||||
UInt16 saveID = msg.ReadUInt16();
|
||||
string mapSeed = msg.ReadString();
|
||||
UInt16 currentLocIndex = msg.ReadUInt16();
|
||||
UInt16 selectedLocIndex = msg.ReadUInt16();
|
||||
byte selectedMissionIndex = msg.ReadByte();
|
||||
bool isFirstRound = msg.ReadBoolean();
|
||||
byte campaignID = msg.ReadByte();
|
||||
UInt16 updateID = msg.ReadUInt16();
|
||||
UInt16 saveID = msg.ReadUInt16();
|
||||
string mapSeed = msg.ReadString();
|
||||
UInt16 currentLocIndex = msg.ReadUInt16();
|
||||
UInt16 selectedLocIndex = msg.ReadUInt16();
|
||||
byte selectedMissionIndex = msg.ReadByte();
|
||||
float? reputation = null;
|
||||
if (msg.ReadBoolean()) { reputation = msg.ReadSingle(); }
|
||||
|
||||
Dictionary<string, float> factionReps = new Dictionary<string, float>();
|
||||
byte factionsCount = msg.ReadByte();
|
||||
for (int i = 0; i < factionsCount; i++)
|
||||
{
|
||||
factionReps.Add(msg.ReadString(), msg.ReadSingle());
|
||||
}
|
||||
|
||||
UInt16 startWatchmanID = msg.ReadUInt16();
|
||||
UInt16 endWatchmanID = msg.ReadUInt16();
|
||||
bool forceMapUI = msg.ReadBoolean();
|
||||
|
||||
int money = msg.ReadInt32();
|
||||
bool purchasedHullRepairs = msg.ReadBoolean();
|
||||
bool purchasedItemRepairs = msg.ReadBoolean();
|
||||
bool purchasedLostShuttles = msg.ReadBoolean();
|
||||
bool purchasedHullRepairs = msg.ReadBoolean();
|
||||
bool purchasedItemRepairs = msg.ReadBoolean();
|
||||
bool purchasedLostShuttles = msg.ReadBoolean();
|
||||
|
||||
byte missionCount = msg.ReadByte();
|
||||
List<Pair<string, byte>> availableMissions = new List<Pair<string, byte>>();
|
||||
for (int i = 0; i < missionCount; i++)
|
||||
{
|
||||
string missionIdentifier = msg.ReadString();
|
||||
byte connectionIndex = msg.ReadByte();
|
||||
availableMissions.Add(new Pair<string, byte>(missionIdentifier, connectionIndex));
|
||||
}
|
||||
|
||||
UInt16? storeBalance = null;
|
||||
if (msg.ReadBoolean())
|
||||
{
|
||||
storeBalance = msg.ReadUInt16();
|
||||
}
|
||||
|
||||
UInt16 buyCrateItemCount = msg.ReadUInt16();
|
||||
List<PurchasedItem> buyCrateItems = new List<PurchasedItem>();
|
||||
for (int i = 0; i < buyCrateItemCount; i++)
|
||||
{
|
||||
string itemPrefabIdentifier = msg.ReadString();
|
||||
int itemQuantity = msg.ReadRangedInteger(0, CargoManager.MaxQuantity);
|
||||
buyCrateItems.Add(new PurchasedItem(ItemPrefab.Prefabs[itemPrefabIdentifier], itemQuantity));
|
||||
}
|
||||
|
||||
UInt16 purchasedItemCount = msg.ReadUInt16();
|
||||
List<PurchasedItem> purchasedItems = new List<PurchasedItem>();
|
||||
@@ -166,65 +578,129 @@ namespace Barotrauma
|
||||
purchasedItems.Add(new PurchasedItem(ItemPrefab.Prefabs[itemPrefabIdentifier], itemQuantity));
|
||||
}
|
||||
|
||||
UInt16 soldItemCount = msg.ReadUInt16();
|
||||
List<SoldItem> soldItems = new List<SoldItem>();
|
||||
for (int i = 0; i < soldItemCount; i++)
|
||||
{
|
||||
string itemPrefabIdentifier = msg.ReadString();
|
||||
UInt16 id = msg.ReadUInt16();
|
||||
bool removed = msg.ReadBoolean();
|
||||
byte sellerId = msg.ReadByte();
|
||||
soldItems.Add(new SoldItem(ItemPrefab.Prefabs[itemPrefabIdentifier], id, removed, sellerId));
|
||||
}
|
||||
|
||||
ushort pendingUpgradeCount = msg.ReadUInt16();
|
||||
List<PurchasedUpgrade> pendingUpgrades = new List<PurchasedUpgrade>();
|
||||
for (int i = 0; i < pendingUpgradeCount; i++)
|
||||
{
|
||||
string upgradeIdentifier = msg.ReadString();
|
||||
UpgradePrefab prefab = UpgradePrefab.Find(upgradeIdentifier);
|
||||
string categoryIdentifier = msg.ReadString();
|
||||
UpgradeCategory category = UpgradeCategory.Find(categoryIdentifier);
|
||||
int upgradeLevel = msg.ReadByte();
|
||||
if (prefab == null || category == null) { continue; }
|
||||
pendingUpgrades.Add(new PurchasedUpgrade(prefab, category, upgradeLevel));
|
||||
}
|
||||
|
||||
bool hasCharacterData = msg.ReadBoolean();
|
||||
CharacterInfo myCharacterInfo = null;
|
||||
if (hasCharacterData)
|
||||
{
|
||||
myCharacterInfo = CharacterInfo.ClientRead(CharacterPrefab.HumanSpeciesName, msg);
|
||||
}
|
||||
|
||||
MultiPlayerCampaign campaign = GameMain.GameSession?.GameMode as MultiPlayerCampaign;
|
||||
if (campaign == null || campaignID != campaign.CampaignID)
|
||||
|
||||
if (!(GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign) || campaignID != campaign.CampaignID)
|
||||
{
|
||||
string savePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Multiplayer);
|
||||
|
||||
GameMain.GameSession = new GameSession(null, savePath,
|
||||
GameModePreset.List.Find(g => g.Identifier == "multiplayercampaign"));
|
||||
|
||||
campaign = ((MultiPlayerCampaign)GameMain.GameSession.GameMode);
|
||||
GameMain.GameSession = new GameSession(null, savePath, GameModePreset.MultiPlayerCampaign, mapSeed);
|
||||
campaign = (MultiPlayerCampaign)GameMain.GameSession.GameMode;
|
||||
campaign.CampaignID = campaignID;
|
||||
campaign.GenerateMap(mapSeed);
|
||||
GameMain.NetLobbyScreen.ToggleCampaignMode(true);
|
||||
}
|
||||
|
||||
|
||||
//server has a newer save file
|
||||
if (NetIdUtils.IdMoreRecent(saveID, campaign.PendingSaveID))
|
||||
{
|
||||
/*//stop any active campaign save transfers, they're outdated now
|
||||
List<FileReceiver.FileTransferIn> saveTransfers =
|
||||
GameMain.Client.FileReceiver.ActiveTransfers.FindAll(t => t.FileType == FileTransferType.CampaignSave);
|
||||
|
||||
foreach (var transfer in saveTransfers)
|
||||
{
|
||||
GameMain.Client.FileReceiver.StopTransfer(transfer);
|
||||
}
|
||||
|
||||
GameMain.Client.RequestFile(FileTransferType.CampaignSave, null, null);*/
|
||||
campaign.PendingSaveID = saveID;
|
||||
}
|
||||
|
||||
if (NetIdUtils.IdMoreRecent(updateID, campaign.lastUpdateID))
|
||||
{
|
||||
campaign.SuppressStateSending = true;
|
||||
campaign.IsFirstRound = isFirstRound;
|
||||
|
||||
//we need to have the latest save file to display location/mission/store
|
||||
if (campaign.LastSaveID == saveID)
|
||||
{
|
||||
campaign.ForceMapUI = forceMapUI;
|
||||
|
||||
UpgradeStore.WaitForServerUpdate = false;
|
||||
|
||||
campaign.Map.SetLocation(currentLocIndex == UInt16.MaxValue ? -1 : currentLocIndex);
|
||||
campaign.Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex);
|
||||
campaign.Map.SelectMission(selectedMissionIndex);
|
||||
campaign.CargoManager.SetItemsInBuyCrate(buyCrateItems);
|
||||
campaign.CargoManager.SetPurchasedItems(purchasedItems);
|
||||
campaign.CargoManager.SetSoldItems(soldItems);
|
||||
if (storeBalance.HasValue) { campaign.Map.CurrentLocation.StoreCurrentBalance = storeBalance.Value; }
|
||||
campaign.UpgradeManager.SetPendingUpgrades(pendingUpgrades);
|
||||
campaign.UpgradeManager.PurchasedUpgrades.Clear();
|
||||
|
||||
foreach (var (identifier, rep) in factionReps)
|
||||
{
|
||||
Faction faction = campaign.Factions.FirstOrDefault(f => f.Prefab.Identifier.Equals(identifier, StringComparison.OrdinalIgnoreCase));
|
||||
if (faction?.Reputation != null)
|
||||
{
|
||||
faction.Reputation.Value = rep;
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError($"Received an update for a faction that doesn't exist \"{identifier}\".");
|
||||
}
|
||||
}
|
||||
|
||||
if (reputation.HasValue)
|
||||
{
|
||||
campaign.Map.CurrentLocation.Reputation.Value = reputation.Value;
|
||||
campaign?.CampaignUI?.UpgradeStore?.RefreshAll();
|
||||
}
|
||||
|
||||
foreach (var availableMission in availableMissions)
|
||||
{
|
||||
MissionPrefab missionPrefab = MissionPrefab.List.Find(mp => mp.Identifier == availableMission.First);
|
||||
if (missionPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error when receiving campaign data from the server: mission prefab \"{availableMission.First}\" not found.");
|
||||
continue;
|
||||
}
|
||||
if (availableMission.Second < 0 || availableMission.Second >= campaign.Map.CurrentLocation.Connections.Count)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error when receiving campaign data from the server: connection index for mission \"{availableMission.First}\" out of range (index: {availableMission.Second}, current location: {campaign.Map.CurrentLocation.Name}, connections: {campaign.Map.CurrentLocation.Connections.Count}).");
|
||||
continue;
|
||||
}
|
||||
LocationConnection connection = campaign.Map.CurrentLocation.Connections[availableMission.Second];
|
||||
campaign.Map.CurrentLocation.UnlockMission(missionPrefab, connection);
|
||||
}
|
||||
|
||||
GameMain.NetLobbyScreen.ToggleCampaignMode(true);
|
||||
}
|
||||
|
||||
campaign.startWatchmanID = startWatchmanID;
|
||||
campaign.endWatchmanID = endWatchmanID;
|
||||
bool shouldRefresh = campaign.Money != money ||
|
||||
campaign.PurchasedHullRepairs != purchasedHullRepairs ||
|
||||
campaign.PurchasedItemRepairs != purchasedItemRepairs ||
|
||||
campaign.PurchasedLostShuttles != purchasedLostShuttles;
|
||||
|
||||
campaign.Money = money;
|
||||
campaign.PurchasedHullRepairs = purchasedHullRepairs;
|
||||
campaign.PurchasedItemRepairs = purchasedItemRepairs;
|
||||
campaign.PurchasedLostShuttles = purchasedLostShuttles;
|
||||
|
||||
if (shouldRefresh)
|
||||
{
|
||||
campaign?.CampaignUI?.UpgradeStore?.RefreshAll();
|
||||
}
|
||||
|
||||
if (myCharacterInfo != null)
|
||||
{
|
||||
GameMain.Client.CharacterInfo = myCharacterInfo;
|
||||
@@ -240,9 +716,68 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientReadCrew(IReadMessage msg)
|
||||
{
|
||||
ushort availableHireLength = msg.ReadUInt16();
|
||||
List<CharacterInfo> availableHires = new List<CharacterInfo>();
|
||||
for (int i = 0; i < availableHireLength; i++)
|
||||
{
|
||||
CharacterInfo hire = CharacterInfo.ClientRead("human", msg);
|
||||
hire.Salary = msg.ReadInt32();
|
||||
availableHires.Add(hire);
|
||||
}
|
||||
|
||||
ushort pendingHireLength = msg.ReadUInt16();
|
||||
List<int> pendingHires = new List<int>();
|
||||
for (int i = 0; i < pendingHireLength; i++)
|
||||
{
|
||||
pendingHires.Add(msg.ReadInt32());
|
||||
}
|
||||
|
||||
bool validateHires = msg.ReadBoolean();
|
||||
|
||||
bool fireCharacter = msg.ReadBoolean();
|
||||
|
||||
int firedIdentifier = -1;
|
||||
if (fireCharacter) { firedIdentifier = msg.ReadInt32(); }
|
||||
|
||||
if (fireCharacter)
|
||||
{
|
||||
CharacterInfo firedCharacter = CrewManager.CharacterInfos.FirstOrDefault(info => info.GetIdentifier() == firedIdentifier);
|
||||
// this one might and is allowed to be null since the character is already fired on the original sender's game
|
||||
if (firedCharacter != null) { CrewManager.FireCharacter(firedCharacter); }
|
||||
}
|
||||
|
||||
if (map?.CurrentLocation?.HireManager != null && CampaignUI?.CrewManagement != null)
|
||||
{
|
||||
CampaignUI?.CrewManagement?.SetHireables(map.CurrentLocation, availableHires);
|
||||
if (validateHires) { CampaignUI?.CrewManagement.ValidatePendingHires(); }
|
||||
CampaignUI?.CrewManagement?.SetPendingHires(pendingHires, map?.CurrentLocation);
|
||||
if (fireCharacter) { CampaignUI?.CrewManagement.UpdateCrew(); }
|
||||
}
|
||||
}
|
||||
|
||||
public override void Save(XElement element)
|
||||
{
|
||||
//do nothing, the clients get the save files from the server
|
||||
}
|
||||
|
||||
public void LoadState(string filePath)
|
||||
{
|
||||
DebugConsole.Log($"Loading save file for an existing game session ({filePath})");
|
||||
SaveUtil.DecompressToDirectory(filePath, SaveUtil.TempPath, null);
|
||||
|
||||
string gamesessionDocPath = Path.Combine(SaveUtil.TempPath, "gamesession.xml");
|
||||
XDocument doc = XMLExtensions.TryLoadXml(gamesessionDocPath);
|
||||
if (doc == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to load the state of a multiplayer campaign. Could not open the file \"{gamesessionDocPath}\".");
|
||||
return;
|
||||
}
|
||||
Load(doc.Root.Element("MultiPlayerCampaign"));
|
||||
SubmarineInfo selectedSub;
|
||||
GameMain.GameSession.OwnedSubmarines = SaveUtil.LoadOwnedSubmarines(doc, out selectedSub);
|
||||
GameMain.GameSession.SubmarineInfo = selectedSub;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+607
-380
File diff suppressed because it is too large
Load Diff
@@ -1,80 +0,0 @@
|
||||
using Barotrauma.Tutorials;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class SubTestMode : GameMode
|
||||
{
|
||||
public SubTestMode(GameModePreset preset, object param)
|
||||
: base(preset, param)
|
||||
{
|
||||
foreach (JobPrefab jobPrefab in JobPrefab.Prefabs)
|
||||
{
|
||||
for (int i = 0; i < jobPrefab.InitialCount; i++)
|
||||
{
|
||||
var variant = Rand.Range(0, jobPrefab.Variants);
|
||||
CrewManager.AddCharacterInfo(new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: jobPrefab, variant: variant));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Start()
|
||||
{
|
||||
base.Start();
|
||||
|
||||
isRunning = true;
|
||||
CrewManager.InitSinglePlayerRound();
|
||||
|
||||
Submarine.MainSub.SetPosition(Vector2.Zero);
|
||||
}
|
||||
|
||||
public override void Draw(SpriteBatch spriteBatch)
|
||||
{
|
||||
if (!isRunning|| GUI.DisableHUD || GUI.DisableUpperHUD) return;
|
||||
|
||||
if (Submarine.MainSub == null) return;
|
||||
}
|
||||
|
||||
public override void AddToGUIUpdateList()
|
||||
{
|
||||
if (!isRunning) return;
|
||||
|
||||
base.AddToGUIUpdateList();
|
||||
CrewManager.AddToGUIUpdateList();
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (!isRunning) { return; }
|
||||
|
||||
base.Update(deltaTime);
|
||||
}
|
||||
|
||||
public override void End(string endMessage = "")
|
||||
{
|
||||
isRunning = false;
|
||||
|
||||
GameMain.GameSession.EndRound("");
|
||||
|
||||
CrewManager.EndRound();
|
||||
|
||||
Submarine.Unload();
|
||||
|
||||
GameMain.SubEditorScreen.Select();
|
||||
}
|
||||
|
||||
private bool EndRound(Submarine leavingSub)
|
||||
{
|
||||
isRunning = false;
|
||||
|
||||
End("");
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class TestGameMode : GameMode
|
||||
{
|
||||
public Action OnRoundEnd;
|
||||
|
||||
public TestGameMode(GameModePreset preset) : base(preset)
|
||||
{
|
||||
foreach (JobPrefab jobPrefab in JobPrefab.Prefabs)
|
||||
{
|
||||
for (int i = 0; i < jobPrefab.InitialCount; i++)
|
||||
{
|
||||
var variant = Rand.Range(0, jobPrefab.Variants);
|
||||
CrewManager.AddCharacterInfo(new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: jobPrefab, variant: variant));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Start()
|
||||
{
|
||||
base.Start();
|
||||
|
||||
CrewManager.InitSinglePlayerRound();
|
||||
}
|
||||
|
||||
public override void End(CampaignMode.TransitionType transitionType = CampaignMode.TransitionType.None)
|
||||
{
|
||||
OnRoundEnd?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -614,7 +614,7 @@ namespace Barotrauma.Tutorials
|
||||
GameMain.GameScreen.Cam.TargetPos = Vector2.Zero;
|
||||
GameMain.LightManager.LosEnabled = false;
|
||||
|
||||
var cinematic = new RoundEndCinematic(Submarine.MainSub, GameMain.GameScreen.Cam, 5.0f);
|
||||
var cinematic = new CameraTransition(Submarine.MainSub, GameMain.GameScreen.Cam, Alignment.CenterLeft, Alignment.CenterRight, duration: 5.0f);
|
||||
|
||||
while (cinematic.Running)
|
||||
{
|
||||
|
||||
+5
-1
@@ -101,6 +101,7 @@ namespace Barotrauma.Tutorials
|
||||
tutorial_submarineDoorLight = Item.ItemList.Find(i => i.HasTag("tutorial_submarinedoorlight")).GetComponent<LightComponent>();
|
||||
var medicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("medicaldoctor"));
|
||||
captain_medic = Character.Create(medicInfo, captain_medicSpawnPos, "medicaldoctor");
|
||||
captain_medic.TeamID = Character.TeamType.Team1;
|
||||
captain_medic.GiveJobItems(null);
|
||||
captain_medic.CanSpeak = captain_medic.AIController.Enabled = false;
|
||||
SetDoorAccess(tutorial_submarineDoor, tutorial_submarineDoorLight, false);
|
||||
@@ -123,14 +124,17 @@ namespace Barotrauma.Tutorials
|
||||
|
||||
var mechanicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("mechanic"));
|
||||
captain_mechanic = Character.Create(mechanicInfo, WayPoint.GetRandom(SpawnType.Human, mechanicInfo.Job, Submarine.MainSub).WorldPosition, "mechanic");
|
||||
captain_mechanic.TeamID = Character.TeamType.Team1;
|
||||
captain_mechanic.GiveJobItems();
|
||||
|
||||
var securityInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("securityofficer"));
|
||||
captain_security = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, securityInfo.Job, Submarine.MainSub).WorldPosition, "securityofficer");
|
||||
captain_security.TeamID = Character.TeamType.Team1;
|
||||
captain_security.GiveJobItems();
|
||||
|
||||
var engineerInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("engineer"));
|
||||
captain_engineer = Character.Create(engineerInfo, WayPoint.GetRandom(SpawnType.Human, engineerInfo.Job, Submarine.MainSub).WorldPosition, "engineer");
|
||||
captain_engineer.TeamID = Character.TeamType.Team1;
|
||||
captain_engineer.GiveJobItems();
|
||||
|
||||
captain_mechanic.CanSpeak = captain_security.CanSpeak = captain_engineer.CanSpeak = false;
|
||||
@@ -195,7 +199,7 @@ namespace Barotrauma.Tutorials
|
||||
// GameMain.GameSession.CrewManager.HighlightOrderButton(captain_security, "operateweapons", highlightColor, new Vector2(5, 5));
|
||||
HighlightOrderOption("fireatwill");
|
||||
}
|
||||
while (!HasOrder(captain_security, "operateweapons", "fireatwill"));
|
||||
while (!HasOrder(captain_security, "operateweapons"));
|
||||
RemoveCompletedObjective(segments[2]);
|
||||
yield return new WaitForSeconds(4f, false);
|
||||
TriggerTutorialSegment(3, GameMain.Config.KeyBindText(InputType.Command));
|
||||
|
||||
+5
@@ -80,6 +80,7 @@ namespace Barotrauma.Tutorials
|
||||
|
||||
var assistantInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("assistant"));
|
||||
patient1 = Character.Create(assistantInfo, patientHull1.WorldPosition, "1");
|
||||
patient1.TeamID = Character.TeamType.Team1;
|
||||
patient1.GiveJobItems(null);
|
||||
patient1.CanSpeak = false;
|
||||
patient1.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.Burn, 45.0f) }, stun: 0, playSound: false);
|
||||
@@ -87,22 +88,26 @@ namespace Barotrauma.Tutorials
|
||||
|
||||
assistantInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("assistant"));
|
||||
patient2 = Character.Create(assistantInfo, patientHull2.WorldPosition, "2");
|
||||
patient2.TeamID = Character.TeamType.Team1;
|
||||
patient2.GiveJobItems(null);
|
||||
patient2.CanSpeak = false;
|
||||
patient2.AIController.Enabled = false;
|
||||
|
||||
var mechanicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("engineer"));
|
||||
var subPatient1 = Character.Create(mechanicInfo, WayPoint.GetRandom(SpawnType.Human, mechanicInfo.Job, Submarine.MainSub).WorldPosition, "3");
|
||||
subPatient1.TeamID = Character.TeamType.Team1;
|
||||
subPatient1.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.Burn, 40.0f) }, stun: 0, playSound: false);
|
||||
subPatients.Add(subPatient1);
|
||||
|
||||
var securityInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("securityofficer"));
|
||||
var subPatient2 = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, securityInfo.Job, Submarine.MainSub).WorldPosition, "3");
|
||||
subPatient2.TeamID = Character.TeamType.Team1;
|
||||
subPatient2.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.InternalDamage, 40.0f) }, stun: 0, playSound: false);
|
||||
subPatients.Add(subPatient2);
|
||||
|
||||
var engineerInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("engineer"));
|
||||
var subPatient3 = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, engineerInfo.Job, Submarine.MainSub).WorldPosition, "3");
|
||||
subPatient3.TeamID = Character.TeamType.Team1;
|
||||
subPatient3.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.Burn, 20.0f) }, stun: 0, playSound: false);
|
||||
subPatients.Add(subPatient3);
|
||||
|
||||
|
||||
+15
-10
@@ -376,7 +376,7 @@ namespace Barotrauma.Tutorials
|
||||
}
|
||||
}
|
||||
yield return null;
|
||||
} while (!engineer_brokenJunctionBox.IsFullCondition); // Wait until repaired
|
||||
} while (engineer_brokenJunctionBox.Condition < repairableJunctionBoxComponent.RepairThreshold); // Wait until repaired
|
||||
SetHighlight(engineer_brokenJunctionBox, false);
|
||||
RemoveCompletedObjective(segments[2]);
|
||||
SetDoorAccess(engineer_thirdDoor, engineer_thirdDoorLight, true);
|
||||
@@ -408,15 +408,20 @@ namespace Barotrauma.Tutorials
|
||||
yield return new WaitForSeconds(2f, false);
|
||||
TriggerTutorialSegment(4); // Repair junction box
|
||||
while (ContentRunning) yield return null;
|
||||
SetHighlight(engineer_submarineJunctionBox_1, true);
|
||||
SetHighlight(engineer_submarineJunctionBox_2, true);
|
||||
SetHighlight(engineer_submarineJunctionBox_3, true);
|
||||
engineer.AddActiveObjectiveEntity(engineer_submarineJunctionBox_1, engineer_repairIcon, engineer_repairIconColor);
|
||||
engineer.AddActiveObjectiveEntity(engineer_submarineJunctionBox_2, engineer_repairIcon, engineer_repairIconColor);
|
||||
engineer.AddActiveObjectiveEntity(engineer_submarineJunctionBox_3, engineer_repairIcon, engineer_repairIconColor);
|
||||
SetHighlight(engineer_submarineJunctionBox_1, true);
|
||||
SetHighlight(engineer_submarineJunctionBox_2, true);
|
||||
SetHighlight(engineer_submarineJunctionBox_3, true);
|
||||
|
||||
Repairable repairableJunctionBoxComponent1 = engineer_submarineJunctionBox_1.GetComponent<Repairable>();
|
||||
Repairable repairableJunctionBoxComponent2 = engineer_submarineJunctionBox_2.GetComponent<Repairable>();
|
||||
Repairable repairableJunctionBoxComponent3 = engineer_submarineJunctionBox_3.GetComponent<Repairable>();
|
||||
|
||||
// Remove highlights when each individual machine is repaired
|
||||
do { CheckJunctionBoxHighlights(); yield return null; } while (!engineer_submarineJunctionBox_1.IsFullCondition || !engineer_submarineJunctionBox_2.IsFullCondition || !engineer_submarineJunctionBox_3.IsFullCondition);
|
||||
CheckJunctionBoxHighlights();
|
||||
do { CheckJunctionBoxHighlights(repairableJunctionBoxComponent1, repairableJunctionBoxComponent2, repairableJunctionBoxComponent3); yield return null; } while (engineer_submarineJunctionBox_1.Condition < repairableJunctionBoxComponent1.RepairThreshold || engineer_submarineJunctionBox_2.Condition < repairableJunctionBoxComponent2.RepairThreshold || engineer_submarineJunctionBox_3.Condition < repairableJunctionBoxComponent3.RepairThreshold);
|
||||
CheckJunctionBoxHighlights(repairableJunctionBoxComponent1, repairableJunctionBoxComponent2, repairableJunctionBoxComponent3);
|
||||
RemoveCompletedObjective(segments[4]);
|
||||
yield return new WaitForSeconds(2f, false);
|
||||
|
||||
@@ -557,19 +562,19 @@ namespace Barotrauma.Tutorials
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckJunctionBoxHighlights()
|
||||
private void CheckJunctionBoxHighlights(Repairable comp1, Repairable comp2, Repairable comp3)
|
||||
{
|
||||
if (engineer_submarineJunctionBox_1.IsFullCondition && engineer_submarineJunctionBox_1.ExternalHighlight)
|
||||
if (engineer_submarineJunctionBox_1.Condition > comp1.RepairThreshold && engineer_submarineJunctionBox_1.ExternalHighlight)
|
||||
{
|
||||
SetHighlight(engineer_submarineJunctionBox_1, false);
|
||||
engineer.RemoveActiveObjectiveEntity(engineer_submarineJunctionBox_1);
|
||||
}
|
||||
if (engineer_submarineJunctionBox_2.IsFullCondition && engineer_submarineJunctionBox_2.ExternalHighlight)
|
||||
if (engineer_submarineJunctionBox_2.Condition > comp2.RepairThreshold && engineer_submarineJunctionBox_2.ExternalHighlight)
|
||||
{
|
||||
SetHighlight(engineer_submarineJunctionBox_2, false);
|
||||
engineer.RemoveActiveObjectiveEntity(engineer_submarineJunctionBox_2);
|
||||
}
|
||||
if (engineer_submarineJunctionBox_3.IsFullCondition && engineer_submarineJunctionBox_3.ExternalHighlight)
|
||||
if (engineer_submarineJunctionBox_3.Condition > comp3.RepairThreshold && engineer_submarineJunctionBox_3.ExternalHighlight)
|
||||
{
|
||||
SetHighlight(engineer_submarineJunctionBox_3, false);
|
||||
engineer.RemoveActiveObjectiveEntity(engineer_submarineJunctionBox_3);
|
||||
|
||||
+15
-9
@@ -385,7 +385,8 @@ namespace Barotrauma.Tutorials
|
||||
}
|
||||
}
|
||||
|
||||
if (!gotOxygenTank && mechanic.Inventory.FindItemByIdentifier("oxygentank") != null)
|
||||
if (!gotOxygenTank && (mechanic.Inventory.FindItemByIdentifier("oxygentank") != null ||
|
||||
mechanic_deconstructor.InputContainer.Inventory.FindItemByIdentifier("oxygentank") != null))
|
||||
{
|
||||
gotOxygenTank = true;
|
||||
}
|
||||
@@ -551,7 +552,7 @@ namespace Barotrauma.Tutorials
|
||||
do
|
||||
{
|
||||
yield return null;
|
||||
if (!mechanic_brokenPump.Item.IsFullCondition)
|
||||
if (mechanic_brokenPump.Item.Condition < repairablePumpComponent.RepairThreshold)
|
||||
{
|
||||
if (!mechanic.HasEquippedItem("wrench"))
|
||||
{
|
||||
@@ -575,7 +576,7 @@ namespace Barotrauma.Tutorials
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (!mechanic_brokenPump.Item.IsFullCondition || mechanic_brokenPump.FlowPercentage >= 0 || !mechanic_brokenPump.IsActive);
|
||||
} while (mechanic_brokenPump.Item.Condition < repairablePumpComponent.RepairThreshold || mechanic_brokenPump.FlowPercentage >= 0 || !mechanic_brokenPump.IsActive);
|
||||
RemoveCompletedObjective(segments[9]);
|
||||
SetHighlight(mechanic_brokenPump.Item, false);
|
||||
do { yield return null; } while (mechanic_brokenhull_2.WaterPercentage > waterVolumeBeforeOpening);
|
||||
@@ -592,9 +593,14 @@ namespace Barotrauma.Tutorials
|
||||
SetHighlight(mechanic_ballastPump_1.Item, true);
|
||||
SetHighlight(mechanic_ballastPump_2.Item, true);
|
||||
SetHighlight(mechanic_submarineEngine.Item, true);
|
||||
|
||||
Repairable repairablePumpComponent1 = mechanic_ballastPump_1.Item.GetComponent<Repairable>();
|
||||
Repairable repairablePumpComponent2 = mechanic_ballastPump_2.Item.GetComponent<Repairable>();
|
||||
Repairable repairableEngineComponent = mechanic_submarineEngine.Item.GetComponent<Repairable>();
|
||||
|
||||
// Remove highlights when each individual machine is repaired
|
||||
do { CheckHighlights(); yield return null; } while (!mechanic_ballastPump_1.Item.IsFullCondition || !mechanic_ballastPump_2.Item.IsFullCondition || !mechanic_submarineEngine.Item.IsFullCondition);
|
||||
CheckHighlights();
|
||||
do { CheckHighlights(repairablePumpComponent1, repairablePumpComponent2, repairableEngineComponent); yield return null; } while (mechanic_ballastPump_1.Item.Condition < repairablePumpComponent1.RepairThreshold || mechanic_ballastPump_2.Item.Condition < repairablePumpComponent2.RepairThreshold || mechanic_submarineEngine.Item.Condition < repairableEngineComponent.RepairThreshold);
|
||||
CheckHighlights(repairablePumpComponent1, repairablePumpComponent2, repairableEngineComponent);
|
||||
RemoveCompletedObjective(segments[10]);
|
||||
GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Mechanic.Radio.Complete"), ChatMessageType.Radio, null);
|
||||
|
||||
@@ -617,19 +623,19 @@ namespace Barotrauma.Tutorials
|
||||
return false;
|
||||
}
|
||||
|
||||
private void CheckHighlights()
|
||||
private void CheckHighlights(Repairable comp1, Repairable comp2, Repairable comp3)
|
||||
{
|
||||
if (mechanic_ballastPump_1.Item.IsFullCondition && mechanic_ballastPump_1.Item.ExternalHighlight)
|
||||
if (mechanic_ballastPump_1.Item.Condition > comp1.RepairThreshold && mechanic_ballastPump_1.Item.ExternalHighlight)
|
||||
{
|
||||
SetHighlight(mechanic_ballastPump_1.Item, false);
|
||||
mechanic.RemoveActiveObjectiveEntity(mechanic_ballastPump_1.Item);
|
||||
}
|
||||
if (mechanic_ballastPump_2.Item.IsFullCondition && mechanic_ballastPump_2.Item.ExternalHighlight)
|
||||
if (mechanic_ballastPump_2.Item.Condition > comp2.RepairThreshold && mechanic_ballastPump_2.Item.ExternalHighlight)
|
||||
{
|
||||
SetHighlight(mechanic_ballastPump_2.Item, false);
|
||||
mechanic.RemoveActiveObjectiveEntity(mechanic_ballastPump_2.Item);
|
||||
}
|
||||
if (mechanic_submarineEngine.Item.IsFullCondition && mechanic_submarineEngine.Item.ExternalHighlight)
|
||||
if (mechanic_submarineEngine.Item.Condition > comp3.RepairThreshold && mechanic_submarineEngine.Item.ExternalHighlight)
|
||||
{
|
||||
SetHighlight(mechanic_submarineEngine.Item, false);
|
||||
mechanic.RemoveActiveObjectiveEntity(mechanic_submarineEngine.Item);
|
||||
|
||||
+8
-1
@@ -315,13 +315,14 @@ namespace Barotrauma.Tutorials
|
||||
yield return new WaitForSeconds(2f, false);
|
||||
TriggerTutorialSegment(4, GameMain.Config.KeyBindText(InputType.Select), GameMain.Config.KeyBindText(InputType.Shoot), GameMain.Config.KeyBindText(InputType.Deselect)); // Kill hammerhead
|
||||
officer_hammerhead = SpawnMonster("hammerhead", officer_hammerheadSpawnPos);
|
||||
((EnemyAIController)officer_hammerhead.AIController).StayInsideLevel = false;
|
||||
officer_hammerhead.AIController.SelectTarget(officer.AiTarget);
|
||||
SetHighlight(officer_coilgunPeriscope, true);
|
||||
float originalDistance = Vector2.Distance(officer_coilgunPeriscope.WorldPosition, officer_hammerheadSpawnPos);
|
||||
do
|
||||
{
|
||||
float distance = Vector2.Distance(officer_coilgunPeriscope.WorldPosition, officer_hammerhead.WorldPosition);
|
||||
if (distance > originalDistance * 1.5f)
|
||||
if (distance > originalDistance * 1.5f || officer_hammerhead.WorldPosition.Y > officer_coilgunPeriscope.WorldPosition.Y)
|
||||
{
|
||||
// Don't let the Hammerhead go too far.
|
||||
officer_hammerhead.TeleportTo(officer_hammerheadSpawnPos + new Vector2(0, -1000));
|
||||
@@ -329,7 +330,13 @@ namespace Barotrauma.Tutorials
|
||||
if (distance > originalDistance)
|
||||
{
|
||||
// Ensure that the Hammerhead targets the player
|
||||
officer.AiTarget.SoundRange = float.MaxValue;
|
||||
officer.AiTarget.SightRange = float.MaxValue;
|
||||
officer_hammerhead.AIController.SelectTarget(officer.AiTarget);
|
||||
if ((officer_hammerhead.AIController as EnemyAIController)?.SelectedTargetingParams != null)
|
||||
{
|
||||
((EnemyAIController)officer_hammerhead.AIController).SelectedTargetingParams.ReactDistance = 5000.0f;
|
||||
}
|
||||
/*var ai = officer_hammerhead.AIController as EnemyAIController;
|
||||
ai.sight = 2.0f;*/
|
||||
}
|
||||
|
||||
+23
-19
@@ -58,30 +58,31 @@ namespace Barotrauma.Tutorials
|
||||
{
|
||||
SubmarineInfo subInfo = new SubmarineInfo(submarinePath);
|
||||
|
||||
LevelGenerationParams generationParams = LevelGenerationParams.LevelParams.Find(p => p.Name == levelParams);
|
||||
LevelGenerationParams generationParams = LevelGenerationParams.LevelParams.Find(p => p.Identifier.Equals(levelParams, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
yield return CoroutineStatus.Running;
|
||||
|
||||
GameMain.GameSession = new GameSession(subInfo, "",
|
||||
GameModePreset.List.Find(g => g.Identifier == "tutorial"));
|
||||
GameMain.GameSession = new GameSession(subInfo, GameModePreset.Tutorial, missionPrefab: null);
|
||||
(GameMain.GameSession.GameMode as TutorialMode).Tutorial = this;
|
||||
|
||||
if (generationParams != null)
|
||||
{
|
||||
Biome biome = LevelGenerationParams.GetBiomes().Find(b => generationParams.AllowedBiomes.Contains(b));
|
||||
Biome biome =
|
||||
LevelGenerationParams.GetBiomes().FirstOrDefault(b => generationParams.AllowedBiomes.Contains(b)) ??
|
||||
LevelGenerationParams.GetBiomes().First();
|
||||
|
||||
if (startOutpostPath != string.Empty)
|
||||
if (!string.IsNullOrEmpty(startOutpostPath))
|
||||
{
|
||||
startOutpost = new SubmarineInfo(startOutpostPath);
|
||||
}
|
||||
|
||||
if (endOutpostPath != string.Empty)
|
||||
if (!string.IsNullOrEmpty(endOutpostPath))
|
||||
{
|
||||
endOutpost = new SubmarineInfo(endOutpostPath);
|
||||
}
|
||||
|
||||
Level tutorialLevel = new Level(levelSeed, 0, 0, generationParams, biome, startOutpost, endOutpost);
|
||||
GameMain.GameSession.StartRound(tutorialLevel);
|
||||
LevelData tutorialLevel = new LevelData(levelSeed, 0, 0, generationParams, biome);
|
||||
GameMain.GameSession.StartRound(tutorialLevel, startOutpost: startOutpost, endOutpost: endOutpost);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -100,6 +101,13 @@ namespace Barotrauma.Tutorials
|
||||
base.Start();
|
||||
|
||||
Submarine.MainSub.GodMode = true;
|
||||
foreach (Structure wall in Structure.WallList)
|
||||
{
|
||||
if (wall.Submarine != null && wall.Submarine.Info.IsOutpost)
|
||||
{
|
||||
wall.Indestructible = true;
|
||||
}
|
||||
}
|
||||
|
||||
CharacterInfo charInfo = configElement.Element("Character") == null ?
|
||||
new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("engineer")) :
|
||||
@@ -114,6 +122,7 @@ namespace Barotrauma.Tutorials
|
||||
}
|
||||
|
||||
character = Character.Create(charInfo, wayPoint.WorldPosition, "", false, false);
|
||||
character.TeamID = Character.TeamType.Team1;
|
||||
Character.Controlled = character;
|
||||
character.GiveJobItems(null);
|
||||
|
||||
@@ -126,19 +135,14 @@ namespace Barotrauma.Tutorials
|
||||
idCard.AddTag("com");
|
||||
idCard.AddTag("eng");
|
||||
|
||||
List<Entity> entities = Entity.GetEntityList();
|
||||
|
||||
for (int i = 0; i < entities.Count; i++)
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
if (entities[i] is Item)
|
||||
Door door = item.GetComponent<Door>();
|
||||
if (door != null)
|
||||
{
|
||||
Door door = (entities[i] as Item).GetComponent<Door>();
|
||||
if (door != null)
|
||||
{
|
||||
door.CanBeWelded = false;
|
||||
}
|
||||
door.CanBeWelded = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tutorialCoroutine = CoroutineManager.StartCoroutine(UpdateState());
|
||||
}
|
||||
@@ -284,7 +288,7 @@ namespace Barotrauma.Tutorials
|
||||
|
||||
yield return new WaitForSeconds(waitBeforeFade);
|
||||
|
||||
var endCinematic = new RoundEndCinematic(Submarine.MainSub, GameMain.GameScreen.Cam, fadeOutTime);
|
||||
var endCinematic = new CameraTransition(Submarine.MainSub, GameMain.GameScreen.Cam, null, Alignment.Center, duration: fadeOutTime);
|
||||
currentTutorialCompleted = Completed = true;
|
||||
while (endCinematic.Running) yield return null;
|
||||
Stop();
|
||||
|
||||
+3
-1
@@ -26,6 +26,7 @@ namespace Barotrauma.Tutorials
|
||||
protected enum TutorialContentTypes { None = 0, Video = 1, ManualVideo = 2, TextOnly = 3 };
|
||||
protected string playableContentPath;
|
||||
protected Point screenResolution;
|
||||
protected WindowMode windowMode;
|
||||
protected float prevUIScale;
|
||||
|
||||
private GUIFrame holderFrame, objectiveFrame;
|
||||
@@ -207,7 +208,7 @@ namespace Barotrauma.Tutorials
|
||||
|
||||
public virtual void AddToGUIUpdateList()
|
||||
{
|
||||
if (GameMain.GraphicsWidth != screenResolution.X || GameMain.GraphicsHeight != screenResolution.Y || prevUIScale != GUI.Scale)
|
||||
if (GameMain.GraphicsWidth != screenResolution.X || GameMain.GraphicsHeight != screenResolution.Y || prevUIScale != GUI.Scale || GameMain.Config.WindowMode != windowMode)
|
||||
{
|
||||
CreateObjectiveFrame();
|
||||
}
|
||||
@@ -340,6 +341,7 @@ namespace Barotrauma.Tutorials
|
||||
}
|
||||
|
||||
screenResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
|
||||
windowMode = GameMain.Config.WindowMode;
|
||||
prevUIScale = GUI.Scale;
|
||||
}
|
||||
|
||||
|
||||
+7
-2
@@ -11,8 +11,8 @@ namespace Barotrauma
|
||||
tutorial.Initialize();
|
||||
}
|
||||
|
||||
public TutorialMode(GameModePreset preset, object param)
|
||||
: base(preset, param)
|
||||
public TutorialMode(GameModePreset preset)
|
||||
: base(preset)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -21,6 +21,11 @@ namespace Barotrauma
|
||||
base.Start();
|
||||
GameMain.GameSession.CrewManager = new CrewManager(true);
|
||||
Tutorial.Start();
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
//don't consider the items to belong in the outpost to prevent the stealing icon from showing
|
||||
item.SpawnedInOutpost = false;
|
||||
}
|
||||
}
|
||||
|
||||
public override void AddToGUIUpdateList()
|
||||
|
||||
Reference in New Issue
Block a user