diff --git a/Assets/_Project/Definitions.meta b/Assets/_Project/Definitions.meta
index 672694e..d3c452a 100644
--- a/Assets/_Project/Definitions.meta
+++ b/Assets/_Project/Definitions.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
-guid: cf294cbfb17f5a5d7846479a116fc7b3
+guid: 01de85ee5d8a2014594d9910b1a6ff55
folderAsset: yes
DefaultImporter:
externalObjects: {}
diff --git a/Assets/_Project/Definitions/Buffs.meta b/Assets/_Project/Definitions/Buffs.meta
deleted file mode 100644
index 98de849..0000000
--- a/Assets/_Project/Definitions/Buffs.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: eef6329e4294d86c6978ff9453b56956
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Definitions/Buffs/ExtraDamage25.asset b/Assets/_Project/Definitions/Buffs/ExtraDamage25.asset
deleted file mode 100644
index c707eae..0000000
--- a/Assets/_Project/Definitions/Buffs/ExtraDamage25.asset
+++ /dev/null
@@ -1,17 +0,0 @@
-%YAML 1.1
-%TAG !u! tag:unity3d.com,2011:
---- !u!114 &11400000
-MonoBehaviour:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 0}
- m_Enabled: 1
- m_EditorHideFlags: 0
- m_Script: {fileID: 11500000, guid: b01f9aea62ee8a1c1991bb2a097de224, type: 3}
- m_Name: ExtraDamage25
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.BuffDefinition
- DisplayName: 25% Increased Damage
- Stat: 0
- Multiplier: 1.25
diff --git a/Assets/_Project/Definitions/Buffs/ExtraDamage25.asset.meta b/Assets/_Project/Definitions/Buffs/ExtraDamage25.asset.meta
deleted file mode 100644
index b240b6c..0000000
--- a/Assets/_Project/Definitions/Buffs/ExtraDamage25.asset.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 9ec170e540b1f91cca762dcebe81a856
-NativeFormatImporter:
- externalObjects: {}
- mainObjectFileID: 11400000
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Definitions/Buffs/OffensiveBuffs.asset b/Assets/_Project/Definitions/Buffs/OffensiveBuffs.asset
deleted file mode 100644
index 737c5ab..0000000
--- a/Assets/_Project/Definitions/Buffs/OffensiveBuffs.asset
+++ /dev/null
@@ -1,18 +0,0 @@
-%YAML 1.1
-%TAG !u! tag:unity3d.com,2011:
---- !u!114 &11400000
-MonoBehaviour:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 0}
- m_Enabled: 1
- m_EditorHideFlags: 0
- m_Script: {fileID: 11500000, guid: 516e8faec948a4b3d977ec019376c438, type: 3}
- m_Name: OffensiveBuffs
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.BuffCategory
- DisplayName: Offensive Buffs
- Cost: 5
- Pool:
- - {fileID: 11400000, guid: 9ec170e540b1f91cca762dcebe81a856, type: 2}
diff --git a/Assets/_Project/Definitions/Buffs/OffensiveBuffs.asset.meta b/Assets/_Project/Definitions/Buffs/OffensiveBuffs.asset.meta
deleted file mode 100644
index ced41c2..0000000
--- a/Assets/_Project/Definitions/Buffs/OffensiveBuffs.asset.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: d8ed3b9535538f7fc82be878cc307d26
-NativeFormatImporter:
- externalObjects: {}
- mainObjectFileID: 11400000
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Prefabs/Player/Player.prefab b/Assets/_Project/Prefabs/Player/Player.prefab
index 4d7c382..8a1fe80 100644
--- a/Assets/_Project/Prefabs/Player/Player.prefab
+++ b/Assets/_Project/Prefabs/Player/Player.prefab
@@ -13,7 +13,6 @@ GameObject:
- component: {fileID: 2918837822014987993}
- component: {fileID: 7845089877743661692}
- component: {fileID: 4336209376377567030}
- - component: {fileID: 2806524246861401760}
m_Layer: 0
m_Name: Player
m_TagString: Untagged
@@ -48,7 +47,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.NetworkObject
- GlobalObjectIdHash: 1552073510
+ GlobalObjectIdHash: 121878297
InScenePlacedSourceGlobalObjectIdHash: 0
DeferredDespawnTick: 0
Ownership: 1
@@ -102,18 +101,3 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.PlayerMatchState
ShowTopMostFoldoutHeaderGroup: 1
---- !u!114 &2806524246861401760
-MonoBehaviour:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 3493329038866903420}
- m_Enabled: 1
- m_EditorHideFlags: 0
- m_Script: {fileID: 11500000, guid: 7775ee3a2f441b52480aabf54be6c1b6, type: 3}
- m_Name:
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.PlayerBuffManager
- ShowTopMostFoldoutHeaderGroup: 1
- categories:
- - {fileID: 11400000, guid: d8ed3b9535538f7fc82be878cc307d26, type: 2}
diff --git a/Assets/_Project/Scripts/Combat/TowerCombat.cs b/Assets/_Project/Scripts/Combat/TowerCombat.cs
index ebb8950..acb06ef 100644
--- a/Assets/_Project/Scripts/Combat/TowerCombat.cs
+++ b/Assets/_Project/Scripts/Combat/TowerCombat.cs
@@ -1,13 +1,10 @@
// Assets/_Project/Scripts/Combat/TowerCombat.cs
-
-using System;
using System.Collections.Generic;
+using Unity.Netcode;
+using UnityEngine;
using TD.Core;
using TD.Gameplay;
using TD.Towers;
-using Unity.Netcode;
-using UnityEngine;
-using Object = UnityEngine.Object;
namespace TD.Combat
{
@@ -61,11 +58,6 @@ namespace TD.Combat
// Cached on OnNetworkSpawn — avoids GetComponent every Update.
private TowerInstance towerInstance;
- // Lazily resolved on first attack — the owner's slot isn't guaranteed to be
- // set at spawn time, so we defer the lookup until combat actually begins.
- private PlayerBuffManager ownerBuffManager;
- private bool buffManagerResolved;
-
// Shared OverlapSphere result buffer. 32 covers any realistic enemy
// density; size up if profiling reveals overflow.
private static readonly Collider[] s_overlapBuffer = new Collider[32];
@@ -78,17 +70,17 @@ namespace TD.Combat
/// fired in for late-joining clients so visual
/// consumers always initialise from the correct state.
///
- public event Action OnTargetAcquired;
+ public event System.Action OnTargetAcquired;
/// Fired locally on ALL peers when the tower loses its target.
- public event Action OnTargetLost;
+ public event System.Action OnTargetLost;
///
/// Fired locally on ALL peers each time the tower attacks.
/// Argument is the world-space position of the primary target at the moment
/// of fire — use it to aim muzzle flash, spawn a tracer, etc.
///
- public event Action OnFire;
+ public event System.Action OnFire;
// ----- NGO lifecycle -----------------------------------------------
@@ -146,7 +138,7 @@ namespace TD.Combat
if (currentTarget == null) return;
bool dead = currentTarget.IsDead;
- bool destroyed = (currentTarget as Object) == null;
+ bool destroyed = (currentTarget as UnityEngine.Object) == null;
bool outOfRange = Vector3.Distance(
transform.position,
currentTarget.transform.position) > def.Range;
@@ -219,25 +211,6 @@ namespace TD.Combat
ClearTarget();
}
- // ----- Buff multiplier lookup -------------------------------------
-
- // Resolved lazily on the first attack because the owner slot NetworkVariable
- // may not yet have replicated by the time OnNetworkSpawn runs.
- private PlayerBuffManager GetOwnerBuffManager()
- {
- if (buffManagerResolved) return ownerBuffManager;
-
- var slot = towerInstance.Owner;
- if (slot == PlayerSlot.None) return null;
-
- var matchState = PlayerMatchState.GetForSlot(slot);
- if (matchState == null) return null;
-
- ownerBuffManager = matchState.GetComponent();
- buffManagerResolved = true;
- return ownerBuffManager;
- }
-
// ----- Attack tick -------------------------------------------------
private void TickAttack(TowerDefinition def)
@@ -245,9 +218,7 @@ namespace TD.Combat
attackCooldown -= Time.deltaTime;
if (attackCooldown > 0f) return;
- float effectiveFireRate = def.FireRate
- * (GetOwnerBuffManager()?.GetMultiplier(BuffStat.AttackSpeed) ?? 1f);
- attackCooldown = 1f / effectiveFireRate;
+ attackCooldown = 1f / def.FireRate;
Fire(def);
}
@@ -434,11 +405,7 @@ namespace TD.Combat
private void HitEnemy(in CombatProfile p, EnemyHealth target, PlayerSlot owner)
{
- var multiplier = GetOwnerBuffManager()?.GetMultiplier(BuffStat.Damage) ?? 1f;
- float effectiveDamage = p.Damage * multiplier;
- Debug.Log("multiplier: " + multiplier);
- Debug.Log("Effective damage: " + effectiveDamage);
- target.TakeDamage(effectiveDamage, p.DamageType, owner);
+ target.TakeDamage(p.Damage, p.DamageType, owner);
ApplyStatusEffect(p, target, owner);
}
@@ -477,11 +444,9 @@ namespace TD.Combat
return;
}
- float effectiveDamage = def.Damage
- * (GetOwnerBuffManager()?.GetMultiplier(BuffStat.Damage) ?? 1f);
proj.InitializeServer(
target,
- effectiveDamage,
+ p.Damage,
p.DamageType,
p.TargetType,
p.SplashRadius,
diff --git a/Assets/_Project/Scripts/Core/BuffStat.cs b/Assets/_Project/Scripts/Core/BuffStat.cs
deleted file mode 100644
index 3e2132e..0000000
--- a/Assets/_Project/Scripts/Core/BuffStat.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-// Assets/_Project/Scripts/Core/BuffStat.cs
-namespace TD.Core
-{
- ///
- /// Identifies which tower stat a modifies.
- ///
- public enum BuffStat : byte
- {
- Damage = 0,
- AttackSpeed = 1,
- }
-}
diff --git a/Assets/_Project/Scripts/Core/BuffStat.cs.meta b/Assets/_Project/Scripts/Core/BuffStat.cs.meta
deleted file mode 100644
index b91fc16..0000000
--- a/Assets/_Project/Scripts/Core/BuffStat.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: fc84fec503f24bc7693ee29558f45d27
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Gameplay/ActiveBuff.cs b/Assets/_Project/Scripts/Gameplay/ActiveBuff.cs
deleted file mode 100644
index 3a54012..0000000
--- a/Assets/_Project/Scripts/Gameplay/ActiveBuff.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-// Assets/_Project/Scripts/Gameplay/ActiveBuff.cs
-using System;
-using Unity.Collections;
-using Unity.Netcode;
-using TD.Core;
-
-namespace TD.Gameplay
-{
- ///
- /// One buff currently held by a player. Stored in
- /// 's NetworkList so all peers see the same set.
- ///
- ///
- /// IEquatable. NGO's NetworkList indexer setter short-circuits when
- /// Equals returns true, silently dropping the write. Every mutable field
- /// (only ) is included in the comparison so toggling a
- /// buff correctly propagates to clients.
- ///
- public struct ActiveBuff : INetworkSerializable, IEquatable
- {
- /// Which tower stat this buff multiplies.
- public BuffStat Stat;
-
- /// Multiplicative factor, e.g. 1.25 for +25%.
- public float Multiplier;
-
- ///
- /// False when disabled (e.g. by an enemy ability). Excluded from
- /// while false.
- ///
- public bool IsActive;
-
- /// Human-readable name for the HUD buff list.
- public FixedString64Bytes DisplayName;
-
- // ----- INetworkSerializable ---------------------------------------
-
- public void NetworkSerialize(BufferSerializer serializer) where T : IReaderWriter
- {
- byte statByte = (byte)Stat;
- serializer.SerializeValue(ref statByte);
- Stat = (BuffStat)statByte;
-
- serializer.SerializeValue(ref Multiplier);
- serializer.SerializeValue(ref IsActive);
- serializer.SerializeValue(ref DisplayName);
- }
-
- // ----- IEquatable -------------------------------------------------
-
- public bool Equals(ActiveBuff other)
- => Stat == other.Stat
- && Multiplier == other.Multiplier
- && IsActive == other.IsActive
- && DisplayName == other.DisplayName;
-
- public override bool Equals(object obj) => obj is ActiveBuff other && Equals(other);
-
- public override int GetHashCode() => HashCode.Combine((int)Stat, Multiplier, IsActive);
- }
-}
diff --git a/Assets/_Project/Scripts/Gameplay/ActiveBuff.cs.meta b/Assets/_Project/Scripts/Gameplay/ActiveBuff.cs.meta
deleted file mode 100644
index d6868bd..0000000
--- a/Assets/_Project/Scripts/Gameplay/ActiveBuff.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: b09203f8acc450bf3b3c4bd8ef3fe84e
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Gameplay/BuffCategory.cs b/Assets/_Project/Scripts/Gameplay/BuffCategory.cs
deleted file mode 100644
index bad6ee3..0000000
--- a/Assets/_Project/Scripts/Gameplay/BuffCategory.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-// Assets/_Project/Scripts/Gameplay/BuffCategory.cs
-using UnityEngine;
-
-namespace TD.Gameplay
-{
- ///
- /// A purchasable category of buffs. The player pays gold
- /// and receives a randomly chosen from .
- ///
- [CreateAssetMenu(fileName = "BuffCategory", menuName = "TD/Buffs/Buff Category")]
- public class BuffCategory : ScriptableObject
- {
- [Tooltip("Name shown on the buff menu purchase button.")]
- public string DisplayName;
-
- [Tooltip("Gold cost to purchase one random buff from this category.")]
- [Min(0)]
- public int Cost;
-
- [Tooltip("Set of buffs the player can receive. One is chosen uniformly at random.")]
- public BuffDefinition[] Pool;
- }
-}
diff --git a/Assets/_Project/Scripts/Gameplay/BuffCategory.cs.meta b/Assets/_Project/Scripts/Gameplay/BuffCategory.cs.meta
deleted file mode 100644
index c2d766d..0000000
--- a/Assets/_Project/Scripts/Gameplay/BuffCategory.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 516e8faec948a4b3d977ec019376c438
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Gameplay/BuffDefinition.cs b/Assets/_Project/Scripts/Gameplay/BuffDefinition.cs
deleted file mode 100644
index 951972e..0000000
--- a/Assets/_Project/Scripts/Gameplay/BuffDefinition.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-// Assets/_Project/Scripts/Gameplay/BuffDefinition.cs
-using UnityEngine;
-using TD.Core;
-
-namespace TD.Gameplay
-{
- ///
- /// One possible buff that can be awarded to a player. Lives in a
- /// 's pool and is drawn randomly on purchase.
- ///
- [CreateAssetMenu(fileName = "BuffDefinition", menuName = "TD/Buffs/Buff Definition")]
- public class BuffDefinition : ScriptableObject
- {
- [Tooltip("Name shown in the buff menu and tooltip.")]
- public string DisplayName;
-
- [Tooltip("Which tower stat this buff multiplies.")]
- public BuffStat Stat;
-
- [Tooltip("Multiplicative factor applied to the stat. 1.25 = +25%.")]
- [Min(1f)]
- public float Multiplier = 1.25f;
- }
-}
diff --git a/Assets/_Project/Scripts/Gameplay/BuffDefinition.cs.meta b/Assets/_Project/Scripts/Gameplay/BuffDefinition.cs.meta
deleted file mode 100644
index 12b38db..0000000
--- a/Assets/_Project/Scripts/Gameplay/BuffDefinition.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: b01f9aea62ee8a1c1991bb2a097de224
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Gameplay/BuilderInputController.cs b/Assets/_Project/Scripts/Gameplay/BuilderInputController.cs
index ce1e258..5de1f4a 100644
--- a/Assets/_Project/Scripts/Gameplay/BuilderInputController.cs
+++ b/Assets/_Project/Scripts/Gameplay/BuilderInputController.cs
@@ -149,14 +149,6 @@ 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;
diff --git a/Assets/_Project/Scripts/Gameplay/PlayerBuffManager.cs b/Assets/_Project/Scripts/Gameplay/PlayerBuffManager.cs
deleted file mode 100644
index 506791e..0000000
--- a/Assets/_Project/Scripts/Gameplay/PlayerBuffManager.cs
+++ /dev/null
@@ -1,195 +0,0 @@
-// Assets/_Project/Scripts/Gameplay/PlayerBuffManager.cs
-using System.Collections.Generic;
-using Unity.Collections;
-using Unity.Netcode;
-using UnityEngine;
-using TD.Core;
-
-namespace TD.Gameplay
-{
- ///
- /// Holds all buffs currently owned by one player. Lives on the Player prefab
- /// alongside and .
- ///
- ///
- /// Purchase flow. The owning client calls
- /// with a category index. The server
- /// validates gold, deducts the cost, picks a random
- /// from the category's pool, and appends an
- /// to the replicated list.
- ///
- /// Multiplier query. is called by
- /// on the server each time a tower fires.
- /// It multiplies all active buffs for the requested stat together.
- ///
- /// Enable / disable. Enemies can call
- /// and by index to temporarily suppress buffs
- /// without removing them from the list.
- ///
- public class PlayerBuffManager : NetworkBehaviour
- {
- // ----- Static registry (mirrors PlayerGoldManager pattern) -----------
-
- private static readonly Dictionary s_byClientId
- = new Dictionary();
-
- /// Returns the PlayerBuffManager owned by the given client, or null.
- public static PlayerBuffManager GetForClient(ulong clientId)
- {
- s_byClientId.TryGetValue(clientId, out var mgr);
- return mgr;
- }
-
- /// Convenience: the local client's own buff manager.
- 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;
-
- private NetworkList buffs;
-
- private void Awake()
- {
- buffs = new NetworkList();
- }
-
- // ----- 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 -------------------------------------------------
-
- ///
- /// Returns the combined multiplier for across all
- /// active buffs owned by this player. Returns 1.0 if no matching buffs
- /// are active. Safe to call from any peer.
- ///
- public float GetMultiplier(BuffStat stat)
- {
- float result = 1f;
- for (int i = 0; i < buffs.Count; i++)
- {
- var buff = buffs[i];
- if (buff.Stat == stat && buff.IsActive)
- result *= buff.Multiplier;
- }
- return result;
- }
-
- ///
- /// Returns the number of buff categories available, for building the UI.
- ///
- public int CategoryCount => categories?.Length ?? 0;
-
- /// Returns the category at the given index, or null.
- public BuffCategory GetCategory(int index)
- => (categories != null && index >= 0 && index < categories.Length)
- ? categories[index]
- : null;
-
- /// Read-only access to the buff list for UI rendering.
- public NetworkList Buffs => buffs;
-
- // ----- Server helpers ---------------------------------------------
-
- ///
- /// Server-only: disables the buff at .
- /// The buff remains in the list and can be re-enabled.
- ///
- public void ServerDisableBuff(int index)
- {
- if (!IsServer) return;
- if (index < 0 || index >= buffs.Count) return;
-
- var b = buffs[index];
- if (!b.IsActive) return;
- b.IsActive = false;
- buffs[index] = b;
- }
-
- /// Server-only: re-enables the buff at .
- public void ServerEnableBuff(int index)
- {
- if (!IsServer) return;
- if (index < 0 || index >= buffs.Count) return;
-
- var b = buffs[index];
- if (b.IsActive) return;
- b.IsActive = true;
- buffs[index] = b;
- }
-
- // ----- Purchase RPC -----------------------------------------------
-
- ///
- /// Owning-client entry point. Sends a request to the server to purchase
- /// a random buff from the category at .
- /// The server validates gold and adds the buff if the purchase succeeds.
- ///
- [Rpc(SendTo.Server, RequireOwnership = true)]
- public void RequestPurchaseBuffRpc(int categoryIndex)
- {
- if (categories == null || categoryIndex < 0 || categoryIndex >= categories.Length)
- {
- Debug.LogWarning("[PlayerBuffManager] RequestPurchaseBuff: invalid category index.");
- return;
- }
-
- var category = categories[categoryIndex];
- if (category == null || category.Pool == null || category.Pool.Length == 0)
- {
- Debug.LogWarning("[PlayerBuffManager] RequestPurchaseBuff: category has no buffs.");
- return;
- }
-
- var goldManager = PlayerGoldManager.GetForClient(OwnerClientId);
- if (goldManager == null)
- {
- Debug.LogError("[PlayerBuffManager] RequestPurchaseBuff: no PlayerGoldManager found.");
- return;
- }
-
- if (goldManager.CurrentGold < category.Cost)
- {
- Debug.Log($"[PlayerBuffManager] Client {OwnerClientId} cannot afford " +
- $"'{category.DisplayName}' (cost {category.Cost}, " +
- $"gold {goldManager.CurrentGold}).");
- return;
- }
-
- goldManager.DeductGold(category.Cost);
-
- var def = category.Pool[Random.Range(0, category.Pool.Length)];
- buffs.Add(new ActiveBuff
- {
- Stat = def.Stat,
- Multiplier = def.Multiplier,
- IsActive = true,
- DisplayName = new FixedString64Bytes(def.DisplayName ?? string.Empty),
- });
-
- Debug.Log($"[PlayerBuffManager] Client {OwnerClientId} purchased buff " +
- $"'{def.DisplayName}' ({def.Stat} ×{def.Multiplier}).");
- }
- }
-}
diff --git a/Assets/_Project/Scripts/Gameplay/PlayerBuffManager.cs.meta b/Assets/_Project/Scripts/Gameplay/PlayerBuffManager.cs.meta
deleted file mode 100644
index b1951a5..0000000
--- a/Assets/_Project/Scripts/Gameplay/PlayerBuffManager.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 7775ee3a2f441b52480aabf54be6c1b6
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/UI/HUDController.cs b/Assets/_Project/Scripts/UI/HUDController.cs
index 8dfe824..6c3963f 100644
--- a/Assets/_Project/Scripts/UI/HUDController.cs
+++ b/Assets/_Project/Scripts/UI/HUDController.cs
@@ -21,60 +21,55 @@ namespace TD.UI
[RequireComponent(typeof(UIDocument))]
public class HUDController : MonoBehaviour
{
- public static HUDController Instance { get; private set; }
-
// ----- Inspector --------------------------------------------------
- [Header("Scene References")] [Tooltip("The local client's TowerPlacementController.")] [SerializeField]
- private TowerPlacementController placementController;
+ [Header("Scene References")]
+ [Tooltip("The local client's TowerPlacementController.")]
+ [SerializeField] private TowerPlacementController placementController;
[Tooltip("The local client's TowerPaintController (drives the Paint tab + paint cursor).")]
[SerializeField] private TowerPaintController paintController;
- [Tooltip("The TowerPlacementManager NetworkObject in the scene.")] [SerializeField]
- private TowerPlacementManager placementManager;
+ [Tooltip("The TowerPlacementManager NetworkObject in the scene.")]
+ [SerializeField] private TowerPlacementManager placementManager;
[Tooltip("The local client's CameraController. Used by the minimap for click-to-jump " +
"and drag-to-pan.")]
- [SerializeField]
- private CameraController cameraController;
+ [SerializeField] private CameraController cameraController;
- [Header("Settings")] [SerializeField] private float rejectionMessageDuration = 2.5f;
+ [Header("Settings")]
+ [SerializeField] private float rejectionMessageDuration = 2.5f;
[Tooltip("Maximum visible height of the chat feed in pixels. Content past this " +
"height is clipped — older messages scroll off the top of the visible area " +
"but stay in history (scroll up while chat is open to view).")]
- [SerializeField]
- private float chatMaxHeight = 280f;
+ [SerializeField] private float chatMaxHeight = 280f;
[Tooltip("Maximum messages kept in chat history. Defaults to effectively unlimited " +
"(int.MaxValue) — every message sent during a match stays scrollable. " +
"Lower the value if a long match ever shows DOM perf issues; this field " +
"is the safety valve, not a normal-play limit.")]
- [SerializeField]
- private int chatMaxMessages = int.MaxValue;
+ [SerializeField] private int chatMaxMessages = int.MaxValue;
- [Tooltip("Color used for SYSTEM chat messages (e.g. 'Life Lost', income changes).")] [SerializeField]
- private Color chatSystemColor = new Color(1f, 0.7f, 0.2f);
+ [Tooltip("Color used for SYSTEM chat messages (e.g. 'Life Lost', income changes).")]
+ [SerializeField] private Color chatSystemColor = new Color(1f, 0.7f, 0.2f);
[Tooltip("Color used for PLAYER chat message bodies. Sender prefix uses the player's slot color.")]
- [SerializeField]
- private Color chatPlayerColor = new Color(0.92f, 0.92f, 0.92f);
+ [SerializeField] private Color chatPlayerColor = new Color(0.92f, 0.92f, 0.92f);
// ----- Cached UI element references -------------------------------
private Label goldLabel;
private Label waveLabel;
private Label livesLabel;
- private Label nextWaveLabel; // prep countdown ("next: 0:12")
- private Label leakedLabel; // local player's origin-leak count ("leaked: 3")
- private Label incomeLabel; // top-bar per-wave gold-earned counter ("+150 g/wave")
+ private Label nextWaveLabel; // prep countdown ("next: 0:12")
+ private Label leakedLabel; // local player's origin-leak count ("leaked: 3")
+ private Label incomeLabel; // top-bar per-wave gold-earned counter ("+150 g/wave")
private VisualElement playerListContainer; // right-panel scoreboard rows
private Label portraitName;
private Label levelLabel;
private VisualElement statLines;
private VisualElement commandGrid;
-
private VisualElement actionFrame; // hidden via display:none when no actions are available
private VisualElement commandTabs; // Build/Paint tab row — shown only for a Builder selection
private Button tabBuild;
@@ -82,7 +77,6 @@ namespace TD.UI
private VisualElement buildProgressContainer; // info-panel sub-view, shown for BuildSiteVisual selections
private VisualElement buildProgressFill; // width driven each frame from progress
private Label buildProgressPercent;
-
private Label ttTitle;
private Label ttDesc;
private Label ttStats;
@@ -95,23 +89,19 @@ namespace TD.UI
// without rebuilding the elements.
private VisualElement enemyHealthBar;
private VisualElement enemyHealthFill;
- private Label enemyHealthText;
+ private Label enemyHealthText;
// 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;
+ private Label matchEndTitle;
// Chat panel (bottom-left, above portrait) — programmatic. The container
// holds both the scrollable feed and the input. Highlight + scroll
// interactivity are toggled on the container when typing.
private VisualElement chatContainer;
- private ScrollView chatFeed;
- private TextField chatInput;
- private bool chatInputOpen;
+ private ScrollView chatFeed;
+ private TextField chatInput;
+ private bool chatInputOpen;
// Frame on which the chat input was opened or closed. Enter on that frame
// and the next one is ignored to prevent the open/close-triggering keypress
@@ -133,12 +123,12 @@ namespace TD.UI
private CommandTab activeTab = CommandTab.Build;
private Coroutine rejectionFadeCoroutine;
- private bool placementManagerReady; // true once TowerPlacementManager.Instance is non-null
+ private bool placementManagerReady; // true once TowerPlacementManager.Instance is non-null
private bool uiInitialized;
- private bool selectionSubscribed; // true once we've successfully hooked SelectionState.OnSelectionChanged
- private bool matchStateSubscribed; // true once OnPhaseChanged is hooked
+ private bool selectionSubscribed; // true once we've successfully hooked SelectionState.OnSelectionChanged
+ private bool matchStateSubscribed; // true once OnPhaseChanged is hooked
private MinimapView minimapView;
- private IPanel myPanel; // tracked separately so OnDestroy only clears the static if it still points at us
+ private IPanel myPanel; // tracked separately so OnDestroy only clears the static if it still points at us
// ----- Hotkeys ----------------------------------------------------
//
@@ -160,15 +150,10 @@ namespace TD.UI
private readonly struct HotkeyBinding
{
public readonly Key Key;
- public readonly VisualElement Button; // for enabledSelf gating
+ public readonly VisualElement Button; // for enabledSelf gating
public readonly System.Action Action;
-
public HotkeyBinding(Key k, VisualElement b, System.Action a)
- {
- Key = k;
- Button = b;
- Action = a;
- }
+ { Key = k; Button = b; Action = a; }
}
// ----- Static hit-test probe --------------------------------------
@@ -264,7 +249,6 @@ namespace TD.UI
// Cache element references — log a warning for any that are missing
// so UXML/USS mismatches surface immediately.
-
goldLabel = Require