Faction Test 100.4.0.0
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -15,8 +15,6 @@ namespace Barotrauma
|
||||
protected bool crewDead;
|
||||
|
||||
protected Color overlayColor;
|
||||
protected LocalizedString overlayText, overlayTextBottom;
|
||||
protected Color overlayTextColor;
|
||||
protected Sprite overlaySprite;
|
||||
|
||||
private TransitionType prevCampaignUIAutoOpenType;
|
||||
@@ -29,6 +27,12 @@ namespace Barotrauma
|
||||
protected GUIFrame campaignUIContainer;
|
||||
public CampaignUI CampaignUI;
|
||||
|
||||
public SlideshowPlayer SlideshowPlayer
|
||||
{
|
||||
get;
|
||||
protected set;
|
||||
}
|
||||
|
||||
public static CancellationTokenSource StartRoundCancellationToken { get; private set; }
|
||||
|
||||
public bool ForceMapUI
|
||||
@@ -76,6 +80,7 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (Mission mission in Missions.ToList())
|
||||
{
|
||||
if (!mission.Prefab.ShowStartMessage) { continue; }
|
||||
new GUIMessageBox(
|
||||
RichString.Rich(mission.Prefab.IsSideObjective ? TextManager.AddPunctuation(':', TextManager.Get("sideobjective"), mission.Name) : mission.Name),
|
||||
RichString.Rich(mission.Description), Array.Empty<LocalizedString>(), type: GUIMessageBox.Type.InGame, icon: mission.Prefab.Icon)
|
||||
@@ -86,6 +91,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsOwner(Client client) => client != null && client.IsOwner;
|
||||
|
||||
/// <summary>
|
||||
/// There is a server-side implementation of the method in <see cref="MultiPlayerCampaign"/>
|
||||
/// </summary>
|
||||
@@ -97,10 +104,8 @@ namespace Barotrauma
|
||||
return
|
||||
GameMain.Client.HasPermission(permissions) ||
|
||||
GameMain.Client.HasPermission(ClientPermissions.ManageCampaign) ||
|
||||
GameMain.Client.ConnectedClients.Count == 1 ||
|
||||
GameMain.Client.IsServerOwner ||
|
||||
//allow managing if no-one with permissions is alive
|
||||
GameMain.Client.ConnectedClients.None(c => c.InGame && c.Character is { IsIncapacitated: false, IsDead: false } && (c.IsOwner || c.HasPermission(permissions)));
|
||||
AnyOneAllowedToManageCampaign(permissions);
|
||||
}
|
||||
|
||||
public static bool AllowedToManageWallets()
|
||||
@@ -127,32 +132,10 @@ namespace Barotrauma
|
||||
{
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), overlayColor, isFilled: true);
|
||||
}
|
||||
if (!overlayText.IsNullOrEmpty() && overlayTextColor.A > 0)
|
||||
{
|
||||
var backgroundSprite = GUIStyle.GetComponentStyle("CommandBackground").GetDefaultSprite();
|
||||
Vector2 centerPos = new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight) / 2;
|
||||
LocalizedString wrappedText = ToolBox.WrapText(overlayText, GameMain.GraphicsWidth / 3, GUIStyle.Font);
|
||||
Vector2 textSize = GUIStyle.Font.MeasureString(wrappedText);
|
||||
Vector2 textPos = centerPos - textSize / 2;
|
||||
backgroundSprite.Draw(spriteBatch,
|
||||
centerPos,
|
||||
Color.White * (overlayTextColor.A / 255.0f),
|
||||
origin: backgroundSprite.size / 2,
|
||||
rotate: 0.0f,
|
||||
scale: new Vector2(GameMain.GraphicsWidth / 2 / backgroundSprite.size.X, textSize.Y / backgroundSprite.size.Y * 1.5f));
|
||||
|
||||
GUI.DrawString(spriteBatch, textPos + Vector2.One, wrappedText, Color.Black * (overlayTextColor.A / 255.0f));
|
||||
GUI.DrawString(spriteBatch, textPos, wrappedText, overlayTextColor);
|
||||
|
||||
if (!overlayTextBottom.IsNullOrEmpty())
|
||||
{
|
||||
Vector2 bottomTextPos = centerPos + new Vector2(0.0f, textSize.Y / 2 + 40 * GUI.Scale) - GUIStyle.Font.MeasureString(overlayTextBottom) / 2;
|
||||
GUI.DrawString(spriteBatch, bottomTextPos + Vector2.One, overlayTextBottom.Value, Color.Black * (overlayTextColor.A / 255.0f));
|
||||
GUI.DrawString(spriteBatch, bottomTextPos, overlayTextBottom.Value, overlayTextColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SlideshowPlayer?.DrawManually(spriteBatch);
|
||||
|
||||
if (GUI.DisableHUD || GUI.DisableUpperHUD || ForceMapUI || CoroutineManager.IsCoroutineRunning("LevelTransition"))
|
||||
{
|
||||
endRoundButton.Visible = false;
|
||||
@@ -192,6 +175,7 @@ namespace Barotrauma
|
||||
case TransitionType.None:
|
||||
default:
|
||||
if (Level.Loaded.Type == LevelData.LevelType.Outpost &&
|
||||
!Level.Loaded.IsEndBiome &&
|
||||
(Character.Controlled?.Submarine?.Info.Type == SubmarineType.Player || (Character.Controlled?.CurrentHull?.OutpostModuleTags.Contains("airlock".ToIdentifier()) ?? false)))
|
||||
{
|
||||
buttonText = TextManager.GetWithVariable("LeaveLocation", "[locationname]", Level.Loaded.StartLocation?.Name ?? "[ERROR]");
|
||||
@@ -203,6 +187,10 @@ namespace Barotrauma
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (Level.IsLoadedOutpost && !ObjectiveManager.AllActiveObjectivesCompleted())
|
||||
{
|
||||
endRoundButton.Visible = false;
|
||||
}
|
||||
|
||||
if (ReadyCheckButton != null) { ReadyCheckButton.Visible = endRoundButton.Visible; }
|
||||
|
||||
@@ -266,7 +254,7 @@ namespace Barotrauma
|
||||
Rand.ThreadId = Thread.CurrentThread.ManagedThreadId;
|
||||
try
|
||||
{
|
||||
GameMain.GameSession.StartRound(newLevel, mirrorLevel: mirror);
|
||||
GameMain.GameSession.StartRound(newLevel, mirrorLevel: mirror, startOutpost: GetPredefinedStartOutpost());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -283,6 +271,18 @@ namespace Barotrauma
|
||||
return loadTask;
|
||||
}
|
||||
|
||||
protected SubmarineInfo GetPredefinedStartOutpost()
|
||||
{
|
||||
if (Map?.CurrentLocation?.Type?.GetForcedOutpostGenerationParams() is OutpostGenerationParams parameters && !parameters.OutpostFilePath.IsNullOrEmpty())
|
||||
{
|
||||
return new SubmarineInfo(parameters.OutpostFilePath.Value)
|
||||
{
|
||||
OutpostGenerationParams = parameters
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
partial void NPCInteractProjSpecific(Character npc, Character interactor)
|
||||
{
|
||||
if (npc == null || interactor == null) { return; }
|
||||
|
||||
+29
-77
@@ -216,51 +216,35 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
Character prevControlled = Character.Controlled;
|
||||
if (prevControlled?.AIController != null)
|
||||
{
|
||||
prevControlled.AIController.Enabled = false;
|
||||
}
|
||||
GUI.DisableHUD = true;
|
||||
if (IsFirstRound)
|
||||
{
|
||||
Character.Controlled = null;
|
||||
if (SlideshowPrefab.Prefabs.TryGet("campaignstart".ToIdentifier(), out var slideshow))
|
||||
{
|
||||
SlideshowPlayer = new SlideshowPlayer(GUICanvas.Instance, slideshow);
|
||||
}
|
||||
|
||||
Character.Controlled = null;
|
||||
prevControlled?.ClearInputs();
|
||||
|
||||
overlayColor = Color.LightGray;
|
||||
overlaySprite = Map.CurrentLocation.Type.GetPortrait(Map.CurrentLocation.PortraitId);
|
||||
overlayTextColor = Color.Transparent;
|
||||
overlayText = TextManager.GetWithVariables("campaignstart",
|
||||
("xxxx", Map.CurrentLocation.Name), ("yyyy", TextManager.Get($"submarineclass.{Submarine.MainSub.Info.SubmarineClass}")));
|
||||
float fadeInDuration = 1.0f;
|
||||
float textDuration = 10.0f;
|
||||
float timer = 0.0f;
|
||||
while (timer < textDuration)
|
||||
{
|
||||
if (GameMain.GameSession == null || Screen.Selected != GameMain.GameScreen)
|
||||
{
|
||||
GUI.DisableHUD = false;
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
// 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();
|
||||
}
|
||||
GameMain.GameScreen.Cam.Freeze = true;
|
||||
overlayTextColor = Color.Lerp(Color.Transparent, Color.White, (timer - 1.0f) / fadeInDuration);
|
||||
timer = Math.Min(timer + CoroutineManager.DeltaTime, textDuration);
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
|
||||
var outpost = GameMain.GameSession.Level.StartOutpost;
|
||||
var borders = outpost.GetDockedBorders();
|
||||
borders.Location += outpost.WorldPosition.ToPoint();
|
||||
GameMain.GameScreen.Cam.Position = new Vector2(borders.X + borders.Width / 2, borders.Y - borders.Height / 2);
|
||||
float startZoom = 0.8f /
|
||||
((float)Math.Max(borders.Width, borders.Height) / (float)GameMain.GameScreen.Cam.Resolution.X);
|
||||
GameMain.GameScreen.Cam.MinZoom = Math.Min(startZoom, GameMain.GameScreen.Cam.MinZoom);
|
||||
GameMain.GameScreen.Cam.Zoom = GameMain.GameScreen.Cam.MinZoom = Math.Min(startZoom, GameMain.GameScreen.Cam.MinZoom);
|
||||
while (SlideshowPlayer != null && !SlideshowPlayer.LastTextShown)
|
||||
{
|
||||
GUI.PreventPauseMenuToggle = true;
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
GUI.PreventPauseMenuToggle = false;
|
||||
prevControlled ??= Character.Controlled;
|
||||
GameMain.LightManager.LosAlpha = 0.0f;
|
||||
var transition = new CameraTransition(prevControlled, GameMain.GameScreen.Cam,
|
||||
null, null,
|
||||
fadeOut: false,
|
||||
@@ -272,16 +256,6 @@ namespace Barotrauma
|
||||
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)
|
||||
{
|
||||
@@ -409,6 +383,8 @@ namespace Barotrauma
|
||||
|
||||
base.Update(deltaTime);
|
||||
|
||||
SlideshowPlayer?.UpdateManually(deltaTime);
|
||||
|
||||
if (PlayerInput.SecondaryMouseButtonClicked() ||
|
||||
PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Escape))
|
||||
{
|
||||
@@ -442,7 +418,8 @@ namespace Barotrauma
|
||||
CampaignUI.SelectTab(InteractionType.Map);
|
||||
}
|
||||
}
|
||||
else
|
||||
//end biome is handled by the server (automatic transition without a map screen when the end of the level is reached)
|
||||
else if (!Level.Loaded.IsEndBiome)
|
||||
{
|
||||
//wasn't initially docked (sub doesn't have a docking port?)
|
||||
// -> choose a destination when the sub is far enough from the start outpost
|
||||
@@ -467,11 +444,17 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public override void UpdateWhilePaused(float deltaTime)
|
||||
{
|
||||
SlideshowPlayer?.UpdateManually(deltaTime);
|
||||
}
|
||||
|
||||
public override void End(TransitionType transitionType = TransitionType.None)
|
||||
{
|
||||
base.End(transitionType);
|
||||
ForceMapUI = ShowCampaignUI = false;
|
||||
|
||||
SlideshowPlayer?.Finish();
|
||||
|
||||
// remove all event dialogue boxes
|
||||
GUIMessageBox.MessageBoxes.ForEachMod(mb =>
|
||||
{
|
||||
@@ -501,7 +484,8 @@ namespace Barotrauma
|
||||
{
|
||||
GUIMessageBox.MessageBoxes.Remove(GUIMessageBox.VisibleBox);
|
||||
}
|
||||
CoroutineManager.StartCoroutine(DoEndCampaignCameraTransition(), "DoEndCampaignCameraTransition");
|
||||
GameMain.CampaignEndScreen.Select();
|
||||
GUI.DisableHUD = false;
|
||||
GameMain.CampaignEndScreen.OnFinished = () =>
|
||||
{
|
||||
GameMain.NetLobbyScreen.Select();
|
||||
@@ -510,32 +494,6 @@ namespace Barotrauma
|
||||
};
|
||||
}
|
||||
|
||||
private IEnumerable<CoroutineStatus> DoEndCampaignCameraTransition()
|
||||
{
|
||||
Character controlled = Character.Controlled;
|
||||
if (controlled != null)
|
||||
{
|
||||
controlled.AIController.Enabled = false;
|
||||
}
|
||||
|
||||
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,
|
||||
panDuration: 10,
|
||||
startZoom: null, endZoom: 0.2f);
|
||||
|
||||
while (transition.Running)
|
||||
{
|
||||
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);
|
||||
@@ -834,8 +792,6 @@ namespace Barotrauma
|
||||
{
|
||||
DebugConsole.Log("Received campaign update (Reputation)");
|
||||
UInt16 id = msg.ReadUInt16();
|
||||
float? reputation = null;
|
||||
if (msg.ReadBoolean()) { reputation = msg.ReadSingle(); }
|
||||
Dictionary<Identifier, float> factionReps = new Dictionary<Identifier, float>();
|
||||
byte factionsCount = msg.ReadByte();
|
||||
for (int i = 0; i < factionsCount; i++)
|
||||
@@ -844,11 +800,6 @@ namespace Barotrauma
|
||||
}
|
||||
if (ShouldApply(NetFlags.Reputation, id, requireUpToDateSave: true))
|
||||
{
|
||||
if (reputation.HasValue)
|
||||
{
|
||||
campaign.Map.CurrentLocation.Reputation.SetReputation(reputation.Value);
|
||||
campaign?.CampaignUI?.UpgradeStore?.RequestRefresh();
|
||||
}
|
||||
foreach (var (identifier, rep) in factionReps)
|
||||
{
|
||||
Faction faction = campaign.Factions.FirstOrDefault(f => f.Prefab.Identifier == identifier);
|
||||
@@ -861,6 +812,7 @@ namespace Barotrauma
|
||||
DebugConsole.ThrowError($"Received an update for a faction that doesn't exist \"{identifier}\".");
|
||||
}
|
||||
}
|
||||
campaign?.CampaignUI?.UpgradeStore?.RequestRefresh();
|
||||
}
|
||||
}
|
||||
if (requiredFlags.HasFlag(NetFlags.CharacterInfo))
|
||||
|
||||
+107
-127
@@ -12,7 +12,13 @@ namespace Barotrauma
|
||||
|
||||
public override bool Paused
|
||||
{
|
||||
get { return ForceMapUI || CoroutineManager.IsCoroutineRunning("LevelTransition") || ShowCampaignUI && CampaignUI.SelectedTab == InteractionType.Map; }
|
||||
get
|
||||
{
|
||||
return
|
||||
ForceMapUI || CoroutineManager.IsCoroutineRunning("LevelTransition") ||
|
||||
ShowCampaignUI && CampaignUI.SelectedTab == InteractionType.Map ||
|
||||
(SlideshowPlayer != null && !SlideshowPlayer.LastTextShown);
|
||||
}
|
||||
}
|
||||
|
||||
public override void UpdateWhilePaused(float deltaTime)
|
||||
@@ -31,6 +37,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
SlideshowPlayer?.UpdateManually(deltaTime);
|
||||
|
||||
CrewManager.ChatBox?.Update(deltaTime);
|
||||
CrewManager.UpdateReports();
|
||||
}
|
||||
@@ -77,9 +85,10 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
private SinglePlayerCampaign(string mapSeed, CampaignSettings settings) : base(GameModePreset.SinglePlayerCampaign, settings)
|
||||
{
|
||||
CampaignMetadata = new CampaignMetadata(this);
|
||||
CampaignMetadata = new CampaignMetadata();
|
||||
UpgradeManager = new UpgradeManager(this);
|
||||
Settings = settings;
|
||||
InitFactions();
|
||||
map = new Map(this, mapSeed);
|
||||
foreach (JobPrefab jobPrefab in JobPrefab.Prefabs)
|
||||
{
|
||||
@@ -89,7 +98,6 @@ namespace Barotrauma
|
||||
CrewManager.AddCharacterInfo(new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: jobPrefab, variant: variant));
|
||||
}
|
||||
}
|
||||
InitCampaignData();
|
||||
InitUI();
|
||||
}
|
||||
|
||||
@@ -100,6 +108,19 @@ namespace Barotrauma
|
||||
{
|
||||
IsFirstRound = false;
|
||||
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "metadata":
|
||||
CampaignMetadata = new CampaignMetadata(subElement);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CampaignMetadata ??= new CampaignMetadata();
|
||||
InitFactions();
|
||||
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
@@ -114,9 +135,6 @@ namespace Barotrauma
|
||||
case "map":
|
||||
map = Map.Load(this, subElement);
|
||||
break;
|
||||
case "metadata":
|
||||
CampaignMetadata = new CampaignMetadata(this, subElement);
|
||||
break;
|
||||
case "cargo":
|
||||
CargoManager.LoadPurchasedItems(subElement);
|
||||
break;
|
||||
@@ -136,11 +154,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
CampaignMetadata ??= new CampaignMetadata(this);
|
||||
UpgradeManager ??= new UpgradeManager(this);
|
||||
|
||||
InitCampaignData();
|
||||
|
||||
InitUI();
|
||||
|
||||
//backwards compatibility for saves made prior to the addition of personal wallets
|
||||
@@ -265,8 +280,7 @@ namespace Barotrauma
|
||||
|
||||
private IEnumerable<CoroutineStatus> DoLoadInitialLevel(LevelData level, bool mirror)
|
||||
{
|
||||
GameMain.GameSession.StartRound(level,
|
||||
mirrorLevel: mirror);
|
||||
GameMain.GameSession.StartRound(level, mirrorLevel: mirror, startOutpost: GetPredefinedStartOutpost());
|
||||
GameMain.GameScreen.Select();
|
||||
|
||||
CoroutineManager.StartCoroutine(DoInitialCameraTransition(), "SinglePlayerCampaign.DoInitialCameraTransition");
|
||||
@@ -296,34 +310,9 @@ namespace Barotrauma
|
||||
|
||||
if (IsFirstRound || showCampaignResetText)
|
||||
{
|
||||
overlayColor = Color.LightGray;
|
||||
overlaySprite = Map.CurrentLocation.Type.GetPortrait(Map.CurrentLocation.PortraitId);
|
||||
overlayTextColor = Color.Transparent;
|
||||
overlayText = TextManager.GetWithVariables(showCampaignResetText ? "campaignend4" : "campaignstart",
|
||||
("xxxx", Map.CurrentLocation.Name),
|
||||
("yyyy", TextManager.Get("submarineclass." + Submarine.MainSub.Info.SubmarineClass)));
|
||||
LocalizedString pressAnyKeyText = TextManager.Get("pressanykey");
|
||||
float fadeInDuration = 2.0f;
|
||||
float textDuration = 10.0f;
|
||||
float timer = 0.0f;
|
||||
while (true)
|
||||
if (SlideshowPrefab.Prefabs.TryGet("campaignstart".ToIdentifier(), out var slideshow))
|
||||
{
|
||||
if (timer > fadeInDuration)
|
||||
{
|
||||
overlayTextBottom = pressAnyKeyText;
|
||||
if (PlayerInput.GetKeyboardState.GetPressedKeys().Length > 0 || PlayerInput.PrimaryMouseButtonClicked())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (GameMain.GameSession == null)
|
||||
{
|
||||
GUI.DisableHUD = false;
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
overlayTextColor = Color.Lerp(Color.Transparent, Color.White, (timer - 1.0f) / fadeInDuration);
|
||||
timer = Math.Min(timer + CoroutineManager.DeltaTime, textDuration);
|
||||
yield return CoroutineStatus.Running;
|
||||
SlideshowPlayer = new SlideshowPlayer(GUICanvas.Instance, slideshow);
|
||||
}
|
||||
var outpost = GameMain.GameSession.Level.StartOutpost;
|
||||
var borders = outpost.GetDockedBorders();
|
||||
@@ -331,7 +320,13 @@ namespace Barotrauma
|
||||
GameMain.GameScreen.Cam.Position = new Vector2(borders.X + borders.Width / 2, borders.Y - borders.Height / 2);
|
||||
float startZoom = 0.8f /
|
||||
((float)Math.Max(borders.Width, borders.Height) / (float)GameMain.GameScreen.Cam.Resolution.X);
|
||||
GameMain.GameScreen.Cam.MinZoom = Math.Min(startZoom, GameMain.GameScreen.Cam.MinZoom);
|
||||
GameMain.GameScreen.Cam.Zoom = GameMain.GameScreen.Cam.MinZoom = Math.Min(startZoom, GameMain.GameScreen.Cam.MinZoom);
|
||||
while (SlideshowPlayer != null && !SlideshowPlayer.LastTextShown)
|
||||
{
|
||||
GUI.PreventPauseMenuToggle = true;
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
GUI.PreventPauseMenuToggle = false;
|
||||
var transition = new CameraTransition(prevControlled, GameMain.GameScreen.Cam,
|
||||
null, null,
|
||||
fadeOut: false,
|
||||
@@ -343,17 +338,6 @@ namespace Barotrauma
|
||||
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;
|
||||
@@ -407,6 +391,11 @@ namespace Barotrauma
|
||||
GUI.SetSavingIndicatorState(success);
|
||||
crewDead = false;
|
||||
|
||||
if (success)
|
||||
{
|
||||
// Event history must be registered before ending the round or it will be cleared
|
||||
GameMain.GameSession.EventManager.RegisterEventHistory();
|
||||
}
|
||||
GameMain.GameSession.EndRound("", traitorResults, transitionType);
|
||||
var continueButton = GameMain.GameSession.RoundSummary?.ContinueButton;
|
||||
RoundSummary roundSummary = null;
|
||||
@@ -437,60 +426,65 @@ namespace Barotrauma
|
||||
case TransitionType.ProgressToNextEmptyLocation:
|
||||
TotalPassedLevels++;
|
||||
break;
|
||||
case TransitionType.End:
|
||||
EndCampaign();
|
||||
IsFirstRound = true;
|
||||
break;
|
||||
}
|
||||
|
||||
Map.ProgressWorld(transitionType, (float)(Timing.TotalTime - GameMain.GameSession.RoundStartTime));
|
||||
Map.ProgressWorld(this, transitionType, (float)(Timing.TotalTime - GameMain.GameSession.RoundStartTime));
|
||||
|
||||
var endTransition = new CameraTransition(Submarine.MainSub, GameMain.GameScreen.Cam, null,
|
||||
GUI.ClearMessages();
|
||||
|
||||
//--------------------------------------
|
||||
if (transitionType != TransitionType.End)
|
||||
{
|
||||
var endTransition = new CameraTransition(Submarine.MainSub, GameMain.GameScreen.Cam, null,
|
||||
transitionType == TransitionType.LeaveLocation ? Alignment.BottomCenter : Alignment.Center,
|
||||
fadeOut: false,
|
||||
panDuration: EndTransitionDuration);
|
||||
|
||||
GUI.ClearMessages();
|
||||
|
||||
Location portraitLocation = Map.SelectedLocation ?? Map.CurrentLocation;
|
||||
overlaySprite = portraitLocation.Type.GetPortrait(portraitLocation.PortraitId);
|
||||
float fadeOutDuration = endTransition.PanDuration;
|
||||
float t = 0.0f;
|
||||
while (t < fadeOutDuration || endTransition.Running)
|
||||
{
|
||||
t += CoroutineManager.DeltaTime;
|
||||
overlayColor = Color.Lerp(Color.Transparent, Color.White, t / fadeOutDuration);
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
overlayColor = Color.White;
|
||||
yield return CoroutineStatus.Running;
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
if (success)
|
||||
{
|
||||
GameMain.GameSession.SubmarineInfo = new SubmarineInfo(GameMain.GameSession.Submarine);
|
||||
GameMain.GameSession.EventManager.RegisterEventHistory();
|
||||
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
PendingSubmarineSwitch = null;
|
||||
EnableRoundSummaryGameOverState();
|
||||
}
|
||||
|
||||
CrewManager?.ClearCurrentOrders();
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
SelectSummaryScreen(roundSummary, newLevel, mirror, () =>
|
||||
{
|
||||
GameMain.GameScreen.Select();
|
||||
if (continueButton != null)
|
||||
Location portraitLocation = Map.SelectedLocation ?? Map.CurrentLocation;
|
||||
overlaySprite = portraitLocation.Type.GetPortrait(portraitLocation.PortraitId);
|
||||
float fadeOutDuration = endTransition.PanDuration;
|
||||
float t = 0.0f;
|
||||
while (t < fadeOutDuration || endTransition.Running)
|
||||
{
|
||||
continueButton.Visible = true;
|
||||
t += CoroutineManager.DeltaTime;
|
||||
overlayColor = Color.Lerp(Color.Transparent, Color.White, t / fadeOutDuration);
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
overlayColor = Color.White;
|
||||
yield return CoroutineStatus.Running;
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
if (success)
|
||||
{
|
||||
GameMain.GameSession.SubmarineInfo = new SubmarineInfo(GameMain.GameSession.Submarine);
|
||||
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
PendingSubmarineSwitch = null;
|
||||
EnableRoundSummaryGameOverState();
|
||||
}
|
||||
|
||||
GUI.DisableHUD = false;
|
||||
GUI.ClearCursorWait();
|
||||
overlayColor = Color.Transparent;
|
||||
});
|
||||
CrewManager?.ClearCurrentOrders();
|
||||
|
||||
SelectSummaryScreen(roundSummary, newLevel, mirror, () =>
|
||||
{
|
||||
GameMain.GameScreen.Select();
|
||||
if (continueButton != null)
|
||||
{
|
||||
continueButton.Visible = true;
|
||||
}
|
||||
|
||||
GUI.DisableHUD = false;
|
||||
GUI.ClearCursorWait();
|
||||
overlayColor = Color.Transparent;
|
||||
});
|
||||
}
|
||||
|
||||
GUI.SetSavingIndicatorState(false);
|
||||
yield return CoroutineStatus.Success;
|
||||
@@ -498,7 +492,10 @@ namespace Barotrauma
|
||||
|
||||
protected override void EndCampaignProjSpecific()
|
||||
{
|
||||
CoroutineManager.StartCoroutine(DoEndCampaignCameraTransition(), "DoEndCampaignCameraTransition");
|
||||
GameMain.GameSession.SubmarineInfo = new SubmarineInfo(GameMain.GameSession.Submarine);
|
||||
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
|
||||
GameMain.CampaignEndScreen.Select();
|
||||
GUI.DisableHUD = false;
|
||||
GameMain.CampaignEndScreen.OnFinished = () =>
|
||||
{
|
||||
showCampaignResetText = true;
|
||||
@@ -507,39 +504,14 @@ namespace Barotrauma
|
||||
};
|
||||
}
|
||||
|
||||
private IEnumerable<CoroutineStatus> DoEndCampaignCameraTransition()
|
||||
{
|
||||
if (Character.Controlled != null)
|
||||
{
|
||||
Character.Controlled.AIController.Enabled = false;
|
||||
Character.Controlled = null;
|
||||
}
|
||||
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,
|
||||
panDuration: 10,
|
||||
startZoom: null, endZoom: 0.2f);
|
||||
|
||||
while (transition.Running)
|
||||
{
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
GameMain.GameSession.SubmarineInfo = new SubmarineInfo(GameMain.GameSession.Submarine);
|
||||
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
|
||||
GameMain.CampaignEndScreen.Select();
|
||||
GUI.DisableHUD = false;
|
||||
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (CoroutineManager.IsCoroutineRunning("LevelTransition") || CoroutineManager.IsCoroutineRunning("SubmarineTransition") || gameOver) { return; }
|
||||
|
||||
base.Update(deltaTime);
|
||||
|
||||
|
||||
SlideshowPlayer?.UpdateManually(deltaTime);
|
||||
|
||||
Map?.Radiation?.UpdateRadiation(deltaTime);
|
||||
|
||||
if (PlayerInput.SecondaryMouseButtonClicked() ||
|
||||
@@ -590,11 +562,19 @@ namespace Barotrauma
|
||||
CampaignUI.SelectTab(InteractionType.Map);
|
||||
}
|
||||
}
|
||||
else if (Level.Loaded.IsEndBiome)
|
||||
{
|
||||
var transitionType = GetAvailableTransition(out _, out Submarine leavingSub);
|
||||
if (transitionType == TransitionType.ProgressToNextLocation)
|
||||
{
|
||||
LoadNewLevel();
|
||||
}
|
||||
}
|
||||
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.AtStartExit)
|
||||
if (!Submarine.MainSub.AtStartExit && !Level.Loaded.StartOutpost.ExitPoints.Any())
|
||||
{
|
||||
ForceMapUI = true;
|
||||
CampaignUI.SelectTab(InteractionType.Map);
|
||||
@@ -604,11 +584,11 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
var transitionType = GetAvailableTransition(out _, out Submarine leavingSub);
|
||||
if (transitionType == TransitionType.End)
|
||||
if (Level.Loaded.IsEndBiome && transitionType == TransitionType.ProgressToNextLocation)
|
||||
{
|
||||
EndCampaign();
|
||||
LoadNewLevel();
|
||||
}
|
||||
if (transitionType == TransitionType.ProgressToNextLocation &&
|
||||
else if (transitionType == TransitionType.ProgressToNextLocation &&
|
||||
Level.Loaded.EndOutpost != null && Level.Loaded.EndOutpost.DockedTo.Contains(leavingSub))
|
||||
{
|
||||
LoadNewLevel();
|
||||
|
||||
+64
-522
@@ -1,7 +1,5 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.IO;
|
||||
using Barotrauma.Items.Components;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -25,107 +23,9 @@ namespace Barotrauma.Tutorials
|
||||
#region Tutorial variables
|
||||
|
||||
public readonly Identifier Identifier;
|
||||
|
||||
public LocalizedString DisplayName { get; }
|
||||
|
||||
public bool ContentRunning { get; private set; }
|
||||
|
||||
private GUIComponent infoBox;
|
||||
private Action infoBoxClosedCallback;
|
||||
|
||||
private VideoPlayer videoPlayer;
|
||||
private Point screenResolution;
|
||||
private WindowMode windowMode;
|
||||
private float prevUIScale;
|
||||
|
||||
private GUILayoutGroup objectiveGroup;
|
||||
private readonly LocalizedString objectiveTextTranslated;
|
||||
|
||||
private readonly List<Segment> ActiveObjectives = new List<Segment>();
|
||||
private const float ObjectiveComponentAnimationTime = 1.5f;
|
||||
private Segment ActiveContentSegment { get; set; }
|
||||
|
||||
public class Segment
|
||||
{
|
||||
public readonly record struct Text(
|
||||
Identifier Tag,
|
||||
int Width = DefaultWidth,
|
||||
int Height = DefaultHeight,
|
||||
Anchor Anchor = Anchor.Center);
|
||||
|
||||
public readonly record struct Video(
|
||||
string FullPath,
|
||||
Identifier TextTag,
|
||||
int Width = DefaultWidth,
|
||||
int Height = DefaultHeight)
|
||||
{
|
||||
public string FileName => Path.GetFileName(FullPath.CleanUpPath());
|
||||
public string ContentPath => Path.GetDirectoryName(FullPath.CleanUpPath());
|
||||
}
|
||||
|
||||
private const int DefaultWidth = 450;
|
||||
private const int DefaultHeight = 80;
|
||||
|
||||
public GUIImage ObjectiveStateIndicator;
|
||||
public GUIButton ObjectiveButton;
|
||||
public GUITextBlock LinkedTextBlock;
|
||||
public LocalizedString ObjectiveText;
|
||||
|
||||
public readonly Identifier Id;
|
||||
public readonly Text TextContent;
|
||||
public readonly Video VideoContent;
|
||||
public readonly AutoPlayVideo AutoPlayVideo;
|
||||
|
||||
public Action OnClickObjective;
|
||||
|
||||
public TutorialSegmentType SegmentType { get; private set; }
|
||||
|
||||
public static Segment CreateInfoBoxSegment(Identifier id, Identifier objectiveTextTag, AutoPlayVideo autoPlayVideo, Text textContent = default, Video videoContent = default)
|
||||
{
|
||||
return new Segment(id, objectiveTextTag, autoPlayVideo, textContent, videoContent);
|
||||
}
|
||||
|
||||
public static Segment CreateMessageBoxSegment(Identifier id, Identifier objectiveTextTag, Action onClickObjective)
|
||||
{
|
||||
return new Segment(id, objectiveTextTag, onClickObjective);
|
||||
}
|
||||
|
||||
public static Segment CreateObjectiveSegment(Identifier id, Identifier objectiveTextTag)
|
||||
{
|
||||
return new Segment(id, objectiveTextTag);
|
||||
}
|
||||
|
||||
private Segment(Identifier id, Identifier objectiveTextTag, AutoPlayVideo autoPlayVideo, Text textContent = default, Video videoContent = default)
|
||||
{
|
||||
Id = id;
|
||||
ObjectiveText = TextManager.ParseInputTypes(TextManager.Get(objectiveTextTag));
|
||||
AutoPlayVideo = autoPlayVideo;
|
||||
TextContent = textContent;
|
||||
VideoContent = videoContent;
|
||||
SegmentType = TutorialSegmentType.InfoBox;
|
||||
}
|
||||
|
||||
private Segment(Identifier id, Identifier objectiveTextTag, Action onClickObjective)
|
||||
{
|
||||
Id = id;
|
||||
ObjectiveText = TextManager.ParseInputTypes(TextManager.Get(objectiveTextTag));
|
||||
OnClickObjective = onClickObjective;
|
||||
SegmentType = TutorialSegmentType.MessageBox;
|
||||
}
|
||||
|
||||
private Segment(Identifier id, Identifier objectiveTextTag)
|
||||
{
|
||||
Id = id;
|
||||
ObjectiveText = TextManager.ParseInputTypes(TextManager.Get(objectiveTextTag));
|
||||
SegmentType = TutorialSegmentType.Objective;
|
||||
}
|
||||
|
||||
public void ConnectMessageBox(Segment messageBoxSegment)
|
||||
{
|
||||
SegmentType = TutorialSegmentType.MessageBox;
|
||||
OnClickObjective = messageBoxSegment.OnClickObjective;
|
||||
}
|
||||
}
|
||||
public LocalizedString Description { get; }
|
||||
|
||||
|
||||
private bool completed;
|
||||
public bool Completed
|
||||
@@ -163,6 +63,8 @@ namespace Barotrauma.Tutorials
|
||||
|
||||
public readonly List<(Entity entity, Identifier iconStyle)> Icons = new List<(Entity entity, Identifier iconStyle)>();
|
||||
|
||||
public bool Paused { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Tutorial Controls
|
||||
@@ -171,8 +73,7 @@ namespace Barotrauma.Tutorials
|
||||
{
|
||||
Identifier = $"tutorial.{prefab.Identifier}".ToIdentifier();
|
||||
DisplayName = TextManager.Get(Identifier);
|
||||
objectiveTextTranslated = TextManager.Get("Tutorial.Objective");
|
||||
|
||||
Description = TextManager.Get($"tutorial.{prefab.Identifier}.description");
|
||||
TutorialPrefab = prefab;
|
||||
eventPrefab = EventSet.GetEventPrefab(prefab.EventIdentifier);
|
||||
}
|
||||
@@ -260,35 +161,26 @@ namespace Barotrauma.Tutorials
|
||||
|
||||
tutorialCoroutine = CoroutineManager.StartCoroutine(UpdateState());
|
||||
|
||||
Initialize();
|
||||
GameMain.GameSession.CrewManager.AllowCharacterSwitch = TutorialPrefab.AllowCharacterSwitch;
|
||||
GameMain.GameSession.CrewManager.AutoHideCrewList();
|
||||
|
||||
if (Character.Controlled?.Inventory is CharacterInventory inventory)
|
||||
{
|
||||
foreach (Item item in inventory.AllItemsMod)
|
||||
{
|
||||
if (item.HasTag(TutorialPrefab.StartingItemTags)) { continue; }
|
||||
item.Unequip(Character.Controlled);
|
||||
Character.Controlled.Inventory.RemoveItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
GameMain.GameSession.CrewManager.AllowCharacterSwitch = TutorialPrefab.AllowCharacterSwitch;
|
||||
GameMain.GameSession.CrewManager.AutoHideCrewList();
|
||||
|
||||
if (Character.Controlled is Character character)
|
||||
{
|
||||
foreach (Item item in character.Inventory.AllItemsMod)
|
||||
{
|
||||
if (item.HasTag(TutorialPrefab.StartingItemTags)) { continue; }
|
||||
item.Unequip(character);
|
||||
character.Inventory.RemoveItem(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
videoPlayer = new VideoPlayer();
|
||||
GameMain.Instance.ShowLoading(Loading());
|
||||
ActiveObjectives.Clear();
|
||||
ActiveContentSegment = null;
|
||||
|
||||
CreateObjectiveFrame();
|
||||
ObjectiveManager.ResetObjectives();
|
||||
|
||||
// Setup doors: Clear all requirements, unless the door is setup as locked.
|
||||
foreach (var item in Item.ItemList)
|
||||
@@ -304,24 +196,8 @@ namespace Barotrauma.Tutorials
|
||||
}
|
||||
}
|
||||
|
||||
public void AddToGUIUpdateList()
|
||||
{
|
||||
if (GameMain.GraphicsWidth != screenResolution.X || GameMain.GraphicsHeight != screenResolution.Y || prevUIScale != GUI.Scale || GameSettings.CurrentConfig.Graphics.DisplayMode != windowMode)
|
||||
{
|
||||
CreateObjectiveFrame();
|
||||
}
|
||||
if (ActiveObjectives.Count > 0)
|
||||
{
|
||||
objectiveGroup?.AddToGUIUpdateList(order: -1);
|
||||
}
|
||||
infoBox?.AddToGUIUpdateList(order: 100);
|
||||
videoPlayer?.AddToGUIUpdateList(order: 100);
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
videoPlayer?.Update();
|
||||
|
||||
if (character != null)
|
||||
{
|
||||
if (character.Oxygen < 1)
|
||||
@@ -342,8 +218,7 @@ namespace Barotrauma.Tutorials
|
||||
{
|
||||
GUI.PreventPauseMenuToggle = false;
|
||||
}
|
||||
ContentRunning = false;
|
||||
infoBox = null;
|
||||
ObjectiveManager.ClearContent();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -374,18 +249,6 @@ namespace Barotrauma.Tutorials
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
public void CloseActiveContentGUI()
|
||||
{
|
||||
if (videoPlayer.IsPlaying)
|
||||
{
|
||||
videoPlayer.Stop();
|
||||
}
|
||||
else if (infoBox != null)
|
||||
{
|
||||
CloseInfoFrame();
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<CoroutineStatus> UpdateState()
|
||||
{
|
||||
while (GameMain.Instance.LoadingScreenOpen || Level.Loaded == null || Level.Loaded.Generating)
|
||||
@@ -432,13 +295,56 @@ namespace Barotrauma.Tutorials
|
||||
|
||||
yield return new WaitForSeconds(WaitBeforeFade);
|
||||
|
||||
Action onEnd = () => GameMain.MainMenuScreen.ReturnToMainMenu(null, null);
|
||||
|
||||
TutorialPrefab nextTutorialPrefab = null;
|
||||
bool displayEndMessage =
|
||||
TutorialPrefab.EndMessage.EndType == TutorialPrefab.EndType.Restart ||
|
||||
(TutorialPrefab.EndMessage.EndType == TutorialPrefab.EndType.Continue && TutorialPrefab.Prefabs.TryGet(TutorialPrefab.EndMessage.NextTutorialIdentifier, out nextTutorialPrefab));
|
||||
|
||||
if (displayEndMessage)
|
||||
{
|
||||
Paused = true;
|
||||
var endingMessageBox = new GUIMessageBox(
|
||||
headerText: "",
|
||||
text: TextManager.Get($"{Identifier}.completed"),
|
||||
buttons: new LocalizedString[]
|
||||
{
|
||||
TextManager.Get(nextTutorialPrefab is null ? "restart" : "campaigncontinue"),
|
||||
TextManager.Get("pausemenuquit")
|
||||
});
|
||||
|
||||
endingMessageBox.Buttons[0].OnClicked += (_, _) =>
|
||||
{
|
||||
if (nextTutorialPrefab is null)
|
||||
{
|
||||
onEnd = () => Restart(null, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
onEnd = () =>
|
||||
{
|
||||
GameMain.MainMenuScreen.ReturnToMainMenu(null, null);
|
||||
new Tutorial(nextTutorialPrefab).Start();
|
||||
};
|
||||
}
|
||||
return true;
|
||||
};
|
||||
endingMessageBox.Buttons[0].OnClicked += endingMessageBox.Close;
|
||||
endingMessageBox.Buttons[0].OnClicked += (_, _) => Paused = false;
|
||||
endingMessageBox.Buttons[1].OnClicked += endingMessageBox.Close;
|
||||
endingMessageBox.Buttons[1].OnClicked += (_, _) => Paused = false;
|
||||
}
|
||||
|
||||
while (Paused) { yield return CoroutineStatus.Running; }
|
||||
|
||||
var endCinematic = new CameraTransition(Submarine.MainSub, GameMain.GameScreen.Cam, null, Alignment.Center, panDuration: FadeOutTime);
|
||||
Completed = true;
|
||||
|
||||
while (endCinematic.Running) { yield return CoroutineStatus.Running; }
|
||||
|
||||
Stop();
|
||||
GameMain.MainMenuScreen.ReturnToMainMenu(null, null);
|
||||
onEnd();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -450,379 +356,15 @@ namespace Barotrauma.Tutorials
|
||||
return true;
|
||||
}
|
||||
|
||||
public void TriggerTutorialSegment(Segment segment, bool connectObjective = false)
|
||||
{
|
||||
if (segment.SegmentType != TutorialSegmentType.InfoBox)
|
||||
{
|
||||
ActiveObjectives.Add(segment);
|
||||
AddToObjectiveList(segment, connectObjective);
|
||||
return;
|
||||
}
|
||||
|
||||
Inventory.DraggingItems.Clear();
|
||||
ContentRunning = true;
|
||||
ActiveContentSegment = segment;
|
||||
|
||||
var title = TextManager.Get(segment.Id);
|
||||
LocalizedString tutorialText = TextManager.GetFormatted(segment.TextContent.Tag);
|
||||
tutorialText = TextManager.ParseInputTypes(tutorialText);
|
||||
|
||||
switch (segment.AutoPlayVideo)
|
||||
{
|
||||
case AutoPlayVideo.Yes:
|
||||
infoBox = CreateInfoFrame(
|
||||
title,
|
||||
tutorialText,
|
||||
segment.TextContent.Width,
|
||||
segment.TextContent.Height,
|
||||
segment.TextContent.Anchor,
|
||||
hasButton: true,
|
||||
onInfoBoxClosed: LoadActiveContentVideo);
|
||||
break;
|
||||
case AutoPlayVideo.No:
|
||||
infoBox = CreateInfoFrame(
|
||||
title,
|
||||
tutorialText,
|
||||
segment.TextContent.Width,
|
||||
segment.TextContent.Height,
|
||||
segment.TextContent.Anchor,
|
||||
hasButton: true,
|
||||
onInfoBoxClosed: StopCurrentContentSegment,
|
||||
onVideoButtonClicked: LoadActiveContentVideo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void CompleteTutorialSegment(Identifier segmentId)
|
||||
{
|
||||
if (GetActiveObjective(segmentId) is not Segment segment)
|
||||
{
|
||||
DebugConsole.AddWarning($"Warning: tried to complete the tutorial segment \"{segmentId}\" in tutorial \"{Identifier}\" but it isn't active!");
|
||||
return;
|
||||
}
|
||||
if (GUIStyle.GetComponentStyle("ObjectiveIndicatorCompleted") is GUIComponentStyle style)
|
||||
{
|
||||
//return if already completed
|
||||
if (segment.ObjectiveStateIndicator.Style == style) { return; }
|
||||
segment.ObjectiveStateIndicator.ApplyStyle(style);
|
||||
}
|
||||
segment.ObjectiveStateIndicator.Parent.Flash(color: GUIStyle.Green, flashDuration: 0.35f, useRectangleFlash: true);
|
||||
segment.ObjectiveButton.OnClicked = null;
|
||||
segment.ObjectiveButton.CanBeFocused = false;
|
||||
GameAnalyticsManager.AddDesignEvent($"Tutorial:{Identifier}:{segmentId}:Completed");
|
||||
}
|
||||
|
||||
public void RemoveTutorialSegment(Identifier segmentId)
|
||||
{
|
||||
if (GetActiveObjective(segmentId) is not Segment segment)
|
||||
{
|
||||
DebugConsole.AddWarning($"Warning: tried to remove the tutorial segment \"{segmentId}\" in tutorial \"{Identifier}\" but it isn't active!");
|
||||
return;
|
||||
}
|
||||
segment.ObjectiveStateIndicator.FadeOut(ObjectiveComponentAnimationTime, false);
|
||||
segment.LinkedTextBlock.FadeOut(ObjectiveComponentAnimationTime, false);
|
||||
var parent = segment.LinkedTextBlock.Parent;
|
||||
parent.FadeOut(ObjectiveComponentAnimationTime, true, onRemove: () =>
|
||||
{
|
||||
ActiveObjectives.Remove(segment);
|
||||
objectiveGroup?.Recalculate();
|
||||
});
|
||||
parent.RectTransform.MoveOverTime(GetObjectiveHiddenPosition(parent.RectTransform), ObjectiveComponentAnimationTime);
|
||||
segment.ObjectiveButton.OnClicked = null;
|
||||
segment.ObjectiveButton.CanBeFocused = false;
|
||||
}
|
||||
|
||||
private Segment GetActiveObjective(Identifier id) => ActiveObjectives.FirstOrDefault(s => s.Id == id);
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (tutorialCoroutine != null)
|
||||
{
|
||||
CoroutineManager.StopCoroutines(tutorialCoroutine);
|
||||
}
|
||||
ContentRunning = false;
|
||||
infoBox = null;
|
||||
videoPlayer?.Remove();
|
||||
ObjectiveManager.ResetUI();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Objectives
|
||||
|
||||
/// <summary>
|
||||
/// Create the objective list that holds the objectives (called on start and on resolution change)
|
||||
/// </summary>
|
||||
private void CreateObjectiveFrame()
|
||||
{
|
||||
var objectiveListFrame = new GUIFrame(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.TutorialObjectiveListArea, GUI.Canvas), style: null);
|
||||
objectiveGroup = new GUILayoutGroup(new RectTransform(Vector2.One, objectiveListFrame.RectTransform))
|
||||
{
|
||||
AbsoluteSpacing = (int)GUIStyle.Font.LineHeight
|
||||
};
|
||||
for (int i = 0; i < ActiveObjectives.Count; i++)
|
||||
{
|
||||
AddToObjectiveList(ActiveObjectives[i]);
|
||||
}
|
||||
screenResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
|
||||
windowMode = GameSettings.CurrentConfig.Graphics.DisplayMode;
|
||||
prevUIScale = GUI.Scale;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops content running and adds the active segment to the objective list
|
||||
/// </summary>
|
||||
private void StopCurrentContentSegment()
|
||||
{
|
||||
if (!ActiveContentSegment.ObjectiveText.IsNullOrEmpty())
|
||||
{
|
||||
ActiveObjectives.Add(ActiveContentSegment);
|
||||
AddToObjectiveList(ActiveContentSegment);
|
||||
}
|
||||
ContentRunning = false;
|
||||
ActiveContentSegment = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the segment to the objective list
|
||||
/// </summary>
|
||||
private void AddToObjectiveList(Segment segment, bool connectExisting = false)
|
||||
{
|
||||
if (connectExisting)
|
||||
{
|
||||
if (ActiveObjectives.Find(o => o.Id == segment.Id) is { } existingSegment)
|
||||
{
|
||||
existingSegment.ConnectMessageBox(segment);
|
||||
SetButtonBehavior(existingSegment);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var frameRt = new RectTransform(new Vector2(1.0f, 0.1f), objectiveGroup.RectTransform)
|
||||
{
|
||||
AbsoluteOffset = GetObjectiveHiddenPosition(),
|
||||
MinSize = new Point(0, objectiveGroup.AbsoluteSpacing)
|
||||
};
|
||||
var frame = new GUIFrame(frameRt, style: null)
|
||||
{
|
||||
CanBeFocused = true
|
||||
};
|
||||
objectiveGroup.Recalculate();
|
||||
|
||||
segment.LinkedTextBlock = new GUITextBlock(
|
||||
new RectTransform(new Point(frameRt.Rect.Width - objectiveGroup.AbsoluteSpacing, 0), frame.RectTransform, anchor: Anchor.TopRight),
|
||||
TextManager.ParseInputTypes(segment.ObjectiveText),
|
||||
wrap: true);
|
||||
|
||||
var size = new Point(segment.LinkedTextBlock.Rect.Width, segment.LinkedTextBlock.Rect.Height);
|
||||
segment.LinkedTextBlock.RectTransform.NonScaledSize = size;
|
||||
segment.LinkedTextBlock.RectTransform.MinSize = size;
|
||||
segment.LinkedTextBlock.RectTransform.MaxSize = size;
|
||||
segment.LinkedTextBlock.RectTransform.IsFixedSize = true;
|
||||
frame.RectTransform.Resize(new Point(frame.Rect.Width, segment.LinkedTextBlock.RectTransform.Rect.Height), resizeChildren: false);
|
||||
frame.RectTransform.IsFixedSize = true;
|
||||
|
||||
var indicatorRt = new RectTransform(new Point(objectiveGroup.AbsoluteSpacing), frame.RectTransform, isFixedSize: true);
|
||||
segment.ObjectiveStateIndicator = new GUIImage(indicatorRt, "ObjectiveIndicatorIncomplete");
|
||||
|
||||
SetTransparent(segment.LinkedTextBlock);
|
||||
|
||||
segment.ObjectiveButton = new GUIButton(new RectTransform(Vector2.One, segment.LinkedTextBlock.RectTransform, Anchor.TopLeft, Pivot.TopLeft), style: null)
|
||||
{
|
||||
ToolTip = objectiveTextTranslated
|
||||
};
|
||||
SetButtonBehavior(segment);
|
||||
SetTransparent(segment.ObjectiveButton);
|
||||
|
||||
frameRt.MoveOverTime(new Point(0, frameRt.AbsoluteOffset.Y), ObjectiveComponentAnimationTime, onDoneMoving: () => objectiveGroup?.Recalculate());
|
||||
|
||||
static void SetTransparent(GUIComponent component) => component.Color = component.HoverColor = component.PressedColor = component.SelectedColor = Color.Transparent;
|
||||
|
||||
void SetButtonBehavior(Segment segment)
|
||||
{
|
||||
segment.ObjectiveButton.CanBeFocused = segment.SegmentType != TutorialSegmentType.Objective;
|
||||
segment.ObjectiveButton.OnClicked = (GUIButton btn, object userdata) =>
|
||||
{
|
||||
if (segment.SegmentType == TutorialSegmentType.InfoBox)
|
||||
{
|
||||
if (segment.AutoPlayVideo == AutoPlayVideo.Yes)
|
||||
{
|
||||
ReplaySegmentVideo(segment);
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowSegmentText(segment);
|
||||
}
|
||||
}
|
||||
else if (segment.SegmentType == TutorialSegmentType.MessageBox)
|
||||
{
|
||||
segment.OnClickObjective?.Invoke();
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private void ReplaySegmentVideo(Segment segment)
|
||||
{
|
||||
if (ContentRunning) { return; }
|
||||
Inventory.DraggingItems.Clear();
|
||||
ContentRunning = true;
|
||||
LoadVideo(segment);
|
||||
}
|
||||
|
||||
private void ShowSegmentText(Segment segment)
|
||||
{
|
||||
if (ContentRunning) { return; }
|
||||
Inventory.DraggingItems.Clear();
|
||||
ContentRunning = true;
|
||||
ActiveContentSegment = segment;
|
||||
infoBox = CreateInfoFrame(
|
||||
TextManager.Get(segment.Id),
|
||||
TextManager.Get(segment.TextContent.Tag),
|
||||
segment.TextContent.Width,
|
||||
segment.TextContent.Height,
|
||||
segment.TextContent.Anchor,
|
||||
hasButton: true,
|
||||
onInfoBoxClosed: () => ContentRunning = false,
|
||||
onVideoButtonClicked: () => LoadVideo(segment));
|
||||
}
|
||||
|
||||
private Point GetObjectiveHiddenPosition(RectTransform rt = null)
|
||||
{
|
||||
return new Point(GameMain.GraphicsWidth - objectiveGroup.Rect.X, rt?.AbsoluteOffset.Y ?? 0);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region InfoFrame
|
||||
|
||||
private void CloseInfoFrame() => CloseInfoFrame(null, null);
|
||||
|
||||
private bool CloseInfoFrame(GUIButton button, object userData)
|
||||
{
|
||||
infoBox = null;
|
||||
infoBoxClosedCallback?.Invoke();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
// Creates and displays a tutorial info box
|
||||
/// </summary>
|
||||
private GUIComponent CreateInfoFrame(LocalizedString title, LocalizedString text, int width = 300, int height = 80, Anchor anchor = Anchor.TopRight, bool hasButton = false, Action onInfoBoxClosed = null, Action onVideoButtonClicked = null)
|
||||
{
|
||||
if (hasButton)
|
||||
{
|
||||
height += 60;
|
||||
}
|
||||
|
||||
width = (int)(width * GUI.Scale);
|
||||
height = (int)(height * GUI.Scale);
|
||||
|
||||
LocalizedString wrappedText = ToolBox.WrapText(text, width, GUIStyle.Font);
|
||||
height += (int)GUIStyle.Font.MeasureString(wrappedText).Y;
|
||||
|
||||
if (title.Length > 0)
|
||||
{
|
||||
height += (int)GUIStyle.Font.MeasureString(title).Y + (int)(150 * GUI.Scale);
|
||||
}
|
||||
|
||||
var background = new GUIFrame(new RectTransform(new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight), GUI.Canvas, Anchor.Center), style: "GUIBackgroundBlocker");
|
||||
|
||||
var infoBlock = new GUIFrame(new RectTransform(new Point(width, height), background.RectTransform, anchor));
|
||||
infoBlock.Flash(GUIStyle.Green);
|
||||
|
||||
var infoContent = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.9f), infoBlock.RectTransform, Anchor.Center))
|
||||
{
|
||||
Stretch = true,
|
||||
AbsoluteSpacing = 5
|
||||
};
|
||||
|
||||
if (title.Length > 0)
|
||||
{
|
||||
var titleBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoContent.RectTransform),
|
||||
title, font: GUIStyle.LargeFont, textAlignment: Alignment.Center, textColor: new Color(253, 174, 0));
|
||||
titleBlock.RectTransform.IsFixedSize = true;
|
||||
}
|
||||
|
||||
text = RichString.Rich(text);
|
||||
GUITextBlock textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoContent.RectTransform), text, wrap: true);
|
||||
|
||||
textBlock.RectTransform.IsFixedSize = true;
|
||||
infoBoxClosedCallback = onInfoBoxClosed;
|
||||
|
||||
if (hasButton)
|
||||
{
|
||||
var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.15f), infoContent.RectTransform), isHorizontal: true)
|
||||
{
|
||||
RelativeSpacing = 0.1f
|
||||
};
|
||||
buttonContainer.RectTransform.IsFixedSize = true;
|
||||
|
||||
if (onVideoButtonClicked != null)
|
||||
{
|
||||
buttonContainer.Stretch = true;
|
||||
var videoButton = new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), buttonContainer.RectTransform),
|
||||
TextManager.Get("Video"), style: "GUIButtonLarge")
|
||||
{
|
||||
OnClicked = (GUIButton button, object obj) =>
|
||||
{
|
||||
onVideoButtonClicked();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
buttonContainer.Stretch = false;
|
||||
buttonContainer.ChildAnchor = Anchor.Center;
|
||||
}
|
||||
|
||||
var okButton = new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), buttonContainer.RectTransform),
|
||||
TextManager.Get("OK"), style: "GUIButtonLarge")
|
||||
{
|
||||
OnClicked = CloseInfoFrame
|
||||
};
|
||||
}
|
||||
|
||||
infoBlock.RectTransform.NonScaledSize = new Point(infoBlock.Rect.Width, (int)(infoContent.Children.Sum(c => c.Rect.Height + infoContent.AbsoluteSpacing) / infoContent.RectTransform.RelativeSize.Y));
|
||||
|
||||
SoundPlayer.PlayUISound(GUISoundType.UIMessage);
|
||||
|
||||
return background;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Video
|
||||
|
||||
private void LoadVideo(Segment segment)
|
||||
{
|
||||
videoPlayer ??= new VideoPlayer();
|
||||
if (segment.AutoPlayVideo == AutoPlayVideo.Yes)
|
||||
{
|
||||
videoPlayer.LoadContent(
|
||||
contentPath: segment.VideoContent.ContentPath,
|
||||
videoSettings: new VideoPlayer.VideoSettings(segment.VideoContent.FileName),
|
||||
textSettings: new VideoPlayer.TextSettings(segment.VideoContent.TextTag, segment.VideoContent.Width),
|
||||
contentId: segment.Id,
|
||||
startPlayback: true,
|
||||
objective: segment.ObjectiveText,
|
||||
onStop: StopCurrentContentSegment);
|
||||
}
|
||||
else
|
||||
{
|
||||
videoPlayer.LoadContent(
|
||||
contentPath: segment.VideoContent.ContentPath,
|
||||
videoSettings: new VideoPlayer.VideoSettings(segment.VideoContent.FileName),
|
||||
textSettings: null,
|
||||
contentId: segment.Id,
|
||||
startPlayback: true,
|
||||
objective: string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadActiveContentVideo() => LoadVideo(ActiveContentSegment);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
+2
-6
@@ -6,6 +6,8 @@ namespace Barotrauma
|
||||
{
|
||||
public Tutorial Tutorial;
|
||||
|
||||
public override bool Paused => Tutorial.Paused;
|
||||
|
||||
public TutorialMode(GameModePreset preset) : base(preset) { }
|
||||
|
||||
public override void Start()
|
||||
@@ -19,12 +21,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public override void AddToGUIUpdateList()
|
||||
{
|
||||
base.AddToGUIUpdateList();
|
||||
Tutorial.AddToGUIUpdateList();
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
base.Update(deltaTime);
|
||||
|
||||
Reference in New Issue
Block a user