diff --git a/Assets/_Project/Scripts/Gameplay/BuilderUpgradeManager.cs b/Assets/_Project/Scripts/Gameplay/BuilderUpgradeManager.cs
index 0013ef9..5f07d31 100644
--- a/Assets/_Project/Scripts/Gameplay/BuilderUpgradeManager.cs
+++ b/Assets/_Project/Scripts/Gameplay/BuilderUpgradeManager.cs
@@ -13,10 +13,12 @@ namespace TD.Gameplay
/// .
///
///
- /// Bitmask, not a list. A builder effect carries no per-grant state (no level,
- /// no enable/disable toggle) — it's either granted or it isn't — so the granted set is one
- /// bitmask keyed by rather
- /// than a NetworkList. "Has effect" and "grant effect" are both O(1) bit ops.
+ /// List of granted kinds. A builder effect carries no per-grant state (no
+ /// level, no enable/disable toggle) — it's either granted or it isn't — so the granted set
+ /// is a of values. Stored as
+ /// byte because NetworkList<T> requires T : IEquatable<T>
+ /// and plain enums don't implement that (see for the
+ /// same constraint on a struct); byte already does, for free.
///
/// Granted via draft.
/// calls when a player picks it. Effects are permanent for
@@ -60,13 +62,10 @@ namespace TD.Gameplay
// ----- Networked state --------------------------------------------
- // One bit per BuilderEffectKind. readPerm Everyone (consistent with the other
- // per-player managers); writePerm Server.
- private readonly NetworkVariable grantedMask = new NetworkVariable(
- value: 0,
- readPerm: NetworkVariableReadPermission.Everyone,
- writePerm: NetworkVariableWritePermission.Server
- );
+ // Granted effect kinds, stored as byte (see class remarks for why not the enum
+ // directly). readPerm Everyone (consistent with the other per-player managers);
+ // writePerm Server — both are NetworkList's defaults.
+ private readonly NetworkList grantedKinds = new NetworkList();
/// Fired on every peer when a new effect is granted.
public event System.Action OnUpgradesChanged;
@@ -76,23 +75,23 @@ namespace TD.Gameplay
public override void OnNetworkSpawn()
{
s_byClientId[OwnerClientId] = this;
- grantedMask.OnValueChanged += HandleMaskChanged;
+ grantedKinds.OnListChanged += HandleGrantedKindsChanged;
}
public override void OnNetworkDespawn()
{
- grantedMask.OnValueChanged -= HandleMaskChanged;
+ grantedKinds.OnListChanged -= HandleGrantedKindsChanged;
if (s_byClientId.TryGetValue(OwnerClientId, out var registered) && registered == this)
s_byClientId.Remove(OwnerClientId);
}
- private void HandleMaskChanged(uint previous, uint current) => OnUpgradesChanged?.Invoke();
+ private void HandleGrantedKindsChanged(NetworkListEvent change) => OnUpgradesChanged?.Invoke();
// ----- Read API -----------------------------------------------------
/// True if this player has been granted the given effect.
- public bool PlayerHasEffect(BuilderEffectKind kind) => (grantedMask.Value & BitFor(kind)) != 0;
+ public bool PlayerHasEffect(BuilderEffectKind kind) => grantedKinds.Contains((byte)kind);
///
/// Returns this player's for
@@ -113,13 +112,11 @@ namespace TD.Gameplay
{
if (!IsServer) return false;
- uint bit = BitFor(kind);
- if ((grantedMask.Value & bit) != 0) return false;
+ byte value = (byte)kind;
+ if (grantedKinds.Contains(value)) return false;
- grantedMask.Value |= bit;
+ grantedKinds.Add(value);
return true;
}
-
- private static uint BitFor(BuilderEffectKind kind) => 1u << (int)kind;
}
}