diff --git a/Assets/_Project/Prefabs/Towers/Tower Construction Sets.meta b/Assets/_Project/Prefabs/Towers/Tower Construction Sets.meta new file mode 100644 index 0000000..e7fd49e --- /dev/null +++ b/Assets/_Project/Prefabs/Towers/Tower Construction Sets.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3f1ef90255e065249a86a838844f7be2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/_Project/Prefabs/Towers/BuildSiteVisual.prefab b/Assets/_Project/Prefabs/Towers/Tower Construction Sets/BuildSiteVisual.prefab similarity index 99% rename from Assets/_Project/Prefabs/Towers/BuildSiteVisual.prefab rename to Assets/_Project/Prefabs/Towers/Tower Construction Sets/BuildSiteVisual.prefab index 66abbc6..6b3815f 100644 --- a/Assets/_Project/Prefabs/Towers/BuildSiteVisual.prefab +++ b/Assets/_Project/Prefabs/Towers/Tower Construction Sets/BuildSiteVisual.prefab @@ -298,6 +298,7 @@ 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/BuildSiteVisual.prefab.meta b/Assets/_Project/Prefabs/Towers/Tower Construction Sets/BuildSiteVisual.prefab.meta similarity index 100% rename from Assets/_Project/Prefabs/Towers/BuildSiteVisual.prefab.meta rename to Assets/_Project/Prefabs/Towers/Tower Construction Sets/BuildSiteVisual.prefab.meta diff --git a/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet.meta b/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet.meta new file mode 100644 index 0000000..e070754 --- /dev/null +++ b/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet.meta @@ -0,0 +1,8 @@ +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 new file mode 100644 index 0000000..2272a43 --- /dev/null +++ b/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet/DefaultConstructionSet.asset @@ -0,0 +1,18 @@ +%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 new file mode 100644 index 0000000..3832c3f --- /dev/null +++ b/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet/DefaultConstructionSet.asset.meta @@ -0,0 +1,8 @@ +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 new file mode 100644 index 0000000..70ca06b --- /dev/null +++ b/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet/DefaultConstruction_Phase_1.prefab @@ -0,0 +1,114 @@ +%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 new file mode 100644 index 0000000..684c909 --- /dev/null +++ b/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet/DefaultConstruction_Phase_1.prefab.meta @@ -0,0 +1,7 @@ +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 new file mode 100644 index 0000000..43ed50f --- /dev/null +++ b/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet/DefaultConstruction_Phase_2.prefab @@ -0,0 +1,114 @@ +%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 new file mode 100644 index 0000000..bb668c4 --- /dev/null +++ b/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet/DefaultConstruction_Phase_2.prefab.meta @@ -0,0 +1,7 @@ +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 new file mode 100644 index 0000000..0c380ed --- /dev/null +++ b/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet/DefaultConstruction_Phase_3.prefab @@ -0,0 +1,114 @@ +%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 new file mode 100644 index 0000000..dc59d87 --- /dev/null +++ b/Assets/_Project/Prefabs/Towers/Tower Construction Sets/DefaultConstructionSet/DefaultConstruction_Phase_3.prefab.meta @@ -0,0 +1,7 @@ +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 b0e6e55..3ba4d76 100644 --- a/Assets/_Project/Scripts/Gameplay/BuildSiteVisual.cs +++ b/Assets/_Project/Scripts/Gameplay/BuildSiteVisual.cs @@ -1,5 +1,4 @@ // Assets/_Project/Scripts/Gameplay/BuildSiteVisual.cs -using Unity.Collections; using Unity.Netcode; using UnityEngine; using TD.Core; @@ -63,27 +62,25 @@ namespace TD.Gameplay "Suggested: muted/grey-tinted variant of the constructing material.")] [SerializeField] private Material pausedMaterial; - [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).")] + [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.")] [SerializeField] private int stageCount = 4; - [Tooltip("Y-scale applied to scaleTarget when Stage == Queued. Visually " + - "distinct from any constructing height so the queued ghost reads as " + - "'intent, not progress'.")] + [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'.")] [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( @@ -232,7 +229,6 @@ namespace TD.Gameplay // ----- Pre-spawn init data (server) ------------------------------- - private string pendingDefName; private PlayerSlot pendingOwner = PlayerSlot.None; private float pendingBuildTime; private Vector2Int pendingAnchor; @@ -240,6 +236,17 @@ 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 -------------------------------------------------- /// @@ -261,7 +268,6 @@ namespace TD.Gameplay return; } - pendingDefName = def != null ? def.name : string.Empty; pendingOwner = owner; pendingBuildTime = def != null ? def.BuildTime : 0f; pendingAnchor = anchorTile; @@ -281,7 +287,6 @@ 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; @@ -290,6 +295,10 @@ 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; @@ -344,14 +353,25 @@ namespace TD.Gameplay private void Update() { - // 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. + // 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. if (currentStage.Value != BuildStage.Constructing) return; - float yScale = ComputeConstructingYScale(); - ApplyYScale(yScale); + 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()); } // ----- Server API ------------------------------------------------- @@ -498,33 +518,117 @@ 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); - ApplyYScale(queuedYScale); + SwapMaterial(queuedMaterial); // green ghost — no owner tint 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/Towers/ConstructionPhaseSet.cs b/Assets/_Project/Scripts/Towers/ConstructionPhaseSet.cs new file mode 100644 index 0000000..46c7c61 --- /dev/null +++ b/Assets/_Project/Scripts/Towers/ConstructionPhaseSet.cs @@ -0,0 +1,32 @@ +// 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 new file mode 100644 index 0000000..4556cad --- /dev/null +++ b/Assets/_Project/Scripts/Towers/ConstructionPhaseSet.cs.meta @@ -0,0 +1,11 @@ +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 8d80828..24232ad 100644 --- a/Assets/_Project/Scripts/Towers/TowerDefinition.cs +++ b/Assets/_Project/Scripts/Towers/TowerDefinition.cs @@ -68,6 +68,11 @@ 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.")]