MainHud/ShopHUD.razor

A UI Razor panel for the in-game shop HUD. It renders purchasable items, reads values from a GameManager component, computes costs and scaled stat gains, and calls manager methods to perform purchases and update player state.

NetworkingFile Access
@using Sandbox;
@using Sandbox.UI;
@using System.Linq;
@using Keyboard_Warriors_.Systems;
@using Keyboard_Warriors_.Managers;
@inherits Panel

<root class="shop-popup-wrapper @(IsVisible ? "is-visible" : "")">
    <div class="shop-box">
        <h2 class="shop-title">🛒 WEAPON SHOP</h2>
        
        <div class="shop-items-container">
            @{
                // We evaluate the method safely inside a local C# block
                var managerInstance = GetManager();
            }

            @if ( managerInstance != null )
            {
                // Pull the active prestige level to calculate text values on the fly
                int currentPrestige = managerInstance.CurrentPrestige;

                // Pre-calculate baseline cost frameworks so the HTML buttons only read simple numbers
                int damageCost = managerInstance.GetPotionCost("damage");
                int armorCost = managerInstance.GetPotionCost("armor");
                int magicCost = managerInstance.GetPotionCost("magic");
                
                // Pre-calculate new expansion patch option costs safely
                int cureCost = managerInstance.GetPotionCost("cure");
                int rebootCost = managerInstance.GetPotionCost("reboot");
                int overclockCost = managerInstance.GetPotionCost("overclock");
                int gambitCost = managerInstance.GetPotionCost("gambit");

                <!-- Item 1: Damage Potion -->
                <div class="shop-item-row">
                    <div class="item-details">
                        <!-- 🔮 DYNAMIC TEXT BINDING: Displays your balanced +1 per prestige scaling value -->
                        <span class="item-name">⚔️ Attack Damage +@GetScaledDamageValue(currentPrestige)</span>
                        <span class="item-desc">Crush opponents with brute power [LIMIT: @(managerInstance.LocalPlayer?.DamageUpgradesCount ?? 0)/3]</span>
                    </div>
                    <!-- Passing the pre-calculated integer avoids any method parsing errors -->
                    <button class="buy-btn @(managerInstance.LocalPlayer?.DamageUpgradesCount >= 3 ? "btn-disabled" : "")" 
                            onclick="@(() => BuyItem("damage", damageCost))">
                        @damageCost PTS
                    </button>
                </div>

                <!-- Item 2: Armor Potion -->
                <div class="shop-item-row">
                    <div class="item-details">
                        <!-- 🔮 DYNAMIC TEXT BINDING: Displays your balanced +3 per prestige scaling value -->
                        <span class="item-name">🛡️ Max Armor +@GetScaledArmorValue(currentPrestige)</span>
                        <span class="item-desc">Absorb heavy incoming strikes</span>
                    </div>
                    <button class="buy-btn" onclick="@(() => BuyItem("armor", armorCost))">
                        @armorCost PTS
                    </button>
                </div>

                <!-- Item 3: Magic Potion -->
                <div class="shop-item-row">
                    <div class="item-details">
                        <!-- 🔮 DYNAMIC TEXT BINDING: Displays your balanced +2 per prestige scaling value -->
                        <span class="item-name">🧙‍ Magic +@GetScaledMagicValue(currentPrestige)</span>
                        <span class="item-desc">Harness mystical energies</span>
                    </div>
                    <button class="buy-btn" onclick="@(() => BuyItem("magic", magicCost))">
                        @magicCost PTS
                    </button>
                </div>

                <!-- Item 4: Antidote Patch -->
                <div class="shop-item-row">
                    <div class="item-details">
                        <span class="item-name">🧪 Cleanse_Curse.exe</span>
                        <span class="item-desc">Instantly purges active toxic poison and status curses</span>
                    </div>
                    <!-- Dynamically locks out button if player isn't actively poisoned -->
                    <button class="buy-btn @(managerInstance.LocalPlayer?.cursed != true ? "btn-disabled" : "")" 
                            onclick="@(() => BuyItem("cure", cureCost))">
                        @cureCost PTS
                    </button>
                </div>

                <!-- Item 5: Shock Reboot -->
                <div class="shop-item-row">
                    <div class="item-details">
                        <span class="item-name">⚡ System Reboot Patch</span>
                        <span class="item-desc">Clears active stuns and forces player status indicators out of negative loops</span>
                    </div>
                    <button class="buy-btn" onclick="@(() => BuyItem("reboot", rebootCost))">
                        @rebootCost PTS
                    </button>
                </div>

                <!-- Item 6: Overclock Core Elixir -->
                <div class="shop-item-row">
                    <div class="item-details">
                        <span class="item-name">🔋 Matrix Overclock Charge</span>
                        <span class="item-desc">Instant max reload (+100 SP) to activate upper damage variance rules</span>
                    </div>
                    <!-- Dynamically locks out button if magic reserves are already maxed out at 100 -->
                    <button class="buy-btn @(managerInstance.LocalPlayer?.magic >= 100 ? "btn-disabled" : "")" 
                            onclick="@(() => BuyItem("overclock", overclockCost))">
                        @overclockCost PTS
                    </button>
                </div>

                <!-- Item 7: Rogue Gambit -->
                <div class="shop-item-row">
                    <div class="item-details">
                        <span class="item-name">🎲 Rogue Code Gambit</span>
                        <span class="item-desc">Injects a highly chaotic attack buff or backfires a random chunk of self-damage</span>
                    </div>
                    <button class="buy-btn" onclick="@(() => BuyItem("gambit", gambitCost))">
                        @gambitCost PTS
                    </button>
                </div>
            }
            else
            {
                <div class="error-msg">Connecting to GameManager...</div>
            }
        </div>

        <button class="close-window-btn" onclick="@(() => Close())">Close Menu ✕</button>
    </div>
</root>

@code {
    public bool IsVisible { get; set; }

    /// <summary>
    /// Quick helper to fetch the active GameManager component from the Scene tree.
    /// </summary>
    private GameManager GetManager()
    {
        return Scene.GetAllComponents<GameManager>().FirstOrDefault();
    }

    private void BuyItem(string itemType, int cost)
    {
        var manager = GetManager();
        var player = manager?.LocalPlayer;

        if (manager == null || player == null || manager.PlayerPoints < cost) return;

        // Custom validation checking rules for item types
        if (itemType == "damage")
        {
            if (player.DamageUpgradesCount >= 3)
            {
                Log.Info("Max attack upgrades reached!");
                return; 
            }
        }
        else if (itemType == "cure")
        {
            if (player.cursed != true)
            {
                Log.Info("System is not afflicted by poison matrix code!");
                return;
            }
        }
        else if (itemType == "overclock")
        {
            if (player.magic >= 100)
            {
                Log.Info("Magic registers already at capacity!");
                return;
            }
        }

        bool success = manager.TryBuyPotion(itemType, cost);

        if (success)
        {
            manager.PlayerPoints -= cost;

            // 👑 BALANCED BALANCING: Using lower, more calculated scalar values
            int currentPrestige = manager.CurrentPrestige;

            if (itemType == "magic")
            {
                int scaledMagicValue = GetScaledMagicValue(currentPrestige);
                player.magic = System.Math.Clamp(player.magic + scaledMagicValue, 0, 100);
                
                GameManager.AddLog($"🧪 Drank Magic Potion! Restored {scaledMagicValue} Magic.");
            }
            else if (itemType == "armor")
            {
                int scaledArmorValue = GetScaledArmorValue(currentPrestige);
                player.defense = System.Math.Clamp(player.defense + scaledArmorValue, 0, player.maxDefense);
                
                GameManager.AddLog($"🛡️ Drank Armor Potion! Reinforced shield partition by +{scaledArmorValue} Defense.");
            }
            else if (itemType == "damage")
            {
                int scaledDamageValue = GetScaledDamageValue(currentPrestige);
                player.attackDamage += scaledDamageValue;
                player.DamageUpgradesCount++;
                
                GameManager.AddLog($"⚔️ Drank Damage Potion! Attack protocols overmatched by +{scaledDamageValue} Permanent Damage.");
            }
            else if (itemType == "cure")
            {
                player.cursed = false;
                GameManager.AddLog("🧪 Executed Cleanse_Curse.exe! Poison metrics wiped completely from active system caches.");
            }
            else if (itemType == "reboot")
            {
                // Reset player's negative turn indicator variables if locked by a stun event loop
                var p = player;
                // If your game tracks active stun status blocks using a manager field or player indicator, clear it here!
                GameManager.AddLog("⚡ Executed System Reboot! Active status freezes broken and operational vectors restored.");
            }
            else if (itemType == "overclock")
            {
                player.magic = 100;
                GameManager.AddLog("🔋 Overclock Core Ignited! Magic reserves pinned to absolute 100/100 ceiling! Max damage variance rules unlocked!");
            }
            else if (itemType == "gambit")
            {
                // Roll a high-stakes developer 60/40 risk matrix using engine random generation utilities
                if ( System.Random.Shared.Next( 100 ) < 60 )
                {
                    int structuralGambitAtk = 20 + (currentPrestige * 4);
                    player.eventAttackBuff += structuralGambitAtk;
                    GameManager.AddLog($"🎲 SUCCESS! Rogue Gambit injection went undetected! Next weapon hit gains +{structuralGambitAtk} Damage!");
                }
                else
                {
                    int crashBackfireDmg = 15 + (currentPrestige * 3);
                    GameManager.AddLog($"💥 CRASH! Rogue code rejected by kernel security layers! System loop backfired for {crashBackfireDmg} self-damage!");
                    CombatManager.applyDamage( ref player, crashBackfireDmg );
                }
            }

            StateHasChanged();
            
            // Tells GameManager to announce the point layout redraw safely using the method option
            manager.NotifyStateChanged();
        }
    }

    // 👑 CENTRAL MATH CONTROLLERS: These power BOTH your purchase engine and your shop UI text cards!
    private int GetScaledDamageValue(int prestige) => 5 + (prestige * 1); // Reduced from *3 to +1 per tier
    private int GetScaledArmorValue(int prestige)  => 10 + (prestige * 3); // Reduced from *10 to +3 per tier
    private int GetScaledMagicValue(int prestige)  => 10 + (prestige * 2); // Reduced from *5 to +2 per tier

    public void Open()
    {
        IsVisible = true;
        StateHasChanged();
    }

    public void Close()
    {
        IsVisible = false;
        StateHasChanged();
    }

    protected override int BuildHash()
    {
        return System.HashCode.Combine(IsVisible, GetManager()?.PlayerPoints ?? 0);
    }
}