diff --git a/Assets/_Project/Prefabs/Towers/Tower Construction Sets/BuildSiteVisual.prefab b/Assets/_Project/Prefabs/Towers/BuildSiteVisual.prefab similarity index 99% rename from Assets/_Project/Prefabs/Towers/Tower Construction Sets/BuildSiteVisual.prefab rename to Assets/_Project/Prefabs/Towers/BuildSiteVisual.prefab index 6b3815f..66abbc6 100644 --- a/Assets/_Project/Prefabs/Towers/Tower Construction Sets/BuildSiteVisual.prefab +++ b/Assets/_Project/Prefabs/Towers/BuildSiteVisual.prefab @@ -298,7 +298,6 @@ MonoBehaviour: queuedMaterial: {fileID: 2100000, guid: f8d951a6841d3f74098bb31255379774, type: 2} constructingMaterial: {fileID: 2100000, guid: 88f1dd7b174716645953857b38fb6948, type: 2} pausedMaterial: {fileID: 0} - defaultPhaseSet: {fileID: 11400000, guid: ed951755c09d6d0479b4fd924b6915da, type: 2} stageCount: 4 queuedYScale: 0.15 --- !u!65 &6259437393614329336 diff --git a/Assets/_Project/Prefabs/Towers/Tower Construction Sets/BuildSiteVisual.prefab.meta b/Assets/_Project/Prefabs/Towers/BuildSiteVisual.prefab.meta similarity index 100% rename from Assets/_Project/Prefabs/Towers/Tower Construction Sets/BuildSiteVisual.prefab.meta rename to Assets/_Project/Prefabs/Towers/BuildSiteVisual.prefab.meta diff --git a/Assets/_Project/Prefabs/Towers/Tower Construction Sets.meta b/Assets/_Project/Prefabs/Towers/Tower Construction Sets.meta deleted file mode 100644 index e7fd49e..0000000 --- a/Assets/_Project/Prefabs/Towers/Tower Construction Sets.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 3f1ef90255e065249a86a838844f7be2 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet.meta b/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet.meta deleted file mode 100644 index e070754..0000000 --- a/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 82e418e7a5a804747a9a4865648b7793 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet/DefaultConstructionSet.asset b/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet/DefaultConstructionSet.asset deleted file mode 100644 index 2272a43..0000000 --- a/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet/DefaultConstructionSet.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: c8f2a4e1b9d6437052a1e8c4f7b3d069, type: 3} - m_Name: DefaultConstructionSet - m_EditorClassIdentifier: Assembly-CSharp::TD.Towers.ConstructionPhaseSet - phases: - - {fileID: 562891720401665892, guid: e7be86c29087e99439507b508425b612, type: 3} - - {fileID: 3284400382278582069, guid: c6dabc822d0afb746adc08fb8602c2b4, type: 3} - - {fileID: 2161157853692362223, guid: d00c71c0cfc7e8744b9f8c1d48c2d256, type: 3} diff --git a/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet/DefaultConstructionSet.asset.meta b/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet/DefaultConstructionSet.asset.meta deleted file mode 100644 index 3832c3f..0000000 --- a/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet/DefaultConstructionSet.asset.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: ed951755c09d6d0479b4fd924b6915da -NativeFormatImporter: - externalObjects: {} - mainObjectFileID: 11400000 - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet/DefaultConstruction_Phase_1.prefab b/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet/DefaultConstruction_Phase_1.prefab deleted file mode 100644 index 70ca06b..0000000 --- a/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet/DefaultConstruction_Phase_1.prefab +++ /dev/null @@ -1,114 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!1 &562891720401665892 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 5433206400477731590} - - component: {fileID: 3041202948021905482} - - component: {fileID: 2318454595080774531} - - component: {fileID: 2971339723339766249} - m_Layer: 0 - m_Name: DefaultConstruction_Phase_1 - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!4 &5433206400477731590 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 562891720401665892} - serializedVersion: 2 - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 1, y: 0.1, z: 1} - m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 0} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!33 &3041202948021905482 -MeshFilter: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 562891720401665892} - m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} ---- !u!23 &2318454595080774531 -MeshRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 562891720401665892} - m_Enabled: 1 - m_CastShadows: 1 - m_ReceiveShadows: 1 - m_DynamicOccludee: 1 - m_StaticShadowCaster: 0 - m_MotionVectors: 1 - m_LightProbeUsage: 1 - m_ReflectionProbeUsage: 1 - m_RayTracingMode: 2 - m_RayTraceProcedural: 0 - m_RayTracingAccelStructBuildFlagsOverride: 0 - m_RayTracingAccelStructBuildFlags: 1 - m_SmallMeshCulling: 1 - m_ForceMeshLod: -1 - m_MeshLodSelectionBias: 0 - m_RenderingLayerMask: 1 - m_RendererPriority: 0 - m_Materials: - - {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2} - m_StaticBatchInfo: - firstSubMesh: 0 - subMeshCount: 0 - m_StaticBatchRoot: {fileID: 0} - m_ProbeAnchor: {fileID: 0} - m_LightProbeVolumeOverride: {fileID: 0} - m_ScaleInLightmap: 1 - m_ReceiveGI: 1 - m_PreserveUVs: 0 - m_IgnoreNormalsForChartDetection: 0 - m_ImportantGI: 0 - m_StitchLightmapSeams: 1 - m_SelectedEditorRenderState: 3 - m_MinimumChartSize: 4 - m_AutoUVMaxDistance: 0.5 - m_AutoUVMaxAngle: 89 - m_LightmapParameters: {fileID: 0} - m_GlobalIlluminationMeshLod: 0 - m_SortingLayerID: 0 - m_SortingLayer: 0 - m_SortingOrder: 0 - m_MaskInteraction: 0 - m_AdditionalVertexStreams: {fileID: 0} ---- !u!65 &2971339723339766249 -BoxCollider: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 562891720401665892} - m_Material: {fileID: 0} - m_IncludeLayers: - serializedVersion: 2 - m_Bits: 0 - m_ExcludeLayers: - serializedVersion: 2 - m_Bits: 0 - m_LayerOverridePriority: 0 - m_IsTrigger: 0 - m_ProvidesContacts: 0 - m_Enabled: 1 - serializedVersion: 3 - m_Size: {x: 1, y: 1, z: 1} - m_Center: {x: 0, y: 0, z: 0} diff --git a/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet/DefaultConstruction_Phase_1.prefab.meta b/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet/DefaultConstruction_Phase_1.prefab.meta deleted file mode 100644 index 684c909..0000000 --- a/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet/DefaultConstruction_Phase_1.prefab.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: e7be86c29087e99439507b508425b612 -PrefabImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet/DefaultConstruction_Phase_2.prefab b/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet/DefaultConstruction_Phase_2.prefab deleted file mode 100644 index 43ed50f..0000000 --- a/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet/DefaultConstruction_Phase_2.prefab +++ /dev/null @@ -1,114 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!1 &3284400382278582069 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 3820746952356246079} - - component: {fileID: 3350984935134180217} - - component: {fileID: 6336690892943026752} - - component: {fileID: 2003287843732108245} - m_Layer: 0 - m_Name: DefaultConstruction_Phase_2 - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!4 &3820746952356246079 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3284400382278582069} - serializedVersion: 2 - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 1.5, y: 0.5, z: 1.5} - m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 0} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!33 &3350984935134180217 -MeshFilter: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3284400382278582069} - m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} ---- !u!23 &6336690892943026752 -MeshRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3284400382278582069} - m_Enabled: 1 - m_CastShadows: 1 - m_ReceiveShadows: 1 - m_DynamicOccludee: 1 - m_StaticShadowCaster: 0 - m_MotionVectors: 1 - m_LightProbeUsage: 1 - m_ReflectionProbeUsage: 1 - m_RayTracingMode: 2 - m_RayTraceProcedural: 0 - m_RayTracingAccelStructBuildFlagsOverride: 0 - m_RayTracingAccelStructBuildFlags: 1 - m_SmallMeshCulling: 1 - m_ForceMeshLod: -1 - m_MeshLodSelectionBias: 0 - m_RenderingLayerMask: 1 - m_RendererPriority: 0 - m_Materials: - - {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2} - m_StaticBatchInfo: - firstSubMesh: 0 - subMeshCount: 0 - m_StaticBatchRoot: {fileID: 0} - m_ProbeAnchor: {fileID: 0} - m_LightProbeVolumeOverride: {fileID: 0} - m_ScaleInLightmap: 1 - m_ReceiveGI: 1 - m_PreserveUVs: 0 - m_IgnoreNormalsForChartDetection: 0 - m_ImportantGI: 0 - m_StitchLightmapSeams: 1 - m_SelectedEditorRenderState: 3 - m_MinimumChartSize: 4 - m_AutoUVMaxDistance: 0.5 - m_AutoUVMaxAngle: 89 - m_LightmapParameters: {fileID: 0} - m_GlobalIlluminationMeshLod: 0 - m_SortingLayerID: 0 - m_SortingLayer: 0 - m_SortingOrder: 0 - m_MaskInteraction: 0 - m_AdditionalVertexStreams: {fileID: 0} ---- !u!65 &2003287843732108245 -BoxCollider: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3284400382278582069} - m_Material: {fileID: 0} - m_IncludeLayers: - serializedVersion: 2 - m_Bits: 0 - m_ExcludeLayers: - serializedVersion: 2 - m_Bits: 0 - m_LayerOverridePriority: 0 - m_IsTrigger: 0 - m_ProvidesContacts: 0 - m_Enabled: 1 - serializedVersion: 3 - m_Size: {x: 1, y: 1, z: 1} - m_Center: {x: 0, y: 0, z: 0} diff --git a/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet/DefaultConstruction_Phase_2.prefab.meta b/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet/DefaultConstruction_Phase_2.prefab.meta deleted file mode 100644 index bb668c4..0000000 --- a/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet/DefaultConstruction_Phase_2.prefab.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: c6dabc822d0afb746adc08fb8602c2b4 -PrefabImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet/DefaultConstruction_Phase_3.prefab b/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet/DefaultConstruction_Phase_3.prefab deleted file mode 100644 index 0c380ed..0000000 --- a/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet/DefaultConstruction_Phase_3.prefab +++ /dev/null @@ -1,114 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!1 &2161157853692362223 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 6591719589271492770} - - component: {fileID: 3693505870455731207} - - component: {fileID: 2366594505425509977} - - component: {fileID: 5474707419433840976} - m_Layer: 0 - m_Name: DefaultConstruction_Phase_3 - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!4 &6591719589271492770 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2161157853692362223} - serializedVersion: 2 - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 1.75, y: 1, z: 1.75} - m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 0} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!33 &3693505870455731207 -MeshFilter: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2161157853692362223} - m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} ---- !u!23 &2366594505425509977 -MeshRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2161157853692362223} - m_Enabled: 1 - m_CastShadows: 1 - m_ReceiveShadows: 1 - m_DynamicOccludee: 1 - m_StaticShadowCaster: 0 - m_MotionVectors: 1 - m_LightProbeUsage: 1 - m_ReflectionProbeUsage: 1 - m_RayTracingMode: 2 - m_RayTraceProcedural: 0 - m_RayTracingAccelStructBuildFlagsOverride: 0 - m_RayTracingAccelStructBuildFlags: 1 - m_SmallMeshCulling: 1 - m_ForceMeshLod: -1 - m_MeshLodSelectionBias: 0 - m_RenderingLayerMask: 1 - m_RendererPriority: 0 - m_Materials: - - {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2} - m_StaticBatchInfo: - firstSubMesh: 0 - subMeshCount: 0 - m_StaticBatchRoot: {fileID: 0} - m_ProbeAnchor: {fileID: 0} - m_LightProbeVolumeOverride: {fileID: 0} - m_ScaleInLightmap: 1 - m_ReceiveGI: 1 - m_PreserveUVs: 0 - m_IgnoreNormalsForChartDetection: 0 - m_ImportantGI: 0 - m_StitchLightmapSeams: 1 - m_SelectedEditorRenderState: 3 - m_MinimumChartSize: 4 - m_AutoUVMaxDistance: 0.5 - m_AutoUVMaxAngle: 89 - m_LightmapParameters: {fileID: 0} - m_GlobalIlluminationMeshLod: 0 - m_SortingLayerID: 0 - m_SortingLayer: 0 - m_SortingOrder: 0 - m_MaskInteraction: 0 - m_AdditionalVertexStreams: {fileID: 0} ---- !u!65 &5474707419433840976 -BoxCollider: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2161157853692362223} - m_Material: {fileID: 0} - m_IncludeLayers: - serializedVersion: 2 - m_Bits: 0 - m_ExcludeLayers: - serializedVersion: 2 - m_Bits: 0 - m_LayerOverridePriority: 0 - m_IsTrigger: 0 - m_ProvidesContacts: 0 - m_Enabled: 1 - serializedVersion: 3 - m_Size: {x: 1, y: 1, z: 1} - m_Center: {x: 0, y: 0, z: 0} diff --git a/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet/DefaultConstruction_Phase_3.prefab.meta b/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet/DefaultConstruction_Phase_3.prefab.meta deleted file mode 100644 index dc59d87..0000000 --- a/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet/DefaultConstruction_Phase_3.prefab.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: d00c71c0cfc7e8744b9f8c1d48c2d256 -PrefabImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Project/Scripts/Gameplay/BuildSiteVisual.cs b/Assets/_Project/Scripts/Gameplay/BuildSiteVisual.cs index 3ba4d76..b0e6e55 100644 --- a/Assets/_Project/Scripts/Gameplay/BuildSiteVisual.cs +++ b/Assets/_Project/Scripts/Gameplay/BuildSiteVisual.cs @@ -1,4 +1,5 @@ // Assets/_Project/Scripts/Gameplay/BuildSiteVisual.cs +using Unity.Collections; using Unity.Netcode; using UnityEngine; using TD.Core; @@ -62,25 +63,27 @@ namespace TD.Gameplay "Suggested: muted/grey-tinted variant of the constructing material.")] [SerializeField] private Material pausedMaterial; - [Header("Construction phases")] - [Tooltip("Project-default construction-stage visuals used when the tower being built " + - "doesn't define its own ConstructionPhases. When this (or the tower's set) " + - "has phases, the build site swaps between those phase prefabs as it builds. " + - "Leave empty to fall back to the legacy cube Y-scale growth below.")] - [SerializeField] private ConstructionPhaseSet defaultPhaseSet; - - [Header("Construction stages (cube fallback)")] - [Tooltip("FALLBACK ONLY (no phase set assigned): number of discrete growth stages " + - "while constructing. 4 = 1/4 → 2/4 → 3/4 → 4/4 height.")] + [Header("Construction stages")] + [Tooltip("Number of discrete growth stages while constructing. 4 matches the " + + "design doc (1/4 → 2/4 → 3/4 → 4/4 height).")] [SerializeField] private int stageCount = 4; - [Tooltip("FALLBACK ONLY: Y-scale applied to scaleTarget when Stage == Queued. " + - "Visually distinct from any constructing height so the queued ghost reads " + - "as 'intent, not progress'.")] + [Tooltip("Y-scale applied to scaleTarget when Stage == Queued. Visually " + + "distinct from any constructing height so the queued ghost reads as " + + "'intent, not progress'.")] [SerializeField] private float queuedYScale = 0.15f; // ----- Networked state -------------------------------------------- + // Replicated definition name so clients can resolve the source TowerDefinition + // (matches TowerInstance's pattern). Used for footprint size only — the visual + // prefab is the same regardless of tower type for now. + private readonly NetworkVariable definitionName = + new NetworkVariable( + default, + readPerm: NetworkVariableReadPermission.Everyone, + writePerm: NetworkVariableWritePermission.Server); + // Replicated owner slot for color tinting. Mirrors TowerInstance. private readonly NetworkVariable ownerSlot = new NetworkVariable( @@ -229,6 +232,7 @@ namespace TD.Gameplay // ----- Pre-spawn init data (server) ------------------------------- + private string pendingDefName; private PlayerSlot pendingOwner = PlayerSlot.None; private float pendingBuildTime; private Vector2Int pendingAnchor; @@ -236,17 +240,6 @@ namespace TD.Gameplay private int pendingGoldSpent; private bool hasPendingInit; - // ----- Construction-phase runtime (local, all peers) -------------- - - // Resolved on spawn from the tower's ConstructionPhases or the defaultPhaseSet. - // When non-null with PhaseCount > 0, the build site swaps between these - // instantiated phase prefabs instead of Y-scaling the fallback cube. - private ConstructionPhaseSet effectivePhaseSet; - private GameObject[] phaseInstances; // one instantiated child per phase - private MeshRenderer[][] phaseRenderers; // cached renderers per phase, for tinting - private int activePhaseIndex = -1; - private bool usePhases; - // ----- Lifecycle -------------------------------------------------- /// @@ -268,6 +261,7 @@ namespace TD.Gameplay return; } + pendingDefName = def != null ? def.name : string.Empty; pendingOwner = owner; pendingBuildTime = def != null ? def.BuildTime : 0f; pendingAnchor = anchorTile; @@ -287,6 +281,7 @@ namespace TD.Gameplay // message so clients see correct values on their first OnNetworkSpawn. if (IsServer && hasPendingInit) { + definitionName.Value = new FixedString64Bytes(pendingDefName ?? string.Empty); ownerSlot.Value = pendingOwner; buildTime.Value = pendingBuildTime; anchor.Value = pendingAnchor; @@ -295,10 +290,6 @@ namespace TD.Gameplay hasPendingInit = false; } - // Resolve and instantiate the construction-phase visuals (or fall back to the - // cube). Depends on towerTypeId, which is replicated by now on every peer. - ResolvePhaseSet(); - // Subscribe to value changes so visual updates are reactive. currentStage.OnValueChanged += HandleStageChanged; @@ -353,25 +344,14 @@ namespace TD.Gameplay private void Update() { - // While constructing, advance the visual based on server time. Runs on every - // peer so visuals stay synchronized. Paused does NOT update — the visual is - // frozen at the pause point. + // While constructing, smoothly interpolate Y-scale through the stages + // based on server time. This runs on every peer (server + clients) so + // visuals stay synchronized regardless of who's looking. + // Paused stage does NOT update — Y-scale is frozen at the pause point. if (currentStage.Value != BuildStage.Constructing) return; - if (usePhases) - { - int index = PhaseIndexFromProgress(); - if (index != activePhaseIndex) - { - ShowPhase(index); - // Renderers changed with the swap — re-apply material + owner tint. - ApplyStageMaterialAndTint(BuildStage.Constructing); - } - return; - } - - // Fallback: smoothly grow the cube's Y-scale through the stages. - ApplyYScale(ComputeConstructingYScale()); + float yScale = ComputeConstructingYScale(); + ApplyYScale(yScale); } // ----- Server API ------------------------------------------------- @@ -518,117 +498,33 @@ namespace TD.Gameplay } private void ApplyStageVisual(BuildStage stage) - { - if (usePhases) - { - // Queued shows the first phase; constructing/paused show the phase - // matching current progress. ShowPhase repoints tintedRenderers at the - // active phase instance, so material + tint are applied AFTER it. - int index = stage == BuildStage.Queued ? 0 : PhaseIndexFromProgress(); - ShowPhase(index); - ApplyStageMaterialAndTint(stage); - return; - } - - // Fallback: legacy cube Y-scale growth (no phase set wired). - ApplyStageMaterialAndTint(stage); - switch (stage) - { - case BuildStage.Queued: ApplyYScale(queuedYScale); break; - case BuildStage.Constructing: ApplyYScale(ComputeConstructingYScale()); break; - // ComputePausedYScale uses accumulatedConstructionTime alone when - // constructionStartServerTime is -1 (the pause sentinel). - case BuildStage.Paused: ApplyYScale(ComputePausedYScale()); break; - } - } - - // Applies the stage's material + owner tint to whatever renderers are currently - // active — the cube fallback, or the active phase instance's renderers. Paused - // falls back to the constructing material if no paused material is assigned. - private void ApplyStageMaterialAndTint(BuildStage stage) { switch (stage) { case BuildStage.Queued: - SwapMaterial(queuedMaterial); // green ghost — no owner tint + SwapMaterial(queuedMaterial); + ApplyYScale(queuedYScale); break; + case BuildStage.Constructing: SwapMaterial(constructingMaterial); ApplyOwnerTint(); + ApplyYScale(ComputeConstructingYScale()); break; + case BuildStage.Paused: + // Use paused material if assigned; fall back to constructing + // material if not (still readable, just less distinct). SwapMaterial(pausedMaterial != null ? pausedMaterial : constructingMaterial); ApplyOwnerTint(); + // Freeze Y-scale at whatever the accumulated progress represents. + // ComputeConstructingYScale uses accumulatedConstructionTime alone + // when constructionStartServerTime is -1 (the pause sentinel). + ApplyYScale(ComputePausedYScale()); break; } } - // ----- Construction phases ---------------------------------------- - - // Resolves the effective phase set (tower's own, else the project default) and - // instantiates one inactive child per phase. Sets usePhases=false (cube fallback) - // if neither set has any phases. Runs on every peer in OnNetworkSpawn. - private void ResolvePhaseSet() - { - var def = TowerPlacementManager.GetDefinition(towerTypeId.Value); - effectivePhaseSet = - (def != null && def.ConstructionPhases != null && def.ConstructionPhases.PhaseCount > 0) - ? def.ConstructionPhases - : defaultPhaseSet; - - int n = effectivePhaseSet != null ? effectivePhaseSet.PhaseCount : 0; - if (n <= 0) - { - usePhases = false; - return; - } - - usePhases = true; - phaseInstances = new GameObject[n]; - phaseRenderers = new MeshRenderer[n][]; - - // Hide the legacy fallback cube — the instantiated phase prefabs replace it. - if (scaleTarget != null) scaleTarget.gameObject.SetActive(false); - - for (int i = 0; i < n; i++) - { - var prefab = effectivePhaseSet.GetPhase(i); - if (prefab == null) continue; - - var go = Instantiate(prefab, transform); - go.transform.localPosition = Vector3.zero; - go.transform.localRotation = Quaternion.identity; - go.SetActive(false); - - phaseInstances[i] = go; - phaseRenderers[i] = go.GetComponentsInChildren(true); - } - } - - // Activates only phase and points tintedRenderers at its - // renderers so the existing material/tint helpers drive the active phase. - private void ShowPhase(int index) - { - if (phaseInstances == null) return; - index = Mathf.Clamp(index, 0, phaseInstances.Length - 1); - - for (int i = 0; i < phaseInstances.Length; i++) - if (phaseInstances[i] != null) - phaseInstances[i].SetActive(i == index); - - activePhaseIndex = index; - tintedRenderers = phaseRenderers[index]; - } - - // Maps current normalized progress to a phase index (0..PhaseCount-1). - private int PhaseIndexFromProgress() - { - int n = effectivePhaseSet != null ? effectivePhaseSet.PhaseCount : 1; - if (n <= 1) return 0; - float p = ComputeProgressNormalized(); - return Mathf.Clamp(Mathf.FloorToInt(p * n), 0, n - 1); - } - // Stage index 0..stageCount-1 based on elapsed server time PLUS any accumulated // time from previous Constructing runs (resume support). // Returned Y-scale is (stageIndex + 1) / stageCount, so stage 0 = 1/4, diff --git a/Assets/_Project/Scripts/Gameplay/RaceDefinition.cs b/Assets/_Project/Scripts/Gameplay/RaceDefinition.cs index b809684..83aec06 100644 --- a/Assets/_Project/Scripts/Gameplay/RaceDefinition.cs +++ b/Assets/_Project/Scripts/Gameplay/RaceDefinition.cs @@ -68,8 +68,8 @@ namespace TD.Gameplay "the prefab based on the player's RaceSelection.")] public GameObject BuilderPrefab; - [Tooltip("STUB: tower roster available to this race/builder. The draft pool or " + - "starting deck will eventually filter to this list for this race.")] + [Tooltip("STUB (Phase 1.8): tower roster available to this race. TowerRegistry will " + + "filter to this list when the active player belongs to this race.")] public TowerDefinition[] Towers; } } diff --git a/Assets/_Project/Scripts/Gameplay/TowerInstance.cs b/Assets/_Project/Scripts/Gameplay/TowerInstance.cs index 724776e..b99c0c2 100644 --- a/Assets/_Project/Scripts/Gameplay/TowerInstance.cs +++ b/Assets/_Project/Scripts/Gameplay/TowerInstance.cs @@ -1,4 +1,5 @@ // Assets/_Project/Scripts/Gameplay/TowerInstance.cs +using Unity.Collections; using Unity.Netcode; using UnityEngine; using TD.Core; @@ -31,11 +32,11 @@ namespace TD.Gameplay /// are idempotent writes, so double-stamping is safe. /// /// Definition reference replication. TowerDefinition assets live in the - /// project on all clients. We replicate the tower's TowerTypeId (its index in - /// 's catalog) via a , - /// then resolve the full asset locally with . - /// This avoids serializing the ScriptableObject over the network and reuses the same - /// identifier placement and the deck already use — a single source of truth. + /// project on all clients. We replicate the asset by name via a + /// holding a FixedString64Bytes, then look + /// up the asset locally. This avoids serializing the full ScriptableObject over the + /// network. The lookup uses a singleton that must be + /// present in the scene. (Temporary: will be driven by RaceDefinition in Path E.) /// /// Combat. No combat logic here yet. Combat fields live stubbed on /// ; they will be consumed by a future @@ -57,13 +58,12 @@ namespace TD.Gameplay // ----- Networked state ------------------------------------------------ - // The TowerTypeId (index into TowerPlacementManager's catalog) for this tower. - // Replicated so every client resolves the full definition locally via the catalog — - // the same identifier placement and the deck already use. 0 = unset/invalid (the - // reserved catalog index). Replaces the old replicate-by-name + TowerRegistry path. - private readonly NetworkVariable definitionTypeId = - new NetworkVariable( - 0, + // The name of the TowerDefinition asset for this tower. Replicated so all + // clients can look up the full definition locally without sending the whole + // ScriptableObject over the wire. + private readonly NetworkVariable definitionName = + new NetworkVariable( + default, readPerm: NetworkVariableReadPermission.Everyone, writePerm: NetworkVariableWritePermission.Server); @@ -95,8 +95,8 @@ namespace TD.Gameplay // ----- Local resolved state ------------------------------------------- - // Resolved on every client in OnNetworkSpawn from definitionTypeId via the catalog. - // Null if the lookup fails (TypeId not in the catalog on this peer). + // Resolved on every client in OnNetworkSpawn from definitionName. + // Null if the lookup fails (missing TowerRegistry or unknown name). private TowerDefinition resolvedDefinition; // ----- Pre-spawn initialization data ---------------------------------- @@ -192,7 +192,7 @@ namespace TD.Gameplay hasPendingInit = true; // Cache the resolved definition on the server immediately — clients - // resolve from the replicated TowerTypeId via the catalog once it arrives. + // will resolve via the registry lookup once definitionName arrives. resolvedDefinition = def; } @@ -206,17 +206,7 @@ namespace TD.Gameplay // every client sees correct values on its very first OnNetworkSpawn. if (IsServer && hasPendingInit) { - // Resolve the catalog index for this definition and replicate that. - int typeId = 0; - var pm = TowerPlacementManager.Instance; - if (pm == null || !pm.TryGetTypeId(pendingDefinition, out typeId)) - { - Debug.LogError($"[TowerInstance] Could not resolve a TowerTypeId for " + - $"'{pendingDefinition?.name}'. Ensure it is in the " + - $"TowerPlacementManager catalog (towerDefinitions)."); - } - - definitionTypeId.Value = typeId; + definitionName.Value = new FixedString64Bytes(pendingDefinition.name); anchorTile.Value = pendingAnchor; ownerSlot.Value = pendingOwner; @@ -341,13 +331,27 @@ namespace TD.Gameplay // Already resolved (server path via InitializeServer). if (resolvedDefinition != null) return; - int typeId = definitionTypeId.Value; - resolvedDefinition = TowerPlacementManager.GetDefinition(typeId); + string defName = definitionName.Value.ToString(); + if (string.IsNullOrEmpty(defName)) + { + Debug.LogError($"[TowerInstance] NetworkObject {NetworkObjectId}: " + + $"definitionName is empty. Cannot resolve TowerDefinition."); + return; + } + + var registry = TowerRegistry.Instance; + if (registry == null) + { + Debug.LogError($"[TowerInstance] NetworkObject {NetworkObjectId}: " + + $"No TowerRegistry found in scene. Cannot resolve '{defName}'."); + return; + } + + resolvedDefinition = registry.Get(defName); if (resolvedDefinition == null) { Debug.LogError($"[TowerInstance] NetworkObject {NetworkObjectId}: " + - $"no TowerDefinition for TypeId {typeId}. Is the " + - $"TowerPlacementManager catalog populated on this peer?"); + $"TowerRegistry does not contain a definition named '{defName}'."); } } diff --git a/Assets/_Project/Scripts/Gameplay/TowerRegistry.cs b/Assets/_Project/Scripts/Gameplay/TowerRegistry.cs new file mode 100644 index 0000000..389df11 --- /dev/null +++ b/Assets/_Project/Scripts/Gameplay/TowerRegistry.cs @@ -0,0 +1,120 @@ +// Assets/_Project/Scripts/Gameplay/TowerRegistry.cs +using System.Collections.Generic; +using UnityEngine; +using TD.Towers; + +namespace TD.Gameplay +{ + /// + /// Scene singleton that holds every available in the + /// current match and lets any code look one up by asset name. + /// + /// + /// Why this exists. replicates a tower's + /// definition by name (a FixedString64Bytes 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. + /// + /// Registration. Drag every asset into the + /// Definitions list on this component in the inspector. Assets can live anywhere + /// in the project — no special folder required. + /// + /// Path E upgrade path. In Path E the registry will filter to only the + /// definitions belonging to the active match's RaceDefinition rosters. For now + /// all assigned assets are registered. + /// + /// Plain MonoBehaviour. Not a NetworkBehaviour — the registry is + /// identical on every peer (same assets, same names), so there is nothing to sync. + /// + public class TowerRegistry : MonoBehaviour + { + // ----- Singleton -------------------------------------------------- + + /// + /// The active TowerRegistry. Null before Awake or after the scene unloads. + /// Always null-check before use. + /// + public static TowerRegistry Instance { get; private set; } + + // ----- Inspector -------------------------------------------------- + + [Tooltip("All TowerDefinition assets available in this match. " + + "Drag assets here from Assets/_Project/Data/TowerDefinitions/ " + + "(or wherever they live). Asset name is used as the registry key.")] + [SerializeField] private TowerDefinition[] definitions; + + // ----- Internal lookup table -------------------------------------- + + // Keyed by TowerDefinition.name (the asset name, not DisplayName). + private readonly Dictionary byName + = new Dictionary(); + + // ----- 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 ------------------------------------------------- + + /// + /// Returns the whose asset name equals + /// , or null if no match is found. + /// + public TowerDefinition Get(string assetName) + { + byName.TryGetValue(assetName, out var def); + return def; + } + + /// + /// Returns all registered tower definitions. Enumerates the internal + /// dictionary values — do not modify the returned collection. + /// + public IEnumerable All => byName.Values; + + // ----- Private ---------------------------------------------------- + + private void BuildLookupTable() + { + byName.Clear(); + + if (definitions == null || definitions.Length == 0) + { + Debug.LogWarning("[TowerRegistry] No TowerDefinition assets assigned. " + + "Drag assets into the Definitions list on the TowerRegistry component."); + return; + } + + foreach (var def in definitions) + { + 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] Registered {byName.Count} tower definition(s)."); + } + } +} diff --git a/Assets/_Project/Scripts/Gameplay/TowerRegistry.cs.meta b/Assets/_Project/Scripts/Gameplay/TowerRegistry.cs.meta new file mode 100644 index 0000000..6a7a87c --- /dev/null +++ b/Assets/_Project/Scripts/Gameplay/TowerRegistry.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: a9dc0fbbe4422bc479ab8db7658c082b \ No newline at end of file diff --git a/Assets/_Project/Scripts/Towers/ConstructionPhaseSet.cs b/Assets/_Project/Scripts/Towers/ConstructionPhaseSet.cs deleted file mode 100644 index 46c7c61..0000000 --- a/Assets/_Project/Scripts/Towers/ConstructionPhaseSet.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Assets/_Project/Scripts/Towers/ConstructionPhaseSet.cs -using UnityEngine; - -namespace TD.Towers -{ - /// - /// An ordered set of construction-stage visuals a build site transitions between as a - /// tower is built — typically 3 assets (early → mid → late). A tower references its own - /// set via ; build sites fall back to a - /// project-default set when a tower doesn't define one. - /// - /// - /// The entries are shape prefabs only. BuildSiteVisual applies the - /// queued (green) / constructing / paused materials and the owner tint on top, so the - /// phase assets don't need their own build-state materials. Keep them simple - /// (single material) — the build site swaps the active phase's renderer material. - /// - [CreateAssetMenu(fileName = "ConstructionPhaseSet", menuName = "TD/Construction Phase Set")] - public class ConstructionPhaseSet : ScriptableObject - { - [Tooltip("Ordered construction-stage prefabs. Shown in sequence as the build " + - "progresses — e.g. 3 entries = early / mid / late thirds of the build.")] - [SerializeField] private GameObject[] phases; - - /// Number of construction phases in this set. - public int PhaseCount => phases != null ? phases.Length : 0; - - /// The phase prefab at , or null if out of range. - public GameObject GetPhase(int index) - => (phases != null && index >= 0 && index < phases.Length) ? phases[index] : null; - } -} diff --git a/Assets/_Project/Scripts/Towers/ConstructionPhaseSet.cs.meta b/Assets/_Project/Scripts/Towers/ConstructionPhaseSet.cs.meta deleted file mode 100644 index 4556cad..0000000 --- a/Assets/_Project/Scripts/Towers/ConstructionPhaseSet.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: c8f2a4e1b9d6437052a1e8c4f7b3d069 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Project/Scripts/Towers/TowerDefinition.cs b/Assets/_Project/Scripts/Towers/TowerDefinition.cs index 24232ad..8d80828 100644 --- a/Assets/_Project/Scripts/Towers/TowerDefinition.cs +++ b/Assets/_Project/Scripts/Towers/TowerDefinition.cs @@ -68,11 +68,6 @@ namespace TD.Towers "replace with a real mesh when art is available.")] public GameObject TowerPrefab; - [Tooltip("Optional per-tower construction-stage visuals shown while this tower is " + - "being built. Leave empty to use the project-default set assigned on the " + - "BuildSiteVisual prefab.")] - public ConstructionPhaseSet ConstructionPhases; - [Tooltip("Icon shown on this tower's tile in the HUD build menu. Auto-generated from " + "the TowerPrefab by the 'TD/Generate Tower Icons' menu item, or assign a " + "Sprite manually. Null falls back to the grey placeholder icon.")] diff --git a/Project_Context.md b/Project_Context.md index b9552bb..7242383 100644 --- a/Project_Context.md +++ b/Project_Context.md @@ -90,7 +90,7 @@ Unity **6.4 (6000.4.4f1)**, URP, IL2CPP, .NET Standard 2.1, Linear color space, - **Visuals are placeholder** throughout (primitive/cone towers, sourced enemy models). See roadmap art TODOs. - **Enemies lack idle animation support** — needs adding across all enemy prefabs. - **Tower footprint visuals don't fill their 2×2 space** — the cone/mesh sits centered and reads smaller than the tile area it occupies; should visually reflect the full footprint. -- **Tower catalog is now a single source of truth:** `TowerPlacementManager.towerDefinitions[]` (by-index, = `TowerTypeId`). `TowerInstance` replicates that index and resolves via `TowerPlacementManager.GetDefinition`. *(The old by-name `TowerRegistry` was removed 2026-06-24 — add new towers only to `towerDefinitions` + `DraftPool`.)* +- **Two redundant tower lists:** `TowerPlacementManager.towerDefinitions[]` (by-index catalog) and `TowerRegistry.definitions[]` (by-name, for instance resolution). Candidate for consolidation. - **Catalog index 0 is a reserved sentinel** (valid `TowerTypeId`s start at 1) — easy to forget when wiring the catalog in the inspector. - **Catalog-index identifiers aren't session-stable** — blocks cross-match persistence until a stable ID is added. - **Paint system frozen**; **Race vs Builder** naming unresolved; the gold **"Buy Roll available any time"** rule is provisional and may change.