purchase buffs menu works
This commit is contained in:
parent
3737ad517c
commit
79cd331141
6 changed files with 222 additions and 7 deletions
|
|
@ -48,7 +48,7 @@ MonoBehaviour:
|
|||
m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.NetworkObject
|
||||
GlobalObjectIdHash: 121878297
|
||||
GlobalObjectIdHash: 1552073510
|
||||
InScenePlacedSourceGlobalObjectIdHash: 0
|
||||
DeferredDespawnTick: 0
|
||||
Ownership: 1
|
||||
|
|
@ -116,5 +116,4 @@ MonoBehaviour:
|
|||
m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.PlayerBuffManager
|
||||
ShowTopMostFoldoutHeaderGroup: 1
|
||||
categories:
|
||||
- {fileID: 0}
|
||||
- {fileID: 0}
|
||||
- {fileID: 11400000, guid: d8ed3b9535538f7fc82be878cc307d26, type: 2}
|
||||
|
|
|
|||
|
|
@ -149,6 +149,14 @@ namespace TD.Gameplay
|
|||
// Right-click. Suppressed entirely during a modal mode (placement/paint
|
||||
// controllers handle right-click as cancel there) and when over HUD.
|
||||
if (isModal) return;
|
||||
|
||||
// B: toggle buff menu.
|
||||
if (keyboard != null && keyboard.bKey.wasPressedThisFrame
|
||||
&& !HUDController.IsTextInputActive)
|
||||
{
|
||||
HUDController.Instance?.ToggleBuffMenu();
|
||||
}
|
||||
|
||||
if (pointerOverHud) return;
|
||||
if (!mouse.rightButton.wasPressedThisFrame) return;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
// Assets/_Project/Scripts/Gameplay/PlayerBuffManager.cs
|
||||
using System.Collections.Generic;
|
||||
using Unity.Collections;
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
|
@ -27,6 +28,31 @@ namespace TD.Gameplay
|
|||
/// </remarks>
|
||||
public class PlayerBuffManager : NetworkBehaviour
|
||||
{
|
||||
// ----- Static registry (mirrors PlayerGoldManager pattern) -----------
|
||||
|
||||
private static readonly Dictionary<ulong, PlayerBuffManager> s_byClientId
|
||||
= new Dictionary<ulong, PlayerBuffManager>();
|
||||
|
||||
/// <summary>Returns the PlayerBuffManager owned by the given client, or null.</summary>
|
||||
public static PlayerBuffManager GetForClient(ulong clientId)
|
||||
{
|
||||
s_byClientId.TryGetValue(clientId, out var mgr);
|
||||
return mgr;
|
||||
}
|
||||
|
||||
/// <summary>Convenience: the local client's own buff manager.</summary>
|
||||
public static PlayerBuffManager Local
|
||||
{
|
||||
get
|
||||
{
|
||||
var nm = NetworkManager.Singleton;
|
||||
if (nm == null) return null;
|
||||
return GetForClient(nm.LocalClientId);
|
||||
}
|
||||
}
|
||||
|
||||
// ----- Inspector ------------------------------------------------------
|
||||
|
||||
[Tooltip("Purchasable buff categories shown in the buff menu. Index in this " +
|
||||
"array is the categoryIndex passed to RequestPurchaseBuffRpc.")]
|
||||
[SerializeField] private BuffCategory[] categories;
|
||||
|
|
@ -38,6 +64,19 @@ namespace TD.Gameplay
|
|||
buffs = new NetworkList<ActiveBuff>();
|
||||
}
|
||||
|
||||
// ----- NGO lifecycle ----------------------------------------------
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
s_byClientId[OwnerClientId] = this;
|
||||
}
|
||||
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
if (s_byClientId.TryGetValue(OwnerClientId, out var registered) && registered == this)
|
||||
s_byClientId.Remove(OwnerClientId);
|
||||
}
|
||||
|
||||
// ----- Public API -------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ namespace TD.UI
|
|||
[RequireComponent(typeof(UIDocument))]
|
||||
public class HUDController : MonoBehaviour
|
||||
{
|
||||
public static HUDController Instance { get; private set; }
|
||||
|
||||
// ----- Inspector --------------------------------------------------
|
||||
|
||||
[Header("Scene References")]
|
||||
|
|
@ -93,6 +95,10 @@ namespace TD.UI
|
|||
|
||||
// Match-end overlay — built once on Start and toggled on Phase changes.
|
||||
private VisualElement matchEndOverlay;
|
||||
|
||||
// Buff menu overlay — toggled by the B key via ToggleBuffMenu().
|
||||
private VisualElement buffMenuOverlay;
|
||||
private VisualElement buffMenuContent;
|
||||
private Label matchEndTitle;
|
||||
|
||||
// Chat panel (bottom-left, above portrait) — programmatic. The container
|
||||
|
|
@ -309,6 +315,9 @@ namespace TD.UI
|
|||
// MatchState.OnPhaseChanged fires Victory or Defeat.
|
||||
BuildMatchEndOverlay(root);
|
||||
|
||||
// Build the buff menu overlay. Hidden until the player presses B.
|
||||
BuildBuffMenuOverlay(root);
|
||||
|
||||
// Chat feed + input. Anchored bottom-left, just above the portrait/bottom-ui bar.
|
||||
// Player typing toggled with Enter; system messages (e.g. life lost) post via
|
||||
// ChatService.PostLocalSystem on every peer.
|
||||
|
|
@ -323,6 +332,11 @@ namespace TD.UI
|
|||
uiInitialized = true;
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
TowerPlacementController.OnRejectionMessageReady += ShowRejectionMessage;
|
||||
|
|
@ -437,6 +451,8 @@ namespace TD.UI
|
|||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (Instance == this) Instance = null;
|
||||
|
||||
minimapView?.Dispose();
|
||||
minimapView = null;
|
||||
|
||||
|
|
@ -1208,6 +1224,149 @@ namespace TD.UI
|
|||
|
||||
// ----- Chat feed + input -----------------------------------------
|
||||
|
||||
// ----- Buff menu overlay ------------------------------------------
|
||||
|
||||
private void BuildBuffMenuOverlay(VisualElement root)
|
||||
{
|
||||
buffMenuOverlay = new VisualElement();
|
||||
buffMenuOverlay.style.position = Position.Absolute;
|
||||
buffMenuOverlay.style.left = 0;
|
||||
buffMenuOverlay.style.right = 0;
|
||||
buffMenuOverlay.style.top = 0;
|
||||
buffMenuOverlay.style.bottom = 0;
|
||||
buffMenuOverlay.style.alignItems = Align.Center;
|
||||
buffMenuOverlay.style.justifyContent = Justify.Center;
|
||||
buffMenuOverlay.style.backgroundColor = new Color(0f, 0f, 0f, 0.5f);
|
||||
buffMenuOverlay.style.display = DisplayStyle.None;
|
||||
buffMenuOverlay.pickingMode = PickingMode.Position;
|
||||
|
||||
var panel = new VisualElement();
|
||||
panel.style.minWidth = 340;
|
||||
panel.style.paddingTop = 20;
|
||||
panel.style.paddingBottom = 20;
|
||||
panel.style.paddingLeft = 28;
|
||||
panel.style.paddingRight = 28;
|
||||
panel.style.backgroundColor = new Color(0.08f, 0.08f, 0.10f, 0.95f);
|
||||
panel.style.borderTopWidth = panel.style.borderBottomWidth =
|
||||
panel.style.borderLeftWidth = panel.style.borderRightWidth = 2;
|
||||
var border = new Color(0.4f, 0.4f, 0.45f);
|
||||
panel.style.borderTopColor = panel.style.borderBottomColor =
|
||||
panel.style.borderLeftColor = panel.style.borderRightColor = border;
|
||||
|
||||
var title = new Label("Buffs");
|
||||
title.style.fontSize = 24;
|
||||
title.style.color = Color.white;
|
||||
title.style.unityFontStyleAndWeight = FontStyle.Bold;
|
||||
title.style.marginBottom = 12;
|
||||
panel.Add(title);
|
||||
|
||||
// Scrollable content area: current buffs + purchase buttons.
|
||||
// Rebuilt each time the menu is shown via RefreshBuffMenuContent().
|
||||
buffMenuContent = new VisualElement();
|
||||
panel.Add(buffMenuContent);
|
||||
|
||||
var closeBtn = new Button(() => SetBuffMenuVisible(false)) { text = "Close [B]" };
|
||||
closeBtn.style.marginTop = 16;
|
||||
closeBtn.style.height = 32;
|
||||
panel.Add(closeBtn);
|
||||
|
||||
buffMenuOverlay.Add(panel);
|
||||
root.Add(buffMenuOverlay);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggles the buff menu overlay. Called by <see cref="TD.Gameplay.BuilderInputController"/>
|
||||
/// when the player presses B.
|
||||
/// </summary>
|
||||
public void ToggleBuffMenu()
|
||||
{
|
||||
bool nowVisible = buffMenuOverlay?.style.display == DisplayStyle.None;
|
||||
SetBuffMenuVisible(nowVisible);
|
||||
}
|
||||
|
||||
private void SetBuffMenuVisible(bool visible)
|
||||
{
|
||||
if (buffMenuOverlay == null) return;
|
||||
buffMenuOverlay.style.display = visible ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
if (visible) RefreshBuffMenuContent();
|
||||
}
|
||||
|
||||
private void RefreshBuffMenuContent()
|
||||
{
|
||||
if (buffMenuContent == null) return;
|
||||
buffMenuContent.Clear();
|
||||
|
||||
var buffManager = TD.Gameplay.PlayerBuffManager.Local;
|
||||
|
||||
// Current buffs section.
|
||||
var buffsHeader = new Label("Active Buffs");
|
||||
buffsHeader.style.color = new Color(0.7f, 0.9f, 0.7f);
|
||||
buffsHeader.style.fontSize = 14;
|
||||
buffsHeader.style.marginBottom = 4;
|
||||
buffMenuContent.Add(buffsHeader);
|
||||
|
||||
if (buffManager == null || buffManager.Buffs.Count == 0)
|
||||
{
|
||||
var none = new Label("None");
|
||||
none.style.color = new Color(0.55f, 0.55f, 0.55f);
|
||||
none.style.marginBottom = 8;
|
||||
buffMenuContent.Add(none);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < buffManager.Buffs.Count; i++)
|
||||
{
|
||||
var buff = buffManager.Buffs[i];
|
||||
string statName = buff.Stat == TD.Core.BuffStat.Damage ? "Damage" : "Attack Speed";
|
||||
string activeTag = buff.IsActive ? "" : " [DISABLED]";
|
||||
var row = new Label($"• {buff.DisplayName} ({statName} ×{buff.Multiplier:F2}){activeTag}");
|
||||
row.style.color = buff.IsActive ? Color.white : new Color(0.5f, 0.5f, 0.5f);
|
||||
row.style.fontSize = 13;
|
||||
buffMenuContent.Add(row);
|
||||
}
|
||||
buffMenuContent.style.marginBottom = 12;
|
||||
}
|
||||
|
||||
// Purchase buttons section.
|
||||
var buyHeader = new Label("Purchase");
|
||||
buyHeader.style.color = new Color(0.9f, 0.8f, 0.5f);
|
||||
buyHeader.style.fontSize = 14;
|
||||
buyHeader.style.marginTop = 8;
|
||||
buyHeader.style.marginBottom = 4;
|
||||
buffMenuContent.Add(buyHeader);
|
||||
|
||||
int categoryCount = buffManager?.CategoryCount ?? 0;
|
||||
if (categoryCount == 0)
|
||||
{
|
||||
var none = new Label("No categories available.");
|
||||
none.style.color = new Color(0.55f, 0.55f, 0.55f);
|
||||
buffMenuContent.Add(none);
|
||||
return;
|
||||
}
|
||||
|
||||
int localGold = TD.Gameplay.PlayerGoldManager.Local?.CurrentGold ?? 0;
|
||||
for (int i = 0; i < categoryCount; i++)
|
||||
{
|
||||
var category = buffManager.GetCategory(i);
|
||||
if (category == null) continue;
|
||||
|
||||
int idx = i; // capture for lambda
|
||||
var btn = new Button(() =>
|
||||
{
|
||||
TD.Gameplay.PlayerBuffManager.Local?.RequestPurchaseBuffRpc(idx);
|
||||
RefreshBuffMenuContent();
|
||||
})
|
||||
{
|
||||
text = $"{category.DisplayName} — {category.Cost}g"
|
||||
};
|
||||
btn.style.height = 34;
|
||||
btn.style.fontSize = 13;
|
||||
btn.style.marginBottom = 4;
|
||||
btn.SetEnabled(localGold >= category.Cost);
|
||||
buffMenuContent.Add(btn);
|
||||
}
|
||||
}
|
||||
|
||||
// Bottom-left chat panel. Anchored 12px from the left edge, with the
|
||||
// bottom edge sitting above the 220px bottom-ui. Layout uses a flex
|
||||
// column: scrollable feed on top, input below. The feed clips at
|
||||
|
|
|
|||
|
|
@ -12,11 +12,12 @@
|
|||
"com.unity.netcode.gameobjects": "2.11.0",
|
||||
"com.unity.probuilder": "6.0.9",
|
||||
"com.unity.render-pipelines.universal": "17.4.0",
|
||||
"com.unity.sdk.linux-x86_64": "1.0.2",
|
||||
"com.unity.sdk.linux-x86_64": "1.1.0",
|
||||
"com.unity.services.multiplayer": "2.1.3",
|
||||
"com.unity.terrain-tools": "5.3.2",
|
||||
"com.unity.test-framework": "1.6.0",
|
||||
"com.unity.timeline": "1.8.12",
|
||||
"com.unity.toolchain.linux-x86_64-linux": "1.1.0",
|
||||
"com.unity.toolchain.win-x86_64-linux": "1.0.2",
|
||||
"com.unity.ugui": "2.0.0",
|
||||
"com.unity.visualscripting": "1.9.11",
|
||||
|
|
|
|||
|
|
@ -221,11 +221,11 @@
|
|||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.sdk.linux-x86_64": {
|
||||
"version": "1.0.2",
|
||||
"version": "1.1.0",
|
||||
"depth": 0,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
"com.unity.sysroot.base": "1.0.2"
|
||||
"com.unity.sysroot.base": "1.1.0"
|
||||
},
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
|
|
@ -355,7 +355,7 @@
|
|||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.sysroot.base": {
|
||||
"version": "1.0.2",
|
||||
"version": "1.1.0",
|
||||
"depth": 1,
|
||||
"source": "registry",
|
||||
"dependencies": {},
|
||||
|
|
@ -403,6 +403,15 @@
|
|||
},
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.toolchain.linux-x86_64-linux": {
|
||||
"version": "1.1.0",
|
||||
"depth": 0,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
"com.unity.sysroot.base": "1.1.0"
|
||||
},
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.toolchain.win-x86_64-linux": {
|
||||
"version": "1.0.2",
|
||||
"depth": 0,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue