using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using System; using System.Collections.Generic; using System.Xml.Linq; using Barotrauma.Media; using System.Linq; using Barotrauma.Extensions; namespace Barotrauma { class LoadingScreen { private readonly Texture2D defaultBackgroundTexture, overlay; private readonly SpriteSheet decorativeGraph, decorativeMap; private Texture2D currentBackgroundTexture; private Sprite noiseSprite; private string randText = ""; private Sprite languageSelectionCursor; private ScalableFont languageSelectionFont, languageSelectionFontCJK; private Video currSplashScreen; private DateTime videoStartTime; public struct PendingSplashScreen { public string Filename; public float Gain; public PendingSplashScreen(string filename, float gain) { Filename = filename; Gain = gain; } } private Queue pendingSplashScreens = new Queue(); /// /// Triplet.first = filepath, Triplet.second = resolution, Triplet.third = audio gain /// public Queue PendingSplashScreens { get { lock (loadMutex) { return pendingSplashScreens; } } set { lock (loadMutex) { pendingSplashScreens = value; } } } public bool PlayingSplashScreen { get { lock (loadMutex) { return currSplashScreen != null || pendingSplashScreens.Count > 0; } } } private string selectedTip; private readonly object loadMutex = new object(); private float? loadState; public float? LoadState { get { lock (loadMutex) { return loadState; } } set { lock (loadMutex) { loadState = value; DrawLoadingText = true; } } } public bool DrawLoadingText { get; set; } public bool WaitForLanguageSelection { get; set; } public LoadingScreen(GraphicsDevice graphics) { defaultBackgroundTexture = TextureLoader.FromFile("Content/Map/LocationPortraits/AlienRuins.png"); 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 = TextureLoader.FromFile("Content/UI/LoadingScreenOverlay.png"); noiseSprite = new Sprite("Content/UI/noise.png", Vector2.Zero); DrawLoadingText = true; selectedTip = TextManager.Get("LoadingScreenTip", true); } public void Draw(SpriteBatch spriteBatch, GraphicsDevice graphics, float deltaTime) { if (GameMain.Config.EnableSplashScreen) { try { DrawSplashScreen(spriteBatch, graphics); if (currSplashScreen != null || PendingSplashScreens.Count > 0) { return; } } catch (Exception e) { DebugConsole.ThrowError("Playing splash screen video failed", e); GameMain.Config.EnableSplashScreen = false; } } var titleStyle = GUI.Style?.GetComponentStyle("TitleText"); Sprite titleSprite = null; if (!WaitForLanguageSelection && titleStyle != null && titleStyle.Sprites.ContainsKey(GUIComponent.ComponentState.None)) { titleSprite = titleStyle.Sprites[GUIComponent.ComponentState.None].First()?.Sprite; } drawn = true; currentBackgroundTexture ??= defaultBackgroundTexture; spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, samplerState: GUI.SamplerState); float scale = (GameMain.GraphicsWidth / (float)currentBackgroundTexture.Width) * 1.2f; float paddingX = currentBackgroundTexture.Width * scale - GameMain.GraphicsWidth; float paddingY = currentBackgroundTexture.Height * scale - GameMain.GraphicsHeight; double noiseT = (Timing.TotalTime * 0.02f); Vector2 pos = new Vector2((float)PerlinNoise.CalculatePerlin(noiseT, noiseT, 0) - 0.5f, (float)PerlinNoise.CalculatePerlin(noiseT, noiseT, 0.5f) - 0.5f); pos = new Vector2(pos.X * paddingX, pos.Y * paddingY); spriteBatch.Draw(currentBackgroundTexture, new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight) / 2 + pos, null, Color.White, 0.0f, new Vector2(currentBackgroundTexture.Width / 2, currentBackgroundTexture.Height / 2), scale, SpriteEffects.None, 0.0f); spriteBatch.Draw(overlay, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), null, Color.White, 0.0f, Vector2.Zero, SpriteEffects.None, 0.0f); 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 Point(Rand.Range(0, noiseSprite.SourceRect.Width), Rand.Range(0, noiseSprite.SourceRect.Height)), color: Color.White * noiseStrength * 0.1f, textureScale: Vector2.One * noiseScale); titleSprite?.Draw(spriteBatch, new Vector2(GameMain.GraphicsWidth * 0.05f, GameMain.GraphicsHeight * 0.125f), Color.White, origin: new Vector2(0.0f, titleSprite.SourceRect.Height / 2.0f), scale: GameMain.GraphicsHeight / 2000.0f); if (WaitForLanguageSelection) { DrawLanguageSelectionPrompt(spriteBatch, graphics); } else if (DrawLoadingText) { if (TextManager.Initialized) { string loadText; if (LoadState == 100.0f) { #if DEBUG if (GameMain.Config.AutomaticQuickStartEnabled && GameMain.FirstLoad) { loadText = "QUICKSTARTING ..."; } else { #endif loadText = TextManager.Get("PressAnyKey"); #if DEBUG } #endif } else { loadText = TextManager.Get("Loading"); if (LoadState != null) { loadText += " " + (int)LoadState + " %"; } } if (GUI.LargeFont != null) { GUI.LargeFont.DrawString(spriteBatch, loadText.ToUpper(), new Vector2(GameMain.GraphicsWidth / 2.0f - GUI.LargeFont.MeasureString(loadText.ToUpper()).X / 2.0f, GameMain.GraphicsHeight * 0.75f), Color.White); } } if (GUI.Font != null && selectedTip != null) { string wrappedTip = ToolBox.WrapText(selectedTip, GameMain.GraphicsWidth * 0.5f, GUI.Font); string[] lines = wrappedTip.Split('\n'); float lineHeight = GUI.Font.MeasureString(selectedTip).Y; for (int i = 0; i < lines.Length; i++) { GUI.Font.DrawString(spriteBatch, lines[i], new Vector2((int)(GameMain.GraphicsWidth / 2.0f - GUI.Font.MeasureString(lines[i]).X / 2.0f), (int)(GameMain.GraphicsHeight * 0.8f + i * lineHeight)), Color.White); } } } 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); decorativeGraph.Draw(spriteBatch, (int)(decorativeGraph.FrameCount * noiseVal), new Vector2(GameMain.GraphicsWidth * 0.001f, GameMain.GraphicsHeight * 0.24f), Color.White, Vector2.Zero, 0.0f, decorativeScale, SpriteEffects.FlipVertically); decorativeMap.Draw(spriteBatch, (int)(decorativeMap.FrameCount * noiseVal), new Vector2(GameMain.GraphicsWidth * 0.99f, GameMain.GraphicsHeight * 0.66f), Color.White, decorativeMap.FrameSize.ToVector2(), 0.0f, decorativeScale); 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" }).GetRandom(); } 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'); } GUI.LargeFont?.DrawString(spriteBatch, randText, new Vector2(GameMain.GraphicsWidth - decorativeMap.FrameSize.X * decorativeScale.X * 0.8f, GameMain.GraphicsHeight * 0.57f), Color.White * (1.0f - noiseVal)); spriteBatch.End(); } private void DrawLanguageSelectionPrompt(SpriteBatch spriteBatch, GraphicsDevice graphicsDevice) { 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(GameMain.GraphicsWidth / 2, GameMain.GraphicsHeight * 0.3f); Vector2 textSpacing = new Vector2(0.0f, (GameMain.GraphicsHeight * 0.5f) / TextManager.AvailableLanguages.Count()); foreach (string language in TextManager.AvailableLanguages) { string localizedLanguageName = TextManager.GetTranslatedLanguageName(language); var font = TextManager.IsCJK(localizedLanguageName) ? languageSelectionFontCJK : languageSelectionFont; Vector2 textSize = font.MeasureString(localizedLanguageName); bool hover = Math.Abs(PlayerInput.MousePosition.X - textPos.X) < textSize.X / 2 && Math.Abs(PlayerInput.MousePosition.Y - textPos.Y) < textSpacing.Y / 2; font.DrawString(spriteBatch, localizedLanguageName, textPos - textSize / 2, hover ? Color.White : Color.White * 0.6f); if (hover && PlayerInput.PrimaryMouseButtonClicked()) { GameMain.Config.Language = language; //reload tip in the selected language selectedTip = TextManager.Get("LoadingScreenTip", true); GameMain.Config.SetDefaultBindings(legacy: false); GameMain.Config.CheckBindings(useDefaults: true); 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 && PendingSplashScreens.Count == 0) { return; } if (currSplashScreen == null) { var newSplashScreen = PendingSplashScreens.Dequeue(); string fileName = newSplashScreen.Filename; try { currSplashScreen = Video.Load(graphics, GameMain.SoundManager, fileName); currSplashScreen.AudioGain = newSplashScreen.Gain; videoStartTime = DateTime.Now; } catch (Exception e) { GameMain.Config.EnableSplashScreen = false; DebugConsole.ThrowError("Playing the splash screen \"" + fileName + "\" failed.", e); PendingSplashScreens.Clear(); currSplashScreen = null; } if (currSplashScreen == null) { return; } } if (currSplashScreen.IsPlaying) { spriteBatch.Begin(); spriteBatch.Draw(currSplashScreen.GetTexture(), new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.White); spriteBatch.End(); if (DateTime.Now > videoStartTime + new TimeSpan(0, 0, 0, 0, milliseconds: 500) && GameMain.WindowActive && (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; } } bool drawn; public IEnumerable DoLoading(IEnumerable loader) { drawn = false; LoadState = null; selectedTip = TextManager.Get("LoadingScreenTip", true); currentBackgroundTexture = LocationType.List.GetRandom()?.GetPortrait(Rand.Int(int.MaxValue))?.Texture; 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; } } }