Files
blockout/BlockOutReplica/Assets/Scripts/Presentation/BlockOutHud.cs
T
2026-10-20 13:21:04 +08:00

211 lines
11 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using BlockOutReplica.Gameplay;
using UnityEngine;
namespace BlockOutReplica.Presentation
{
public sealed partial class BlockOutHud : MonoBehaviour
{
private const string SpriteRoot = "OriginalUI/Sprites/";
private BlockOutGameController game;
private Sprite currencyBackground, levelBackground, timerBackground, buttonBackground;
private Sprite replayIcon, pauseIcon, clockIcon, coinIcon, boosterButton, boosterCounter, tutorialHand, buttonFace;
private Sprite homeButton, homeCurrency, homeHeart, homeGameIcon;
private Sprite journeyBar, journeyBarBackground, journeyCheckpoint, journeyTooltip, journeyTick;
private Sprite[] boosterIcons;
private Font originalNumberFont;
public void Initialize(BlockOutGameController controller)
{
game = controller;
currencyBackground = Load("Ingame_Currency_Background");
levelBackground = Load("Ingame_Level_Background");
timerBackground = Load("Ingame_Timer_Background");
buttonBackground = Load("Ingame_Header_Buttons_Bg");
buttonFace = Resources.Load<Sprite>("OriginalRecovered/Ingame_Header_Button_Default");
replayIcon = Load("Ingame_Icon_Replay_Default");
pauseIcon = Load("Ingame_Icon_Pause_Default");
clockIcon = Load("Ingame_Icon_Clock");
coinIcon = Load("Home_Coin_Icon");
boosterButton = Load("Ingame_Booster_Button");
boosterCounter = Load("Ingame_Booster_Counter_Red");
tutorialHand = Load("tutorialHand");
// Original HomeController PlayButton uses the _0 exported variant;
// the unsuffixed variant is used by the leaderboard Continue button.
homeButton = Load("Home_Button_Green_0") ?? Load("Home_Button_Green");
homeCurrency = Load("Home_Currency_Bg");
homeHeart = Load("Home_Heart_Icon");
homeGameIcon = Load("Home_Game_Icon");
journeyBar = Load("Journey_Bar");
journeyBarBackground = Load("Journey_Bar_BG");
journeyCheckpoint = Load("Home_BlockPass_Checkpoint");
journeyTooltip = Load("Journey_CurrentLevel_Tooltip");
journeyTick = Load("Journey_Icon_Tick");
boosterIcons = new[] { Resources.Load<Sprite>("OriginalRecovered/ingame_booster_timefreeze2") ?? Load("ingame_booster_timefreeze"),
Load("ingame_booster_rocket"), Load("ingame_booster_ufo") };
originalNumberFont = Resources.Load<Font>("OriginalUI/Fonts/FreightSans-Black_Num");
}
private void OnGUI()
{
if (game == null) return;
const float designWidth = 540f, designHeight = 960f;
float scale = Mathf.Min(Screen.width / designWidth, Screen.height / designHeight);
float left = (Screen.width - designWidth * scale) * .5f;
GUIStyle text = CreateTextStyle(Mathf.RoundToInt(21f * scale));
GUIStyle timer = CreateOriginalTextStyle(Mathf.RoundToInt(25f * scale),OriginalTextRole.Timer);
text.normal.textColor = new Color(.72f, .66f, 1f);
#if UNITY_EDITOR
if(game.IsFullLoopSandbox) DrawText(game.IsFirstTwentyPreview ? "LEVELS 1–20 • ISOLATED SAVE" : "REVIEW • ISOLATED SAVE",D(left,scale,100,5,340,24),CreateTextStyle(Mathf.RoundToInt(12*scale)));
#endif
if (game.ScreenState == BlockOutScreenState.Home)
{
DrawHome(left, scale, text);
DrawInteractionOverlay(left, scale);
return;
}
bool enabledBefore = GUI.enabled;
GUI.enabled = game.Modal == GameModal.None && game.ScreenState == BlockOutScreenState.Playing;
DrawNativeBacking(currencyBackground, D(left, scale, 94, 46, 74, 31));
DrawSprite(coinIcon, D(left, scale, 70, 42, 34, 34));
DrawText(game.Player.Coins.ToString(), D(left, scale, 105, 46, 60, 29), text);
DrawNativeBacking(levelBackground, D(left, scale, 374, 47, 95, 31));
DrawText(LevelLabel(), D(left, scale, 378, 46, 87, 30), text);
DrawHeaderButton(left, scale, 70, replayIcon, true);
DrawHeaderButton(left, scale, 412, pauseIcon, false);
DrawNativeBacking(timerBackground, D(left, scale, 208, 91, 125, 40));
DrawSprite(clockIcon, D(left, scale, 217, 99, 23, 23));
int seconds = Mathf.CeilToInt(game.TimeLeft);
DrawText($"{seconds / 60:00}:{seconds % 60:00}", D(left, scale, 241, 93, 83, 34), timer);
DrawBooster(left, scale, 134, boosterIcons[0], BoosterType.Freeze);
DrawBooster(left, scale, 232, boosterIcons[1], BoosterType.Rocket);
DrawBooster(left, scale, 330, boosterIcons[2], BoosterType.Ufo);
GUI.enabled = enabledBefore;
if (game.ShowTutorial)
{
float pulse = 1f + Mathf.Sin(Time.unscaledTime * 4f) * .035f;
// Original Level 1 points at the red block near the lower-left exit.
DrawSprite(tutorialHand, D(left, scale, 156, 576, 80 * pulse, 96 * pulse));
}
if (game.FreezeRemaining > 0f)
{
Color old=GUI.color; GUI.color=new Color(1,1,1,Mathf.Min(1,game.FreezeRemaining));
if(!game.HasNativeFreezeScreen)DrawSprite(Load("freeze_front"),D(left,scale,0,0,540,960)); GUI.color=old;
DrawSprite(Load("Ingame_Freeze_Time_Bar_bg"),D(left,scale,209,62,125,18));
GUI.BeginGroup(D(left,scale,209,62,125*game.FreezeRemaining/10f,18));
DrawSprite(Load("Ingame_Freeze_Time_Bar"),new Rect(0,0,125*scale,18*scale)); GUI.EndGroup();
}
if (game.SelectedBooster != BoosterType.None)
{
DrawText(game.SelectedBooster == BoosterType.Rocket ? "Select a block to eliminate" : "Select a color to eliminate",
D(left, scale, 45, 186, 450, 66), text);
if (ActionButton(D(left, scale, 170, 802, 200, 44), "Cancel", scale, true)) game.CancelBoosterSelection();
}
if (game.ScreenState == BlockOutScreenState.LevelComplete)
{
Color previous = GUI.color;
GUI.color = new Color(0f, 0f, 0f, .48f);
GUI.DrawTexture(new Rect(0f, 0f, Screen.width, Screen.height), Texture2D.whiteTexture);
GUI.color = previous;
GUIStyle complete = CreateTextStyle(Mathf.RoundToInt(38f * scale));
DrawText($"LEVEL {game.WonLevel} COMPLETE!", D(left, scale, 45, 380, 450, 70), complete);
DrawText($"+{game.WinReward} COINS", D(left, scale, 120, 464, 300, 50), text);
if (ActionButton(D(left, scale, 160, 550, 220, 68), "Continue", scale)) game.ContinueAfterWin();
}
DrawInteractionOverlay(left, scale);
}
private void DrawHome(float left, float scale, GUIStyle text)
{
DrawJourneyHome(left,scale);
}
private void DrawHeaderButton(float left, float scale, float x, Sprite icon, bool reset)
{
Rect rect = D(left, scale, x, 82, 59, 59);
DrawSprite(buttonBackground, rect);
DrawSprite(buttonFace, D(left, scale, x + 5, 87, 49, 49));
DrawSprite(icon, D(left, scale, x + 13, 95, 33, 33));
if (reset && GUI.Button(rect, GUIContent.none, GUIStyle.none)) game.RequestRestart();
if (!reset && GUI.Button(rect, GUIContent.none, GUIStyle.none)) game.TogglePause();
}
private void DrawBooster(float left, float scale, float x, Sprite icon, BoosterType type)
{
float y = Screen.height / scale - 132f;
DrawSprite(Load("Ingame_Booster_Button_Bg"),D(left,scale,x-3,y-3,82,82));
DrawSprite(boosterButton, D(left, scale, x, y, 76, 76));
float aspect = icon == null ? 1f : icon.rect.width / icon.rect.height;
DrawSprite(icon, D(left, scale, x + 8, y + 10, 60, 60 / aspect));
GUIStyle badge = CreateTextStyle(Mathf.RoundToInt(17f * scale));
Rect counter = D(left, scale, x + 56, y + 60, 27, 27);
if(game.Player.Amount(type)>0)
{DrawSprite(boosterCounter, counter);DrawText(game.Player.Amount(type).ToString(), counter, badge);}
else
{
Sprite priceBacking = Load("Ingame_Booster_Counter_Purple");
Rect priceRect = OriginalSpriteGeometry.FitNativeBacking(D(left,scale,x-1,y+64,81,27), "Ingame_Booster_Counter_Purple");
DrawSlicedSprite(priceBacking, priceRect, OriginalSpriteLayout.NineSlice);
DrawSprite(Load("Ingame_Icon_Coin"),new Rect(priceRect.x-2*scale,priceRect.y+scale,24*scale,24*scale));
var price=CreateOriginalTextStyle(Mathf.RoundToInt(20*scale),OriginalTextRole.Body);
DrawText(game.PriceOfBooster(type).ToString(),new Rect(priceRect.x+19*scale,priceRect.y-scale,
priceRect.width-20*scale,29*scale),price);
}
if (game.SelectedBooster == type)
GUI.Box(D(left, scale, x - 3, y - 3, 82, 82), GUIContent.none);
if (GUI.Button(D(left, scale, x, y, 82, 88), GUIContent.none, GUIStyle.none)) game.ClickBooster(type);
}
private GUIStyle CreateTextStyle(int size) => CreateOriginalTextStyle(size,OriginalTextRole.Counter);
private static void DrawText(string value, Rect rect, GUIStyle style)
{
if(TryDrawOriginalText(value,rect,style))return;
Color color = style.normal.textColor;
style.normal.textColor = new Color(.035f, .02f, .10f, .88f);
GUI.Label(new Rect(rect.x + 2f, rect.y + 3f, rect.width, rect.height), value, style);
style.normal.textColor = color;
GUI.Label(rect, value, style);
}
private static Rect D(float left, float scale, float x, float y, float w, float h)
=> new Rect(left + x * scale, y * scale, w * scale, h * scale);
private static Sprite Load(string name) => Resources.Load<Sprite>("OriginalRecovered/" + name) ?? Resources.Load<Sprite>(SpriteRoot + name);
private static void DrawNativeBacking(Sprite sprite, Rect target)
{
if (sprite == null) return;
DrawSlicedSprite(sprite, OriginalSpriteGeometry.FitNativeBacking(target, sprite.name), OriginalSpriteLayout.NineSlice);
}
private static void DrawFittedSprite(Sprite sprite, Rect target)
{
if (sprite == null) return;
DrawSprite(sprite, OriginalSpriteGeometry.FitIcon(target, sprite.rect.size));
}
private static void DrawSprite(Sprite sprite, Rect target)
{
if (sprite == null) return;
Texture2D texture = sprite.texture;
if (texture == null) return;
Rect source = sprite.textureRect;
Rect uv = new Rect(source.x / texture.width, source.y / texture.height,
source.width / texture.width, source.height / texture.height);
GUI.DrawTextureWithTexCoords(target, texture, uv, true);
}
private string LevelLabel()
{
return "Level " + (game.ScreenState == BlockOutScreenState.LevelComplete ? game.WonLevel : game.LevelNumber);
}
}
}