211 lines
11 KiB
C#
211 lines
11 KiB
C#
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);
|
||
}
|
||
}
|
||
}
|