Decals, ghost textures, placement functionality, builder stub ins, a new camera system, and more.
131 lines
5.3 KiB
C#
131 lines
5.3 KiB
C#
// Assets/_Project/Scripts/Gameplay/TowerRegistry.cs
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using TD.Towers;
|
|
|
|
namespace TD.Gameplay
|
|
{
|
|
/// <summary>
|
|
/// Scene singleton that holds every <see cref="TowerDefinition"/> available in the
|
|
/// current match and lets any code look one up by asset name.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para><b>Why this exists.</b> <see cref="TowerInstance"/> replicates a tower's
|
|
/// definition by name (a <c>FixedString64Bytes</c> over the network), then resolves
|
|
/// the full ScriptableObject locally on every client. TowerRegistry is the lookup
|
|
/// table that makes that resolution possible without hard-coding asset paths.</para>
|
|
///
|
|
/// <para><b>Auto-discovery.</b> On Awake, all <see cref="TowerDefinition"/> assets
|
|
/// under <c>Resources/TowerDefinitions/</c> are loaded automatically. No inspector
|
|
/// drag-and-drop required — add a new asset to that folder and it is registered at
|
|
/// runtime with no other changes needed. This scales cleanly to 100+ tower types.</para>
|
|
///
|
|
/// <para><b>Path E upgrade path.</b> In Path E the registry will filter to only the
|
|
/// definitions belonging to the active match's <c>RaceDefinition</c> rosters. For now
|
|
/// all assets in the Resources folder are registered.</para>
|
|
///
|
|
/// <para><b>Plain MonoBehaviour.</b> Not a NetworkBehaviour — the registry is
|
|
/// identical on every peer (same assets, same names), so there is nothing to sync.</para>
|
|
/// </remarks>
|
|
public class TowerRegistry : MonoBehaviour
|
|
{
|
|
// ----- Singleton --------------------------------------------------
|
|
|
|
/// <summary>
|
|
/// The active TowerRegistry. Null before Awake or after the scene unloads.
|
|
/// Always null-check before use.
|
|
/// </summary>
|
|
public static TowerRegistry Instance { get; private set; }
|
|
|
|
// ----- Constants --------------------------------------------------
|
|
|
|
/// <summary>
|
|
/// Resources-relative folder path that TowerDefinition assets must live under
|
|
/// to be auto-discovered. Create this folder if it doesn't exist.
|
|
/// Full path: Assets/Resources/TowerDefinitions/
|
|
/// </summary>
|
|
private const string ResourcesFolder = "TowerDefinitions";
|
|
|
|
// ----- Internal lookup table --------------------------------------
|
|
|
|
// Keyed by TowerDefinition.name (the asset name, not DisplayName).
|
|
private readonly Dictionary<string, TowerDefinition> byName
|
|
= new Dictionary<string, TowerDefinition>();
|
|
|
|
// ----- Lifecycle --------------------------------------------------
|
|
|
|
private void Awake()
|
|
{
|
|
if (Instance != null && Instance != this)
|
|
{
|
|
Debug.LogError("[TowerRegistry] Multiple instances detected. " +
|
|
"Only one TowerRegistry should exist per scene.");
|
|
return;
|
|
}
|
|
Instance = this;
|
|
|
|
BuildLookupTable();
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
if (Instance == this) Instance = null;
|
|
}
|
|
|
|
// ----- Public API -------------------------------------------------
|
|
|
|
/// <summary>
|
|
/// Returns the <see cref="TowerDefinition"/> whose asset name equals
|
|
/// <paramref name="assetName"/>, or null if no match is found.
|
|
/// </summary>
|
|
public TowerDefinition Get(string assetName)
|
|
{
|
|
byName.TryGetValue(assetName, out var def);
|
|
return def;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns all registered tower definitions. Enumerates the internal
|
|
/// dictionary values — do not modify the returned collection.
|
|
/// </summary>
|
|
public IEnumerable<TowerDefinition> All => byName.Values;
|
|
|
|
// ----- Private ----------------------------------------------------
|
|
|
|
private void BuildLookupTable()
|
|
{
|
|
byName.Clear();
|
|
|
|
// Resources.LoadAll finds every TowerDefinition asset anywhere under
|
|
// Assets/Resources/TowerDefinitions/ (including sub-folders).
|
|
// No manual registration needed — drop an asset in the folder and it
|
|
// is available on the next play session.
|
|
var loaded = Resources.LoadAll<TowerDefinition>(ResourcesFolder);
|
|
|
|
if (loaded.Length == 0)
|
|
{
|
|
Debug.LogWarning($"[TowerRegistry] No TowerDefinition assets found under " +
|
|
$"Resources/{ResourcesFolder}/. " +
|
|
$"Create the folder and add TowerDefinition assets to it.");
|
|
return;
|
|
}
|
|
|
|
foreach (var def in loaded)
|
|
{
|
|
if (def == null) continue;
|
|
|
|
if (byName.ContainsKey(def.name))
|
|
{
|
|
Debug.LogWarning($"[TowerRegistry] Duplicate asset name '{def.name}'. " +
|
|
$"Only the first entry will be used. Rename one of the assets.");
|
|
continue;
|
|
}
|
|
|
|
byName[def.name] = def;
|
|
}
|
|
|
|
Debug.Log($"[TowerRegistry] Auto-discovered and registered " +
|
|
$"{byName.Count} tower definition(s) from Resources/{ResourcesFolder}/.");
|
|
}
|
|
}
|
|
}
|