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.
@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);
}
}