Files
LuaCsForBarotraumaEP/Barotrauma/BarotraumaClient/ClientSource/GUI/LoadingScreen.cs
2023-10-02 16:43:54 +03:00

411 lines
17 KiB
C#

using Barotrauma.Extensions;
using Barotrauma.Media;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma
{
sealed class LoadingScreen
{
private readonly Sprite defaultBackgroundTexture, overlay;
private readonly SpriteSheet decorativeGraph, decorativeMap;
private Sprite currentBackgroundTexture;
private readonly Sprite noiseSprite;
private string randText = "";
private Sprite languageSelectionCursor;
private ScalableFont languageSelectionFont, languageSelectionFontCJK;
private Video currSplashScreen;
private DateTime videoStartTime;
private bool mirrorBackground;
public struct PendingSplashScreen
{
public string Filename;
public float Gain;
public PendingSplashScreen(string filename, float gain)
{
Filename = filename;
Gain = gain;
}
}
/// <summary>
/// Triplet.first = filepath, Triplet.second = resolution, Triplet.third = audio gain
/// </summary>
public readonly ConcurrentQueue<PendingSplashScreen> PendingSplashScreens = new ConcurrentQueue<PendingSplashScreen>();
public bool PlayingSplashScreen
{
get
{
return currSplashScreen != null || PendingSplashScreens.Count > 0;
}
}
private RichString selectedTip;
private void SetSelectedTip(LocalizedString tip)
{
selectedTip = RichString.Rich(tip);
}
public float LoadState;
public bool WaitForLanguageSelection
{
get;
set;
}
public LanguageIdentifier[] AvailableLanguages = null;
public LoadingScreen(GraphicsDevice graphics)
{
defaultBackgroundTexture = new Sprite("Content/Map/LocationPortraits/MainMenu1.png", Vector2.Zero);
decorativeMap = new SpriteSheet("Content/Map/MapHUD.png", 6, 5, Vector2.Zero, sourceRect: new Rectangle(0, 0, 2048, 640));
decorativeGraph = new SpriteSheet("Content/Map/MapHUD.png", 4, 10, Vector2.Zero, sourceRect: new Rectangle(1025, 1259, 1024, 732));
overlay = new Sprite("Content/UI/MainMenuVignette.png", Vector2.Zero);
noiseSprite = new Sprite("Content/UI/noise.png", Vector2.Zero);
SetSelectedTip(TextManager.Get("LoadingScreenTip"));
}
public void Draw(SpriteBatch spriteBatch, GraphicsDevice graphics, float deltaTime)
{
if (GameSettings.CurrentConfig.EnableSplashScreen)
{
try
{
DrawSplashScreen(spriteBatch, graphics);
if (currSplashScreen != null || PendingSplashScreens.Count > 0) { return; }
}
catch (Exception e)
{
DebugConsole.ThrowError("Playing splash screen video failed", e);
DisableSplashScreen();
}
}
drawn = true;
currentBackgroundTexture ??= defaultBackgroundTexture;
float overlayScale = Math.Min(GameMain.GraphicsWidth / overlay.size.X, GameMain.GraphicsHeight / overlay.size.Y);
Rectangle drawArea = new Rectangle(
(int)(overlay.size.X * overlayScale / 2), 0,
(int)(GameMain.GraphicsWidth - overlay.size.X * overlayScale / 2), GameMain.GraphicsHeight);
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, samplerState: GUI.SamplerState);
GUI.DrawBackgroundSprite(spriteBatch, currentBackgroundTexture, Color.White, drawArea,
spriteEffects: mirrorBackground ? SpriteEffects.FlipHorizontally : SpriteEffects.None);
overlay.Draw(spriteBatch, Vector2.Zero, scale: overlayScale);
double noiseT = Timing.TotalTime * 0.02f;
float noiseStrength = (float)PerlinNoise.CalculatePerlin(noiseT, noiseT, 0);
float noiseScale = (float)PerlinNoise.CalculatePerlin(noiseT * 5.0f, noiseT * 2.0f, 0) * 4.0f;
noiseSprite.DrawTiled(spriteBatch, Vector2.Zero, new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight),
startOffset: new Vector2(Rand.Range(0.0f, noiseSprite.SourceRect.Width), Rand.Range(0.0f, noiseSprite.SourceRect.Height)),
color: Color.White * noiseStrength * 0.1f,
textureScale: Vector2.One * noiseScale);
Vector2 textPos = new Vector2((int)(GameMain.GraphicsWidth * 0.05f), (int)(GameMain.GraphicsHeight * 0.75f));
if (WaitForLanguageSelection)
{
DrawLanguageSelectionPrompt(spriteBatch, graphics);
}
else
{
LocalizedString loadText;
var loadState = LoadState; // avoid multiple reads here to prevent jank
if (loadState >= 100.0f)
{
#if DEBUG
if (GameSettings.CurrentConfig.AutomaticQuickStartEnabled || GameSettings.CurrentConfig.AutomaticCampaignLoadEnabled || (GameSettings.CurrentConfig.TestScreenEnabled && GameMain.FirstLoad))
{
loadText = "QUICKSTARTING ...";
}
else
{
#endif
loadText = TextManager.Get("PressAnyKey");
#if DEBUG
}
#endif
}
else
{
loadText = TextManager.Get("Loading");
if (loadState >= 0f)
{
loadText += $" {loadState:N0} %";
}
#if DEBUG
if (GameMain.FirstLoad && GameMain.CancelQuickStart)
{
loadText += " (Quickstart aborted)";
}
#endif
}
if (GUIStyle.LargeFont.HasValue)
{
GUIStyle.LargeFont.DrawString(spriteBatch, loadText.ToUpper(),
textPos,
Color.White);
textPos.Y += GUIStyle.LargeFont.MeasureString(loadText.ToUpper()).Y * 1.2f;
}
if (GUIStyle.Font.HasValue && selectedTip != null)
{
string wrappedTip = ToolBox.WrapText(selectedTip.SanitizedValue, GameMain.GraphicsWidth * 0.3f, GUIStyle.Font.Value);
string[] lines = wrappedTip.Split('\n');
float lineHeight = GUIStyle.Font.MeasureString(selectedTip).Y;
if (selectedTip.RichTextData != null)
{
int rtdOffset = 0;
for (int i = 0; i < lines.Length; i++)
{
GUIStyle.Font.DrawStringWithColors(spriteBatch, lines[i],
new Vector2(textPos.X, (int)(textPos.Y + i * lineHeight)),
Color.White,
0f, Vector2.Zero, 1f, SpriteEffects.None, 0f, selectedTip.RichTextData.Value, rtdOffset);
rtdOffset += lines[i].Length;
}
}
else
{
for (int i = 0; i < lines.Length; i++)
{
GUIStyle.Font.DrawString(spriteBatch, lines[i],
new Vector2(textPos.X, (int)(textPos.Y + i * lineHeight)),
new Color(228, 217, 167, 255));
}
}
}
}
GUI.DrawMessageBoxesOnly(spriteBatch);
spriteBatch.End();
spriteBatch.Begin(blendState: BlendState.Additive);
Vector2 decorativeScale = new Vector2(GameMain.GraphicsHeight / 1080.0f);
float noiseVal = (float)PerlinNoise.CalculatePerlin(Timing.TotalTime * 0.25f, Timing.TotalTime * 0.5f, 0);
if (!WaitForLanguageSelection)
{
decorativeGraph.Draw(spriteBatch, (int)(decorativeGraph.FrameCount * noiseVal),
new Vector2(GameMain.GraphicsWidth * 0.001f, textPos.Y),
Color.White, new Vector2(0, decorativeMap.FrameSize.Y), 0.0f, decorativeScale, SpriteEffects.FlipVertically);
}
decorativeMap.Draw(spriteBatch, (int)(decorativeMap.FrameCount * noiseVal),
new Vector2(GameMain.GraphicsWidth * 0.99f, GameMain.GraphicsHeight * 0.01f),
Color.White, new Vector2(decorativeMap.FrameSize.X, 0), 0.0f, decorativeScale, SpriteEffects.FlipHorizontally | SpriteEffects.FlipVertically);
if (noiseVal < 0.2f)
{
//SCP-CB reference
randText = (new string[] { "NIL", "black white gray", "Sometimes we would have had time to scream", "e8m106]af", "NO" }).GetRandomUnsynced();
}
else if (noiseVal < 0.3f)
{
randText = ToolBox.RandomSeed(9);
}
else if (noiseVal < 0.5f)
{
randText =
Rand.Int(100).ToString().PadLeft(2, '0') + " " +
Rand.Int(100).ToString().PadLeft(2, '0') + " " +
Rand.Int(100).ToString().PadLeft(2, '0') + " " +
Rand.Int(100).ToString().PadLeft(2, '0');
}
if (GUIStyle.LargeFont.HasValue)
{
Vector2 textSize = GUIStyle.LargeFont.MeasureString(randText);
GUIStyle.LargeFont.DrawString(spriteBatch, randText,
new Vector2(GameMain.GraphicsWidth * 0.95f - textSize.X, GameMain.GraphicsHeight * 0.06f),
Color.White * (1.0f - noiseVal));
}
spriteBatch.End();
}
private void DrawLanguageSelectionPrompt(SpriteBatch spriteBatch, GraphicsDevice graphicsDevice)
{
if (AvailableLanguages is null) { return; }
if (languageSelectionFont == null)
{
languageSelectionFont = new ScalableFont("Content/Fonts/NotoSans/NotoSans-Bold.ttf",
(uint)(30 * (GameMain.GraphicsHeight / 1080.0f)), graphicsDevice);
}
if (languageSelectionFontCJK == null)
{
languageSelectionFontCJK = new ScalableFont("Content/Fonts/NotoSans/NotoSansCJKsc-Bold.otf",
(uint)(30 * (GameMain.GraphicsHeight / 1080.0f)), graphicsDevice, dynamicLoading: true);
}
if (languageSelectionCursor == null)
{
languageSelectionCursor = new Sprite("Content/UI/cursor.png", Vector2.Zero);
}
Vector2 textPos = new Vector2((int)(GameMain.GraphicsWidth * 0.05f), (int)(GameMain.GraphicsHeight * 0.3f));
Vector2 textSpacing = new Vector2(0.0f, GameMain.GraphicsHeight * 0.5f / AvailableLanguages.Length);
foreach (LanguageIdentifier language in AvailableLanguages)
{
string localizedLanguageName = TextManager.GetTranslatedLanguageName(language);
var font = TextManager.IsCJK(localizedLanguageName) ? languageSelectionFontCJK : languageSelectionFont;
Vector2 textSize = font.MeasureString(localizedLanguageName);
bool hover =
PlayerInput.MousePosition.X > textPos.X && PlayerInput.MousePosition.X < textPos.X + textSize.X &&
PlayerInput.MousePosition.Y > textPos.Y && PlayerInput.MousePosition.Y < textPos.Y + textSize.Y;
font.DrawString(spriteBatch, localizedLanguageName, textPos,
hover ? Color.White : Color.White * 0.6f);
if (hover && PlayerInput.PrimaryMouseButtonClicked())
{
var config = GameSettings.CurrentConfig;
config.Language = language;
GameSettings.SetCurrentConfig(config);
//reload tip in the selected language
SetSelectedTip(TextManager.Get("LoadingScreenTip"));
WaitForLanguageSelection = false;
languageSelectionFont?.Dispose(); languageSelectionFont = null;
languageSelectionFontCJK?.Dispose(); languageSelectionFontCJK = null;
break;
}
textPos += textSpacing;
}
languageSelectionCursor.Draw(spriteBatch, PlayerInput.LatestMousePosition, scale: 0.5f);
}
private void DrawSplashScreen(SpriteBatch spriteBatch, GraphicsDevice graphics)
{
if (currSplashScreen == null)
{
if (!PendingSplashScreens.TryDequeue(out var newSplashScreen)) { return; }
string fileName = newSplashScreen.Filename;
try
{
currSplashScreen = Video.Load(graphics, GameMain.SoundManager, fileName);
currSplashScreen.AudioGain = newSplashScreen.Gain;
videoStartTime = DateTime.Now;
}
catch (Exception e)
{
DisableSplashScreen();
DebugConsole.ThrowError("Playing the splash screen \"" + fileName + "\" failed.", e);
PendingSplashScreens.Clear();
currSplashScreen = null;
}
}
if (currSplashScreen == null) { return; }
if (currSplashScreen.IsPlaying)
{
graphics.Clear(Color.Black);
float videoAspectRatio = (float)currSplashScreen.Width / (float)currSplashScreen.Height;
int width; int height;
if (GameMain.GraphicsHeight * videoAspectRatio > GameMain.GraphicsWidth)
{
width = GameMain.GraphicsWidth;
height = (int)(GameMain.GraphicsWidth / videoAspectRatio);
}
else
{
width = (int)(GameMain.GraphicsHeight * videoAspectRatio);
height = GameMain.GraphicsHeight;
}
spriteBatch.Begin();
spriteBatch.Draw(
currSplashScreen.GetTexture(),
destinationRectangle: new Rectangle(
GameMain.GraphicsWidth / 2 - width / 2,
GameMain.GraphicsHeight / 2 - height / 2,
width,
height),
sourceRectangle: new Rectangle(0, 0, currSplashScreen.Width, currSplashScreen.Height),
Color.White,
rotation: 0.0f,
origin: Vector2.Zero,
SpriteEffects.None,
layerDepth: 0.0f);
spriteBatch.End();
if (DateTime.Now > videoStartTime + new TimeSpan(0, 0, 0, 0, milliseconds: 500)
&& GameMain.WindowActive
&& (PlayerInput.KeyHit(Keys.Escape)
|| PlayerInput.KeyHit(Keys.Space)
|| PlayerInput.KeyHit(Keys.Enter)
|| PlayerInput.PrimaryMouseButtonDown()))
{
currSplashScreen.Dispose(); currSplashScreen = null;
}
}
else if (DateTime.Now > videoStartTime + new TimeSpan(0, 0, 0, 0, milliseconds: 1500))
{
currSplashScreen.Dispose(); currSplashScreen = null;
}
}
private void DisableSplashScreen()
{
var config = GameSettings.CurrentConfig;
config.EnableSplashScreen = false;
GameSettings.SetCurrentConfig(config);
}
bool drawn;
public IEnumerable<CoroutineStatus> DoLoading(IEnumerable<CoroutineStatus> loader)
{
drawn = false;
LoadState = -1f;
SetSelectedTip(TextManager.Get("LoadingScreenTip"));
currentBackgroundTexture = LocationType.Prefabs.Where(p => p.UsePortraitInRandomLoadingScreens).GetRandomUnsynced()?.GetPortrait(Rand.Int(int.MaxValue));
if (GameMain.GameSession?.GameMode?.Missions is { } missions && missions.Any(m => m.Prefab.HasPortraits))
{
currentBackgroundTexture = missions.Where(m => m.Prefab.HasPortraits).First().Prefab.GetPortrait(Rand.Int(int.MaxValue));
}
mirrorBackground = Rand.Range(0.0f, 1.0f) < 0.5f;
while (!drawn)
{
yield return CoroutineStatus.Running;
}
CoroutineManager.StartCoroutine(loader);
yield return CoroutineStatus.Running;
while (CoroutineManager.IsCoroutineRunning(loader.ToString()))
{
yield return CoroutineStatus.Running;
}
LoadState = 100.0f;
yield return CoroutineStatus.Success;
}
}
}