diff --git a/Assets/_Project/Art/Materials/M_Gold.mat b/Assets/_Project/Art/Materials/M_Gold.mat
deleted file mode 100644
index 3eb0ce0..0000000
--- a/Assets/_Project/Art/Materials/M_Gold.mat
+++ /dev/null
@@ -1,138 +0,0 @@
-%YAML 1.1
-%TAG !u! tag:unity3d.com,2011:
---- !u!21 &2100000
-Material:
- serializedVersion: 8
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_Name: M_Gold
- m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
- m_Parent: {fileID: 0}
- m_ModifiedSerializedProperties: 0
- m_ValidKeywords:
- - _EMISSION
- m_InvalidKeywords: []
- m_LightmapFlags: 2
- m_EnableInstancingVariants: 1
- m_DoubleSidedGI: 0
- m_CustomRenderQueue: -1
- stringTagMap:
- RenderType: Opaque
- disabledShaderPasses:
- - MOTIONVECTORS
- m_LockedProperties:
- m_SavedProperties:
- serializedVersion: 3
- m_TexEnvs:
- - _BaseMap:
- m_Texture: {fileID: 0}
- m_Scale: {x: 1, y: 1}
- m_Offset: {x: 0, y: 0}
- - _BumpMap:
- m_Texture: {fileID: 0}
- m_Scale: {x: 1, y: 1}
- m_Offset: {x: 0, y: 0}
- - _DetailAlbedoMap:
- m_Texture: {fileID: 0}
- m_Scale: {x: 1, y: 1}
- m_Offset: {x: 0, y: 0}
- - _DetailMask:
- m_Texture: {fileID: 0}
- m_Scale: {x: 1, y: 1}
- m_Offset: {x: 0, y: 0}
- - _DetailNormalMap:
- m_Texture: {fileID: 0}
- m_Scale: {x: 1, y: 1}
- m_Offset: {x: 0, y: 0}
- - _EmissionMap:
- m_Texture: {fileID: 0}
- m_Scale: {x: 1, y: 1}
- m_Offset: {x: 0, y: 0}
- - _MainTex:
- m_Texture: {fileID: 0}
- m_Scale: {x: 1, y: 1}
- m_Offset: {x: 0, y: 0}
- - _MetallicGlossMap:
- m_Texture: {fileID: 0}
- m_Scale: {x: 1, y: 1}
- m_Offset: {x: 0, y: 0}
- - _OcclusionMap:
- m_Texture: {fileID: 0}
- m_Scale: {x: 1, y: 1}
- m_Offset: {x: 0, y: 0}
- - _ParallaxMap:
- m_Texture: {fileID: 0}
- m_Scale: {x: 1, y: 1}
- m_Offset: {x: 0, y: 0}
- - _SpecGlossMap:
- m_Texture: {fileID: 0}
- m_Scale: {x: 1, y: 1}
- m_Offset: {x: 0, y: 0}
- - unity_Lightmaps:
- m_Texture: {fileID: 0}
- m_Scale: {x: 1, y: 1}
- m_Offset: {x: 0, y: 0}
- - unity_LightmapsInd:
- m_Texture: {fileID: 0}
- m_Scale: {x: 1, y: 1}
- m_Offset: {x: 0, y: 0}
- - unity_ShadowMasks:
- m_Texture: {fileID: 0}
- m_Scale: {x: 1, y: 1}
- m_Offset: {x: 0, y: 0}
- m_Ints: []
- m_Floats:
- - _AddPrecomputedVelocity: 0
- - _AlphaClip: 0
- - _AlphaToMask: 0
- - _Blend: 0
- - _BlendModePreserveSpecular: 1
- - _BumpScale: 1
- - _ClearCoatMask: 0
- - _ClearCoatSmoothness: 0
- - _Cull: 2
- - _Cutoff: 0.5
- - _DetailAlbedoMapScale: 1
- - _DetailNormalMapScale: 1
- - _DstBlend: 0
- - _DstBlendAlpha: 0
- - _EnvironmentReflections: 1
- - _GlossMapScale: 0
- - _Glossiness: 0
- - _GlossyReflections: 0
- - _Metallic: 0.3
- - _OcclusionStrength: 1
- - _Parallax: 0.005
- - _QueueOffset: 0
- - _ReceiveShadows: 1
- - _Smoothness: 0.85
- - _SmoothnessTextureChannel: 0
- - _SpecularHighlights: 1
- - _SrcBlend: 1
- - _SrcBlendAlpha: 1
- - _Surface: 0
- - _WorkflowMode: 1
- - _XRMotionVectorsPass: 1
- - _ZWrite: 1
- m_Colors:
- - _BaseColor: {r: 0.83137256, g: 0.6862745, b: 0.21568628, a: 1}
- - _Color: {r: 0.83137256, g: 0.68627447, b: 0.2156862, a: 1}
- - _EmissionColor: {r: 2, g: 0.6755797, b: 0.031372547, a: 1}
- - _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
- m_BuildTextureStacks: []
- m_AllowLocking: 1
---- !u!114 &1062544154538963945
-MonoBehaviour:
- m_ObjectHideFlags: 11
- 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: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
- m_Name:
- m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion
- version: 10
diff --git a/Assets/_Project/Art/Materials/M_Gold.mat.meta b/Assets/_Project/Art/Materials/M_Gold.mat.meta
deleted file mode 100644
index 1caa033..0000000
--- a/Assets/_Project/Art/Materials/M_Gold.mat.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 915f376be23f20941837c0809626fa5f
-NativeFormatImporter:
- externalObjects: {}
- mainObjectFileID: 2100000
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Art/Models/Blend Files.meta b/Assets/_Project/Art/Models/Blend Files.meta
deleted file mode 100644
index 14a4fae..0000000
--- a/Assets/_Project/Art/Models/Blend Files.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: d70d84aef04e61749a232234c80f08ce
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Art/Models/Coin.fbx b/Assets/_Project/Art/Models/Coin.fbx
deleted file mode 100644
index c732270..0000000
--- a/Assets/_Project/Art/Models/Coin.fbx
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:b5d924ce92dd6ea66125dea9941f965fb5da7eb4431f76ec9b4dd3b2091f19c1
-size 22476
diff --git a/Assets/_Project/Art/Models/Coin.fbx.meta b/Assets/_Project/Art/Models/Coin.fbx.meta
deleted file mode 100644
index 674a119..0000000
--- a/Assets/_Project/Art/Models/Coin.fbx.meta
+++ /dev/null
@@ -1,110 +0,0 @@
-fileFormatVersion: 2
-guid: 280a727da941fc544b9aab7df7fe9f2d
-ModelImporter:
- serializedVersion: 24200
- internalIDToNameTable: []
- externalObjects: {}
- materials:
- materialImportMode: 2
- materialName: 0
- materialSearch: 1
- materialLocation: 1
- animations:
- legacyGenerateAnimations: 4
- bakeSimulation: 0
- resampleCurves: 1
- optimizeGameObjects: 0
- removeConstantScaleCurves: 0
- motionNodeName:
- animationImportErrors:
- animationImportWarnings:
- animationRetargetingWarnings:
- animationDoRetargetingWarnings: 0
- importAnimatedCustomProperties: 0
- importConstraints: 0
- animationCompression: 1
- animationRotationError: 0.5
- animationPositionError: 0.5
- animationScaleError: 0.5
- animationWrapMode: 0
- extraExposedTransformPaths: []
- extraUserProperties: []
- clipAnimations: []
- isReadable: 0
- meshes:
- lODScreenPercentages: []
- globalScale: 1
- meshCompression: 0
- addColliders: 0
- useSRGBMaterialColor: 1
- sortHierarchyByName: 1
- importPhysicalCameras: 1
- importVisibility: 1
- importBlendShapes: 1
- importCameras: 1
- importLights: 1
- nodeNameCollisionStrategy: 1
- fileIdsGeneration: 2
- swapUVChannels: 0
- generateSecondaryUV: 0
- useFileUnits: 1
- keepQuads: 0
- weldVertices: 1
- bakeAxisConversion: 0
- preserveHierarchy: 0
- skinWeightsMode: 0
- maxBonesPerVertex: 4
- minBoneWeight: 0.001
- optimizeBones: 1
- generateMeshLods: 0
- meshLodGenerationFlags: 0
- maximumMeshLod: -1
- meshOptimizationFlags: -1
- indexFormat: 0
- secondaryUVAngleDistortion: 8
- secondaryUVAreaDistortion: 15.000001
- secondaryUVHardAngle: 88
- secondaryUVMarginMethod: 1
- secondaryUVMinLightmapResolution: 40
- secondaryUVMinObjectScale: 1
- secondaryUVPackMargin: 4
- useFileScale: 1
- strictVertexDataChecks: 0
- tangentSpace:
- normalSmoothAngle: 60
- normalImportMode: 0
- tangentImportMode: 3
- normalCalculationMode: 4
- legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0
- blendShapeNormalImportMode: 1
- normalSmoothingSource: 0
- referencedClips: []
- importAnimation: 1
- humanDescription:
- serializedVersion: 3
- human: []
- skeleton: []
- armTwist: 0.5
- foreArmTwist: 0.5
- upperLegTwist: 0.5
- legTwist: 0.5
- armStretch: 0.05
- legStretch: 0.05
- feetSpacing: 0
- globalScale: 1
- rootMotionBoneName:
- hasTranslationDoF: 0
- hasExtraRoot: 0
- skeletonHasParents: 1
- lastHumanDescriptionAvatarSource: {instanceID: 0}
- autoGenerateAvatarMappingIfUnspecified: 1
- animationType: 2
- humanoidOversampling: 1
- avatarSetup: 0
- addHumanoidExtraRootOnlyWhenUsingAvatar: 1
- importBlendShapeDeformPercent: 1
- remapMaterialsIfMaterialImportModeIsNone: 0
- additionalBone: 0
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Art/Sprites/RaceIcons/BloodAngels.jpg b/Assets/_Project/Art/Sprites/BloodAngels.jpg
similarity index 100%
rename from Assets/_Project/Art/Sprites/RaceIcons/BloodAngels.jpg
rename to Assets/_Project/Art/Sprites/BloodAngels.jpg
diff --git a/Assets/_Project/Art/Sprites/RaceIcons/BloodAngels.jpg.meta b/Assets/_Project/Art/Sprites/BloodAngels.jpg.meta
similarity index 100%
rename from Assets/_Project/Art/Sprites/RaceIcons/BloodAngels.jpg.meta
rename to Assets/_Project/Art/Sprites/BloodAngels.jpg.meta
diff --git a/Assets/_Project/Art/Sprites/EnemyDraftIcons/BlinkIcon.png b/Assets/_Project/Art/Sprites/EnemyDraftIcons/BlinkIcon.png
deleted file mode 100644
index f2a24b8..0000000
--- a/Assets/_Project/Art/Sprites/EnemyDraftIcons/BlinkIcon.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:1b119eb6ab2d6c01573bce480df9fdb6676449398f4afde46673c3c2c2710fcc
-size 13324
diff --git a/Assets/_Project/Art/Sprites/EnemyDraftIcons/BlinkIcon.png.meta b/Assets/_Project/Art/Sprites/EnemyDraftIcons/BlinkIcon.png.meta
deleted file mode 100644
index 8523ae6..0000000
--- a/Assets/_Project/Art/Sprites/EnemyDraftIcons/BlinkIcon.png.meta
+++ /dev/null
@@ -1,117 +0,0 @@
-fileFormatVersion: 2
-guid: df5903159b157404d8e92942029f08f7
-TextureImporter:
- internalIDToNameTable: []
- externalObjects: {}
- serializedVersion: 13
- mipmaps:
- mipMapMode: 0
- enableMipMap: 0
- sRGBTexture: 1
- linearTexture: 0
- fadeOut: 0
- borderMipMap: 0
- mipMapsPreserveCoverage: 0
- alphaTestReferenceValue: 0.5
- mipMapFadeDistanceStart: 1
- mipMapFadeDistanceEnd: 3
- bumpmap:
- convertToNormalMap: 0
- externalNormalMap: 0
- heightScale: 0.25
- normalMapFilter: 0
- flipGreenChannel: 0
- isReadable: 0
- streamingMipmaps: 0
- streamingMipmapsPriority: 0
- vTOnly: 0
- ignoreMipmapLimit: 0
- grayScaleToAlpha: 0
- generateCubemap: 6
- cubemapConvolution: 0
- seamlessCubemap: 0
- textureFormat: 1
- maxTextureSize: 2048
- textureSettings:
- serializedVersion: 2
- filterMode: 1
- aniso: 1
- mipBias: 0
- wrapU: 1
- wrapV: 1
- wrapW: 0
- nPOTScale: 0
- lightmap: 0
- compressionQuality: 50
- spriteMode: 1
- spriteExtrude: 1
- spriteMeshType: 1
- alignment: 0
- spritePivot: {x: 0.5, y: 0.5}
- spritePixelsToUnits: 100
- spriteBorder: {x: 0, y: 0, z: 0, w: 0}
- spriteGenerateFallbackPhysicsShape: 1
- alphaUsage: 1
- alphaIsTransparency: 1
- spriteTessellationDetail: -1
- textureType: 8
- textureShape: 1
- singleChannelComponent: 0
- flipbookRows: 1
- flipbookColumns: 1
- maxTextureSizeSet: 0
- compressionQualitySet: 0
- textureFormatSet: 0
- ignorePngGamma: 0
- applyGammaDecoding: 0
- swizzle: 50462976
- cookieLightType: 0
- platformSettings:
- - serializedVersion: 4
- buildTarget: DefaultTexturePlatform
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 1
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- - serializedVersion: 4
- buildTarget: Standalone
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 1
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- spriteSheet:
- serializedVersion: 2
- sprites: []
- outline: []
- customData:
- physicsShape: []
- bones: []
- spriteID: 5e97eb03825dee720800000000000000
- internalID: 0
- vertices: []
- indices:
- edges: []
- weights: []
- secondaryTextures: []
- spriteCustomMetadata:
- entries: []
- nameFileIdTable: {}
- mipmapLimitGroupName:
- pSDRemoveMatte: 0
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Art/Sprites/EnemyDraftIcons/DoubleUpIcon.png b/Assets/_Project/Art/Sprites/EnemyDraftIcons/DoubleUpIcon.png
deleted file mode 100644
index 6503be4..0000000
--- a/Assets/_Project/Art/Sprites/EnemyDraftIcons/DoubleUpIcon.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:5569ff4e8d0780e2d79dfb9575f34856d59cb99ac751dd4f8caf4c2219eb7976
-size 8730
diff --git a/Assets/_Project/Art/Sprites/EnemyDraftIcons/DoubleUpIcon.png.meta b/Assets/_Project/Art/Sprites/EnemyDraftIcons/DoubleUpIcon.png.meta
deleted file mode 100644
index eef1834..0000000
--- a/Assets/_Project/Art/Sprites/EnemyDraftIcons/DoubleUpIcon.png.meta
+++ /dev/null
@@ -1,117 +0,0 @@
-fileFormatVersion: 2
-guid: e81c6ebff6b98ac44b884d3006611ae6
-TextureImporter:
- internalIDToNameTable: []
- externalObjects: {}
- serializedVersion: 13
- mipmaps:
- mipMapMode: 0
- enableMipMap: 0
- sRGBTexture: 1
- linearTexture: 0
- fadeOut: 0
- borderMipMap: 0
- mipMapsPreserveCoverage: 0
- alphaTestReferenceValue: 0.5
- mipMapFadeDistanceStart: 1
- mipMapFadeDistanceEnd: 3
- bumpmap:
- convertToNormalMap: 0
- externalNormalMap: 0
- heightScale: 0.25
- normalMapFilter: 0
- flipGreenChannel: 0
- isReadable: 0
- streamingMipmaps: 0
- streamingMipmapsPriority: 0
- vTOnly: 0
- ignoreMipmapLimit: 0
- grayScaleToAlpha: 0
- generateCubemap: 6
- cubemapConvolution: 0
- seamlessCubemap: 0
- textureFormat: 1
- maxTextureSize: 2048
- textureSettings:
- serializedVersion: 2
- filterMode: 1
- aniso: 1
- mipBias: 0
- wrapU: 1
- wrapV: 1
- wrapW: 0
- nPOTScale: 0
- lightmap: 0
- compressionQuality: 50
- spriteMode: 1
- spriteExtrude: 1
- spriteMeshType: 1
- alignment: 0
- spritePivot: {x: 0.5, y: 0.5}
- spritePixelsToUnits: 100
- spriteBorder: {x: 0, y: 0, z: 0, w: 0}
- spriteGenerateFallbackPhysicsShape: 1
- alphaUsage: 1
- alphaIsTransparency: 1
- spriteTessellationDetail: -1
- textureType: 8
- textureShape: 1
- singleChannelComponent: 0
- flipbookRows: 1
- flipbookColumns: 1
- maxTextureSizeSet: 0
- compressionQualitySet: 0
- textureFormatSet: 0
- ignorePngGamma: 0
- applyGammaDecoding: 0
- swizzle: 50462976
- cookieLightType: 0
- platformSettings:
- - serializedVersion: 4
- buildTarget: DefaultTexturePlatform
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 1
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- - serializedVersion: 4
- buildTarget: Standalone
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 1
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- spriteSheet:
- serializedVersion: 2
- sprites: []
- outline: []
- customData:
- physicsShape: []
- bones: []
- spriteID: 5e97eb03825dee720800000000000000
- internalID: 0
- vertices: []
- indices:
- edges: []
- weights: []
- secondaryTextures: []
- spriteCustomMetadata:
- entries: []
- nameFileIdTable: {}
- mipmapLimitGroupName:
- pSDRemoveMatte: 0
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Art/Sprites/EnemyDraftIcons/FlightIcon.png b/Assets/_Project/Art/Sprites/EnemyDraftIcons/FlightIcon.png
deleted file mode 100644
index 7ef1d14..0000000
--- a/Assets/_Project/Art/Sprites/EnemyDraftIcons/FlightIcon.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:4779891d6890994436e47c586b11768d6a8eead32c4e740f4c99713598783fea
-size 15796
diff --git a/Assets/_Project/Art/Sprites/EnemyDraftIcons/FlightIcon.png.meta b/Assets/_Project/Art/Sprites/EnemyDraftIcons/FlightIcon.png.meta
deleted file mode 100644
index eb15613..0000000
--- a/Assets/_Project/Art/Sprites/EnemyDraftIcons/FlightIcon.png.meta
+++ /dev/null
@@ -1,117 +0,0 @@
-fileFormatVersion: 2
-guid: 39ac5850677a092479550234de13e868
-TextureImporter:
- internalIDToNameTable: []
- externalObjects: {}
- serializedVersion: 13
- mipmaps:
- mipMapMode: 0
- enableMipMap: 0
- sRGBTexture: 1
- linearTexture: 0
- fadeOut: 0
- borderMipMap: 0
- mipMapsPreserveCoverage: 0
- alphaTestReferenceValue: 0.5
- mipMapFadeDistanceStart: 1
- mipMapFadeDistanceEnd: 3
- bumpmap:
- convertToNormalMap: 0
- externalNormalMap: 0
- heightScale: 0.25
- normalMapFilter: 0
- flipGreenChannel: 0
- isReadable: 0
- streamingMipmaps: 0
- streamingMipmapsPriority: 0
- vTOnly: 0
- ignoreMipmapLimit: 0
- grayScaleToAlpha: 0
- generateCubemap: 6
- cubemapConvolution: 0
- seamlessCubemap: 0
- textureFormat: 1
- maxTextureSize: 2048
- textureSettings:
- serializedVersion: 2
- filterMode: 1
- aniso: 1
- mipBias: 0
- wrapU: 1
- wrapV: 1
- wrapW: 0
- nPOTScale: 0
- lightmap: 0
- compressionQuality: 50
- spriteMode: 1
- spriteExtrude: 1
- spriteMeshType: 1
- alignment: 0
- spritePivot: {x: 0.5, y: 0.5}
- spritePixelsToUnits: 100
- spriteBorder: {x: 0, y: 0, z: 0, w: 0}
- spriteGenerateFallbackPhysicsShape: 1
- alphaUsage: 1
- alphaIsTransparency: 1
- spriteTessellationDetail: -1
- textureType: 8
- textureShape: 1
- singleChannelComponent: 0
- flipbookRows: 1
- flipbookColumns: 1
- maxTextureSizeSet: 0
- compressionQualitySet: 0
- textureFormatSet: 0
- ignorePngGamma: 0
- applyGammaDecoding: 0
- swizzle: 50462976
- cookieLightType: 0
- platformSettings:
- - serializedVersion: 4
- buildTarget: DefaultTexturePlatform
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 1
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- - serializedVersion: 4
- buildTarget: Standalone
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 1
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- spriteSheet:
- serializedVersion: 2
- sprites: []
- outline: []
- customData:
- physicsShape: []
- bones: []
- spriteID: 5e97eb03825dee720800000000000000
- internalID: 0
- vertices: []
- indices:
- edges: []
- weights: []
- secondaryTextures: []
- spriteCustomMetadata:
- entries: []
- nameFileIdTable: {}
- mipmapLimitGroupName:
- pSDRemoveMatte: 0
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Art/Sprites/EnemyDraftIcons/NoBountyIcon.png b/Assets/_Project/Art/Sprites/EnemyDraftIcons/NoBountyIcon.png
deleted file mode 100644
index ffb3b3e..0000000
--- a/Assets/_Project/Art/Sprites/EnemyDraftIcons/NoBountyIcon.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:6e2d28ff550b9a9e83fe317c6c692115d3f5ef27f20f9aa9220e7f5aeb91768f
-size 15522
diff --git a/Assets/_Project/Art/Sprites/EnemyDraftIcons/NoBountyIcon.png.meta b/Assets/_Project/Art/Sprites/EnemyDraftIcons/NoBountyIcon.png.meta
deleted file mode 100644
index 42c8511..0000000
--- a/Assets/_Project/Art/Sprites/EnemyDraftIcons/NoBountyIcon.png.meta
+++ /dev/null
@@ -1,117 +0,0 @@
-fileFormatVersion: 2
-guid: ea9802d37489c9442ba2ffd4a41c6cb2
-TextureImporter:
- internalIDToNameTable: []
- externalObjects: {}
- serializedVersion: 13
- mipmaps:
- mipMapMode: 0
- enableMipMap: 0
- sRGBTexture: 1
- linearTexture: 0
- fadeOut: 0
- borderMipMap: 0
- mipMapsPreserveCoverage: 0
- alphaTestReferenceValue: 0.5
- mipMapFadeDistanceStart: 1
- mipMapFadeDistanceEnd: 3
- bumpmap:
- convertToNormalMap: 0
- externalNormalMap: 0
- heightScale: 0.25
- normalMapFilter: 0
- flipGreenChannel: 0
- isReadable: 0
- streamingMipmaps: 0
- streamingMipmapsPriority: 0
- vTOnly: 0
- ignoreMipmapLimit: 0
- grayScaleToAlpha: 0
- generateCubemap: 6
- cubemapConvolution: 0
- seamlessCubemap: 0
- textureFormat: 1
- maxTextureSize: 2048
- textureSettings:
- serializedVersion: 2
- filterMode: 1
- aniso: 1
- mipBias: 0
- wrapU: 1
- wrapV: 1
- wrapW: 0
- nPOTScale: 0
- lightmap: 0
- compressionQuality: 50
- spriteMode: 1
- spriteExtrude: 1
- spriteMeshType: 1
- alignment: 0
- spritePivot: {x: 0.5, y: 0.5}
- spritePixelsToUnits: 100
- spriteBorder: {x: 0, y: 0, z: 0, w: 0}
- spriteGenerateFallbackPhysicsShape: 1
- alphaUsage: 1
- alphaIsTransparency: 1
- spriteTessellationDetail: -1
- textureType: 8
- textureShape: 1
- singleChannelComponent: 0
- flipbookRows: 1
- flipbookColumns: 1
- maxTextureSizeSet: 0
- compressionQualitySet: 0
- textureFormatSet: 0
- ignorePngGamma: 0
- applyGammaDecoding: 0
- swizzle: 50462976
- cookieLightType: 0
- platformSettings:
- - serializedVersion: 4
- buildTarget: DefaultTexturePlatform
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 1
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- - serializedVersion: 4
- buildTarget: Standalone
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 1
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- spriteSheet:
- serializedVersion: 2
- sprites: []
- outline: []
- customData:
- physicsShape: []
- bones: []
- spriteID: 5e97eb03825dee720800000000000000
- internalID: 0
- vertices: []
- indices:
- edges: []
- weights: []
- secondaryTextures: []
- spriteCustomMetadata:
- entries: []
- nameFileIdTable: {}
- mipmapLimitGroupName:
- pSDRemoveMatte: 0
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Art/Sprites/EnemyDraftIcons/SplitIcon.png b/Assets/_Project/Art/Sprites/EnemyDraftIcons/SplitIcon.png
deleted file mode 100644
index e57b4db..0000000
--- a/Assets/_Project/Art/Sprites/EnemyDraftIcons/SplitIcon.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:7f0919f710e6673a55f44b1dbd55a16e19105fa5fe7d643330a4baea97eff095
-size 19587
diff --git a/Assets/_Project/Art/Sprites/EnemyDraftIcons/SplitIcon.png.meta b/Assets/_Project/Art/Sprites/EnemyDraftIcons/SplitIcon.png.meta
deleted file mode 100644
index 57b7944..0000000
--- a/Assets/_Project/Art/Sprites/EnemyDraftIcons/SplitIcon.png.meta
+++ /dev/null
@@ -1,117 +0,0 @@
-fileFormatVersion: 2
-guid: 43490fdb6e2477643a275956ce5f4596
-TextureImporter:
- internalIDToNameTable: []
- externalObjects: {}
- serializedVersion: 13
- mipmaps:
- mipMapMode: 0
- enableMipMap: 0
- sRGBTexture: 1
- linearTexture: 0
- fadeOut: 0
- borderMipMap: 0
- mipMapsPreserveCoverage: 0
- alphaTestReferenceValue: 0.5
- mipMapFadeDistanceStart: 1
- mipMapFadeDistanceEnd: 3
- bumpmap:
- convertToNormalMap: 0
- externalNormalMap: 0
- heightScale: 0.25
- normalMapFilter: 0
- flipGreenChannel: 0
- isReadable: 0
- streamingMipmaps: 0
- streamingMipmapsPriority: 0
- vTOnly: 0
- ignoreMipmapLimit: 0
- grayScaleToAlpha: 0
- generateCubemap: 6
- cubemapConvolution: 0
- seamlessCubemap: 0
- textureFormat: 1
- maxTextureSize: 2048
- textureSettings:
- serializedVersion: 2
- filterMode: 1
- aniso: 1
- mipBias: 0
- wrapU: 1
- wrapV: 1
- wrapW: 0
- nPOTScale: 0
- lightmap: 0
- compressionQuality: 50
- spriteMode: 1
- spriteExtrude: 1
- spriteMeshType: 1
- alignment: 0
- spritePivot: {x: 0.5, y: 0.5}
- spritePixelsToUnits: 100
- spriteBorder: {x: 0, y: 0, z: 0, w: 0}
- spriteGenerateFallbackPhysicsShape: 1
- alphaUsage: 1
- alphaIsTransparency: 1
- spriteTessellationDetail: -1
- textureType: 8
- textureShape: 1
- singleChannelComponent: 0
- flipbookRows: 1
- flipbookColumns: 1
- maxTextureSizeSet: 0
- compressionQualitySet: 0
- textureFormatSet: 0
- ignorePngGamma: 0
- applyGammaDecoding: 0
- swizzle: 50462976
- cookieLightType: 0
- platformSettings:
- - serializedVersion: 4
- buildTarget: DefaultTexturePlatform
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 1
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- - serializedVersion: 4
- buildTarget: Standalone
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 1
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- spriteSheet:
- serializedVersion: 2
- sprites: []
- outline: []
- customData:
- physicsShape: []
- bones: []
- spriteID: 5e97eb03825dee720800000000000000
- internalID: 0
- vertices: []
- indices:
- edges: []
- weights: []
- secondaryTextures: []
- spriteCustomMetadata:
- entries: []
- nameFileIdTable: {}
- mipmapLimitGroupName:
- pSDRemoveMatte: 0
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Art/Sprites/EnemyDraftIcons/ThiefIcon.png b/Assets/_Project/Art/Sprites/EnemyDraftIcons/ThiefIcon.png
deleted file mode 100644
index 92cd276..0000000
--- a/Assets/_Project/Art/Sprites/EnemyDraftIcons/ThiefIcon.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:90a7787cfc6120d1e6816d59a523093250f0f21200d3ad29fc6c99974837a7ef
-size 18309
diff --git a/Assets/_Project/Art/Sprites/EnemyDraftIcons/ThiefIcon.png.meta b/Assets/_Project/Art/Sprites/EnemyDraftIcons/ThiefIcon.png.meta
deleted file mode 100644
index 6198141..0000000
--- a/Assets/_Project/Art/Sprites/EnemyDraftIcons/ThiefIcon.png.meta
+++ /dev/null
@@ -1,117 +0,0 @@
-fileFormatVersion: 2
-guid: 315be3feae908714a911f596b8533317
-TextureImporter:
- internalIDToNameTable: []
- externalObjects: {}
- serializedVersion: 13
- mipmaps:
- mipMapMode: 0
- enableMipMap: 0
- sRGBTexture: 1
- linearTexture: 0
- fadeOut: 0
- borderMipMap: 0
- mipMapsPreserveCoverage: 0
- alphaTestReferenceValue: 0.5
- mipMapFadeDistanceStart: 1
- mipMapFadeDistanceEnd: 3
- bumpmap:
- convertToNormalMap: 0
- externalNormalMap: 0
- heightScale: 0.25
- normalMapFilter: 0
- flipGreenChannel: 0
- isReadable: 0
- streamingMipmaps: 0
- streamingMipmapsPriority: 0
- vTOnly: 0
- ignoreMipmapLimit: 0
- grayScaleToAlpha: 0
- generateCubemap: 6
- cubemapConvolution: 0
- seamlessCubemap: 0
- textureFormat: 1
- maxTextureSize: 2048
- textureSettings:
- serializedVersion: 2
- filterMode: 1
- aniso: 1
- mipBias: 0
- wrapU: 1
- wrapV: 1
- wrapW: 0
- nPOTScale: 0
- lightmap: 0
- compressionQuality: 50
- spriteMode: 1
- spriteExtrude: 1
- spriteMeshType: 1
- alignment: 0
- spritePivot: {x: 0.5, y: 0.5}
- spritePixelsToUnits: 100
- spriteBorder: {x: 0, y: 0, z: 0, w: 0}
- spriteGenerateFallbackPhysicsShape: 1
- alphaUsage: 1
- alphaIsTransparency: 1
- spriteTessellationDetail: -1
- textureType: 8
- textureShape: 1
- singleChannelComponent: 0
- flipbookRows: 1
- flipbookColumns: 1
- maxTextureSizeSet: 0
- compressionQualitySet: 0
- textureFormatSet: 0
- ignorePngGamma: 0
- applyGammaDecoding: 0
- swizzle: 50462976
- cookieLightType: 0
- platformSettings:
- - serializedVersion: 4
- buildTarget: DefaultTexturePlatform
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 1
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- - serializedVersion: 4
- buildTarget: Standalone
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 1
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- spriteSheet:
- serializedVersion: 2
- sprites: []
- outline: []
- customData:
- physicsShape: []
- bones: []
- spriteID: 5e97eb03825dee720800000000000000
- internalID: 0
- vertices: []
- indices:
- edges: []
- weights: []
- secondaryTextures: []
- spriteCustomMetadata:
- entries: []
- nameFileIdTable: {}
- mipmapLimitGroupName:
- pSDRemoveMatte: 0
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Art/Sprites/PlayerDraftIcons.meta b/Assets/_Project/Art/Sprites/PlayerDraftIcons.meta
deleted file mode 100644
index 16c1caf..0000000
--- a/Assets/_Project/Art/Sprites/PlayerDraftIcons.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 319f59716d010174aa75cc757855ffd3
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Art/Sprites/PlayerDraftIcons/Spells.meta b/Assets/_Project/Art/Sprites/PlayerDraftIcons/Spells.meta
deleted file mode 100644
index 599da31..0000000
--- a/Assets/_Project/Art/Sprites/PlayerDraftIcons/Spells.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: b086eebc9f3f87543bf2c98de8c8e4af
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Art/Sprites/PlayerDraftIcons/Spells/FireballSpell.png b/Assets/_Project/Art/Sprites/PlayerDraftIcons/Spells/FireballSpell.png
deleted file mode 100644
index 6c62964..0000000
--- a/Assets/_Project/Art/Sprites/PlayerDraftIcons/Spells/FireballSpell.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:c58fd337b19c098a34a4eb7730e87fad72ec3b4819665e00535676b008348146
-size 177456
diff --git a/Assets/_Project/Art/Sprites/PlayerDraftIcons/Spells/FireballSpell.png.meta b/Assets/_Project/Art/Sprites/PlayerDraftIcons/Spells/FireballSpell.png.meta
deleted file mode 100644
index 25bac7d..0000000
--- a/Assets/_Project/Art/Sprites/PlayerDraftIcons/Spells/FireballSpell.png.meta
+++ /dev/null
@@ -1,117 +0,0 @@
-fileFormatVersion: 2
-guid: eff45e5e3dab24f438c054a095d34578
-TextureImporter:
- internalIDToNameTable: []
- externalObjects: {}
- serializedVersion: 13
- mipmaps:
- mipMapMode: 0
- enableMipMap: 0
- sRGBTexture: 1
- linearTexture: 0
- fadeOut: 0
- borderMipMap: 0
- mipMapsPreserveCoverage: 0
- alphaTestReferenceValue: 0.5
- mipMapFadeDistanceStart: 1
- mipMapFadeDistanceEnd: 3
- bumpmap:
- convertToNormalMap: 0
- externalNormalMap: 0
- heightScale: 0.25
- normalMapFilter: 0
- flipGreenChannel: 0
- isReadable: 0
- streamingMipmaps: 0
- streamingMipmapsPriority: 0
- vTOnly: 0
- ignoreMipmapLimit: 0
- grayScaleToAlpha: 0
- generateCubemap: 6
- cubemapConvolution: 0
- seamlessCubemap: 0
- textureFormat: 1
- maxTextureSize: 2048
- textureSettings:
- serializedVersion: 2
- filterMode: 1
- aniso: 1
- mipBias: 0
- wrapU: 1
- wrapV: 1
- wrapW: 0
- nPOTScale: 0
- lightmap: 0
- compressionQuality: 50
- spriteMode: 1
- spriteExtrude: 1
- spriteMeshType: 1
- alignment: 0
- spritePivot: {x: 0.5, y: 0.5}
- spritePixelsToUnits: 100
- spriteBorder: {x: 0, y: 0, z: 0, w: 0}
- spriteGenerateFallbackPhysicsShape: 1
- alphaUsage: 1
- alphaIsTransparency: 1
- spriteTessellationDetail: -1
- textureType: 8
- textureShape: 1
- singleChannelComponent: 0
- flipbookRows: 1
- flipbookColumns: 1
- maxTextureSizeSet: 0
- compressionQualitySet: 0
- textureFormatSet: 0
- ignorePngGamma: 0
- applyGammaDecoding: 0
- swizzle: 50462976
- cookieLightType: 0
- platformSettings:
- - serializedVersion: 4
- buildTarget: DefaultTexturePlatform
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 1
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- - serializedVersion: 4
- buildTarget: Standalone
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 1
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- spriteSheet:
- serializedVersion: 2
- sprites: []
- outline: []
- customData:
- physicsShape: []
- bones: []
- spriteID: 5e97eb03825dee720800000000000000
- internalID: 0
- vertices: []
- indices:
- edges: []
- weights: []
- secondaryTextures: []
- spriteCustomMetadata:
- entries: []
- nameFileIdTable: {}
- mipmapLimitGroupName:
- pSDRemoveMatte: 0
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Art/Sprites/PlayerDraftIcons/Spells/SlowSpell.png b/Assets/_Project/Art/Sprites/PlayerDraftIcons/Spells/SlowSpell.png
deleted file mode 100644
index 9f0cdef..0000000
--- a/Assets/_Project/Art/Sprites/PlayerDraftIcons/Spells/SlowSpell.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:3d54e2ec41954205c8775d2c6f09e6a134e8a960a797394c6e01ce67daebc840
-size 357045
diff --git a/Assets/_Project/Art/Sprites/PlayerDraftIcons/Spells/SlowSpell.png.meta b/Assets/_Project/Art/Sprites/PlayerDraftIcons/Spells/SlowSpell.png.meta
deleted file mode 100644
index 98e89b4..0000000
--- a/Assets/_Project/Art/Sprites/PlayerDraftIcons/Spells/SlowSpell.png.meta
+++ /dev/null
@@ -1,117 +0,0 @@
-fileFormatVersion: 2
-guid: 5f5072d8c9f626c4690a7b231c7488f9
-TextureImporter:
- internalIDToNameTable: []
- externalObjects: {}
- serializedVersion: 13
- mipmaps:
- mipMapMode: 0
- enableMipMap: 0
- sRGBTexture: 1
- linearTexture: 0
- fadeOut: 0
- borderMipMap: 0
- mipMapsPreserveCoverage: 0
- alphaTestReferenceValue: 0.5
- mipMapFadeDistanceStart: 1
- mipMapFadeDistanceEnd: 3
- bumpmap:
- convertToNormalMap: 0
- externalNormalMap: 0
- heightScale: 0.25
- normalMapFilter: 0
- flipGreenChannel: 0
- isReadable: 0
- streamingMipmaps: 0
- streamingMipmapsPriority: 0
- vTOnly: 0
- ignoreMipmapLimit: 0
- grayScaleToAlpha: 0
- generateCubemap: 6
- cubemapConvolution: 0
- seamlessCubemap: 0
- textureFormat: 1
- maxTextureSize: 2048
- textureSettings:
- serializedVersion: 2
- filterMode: 1
- aniso: 1
- mipBias: 0
- wrapU: 1
- wrapV: 1
- wrapW: 0
- nPOTScale: 0
- lightmap: 0
- compressionQuality: 50
- spriteMode: 1
- spriteExtrude: 1
- spriteMeshType: 1
- alignment: 0
- spritePivot: {x: 0.5, y: 0.5}
- spritePixelsToUnits: 100
- spriteBorder: {x: 0, y: 0, z: 0, w: 0}
- spriteGenerateFallbackPhysicsShape: 1
- alphaUsage: 1
- alphaIsTransparency: 1
- spriteTessellationDetail: -1
- textureType: 8
- textureShape: 1
- singleChannelComponent: 0
- flipbookRows: 1
- flipbookColumns: 1
- maxTextureSizeSet: 0
- compressionQualitySet: 0
- textureFormatSet: 0
- ignorePngGamma: 0
- applyGammaDecoding: 0
- swizzle: 50462976
- cookieLightType: 0
- platformSettings:
- - serializedVersion: 4
- buildTarget: DefaultTexturePlatform
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 1
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- - serializedVersion: 4
- buildTarget: Standalone
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 1
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- spriteSheet:
- serializedVersion: 2
- sprites: []
- outline: []
- customData:
- physicsShape: []
- bones: []
- spriteID: 5e97eb03825dee720800000000000000
- internalID: 0
- vertices: []
- indices:
- edges: []
- weights: []
- secondaryTextures: []
- spriteCustomMetadata:
- entries: []
- nameFileIdTable: {}
- mipmapLimitGroupName:
- pSDRemoveMatte: 0
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Art/Sprites/PlayerDraftIcons/Towers/ArrowTower.meta b/Assets/_Project/Art/Sprites/PlayerDraftIcons/Towers/ArrowTower.meta
deleted file mode 100644
index 3e3ff27..0000000
--- a/Assets/_Project/Art/Sprites/PlayerDraftIcons/Towers/ArrowTower.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 06352dd9c76072941bbe7ad909c5f2f9
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Art/Sprites/PlayerDraftIcons/Towers/ArrowTower/ArrowTowerUpgrade_1.png b/Assets/_Project/Art/Sprites/PlayerDraftIcons/Towers/ArrowTower/ArrowTowerUpgrade_1.png
deleted file mode 100644
index d7e8a96..0000000
--- a/Assets/_Project/Art/Sprites/PlayerDraftIcons/Towers/ArrowTower/ArrowTowerUpgrade_1.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:5e1b3dd4114b2639901392675da17a1c33146aa112157663fa13cf1dbb718cf4
-size 7076
diff --git a/Assets/_Project/Art/Sprites/PlayerDraftIcons/Towers/ArrowTower/ArrowTowerUpgrade_1.png.meta b/Assets/_Project/Art/Sprites/PlayerDraftIcons/Towers/ArrowTower/ArrowTowerUpgrade_1.png.meta
deleted file mode 100644
index bbe72c7..0000000
--- a/Assets/_Project/Art/Sprites/PlayerDraftIcons/Towers/ArrowTower/ArrowTowerUpgrade_1.png.meta
+++ /dev/null
@@ -1,117 +0,0 @@
-fileFormatVersion: 2
-guid: a140f62a10b8dc04e84625f8008e0c24
-TextureImporter:
- internalIDToNameTable: []
- externalObjects: {}
- serializedVersion: 13
- mipmaps:
- mipMapMode: 0
- enableMipMap: 0
- sRGBTexture: 1
- linearTexture: 0
- fadeOut: 0
- borderMipMap: 0
- mipMapsPreserveCoverage: 0
- alphaTestReferenceValue: 0.5
- mipMapFadeDistanceStart: 1
- mipMapFadeDistanceEnd: 3
- bumpmap:
- convertToNormalMap: 0
- externalNormalMap: 0
- heightScale: 0.25
- normalMapFilter: 0
- flipGreenChannel: 0
- isReadable: 0
- streamingMipmaps: 0
- streamingMipmapsPriority: 0
- vTOnly: 0
- ignoreMipmapLimit: 0
- grayScaleToAlpha: 0
- generateCubemap: 6
- cubemapConvolution: 0
- seamlessCubemap: 0
- textureFormat: 1
- maxTextureSize: 2048
- textureSettings:
- serializedVersion: 2
- filterMode: 1
- aniso: 1
- mipBias: 0
- wrapU: 1
- wrapV: 1
- wrapW: 0
- nPOTScale: 0
- lightmap: 0
- compressionQuality: 50
- spriteMode: 1
- spriteExtrude: 1
- spriteMeshType: 1
- alignment: 0
- spritePivot: {x: 0.5, y: 0.5}
- spritePixelsToUnits: 100
- spriteBorder: {x: 0, y: 0, z: 0, w: 0}
- spriteGenerateFallbackPhysicsShape: 1
- alphaUsage: 1
- alphaIsTransparency: 1
- spriteTessellationDetail: -1
- textureType: 8
- textureShape: 1
- singleChannelComponent: 0
- flipbookRows: 1
- flipbookColumns: 1
- maxTextureSizeSet: 0
- compressionQualitySet: 0
- textureFormatSet: 0
- ignorePngGamma: 0
- applyGammaDecoding: 0
- swizzle: 50462976
- cookieLightType: 0
- platformSettings:
- - serializedVersion: 4
- buildTarget: DefaultTexturePlatform
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 1
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- - serializedVersion: 4
- buildTarget: Standalone
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 1
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- spriteSheet:
- serializedVersion: 2
- sprites: []
- outline: []
- customData:
- physicsShape: []
- bones: []
- spriteID: 5e97eb03825dee720800000000000000
- internalID: 0
- vertices: []
- indices:
- edges: []
- weights: []
- secondaryTextures: []
- spriteCustomMetadata:
- entries: []
- nameFileIdTable: {}
- mipmapLimitGroupName:
- pSDRemoveMatte: 0
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Art/Sprites/RaceIcons.meta b/Assets/_Project/Art/Sprites/RaceIcons.meta
deleted file mode 100644
index 361ea99..0000000
--- a/Assets/_Project/Art/Sprites/RaceIcons.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 45b4fc03b527ce444a7c37579965d990
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Art/Sprites/PlayerDraftIcons/Towers.meta b/Assets/_Project/Art/Sprites/Towers.meta
similarity index 100%
rename from Assets/_Project/Art/Sprites/PlayerDraftIcons/Towers.meta
rename to Assets/_Project/Art/Sprites/Towers.meta
diff --git a/Assets/_Project/Art/Sprites/PlayerDraftIcons/Towers/ArrowTower/arrow.png b/Assets/_Project/Art/Sprites/Towers/arrow.png
similarity index 100%
rename from Assets/_Project/Art/Sprites/PlayerDraftIcons/Towers/ArrowTower/arrow.png
rename to Assets/_Project/Art/Sprites/Towers/arrow.png
diff --git a/Assets/_Project/Art/Sprites/PlayerDraftIcons/Towers/ArrowTower/arrow.png.meta b/Assets/_Project/Art/Sprites/Towers/arrow.png.meta
similarity index 100%
rename from Assets/_Project/Art/Sprites/PlayerDraftIcons/Towers/ArrowTower/arrow.png.meta
rename to Assets/_Project/Art/Sprites/Towers/arrow.png.meta
diff --git a/Assets/_Project/Art/Sprites/PlayerDraftIcons/Towers/siege.png b/Assets/_Project/Art/Sprites/Towers/siege.png
similarity index 100%
rename from Assets/_Project/Art/Sprites/PlayerDraftIcons/Towers/siege.png
rename to Assets/_Project/Art/Sprites/Towers/siege.png
diff --git a/Assets/_Project/Art/Sprites/PlayerDraftIcons/Towers/siege.png.meta b/Assets/_Project/Art/Sprites/Towers/siege.png.meta
similarity index 100%
rename from Assets/_Project/Art/Sprites/PlayerDraftIcons/Towers/siege.png.meta
rename to Assets/_Project/Art/Sprites/Towers/siege.png.meta
diff --git a/Assets/_Project/Art/Sprites/RaceIcons/Ultramarines.jpg b/Assets/_Project/Art/Sprites/Ultramarines.jpg
similarity index 100%
rename from Assets/_Project/Art/Sprites/RaceIcons/Ultramarines.jpg
rename to Assets/_Project/Art/Sprites/Ultramarines.jpg
diff --git a/Assets/_Project/Art/Sprites/RaceIcons/Ultramarines.jpg.meta b/Assets/_Project/Art/Sprites/Ultramarines.jpg.meta
similarity index 100%
rename from Assets/_Project/Art/Sprites/RaceIcons/Ultramarines.jpg.meta
rename to Assets/_Project/Art/Sprites/Ultramarines.jpg.meta
diff --git a/Assets/_Project/Art/VFX/FX_SellTower.prefab b/Assets/_Project/Art/VFX/FX_SellTower.prefab
deleted file mode 100644
index 56bd027..0000000
--- a/Assets/_Project/Art/VFX/FX_SellTower.prefab
+++ /dev/null
@@ -1,4896 +0,0 @@
-%YAML 1.1
-%TAG !u! tag:unity3d.com,2011:
---- !u!1 &2313150535691989867
-GameObject:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- serializedVersion: 6
- m_Component:
- - component: {fileID: 5508247059194889267}
- - component: {fileID: 6710887781589457797}
- - component: {fileID: 7520636153999768792}
- m_Layer: 0
- m_Name: FX_SellTower
- m_TagString: Untagged
- m_Icon: {fileID: 0}
- m_NavMeshLayer: 0
- m_StaticEditorFlags: 0
- m_IsActive: 1
---- !u!4 &5508247059194889267
-Transform:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 2313150535691989867}
- serializedVersion: 2
- m_LocalRotation: {x: -0.7071068, y: 0, z: 0, w: 0.7071068}
- m_LocalPosition: {x: 0, y: 0, z: 0}
- m_LocalScale: {x: 1, y: 1, z: 1}
- m_ConstrainProportionsScale: 0
- m_Children: []
- m_Father: {fileID: 0}
- m_LocalEulerAnglesHint: {x: -90, y: 0, z: 0}
---- !u!198 &6710887781589457797
-ParticleSystem:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 2313150535691989867}
- serializedVersion: 8
- lengthInSec: 1.5
- simulationSpeed: 1
- stopAction: 2
- cullingMode: 0
- ringBufferMode: 0
- ringBufferLoopRange: {x: 0, y: 1}
- emitterVelocityMode: 1
- looping: 0
- prewarm: 0
- playOnAwake: 1
- useUnscaledTime: 0
- autoRandomSeed: 1
- startDelay:
- serializedVersion: 2
- minMaxState: 0
- scalar: 0
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- moveWithTransform: 0
- moveWithCustomTransform: {fileID: 0}
- scalingMode: 1
- randomSeed: 0
- InitialModule:
- serializedVersion: 3
- enabled: 1
- startLifetime:
- serializedVersion: 2
- minMaxState: 3
- scalar: 0.9
- minScalar: 0.6
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- startSpeed:
- serializedVersion: 2
- minMaxState: 3
- scalar: 6
- minScalar: 2
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- startColor:
- serializedVersion: 2
- minMaxState: 0
- minColor: {r: 1, g: 1, b: 1, a: 1}
- maxColor: {r: 1, g: 1, b: 1, a: 1}
- maxGradient:
- serializedVersion: 2
- key0: {r: 1, g: 1, b: 1, a: 1}
- key1: {r: 1, g: 1, b: 1, a: 1}
- key2: {r: 0, g: 0, b: 0, a: 0}
- key3: {r: 0, g: 0, b: 0, a: 0}
- key4: {r: 0, g: 0, b: 0, a: 0}
- key5: {r: 0, g: 0, b: 0, a: 0}
- key6: {r: 0, g: 0, b: 0, a: 0}
- key7: {r: 0, g: 0, b: 0, a: 0}
- ctime0: 0
- ctime1: 65535
- ctime2: 0
- ctime3: 0
- ctime4: 0
- ctime5: 0
- ctime6: 0
- ctime7: 0
- atime0: 0
- atime1: 65535
- atime2: 0
- atime3: 0
- atime4: 0
- atime5: 0
- atime6: 0
- atime7: 0
- m_Mode: 0
- m_ColorSpace: -1
- m_NumColorKeys: 2
- m_NumAlphaKeys: 2
- minGradient:
- serializedVersion: 2
- key0: {r: 1, g: 1, b: 1, a: 1}
- key1: {r: 1, g: 1, b: 1, a: 1}
- key2: {r: 0, g: 0, b: 0, a: 0}
- key3: {r: 0, g: 0, b: 0, a: 0}
- key4: {r: 0, g: 0, b: 0, a: 0}
- key5: {r: 0, g: 0, b: 0, a: 0}
- key6: {r: 0, g: 0, b: 0, a: 0}
- key7: {r: 0, g: 0, b: 0, a: 0}
- ctime0: 0
- ctime1: 65535
- ctime2: 0
- ctime3: 0
- ctime4: 0
- ctime5: 0
- ctime6: 0
- ctime7: 0
- atime0: 0
- atime1: 65535
- atime2: 0
- atime3: 0
- atime4: 0
- atime5: 0
- atime6: 0
- atime7: 0
- m_Mode: 0
- m_ColorSpace: -1
- m_NumColorKeys: 2
- m_NumAlphaKeys: 2
- startSize:
- serializedVersion: 2
- minMaxState: 0
- scalar: 30
- minScalar: 1
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- startSizeY:
- serializedVersion: 2
- minMaxState: 0
- scalar: 1
- minScalar: 1
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- startSizeZ:
- serializedVersion: 2
- minMaxState: 0
- scalar: 1
- minScalar: 1
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- startRotationX:
- serializedVersion: 2
- minMaxState: 3
- scalar: 3.1415925
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- startRotationY:
- serializedVersion: 2
- minMaxState: 3
- scalar: 3.1415927
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- startRotation:
- serializedVersion: 2
- minMaxState: 3
- scalar: 3.1415927
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- randomizeRotationDirection: 0
- gravitySource: 0
- maxNumParticles: 1000
- customEmitterVelocity: {x: 0, y: 0, z: 0}
- size3D: 0
- rotation3D: 1
- gravityModifier:
- serializedVersion: 2
- minMaxState: 3
- scalar: 4
- minScalar: 1
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- ShapeModule:
- serializedVersion: 6
- enabled: 1
- type: 2
- angle: 25
- length: 5
- boxThickness: {x: 0, y: 0, z: 0}
- radiusThickness: 1
- donutRadius: 0.2
- m_Position: {x: 0, y: 0, z: 0}
- m_Rotation: {x: 0, y: 0, z: 0}
- m_Scale: {x: 1, y: 1, z: 1}
- placementMode: 0
- m_MeshMaterialIndex: 0
- m_MeshNormalOffset: 0
- m_MeshSpawn:
- mode: 0
- spread: 0
- speed:
- serializedVersion: 2
- minMaxState: 0
- scalar: 1
- minScalar: 1
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- m_Mesh: {fileID: 0}
- m_MeshRenderer: {fileID: 0}
- m_SkinnedMeshRenderer: {fileID: 0}
- m_Sprite: {fileID: 0}
- m_SpriteRenderer: {fileID: 0}
- m_UseMeshMaterialIndex: 0
- m_UseMeshColors: 1
- alignToDirection: 0
- m_Texture: {fileID: 0}
- m_TextureClipChannel: 3
- m_TextureClipThreshold: 0
- m_TextureUVChannel: 0
- m_TextureColorAffectsParticles: 1
- m_TextureAlphaAffectsParticles: 1
- m_TextureBilinearFiltering: 0
- randomDirectionAmount: 0
- sphericalDirectionAmount: 0
- randomPositionAmount: 0
- radius:
- value: 2
- mode: 0
- spread: 0
- speed:
- serializedVersion: 2
- minMaxState: 0
- scalar: 1
- minScalar: 1
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- arc:
- value: 360
- mode: 0
- spread: 0
- speed:
- serializedVersion: 2
- minMaxState: 0
- scalar: 1
- minScalar: 1
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- EmissionModule:
- enabled: 1
- serializedVersion: 4
- rateOverTime:
- serializedVersion: 2
- minMaxState: 0
- scalar: 0
- minScalar: 10
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- rateOverDistance:
- serializedVersion: 2
- minMaxState: 0
- scalar: 0
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- m_BurstCount: 1
- m_Bursts:
- - serializedVersion: 2
- time: 0
- countCurve:
- serializedVersion: 2
- minMaxState: 3
- scalar: 50
- minScalar: 20
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0
- outWeight: 0
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0
- outWeight: 0
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0
- outWeight: 0
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0
- outWeight: 0
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- cycleCount: 1
- repeatInterval: 0.01
- probability: 1
- SizeModule:
- enabled: 1
- curve:
- serializedVersion: 2
- minMaxState: 1
- scalar: 1
- minScalar: 1
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: -2
- outSlope: -2
- tangentMode: 0
- weightedMode: 0
- inWeight: 0
- outWeight: 0
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0
- outWeight: 0
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- y:
- serializedVersion: 2
- minMaxState: 1
- scalar: 1
- minScalar: 1
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 1
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 1
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- z:
- serializedVersion: 2
- minMaxState: 1
- scalar: 1
- minScalar: 1
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 1
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 1
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- separateAxes: 0
- RotationModule:
- enabled: 1
- x:
- serializedVersion: 2
- minMaxState: 3
- scalar: 0
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- y:
- serializedVersion: 2
- minMaxState: 3
- scalar: 0
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- curve:
- serializedVersion: 2
- minMaxState: 3
- scalar: 0.7853982
- minScalar: 0.2617994
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- separateAxes: 0
- ColorModule:
- enabled: 0
- gradient:
- serializedVersion: 2
- minMaxState: 1
- minColor: {r: 1, g: 1, b: 1, a: 1}
- maxColor: {r: 1, g: 1, b: 1, a: 1}
- maxGradient:
- serializedVersion: 2
- key0: {r: 1, g: 1, b: 1, a: 1}
- key1: {r: 1, g: 1, b: 1, a: 1}
- key2: {r: 0, g: 0, b: 0, a: 0}
- key3: {r: 0, g: 0, b: 0, a: 0}
- key4: {r: 0, g: 0, b: 0, a: 0}
- key5: {r: 0, g: 0, b: 0, a: 0}
- key6: {r: 0, g: 0, b: 0, a: 0}
- key7: {r: 0, g: 0, b: 0, a: 0}
- ctime0: 0
- ctime1: 65535
- ctime2: 0
- ctime3: 0
- ctime4: 0
- ctime5: 0
- ctime6: 0
- ctime7: 0
- atime0: 0
- atime1: 65535
- atime2: 0
- atime3: 0
- atime4: 0
- atime5: 0
- atime6: 0
- atime7: 0
- m_Mode: 0
- m_ColorSpace: -1
- m_NumColorKeys: 2
- m_NumAlphaKeys: 2
- minGradient:
- serializedVersion: 2
- key0: {r: 1, g: 1, b: 1, a: 1}
- key1: {r: 1, g: 1, b: 1, a: 1}
- key2: {r: 0, g: 0, b: 0, a: 0}
- key3: {r: 0, g: 0, b: 0, a: 0}
- key4: {r: 0, g: 0, b: 0, a: 0}
- key5: {r: 0, g: 0, b: 0, a: 0}
- key6: {r: 0, g: 0, b: 0, a: 0}
- key7: {r: 0, g: 0, b: 0, a: 0}
- ctime0: 0
- ctime1: 65535
- ctime2: 0
- ctime3: 0
- ctime4: 0
- ctime5: 0
- ctime6: 0
- ctime7: 0
- atime0: 0
- atime1: 65535
- atime2: 0
- atime3: 0
- atime4: 0
- atime5: 0
- atime6: 0
- atime7: 0
- m_Mode: 0
- m_ColorSpace: -1
- m_NumColorKeys: 2
- m_NumAlphaKeys: 2
- UVModule:
- serializedVersion: 2
- enabled: 0
- mode: 0
- timeMode: 0
- fps: 30
- frameOverTime:
- serializedVersion: 2
- minMaxState: 1
- scalar: 0.9999
- minScalar: 0.9999
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 1
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 1
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- startFrame:
- serializedVersion: 2
- minMaxState: 0
- scalar: 0
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- speedRange: {x: 0, y: 1}
- tilesX: 1
- tilesY: 1
- animationType: 0
- rowIndex: 0
- cycles: 1
- uvChannelMask: -1
- rowMode: 1
- sprites:
- - sprite: {fileID: 0}
- flipU: 0
- flipV: 0
- VelocityModule:
- enabled: 0
- x:
- serializedVersion: 2
- minMaxState: 0
- scalar: 0
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- y:
- serializedVersion: 2
- minMaxState: 0
- scalar: 0
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- z:
- serializedVersion: 2
- minMaxState: 0
- scalar: 0
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- orbitalX:
- serializedVersion: 2
- minMaxState: 0
- scalar: 0
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- orbitalY:
- serializedVersion: 2
- minMaxState: 0
- scalar: 0
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- orbitalZ:
- serializedVersion: 2
- minMaxState: 0
- scalar: 0
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- orbitalOffsetX:
- serializedVersion: 2
- minMaxState: 0
- scalar: 0
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- orbitalOffsetY:
- serializedVersion: 2
- minMaxState: 0
- scalar: 0
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- orbitalOffsetZ:
- serializedVersion: 2
- minMaxState: 0
- scalar: 0
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- radial:
- serializedVersion: 2
- minMaxState: 0
- scalar: 0
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- speedModifier:
- serializedVersion: 2
- minMaxState: 0
- scalar: 1
- minScalar: 1
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- inWorldSpace: 0
- InheritVelocityModule:
- enabled: 0
- m_Mode: 0
- m_Curve:
- serializedVersion: 2
- minMaxState: 0
- scalar: 0
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- LifetimeByEmitterSpeedModule:
- enabled: 0
- m_Curve:
- serializedVersion: 2
- minMaxState: 1
- scalar: 1
- minScalar: 1
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: -0.8
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0.2
- inSlope: -0.8
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- m_Range: {x: 0, y: 1}
- ForceModule:
- enabled: 0
- x:
- serializedVersion: 2
- minMaxState: 0
- scalar: 0
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- y:
- serializedVersion: 2
- minMaxState: 0
- scalar: 0
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- z:
- serializedVersion: 2
- minMaxState: 0
- scalar: 0
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- inWorldSpace: 0
- randomizePerFrame: 0
- ExternalForcesModule:
- serializedVersion: 2
- enabled: 0
- multiplierCurve:
- serializedVersion: 2
- minMaxState: 0
- scalar: 1
- minScalar: 1
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- influenceFilter: 0
- influenceMask:
- serializedVersion: 2
- m_Bits: 4294967295
- influenceList: []
- ClampVelocityModule:
- enabled: 0
- x:
- serializedVersion: 2
- minMaxState: 0
- scalar: 1
- minScalar: 1
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- y:
- serializedVersion: 2
- minMaxState: 0
- scalar: 1
- minScalar: 1
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- z:
- serializedVersion: 2
- minMaxState: 0
- scalar: 1
- minScalar: 1
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- magnitude:
- serializedVersion: 2
- minMaxState: 0
- scalar: 1
- minScalar: 1
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- separateAxis: 0
- inWorldSpace: 0
- multiplyDragByParticleSize: 1
- multiplyDragByParticleVelocity: 1
- dampen: 0
- drag:
- serializedVersion: 2
- minMaxState: 0
- scalar: 0
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- NoiseModule:
- enabled: 0
- strength:
- serializedVersion: 2
- minMaxState: 0
- scalar: 1
- minScalar: 1
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- strengthY:
- serializedVersion: 2
- minMaxState: 0
- scalar: 1
- minScalar: 1
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- strengthZ:
- serializedVersion: 2
- minMaxState: 0
- scalar: 1
- minScalar: 1
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- separateAxes: 0
- frequency: 0.5
- damping: 1
- octaves: 1
- octaveMultiplier: 0.5
- octaveScale: 2
- quality: 1
- scrollSpeed:
- serializedVersion: 2
- minMaxState: 0
- scalar: 0
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- remap:
- serializedVersion: 2
- minMaxState: 1
- scalar: 1
- minScalar: 1
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: -1
- inSlope: 0
- outSlope: 2
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 2
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- remapY:
- serializedVersion: 2
- minMaxState: 1
- scalar: 1
- minScalar: 1
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: -1
- inSlope: 0
- outSlope: 2
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 2
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- remapZ:
- serializedVersion: 2
- minMaxState: 1
- scalar: 1
- minScalar: 1
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: -1
- inSlope: 0
- outSlope: 2
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 2
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- remapEnabled: 0
- positionAmount:
- serializedVersion: 2
- minMaxState: 0
- scalar: 1
- minScalar: 1
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- rotationAmount:
- serializedVersion: 2
- minMaxState: 0
- scalar: 0
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- sizeAmount:
- serializedVersion: 2
- minMaxState: 0
- scalar: 0
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- SizeBySpeedModule:
- enabled: 0
- curve:
- serializedVersion: 2
- minMaxState: 1
- scalar: 1
- minScalar: 1
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 1
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 1
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- y:
- serializedVersion: 2
- minMaxState: 1
- scalar: 1
- minScalar: 1
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 1
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 1
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- z:
- serializedVersion: 2
- minMaxState: 1
- scalar: 1
- minScalar: 1
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 1
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 1
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- range: {x: 0, y: 1}
- separateAxes: 0
- RotationBySpeedModule:
- enabled: 0
- x:
- serializedVersion: 2
- minMaxState: 0
- scalar: 0
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- y:
- serializedVersion: 2
- minMaxState: 0
- scalar: 0
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- curve:
- serializedVersion: 2
- minMaxState: 0
- scalar: 0.7853982
- minScalar: 0.7853982
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- separateAxes: 0
- range: {x: 0, y: 1}
- ColorBySpeedModule:
- enabled: 0
- gradient:
- serializedVersion: 2
- minMaxState: 1
- minColor: {r: 1, g: 1, b: 1, a: 1}
- maxColor: {r: 1, g: 1, b: 1, a: 1}
- maxGradient:
- serializedVersion: 2
- key0: {r: 1, g: 1, b: 1, a: 1}
- key1: {r: 1, g: 1, b: 1, a: 1}
- key2: {r: 0, g: 0, b: 0, a: 0}
- key3: {r: 0, g: 0, b: 0, a: 0}
- key4: {r: 0, g: 0, b: 0, a: 0}
- key5: {r: 0, g: 0, b: 0, a: 0}
- key6: {r: 0, g: 0, b: 0, a: 0}
- key7: {r: 0, g: 0, b: 0, a: 0}
- ctime0: 0
- ctime1: 65535
- ctime2: 0
- ctime3: 0
- ctime4: 0
- ctime5: 0
- ctime6: 0
- ctime7: 0
- atime0: 0
- atime1: 65535
- atime2: 0
- atime3: 0
- atime4: 0
- atime5: 0
- atime6: 0
- atime7: 0
- m_Mode: 0
- m_ColorSpace: -1
- m_NumColorKeys: 2
- m_NumAlphaKeys: 2
- minGradient:
- serializedVersion: 2
- key0: {r: 1, g: 1, b: 1, a: 1}
- key1: {r: 1, g: 1, b: 1, a: 1}
- key2: {r: 0, g: 0, b: 0, a: 0}
- key3: {r: 0, g: 0, b: 0, a: 0}
- key4: {r: 0, g: 0, b: 0, a: 0}
- key5: {r: 0, g: 0, b: 0, a: 0}
- key6: {r: 0, g: 0, b: 0, a: 0}
- key7: {r: 0, g: 0, b: 0, a: 0}
- ctime0: 0
- ctime1: 65535
- ctime2: 0
- ctime3: 0
- ctime4: 0
- ctime5: 0
- ctime6: 0
- ctime7: 0
- atime0: 0
- atime1: 65535
- atime2: 0
- atime3: 0
- atime4: 0
- atime5: 0
- atime6: 0
- atime7: 0
- m_Mode: 0
- m_ColorSpace: -1
- m_NumColorKeys: 2
- m_NumAlphaKeys: 2
- range: {x: 0, y: 1}
- CollisionModule:
- enabled: 0
- serializedVersion: 4
- type: 0
- collisionMode: 0
- colliderForce: 0
- multiplyColliderForceByParticleSize: 0
- multiplyColliderForceByParticleSpeed: 0
- multiplyColliderForceByCollisionAngle: 1
- m_Planes: []
- m_Dampen:
- serializedVersion: 2
- minMaxState: 0
- scalar: 0
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- m_Bounce:
- serializedVersion: 2
- minMaxState: 0
- scalar: 1
- minScalar: 1
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- m_EnergyLossOnCollision:
- serializedVersion: 2
- minMaxState: 0
- scalar: 0
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minKillSpeed: 0
- maxKillSpeed: 10000
- radiusScale: 1
- collidesWith:
- serializedVersion: 2
- m_Bits: 4294967295
- maxCollisionShapes: 256
- quality: 0
- voxelSize: 0.5
- collisionMessages: 0
- collidesWithDynamic: 1
- interiorCollisions: 0
- TriggerModule:
- enabled: 0
- serializedVersion: 2
- inside: 1
- outside: 0
- enter: 0
- exit: 0
- colliderQueryMode: 0
- radiusScale: 1
- primitives: []
- SubModule:
- serializedVersion: 2
- enabled: 0
- subEmitters:
- - serializedVersion: 3
- emitter: {fileID: 0}
- type: 0
- properties: 0
- emitProbability: 1
- LightsModule:
- enabled: 0
- ratio: 0
- light: {fileID: 0}
- randomDistribution: 1
- color: 1
- range: 1
- intensity: 1
- rangeCurve:
- serializedVersion: 2
- minMaxState: 0
- scalar: 1
- minScalar: 1
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- intensityCurve:
- serializedVersion: 2
- minMaxState: 0
- scalar: 1
- minScalar: 1
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- maxLights: 20
- TrailModule:
- enabled: 0
- mode: 0
- ratio: 1
- lifetime:
- serializedVersion: 2
- minMaxState: 0
- scalar: 1
- minScalar: 1
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minVertexDistance: 0.2
- textureMode: 0
- textureScale: {x: 1, y: 1}
- ribbonCount: 1
- shadowBias: 0.5
- worldSpace: 0
- dieWithParticles: 1
- sizeAffectsWidth: 1
- sizeAffectsLifetime: 0
- inheritParticleColor: 1
- generateLightingData: 0
- splitSubEmitterRibbons: 0
- attachRibbonsToTransform: 0
- colorOverLifetime:
- serializedVersion: 2
- minMaxState: 0
- minColor: {r: 1, g: 1, b: 1, a: 1}
- maxColor: {r: 1, g: 1, b: 1, a: 1}
- maxGradient:
- serializedVersion: 2
- key0: {r: 1, g: 1, b: 1, a: 1}
- key1: {r: 1, g: 1, b: 1, a: 1}
- key2: {r: 0, g: 0, b: 0, a: 0}
- key3: {r: 0, g: 0, b: 0, a: 0}
- key4: {r: 0, g: 0, b: 0, a: 0}
- key5: {r: 0, g: 0, b: 0, a: 0}
- key6: {r: 0, g: 0, b: 0, a: 0}
- key7: {r: 0, g: 0, b: 0, a: 0}
- ctime0: 0
- ctime1: 65535
- ctime2: 0
- ctime3: 0
- ctime4: 0
- ctime5: 0
- ctime6: 0
- ctime7: 0
- atime0: 0
- atime1: 65535
- atime2: 0
- atime3: 0
- atime4: 0
- atime5: 0
- atime6: 0
- atime7: 0
- m_Mode: 0
- m_ColorSpace: -1
- m_NumColorKeys: 2
- m_NumAlphaKeys: 2
- minGradient:
- serializedVersion: 2
- key0: {r: 1, g: 1, b: 1, a: 1}
- key1: {r: 1, g: 1, b: 1, a: 1}
- key2: {r: 0, g: 0, b: 0, a: 0}
- key3: {r: 0, g: 0, b: 0, a: 0}
- key4: {r: 0, g: 0, b: 0, a: 0}
- key5: {r: 0, g: 0, b: 0, a: 0}
- key6: {r: 0, g: 0, b: 0, a: 0}
- key7: {r: 0, g: 0, b: 0, a: 0}
- ctime0: 0
- ctime1: 65535
- ctime2: 0
- ctime3: 0
- ctime4: 0
- ctime5: 0
- ctime6: 0
- ctime7: 0
- atime0: 0
- atime1: 65535
- atime2: 0
- atime3: 0
- atime4: 0
- atime5: 0
- atime6: 0
- atime7: 0
- m_Mode: 0
- m_ColorSpace: -1
- m_NumColorKeys: 2
- m_NumAlphaKeys: 2
- widthOverTrail:
- serializedVersion: 2
- minMaxState: 0
- scalar: 1
- minScalar: 1
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- colorOverTrail:
- serializedVersion: 2
- minMaxState: 0
- minColor: {r: 1, g: 1, b: 1, a: 1}
- maxColor: {r: 1, g: 1, b: 1, a: 1}
- maxGradient:
- serializedVersion: 2
- key0: {r: 1, g: 1, b: 1, a: 1}
- key1: {r: 1, g: 1, b: 1, a: 1}
- key2: {r: 0, g: 0, b: 0, a: 0}
- key3: {r: 0, g: 0, b: 0, a: 0}
- key4: {r: 0, g: 0, b: 0, a: 0}
- key5: {r: 0, g: 0, b: 0, a: 0}
- key6: {r: 0, g: 0, b: 0, a: 0}
- key7: {r: 0, g: 0, b: 0, a: 0}
- ctime0: 0
- ctime1: 65535
- ctime2: 0
- ctime3: 0
- ctime4: 0
- ctime5: 0
- ctime6: 0
- ctime7: 0
- atime0: 0
- atime1: 65535
- atime2: 0
- atime3: 0
- atime4: 0
- atime5: 0
- atime6: 0
- atime7: 0
- m_Mode: 0
- m_ColorSpace: -1
- m_NumColorKeys: 2
- m_NumAlphaKeys: 2
- minGradient:
- serializedVersion: 2
- key0: {r: 1, g: 1, b: 1, a: 1}
- key1: {r: 1, g: 1, b: 1, a: 1}
- key2: {r: 0, g: 0, b: 0, a: 0}
- key3: {r: 0, g: 0, b: 0, a: 0}
- key4: {r: 0, g: 0, b: 0, a: 0}
- key5: {r: 0, g: 0, b: 0, a: 0}
- key6: {r: 0, g: 0, b: 0, a: 0}
- key7: {r: 0, g: 0, b: 0, a: 0}
- ctime0: 0
- ctime1: 65535
- ctime2: 0
- ctime3: 0
- ctime4: 0
- ctime5: 0
- ctime6: 0
- ctime7: 0
- atime0: 0
- atime1: 65535
- atime2: 0
- atime3: 0
- atime4: 0
- atime5: 0
- atime6: 0
- atime7: 0
- m_Mode: 0
- m_ColorSpace: -1
- m_NumColorKeys: 2
- m_NumAlphaKeys: 2
- CustomDataModule:
- enabled: 0
- mode0: 0
- vectorComponentCount0: 4
- color0:
- serializedVersion: 2
- minMaxState: 0
- minColor: {r: 1, g: 1, b: 1, a: 1}
- maxColor: {r: 1, g: 1, b: 1, a: 1}
- maxGradient:
- serializedVersion: 2
- key0: {r: 1, g: 1, b: 1, a: 1}
- key1: {r: 1, g: 1, b: 1, a: 1}
- key2: {r: 0, g: 0, b: 0, a: 0}
- key3: {r: 0, g: 0, b: 0, a: 0}
- key4: {r: 0, g: 0, b: 0, a: 0}
- key5: {r: 0, g: 0, b: 0, a: 0}
- key6: {r: 0, g: 0, b: 0, a: 0}
- key7: {r: 0, g: 0, b: 0, a: 0}
- ctime0: 0
- ctime1: 65535
- ctime2: 0
- ctime3: 0
- ctime4: 0
- ctime5: 0
- ctime6: 0
- ctime7: 0
- atime0: 0
- atime1: 65535
- atime2: 0
- atime3: 0
- atime4: 0
- atime5: 0
- atime6: 0
- atime7: 0
- m_Mode: 0
- m_ColorSpace: -1
- m_NumColorKeys: 2
- m_NumAlphaKeys: 2
- minGradient:
- serializedVersion: 2
- key0: {r: 1, g: 1, b: 1, a: 1}
- key1: {r: 1, g: 1, b: 1, a: 1}
- key2: {r: 0, g: 0, b: 0, a: 0}
- key3: {r: 0, g: 0, b: 0, a: 0}
- key4: {r: 0, g: 0, b: 0, a: 0}
- key5: {r: 0, g: 0, b: 0, a: 0}
- key6: {r: 0, g: 0, b: 0, a: 0}
- key7: {r: 0, g: 0, b: 0, a: 0}
- ctime0: 0
- ctime1: 65535
- ctime2: 0
- ctime3: 0
- ctime4: 0
- ctime5: 0
- ctime6: 0
- ctime7: 0
- atime0: 0
- atime1: 65535
- atime2: 0
- atime3: 0
- atime4: 0
- atime5: 0
- atime6: 0
- atime7: 0
- m_Mode: 0
- m_ColorSpace: -1
- m_NumColorKeys: 2
- m_NumAlphaKeys: 2
- colorLabel0: Color
- vector0_0:
- serializedVersion: 2
- minMaxState: 0
- scalar: 0
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- vectorLabel0_0: X
- vector0_1:
- serializedVersion: 2
- minMaxState: 0
- scalar: 0
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- vectorLabel0_1: Y
- vector0_2:
- serializedVersion: 2
- minMaxState: 0
- scalar: 0
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- vectorLabel0_2: Z
- vector0_3:
- serializedVersion: 2
- minMaxState: 0
- scalar: 0
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- vectorLabel0_3: W
- mode1: 0
- vectorComponentCount1: 4
- color1:
- serializedVersion: 2
- minMaxState: 0
- minColor: {r: 1, g: 1, b: 1, a: 1}
- maxColor: {r: 1, g: 1, b: 1, a: 1}
- maxGradient:
- serializedVersion: 2
- key0: {r: 1, g: 1, b: 1, a: 1}
- key1: {r: 1, g: 1, b: 1, a: 1}
- key2: {r: 0, g: 0, b: 0, a: 0}
- key3: {r: 0, g: 0, b: 0, a: 0}
- key4: {r: 0, g: 0, b: 0, a: 0}
- key5: {r: 0, g: 0, b: 0, a: 0}
- key6: {r: 0, g: 0, b: 0, a: 0}
- key7: {r: 0, g: 0, b: 0, a: 0}
- ctime0: 0
- ctime1: 65535
- ctime2: 0
- ctime3: 0
- ctime4: 0
- ctime5: 0
- ctime6: 0
- ctime7: 0
- atime0: 0
- atime1: 65535
- atime2: 0
- atime3: 0
- atime4: 0
- atime5: 0
- atime6: 0
- atime7: 0
- m_Mode: 0
- m_ColorSpace: -1
- m_NumColorKeys: 2
- m_NumAlphaKeys: 2
- minGradient:
- serializedVersion: 2
- key0: {r: 1, g: 1, b: 1, a: 1}
- key1: {r: 1, g: 1, b: 1, a: 1}
- key2: {r: 0, g: 0, b: 0, a: 0}
- key3: {r: 0, g: 0, b: 0, a: 0}
- key4: {r: 0, g: 0, b: 0, a: 0}
- key5: {r: 0, g: 0, b: 0, a: 0}
- key6: {r: 0, g: 0, b: 0, a: 0}
- key7: {r: 0, g: 0, b: 0, a: 0}
- ctime0: 0
- ctime1: 65535
- ctime2: 0
- ctime3: 0
- ctime4: 0
- ctime5: 0
- ctime6: 0
- ctime7: 0
- atime0: 0
- atime1: 65535
- atime2: 0
- atime3: 0
- atime4: 0
- atime5: 0
- atime6: 0
- atime7: 0
- m_Mode: 0
- m_ColorSpace: -1
- m_NumColorKeys: 2
- m_NumAlphaKeys: 2
- colorLabel1: Color
- vector1_0:
- serializedVersion: 2
- minMaxState: 0
- scalar: 0
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- vectorLabel1_0: X
- vector1_1:
- serializedVersion: 2
- minMaxState: 0
- scalar: 0
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- vectorLabel1_1: Y
- vector1_2:
- serializedVersion: 2
- minMaxState: 0
- scalar: 0
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- vectorLabel1_2: Z
- vector1_3:
- serializedVersion: 2
- minMaxState: 0
- scalar: 0
- minScalar: 0
- maxCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- minCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 0
- inSlope: 0
- outSlope: 0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- vectorLabel1_3: W
---- !u!199 &7520636153999768792
-ParticleSystemRenderer:
- serializedVersion: 7
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 2313150535691989867}
- m_Enabled: 1
- m_CastShadows: 0
- m_ReceiveShadows: 0
- m_DynamicOccludee: 1
- m_StaticShadowCaster: 0
- m_MotionVectors: 1
- m_LightProbeUsage: 1
- m_ReflectionProbeUsage: 0
- m_RayTracingMode: 0
- 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: 915f376be23f20941837c0809626fa5f, 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_RenderMode: 4
- m_MeshDistribution: 0
- m_SortMode: 0
- m_MinParticleSize: 0
- m_MaxParticleSize: 0.5
- m_CameraVelocityScale: 0
- m_VelocityScale: 0
- m_LengthScale: 2
- m_SortingFudge: 0
- m_NormalDirection: 1
- m_ShadowBias: 0
- m_RenderAlignment: 0
- m_Pivot: {x: 0, y: 0, z: 0}
- m_Flip: {x: 0, y: 0, z: 0}
- m_EnableGPUInstancing: 1
- m_ApplyActiveColorSpace: 1
- m_AllowRoll: 1
- m_FreeformStretching: 0
- m_RotateWithStretchDirection: 1
- m_UseCustomVertexStreams: 0
- m_VertexStreams: 00010304
- m_UseCustomTrailVertexStreams: 0
- m_TrailVertexStreams: 00010304
- m_Mesh: {fileID: 8211022507781654652, guid: 280a727da941fc544b9aab7df7fe9f2d, type: 3}
- m_Mesh1: {fileID: 0}
- m_Mesh2: {fileID: 0}
- m_Mesh3: {fileID: 0}
- m_MeshWeighting: 1
- m_MeshWeighting1: 1
- m_MeshWeighting2: 1
- m_MeshWeighting3: 1
diff --git a/Assets/_Project/Art/VFX/FX_SellTower.prefab.meta b/Assets/_Project/Art/VFX/FX_SellTower.prefab.meta
deleted file mode 100644
index 2755a51..0000000
--- a/Assets/_Project/Art/VFX/FX_SellTower.prefab.meta
+++ /dev/null
@@ -1,7 +0,0 @@
-fileFormatVersion: 2
-guid: 31f8d8c320b42c54d886d610fb13a1e7
-PrefabImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Art/VFX/spells/Fireball.prefab b/Assets/_Project/Art/VFX/spells/Fireball.prefab
index b7e0984..b51070f 100644
--- a/Assets/_Project/Art/VFX/spells/Fireball.prefab
+++ b/Assets/_Project/Art/VFX/spells/Fireball.prefab
@@ -104,7 +104,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::TD.VFX.FallFromSkyVisual
dropHeight: 20
- duration: 1.2
+ duration: 0.5
fallCurve:
serializedVersion: 2
m_Curve:
@@ -129,5 +129,5 @@ MonoBehaviour:
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
- fallAngleDegrees: 25.6
- fallDirection: {x: 0.2, y: 0.2, z: 1}
+ fallAngleDegrees: 0
+ fallDirection: {x: 0, y: 0, z: 1}
diff --git a/Assets/_Project/Art/VFX/spells/VFX_slowarea.prefab b/Assets/_Project/Art/VFX/spells/VFX_slowarea.prefab
deleted file mode 100644
index b5d5e68..0000000
--- a/Assets/_Project/Art/VFX/spells/VFX_slowarea.prefab
+++ /dev/null
@@ -1,120 +0,0 @@
-%YAML 1.1
-%TAG !u! tag:unity3d.com,2011:
---- !u!1 &1462673130280185047
-GameObject:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- serializedVersion: 6
- m_Component:
- - component: {fileID: 30710218362299472}
- - component: {fileID: 2262385832724230530}
- - component: {fileID: 5239089805406934537}
- m_Layer: 0
- m_Name: VFX_slowarea
- m_TagString: Untagged
- m_Icon: {fileID: 0}
- m_NavMeshLayer: 0
- m_StaticEditorFlags: 0
- m_IsActive: 1
---- !u!4 &30710218362299472
-Transform:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 1462673130280185047}
- serializedVersion: 2
- m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
- m_LocalPosition: {x: -55.61683, y: -0, z: -33.87226}
- m_LocalScale: {x: 1, y: 1, z: 1}
- m_ConstrainProportionsScale: 0
- m_Children: []
- m_Father: {fileID: 0}
- m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
---- !u!2083052967 &2262385832724230530
-VisualEffect:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 1462673130280185047}
- m_Enabled: 1
- m_Asset: {fileID: 8926484042661614526, guid: 397a5bed36b1e63a0a3eef17b04f65bd, type: 3}
- m_InitialEventName: OnPlay
- m_InitialEventNameOverriden: 0
- m_StartSeed: 0
- m_ResetSeedOnPlay: 1
- m_AllowInstancing: 1
- m_ResourceVersion: 1
- m_PropertySheet:
- m_Float:
- m_Array: []
- m_Vector2f:
- m_Array: []
- m_Vector3f:
- m_Array: []
- m_Vector4f:
- m_Array: []
- m_Uint:
- m_Array: []
- m_Int:
- m_Array: []
- m_Matrix4x4f:
- m_Array: []
- m_AnimationCurve:
- m_Array: []
- m_Gradient:
- m_Array: []
- m_NamedObject:
- m_Array: []
- m_Bool:
- m_Array: []
---- !u!73398921 &5239089805406934537
-VFXRenderer:
- serializedVersion: 1
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 1462673130280185047}
- m_Enabled: 1
- m_CastShadows: 0
- m_ReceiveShadows: 0
- m_DynamicOccludee: 1
- m_StaticShadowCaster: 0
- m_MotionVectors: 0
- m_LightProbeUsage: 0
- m_ReflectionProbeUsage: 0
- m_RayTracingMode: 0
- 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_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
diff --git a/Assets/_Project/Art/VFX/spells/VFX_slowarea.prefab.meta b/Assets/_Project/Art/VFX/spells/VFX_slowarea.prefab.meta
deleted file mode 100644
index ecc94a0..0000000
--- a/Assets/_Project/Art/VFX/spells/VFX_slowarea.prefab.meta
+++ /dev/null
@@ -1,7 +0,0 @@
-fileFormatVersion: 2
-guid: bf2f1f2a9a54e9162b80673e3f7eeaf6
-PrefabImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Art/VFX/spells/VFX_slowarea.vfx b/Assets/_Project/Art/VFX/spells/VFX_slowarea.vfx
deleted file mode 100644
index a94dcde..0000000
--- a/Assets/_Project/Art/VFX/spells/VFX_slowarea.vfx
+++ /dev/null
@@ -1,2601 +0,0 @@
-%YAML 1.1
-%TAG !u! tag:unity3d.com,2011:
---- !u!114 &114340500867371532
-MonoBehaviour:
- m_ObjectHideFlags: 1
- 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: d01270efd3285ea4a9d6c555cb0a8027, type: 3}
- m_Name: VFXUI
- m_EditorClassIdentifier:
- groupInfos:
- - title: Base Loop
- position:
- serializedVersion: 2
- x: 851
- y: -1433
- width: 475
- height: 2277
- contents:
- - model: {fileID: 8926484042661614608}
- id: 0
- isStickyNote: 0
- - model: {fileID: 8926484042661614613}
- id: 0
- isStickyNote: 0
- - model: {fileID: 8926484042661614662}
- id: 0
- isStickyNote: 0
- - model: {fileID: 8926484042661614681}
- id: 0
- isStickyNote: 0
- stickyNoteInfos: []
- categories: []
- uiBounds:
- serializedVersion: 2
- x: 852
- y: -1433
- width: 474
- height: 2255
---- !u!114 &114350483966674976
-MonoBehaviour:
- m_ObjectHideFlags: 1
- 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: 7d4c867f6b72b714dbb5fd1780afe208, type: 3}
- m_Name: VFX_slowarea
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 0}
- m_Children:
- - {fileID: 8926484042661614608}
- - {fileID: 8926484042661614613}
- - {fileID: 8926484042661614662}
- - {fileID: 8926484042661614681}
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_UIInfos: {fileID: 114340500867371532}
- m_CustomAttributes: []
- m_ParameterInfo: []
- m_ImportDependencies: []
- m_GraphVersion: 19
- m_ResourceVersion: 1
- m_SubgraphDependencies: []
- m_CategoryPath:
---- !u!2058629511 &8926484042661614527
-VisualEffectResource:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_Name: VFX_slowarea
- m_Graph: {fileID: 114350483966674976}
- m_Infos:
- m_RendererSettings:
- motionVectorGenerationMode: 0
- shadowCastingMode: 0
- m_CullingFlags: 3
- m_UpdateMode: 0
- m_PreWarmDeltaTime: 0.05
- m_PreWarmStepCount: 0
- m_InitialEventName: OnPlay
- m_InstancingMode: 0
- m_InstancingCapacity: 64
---- !u!114 &8926484042661614608
-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: 73a13919d81fb7444849bae8b5c812a2, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 114350483966674976}
- m_Children:
- - {fileID: 8926484042661614610}
- m_UIPosition: {x: 877, y: -1374}
- m_UICollapsed: 0
- m_UISuperCollapsed: 0
- m_InputSlots: []
- m_OutputSlots: []
- m_Label: Spawn system
- m_Data: {fileID: 8926484042661614609}
- m_InputFlowSlot:
- - link: []
- - link: []
- m_OutputFlowSlot:
- - link:
- - context: {fileID: 8926484042661614613}
- slotIndex: 0
- loopDuration: 0
- loopCount: 0
- delayBeforeLoop: 0
- delayAfterLoop: 0
---- !u!114 &8926484042661614609
-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: f68759077adc0b143b6e1c101e82065e, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 0}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- title:
- m_Owners:
- - {fileID: 8926484042661614608}
---- !u!114 &8926484042661614610
-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: f05c6884b705ce14d82ae720f0ec209f, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614608}
- m_Children: []
- m_UIPosition: {x: 357.33423, y: 1216.2454}
- m_UICollapsed: 0
- m_UISuperCollapsed: 0
- m_InputSlots:
- - {fileID: 8926484042661614611}
- m_OutputSlots: []
- m_Disabled: 0
- m_ActivationSlot: {fileID: 8926484042661614612}
---- !u!114 &8926484042661614611
-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: f780aa281814f9842a7c076d436932e7, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 0}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614611}
- m_MasterData:
- m_Owner: {fileID: 8926484042661614610}
- m_Value:
- m_Type:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_SerializableObject: 64
- m_Space: -1
- m_Property:
- name: Rate
- m_serializedType:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614612
-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: b4c11ff25089a324daf359f4b0629b33, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 0}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614612}
- m_MasterData:
- m_Owner: {fileID: 8926484042661614610}
- m_Value:
- m_Type:
- m_SerializableType: System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_SerializableObject: True
- m_Space: -1
- m_Property:
- name: _vfx_enabled
- m_serializedType:
- m_SerializableType: System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614613
-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: 9dfea48843f53fc438eabc12a3a30abc, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 114350483966674976}
- m_Children:
- - {fileID: 8926484042661614628}
- - {fileID: 8926484042661614834}
- - {fileID: 8926484042661614806}
- m_UIPosition: {x: 877, y: -1086}
- m_UICollapsed: 0
- m_UISuperCollapsed: 0
- m_InputSlots:
- - {fileID: 8926484042661614774}
- - {fileID: 8926484042661614783}
- m_OutputSlots: []
- m_Label: Initialize Particles
- m_Data: {fileID: 8926484042661614627}
- m_InputFlowSlot:
- - link:
- - context: {fileID: 8926484042661614608}
- slotIndex: 0
- m_OutputFlowSlot:
- - link:
- - context: {fileID: 8926484042661614662}
- slotIndex: 0
---- !u!114 &8926484042661614627
-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: d78581a96eae8bf4398c282eb0b098bd, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 0}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- title: Simple Loop
- m_Owners:
- - {fileID: 8926484042661614613}
- - {fileID: 8926484042661614662}
- - {fileID: 8926484042661614681}
- dataType: 0
- capacity: 68
- stripCapacity: 1
- particlePerStripCount: 32
- needsComputeBounds: 0
- boundsMode: 0
- m_Space: 1
---- !u!114 &8926484042661614628
-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: a971fa2e110a0ac42ac1d8dae408704b, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614613}
- m_Children: []
- m_UIPosition: {x: 357.33423, y: 1214.2454}
- m_UICollapsed: 0
- m_UISuperCollapsed: 0
- m_InputSlots:
- - {fileID: 8926484042661614629}
- - {fileID: 8926484042661614630}
- m_OutputSlots: []
- m_Disabled: 0
- m_ActivationSlot: {fileID: 8926484042661614631}
- attribute: lifetime
- Composition: 0
- Source: 0
- Random: 2
- channels: 6
---- !u!114 &8926484042661614629
-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: f780aa281814f9842a7c076d436932e7, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 0}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614629}
- m_MasterData:
- m_Owner: {fileID: 8926484042661614628}
- m_Value:
- m_Type:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_SerializableObject: 0.8
- m_Space: -1
- m_Property:
- name: A
- m_serializedType:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614630
-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: f780aa281814f9842a7c076d436932e7, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 0}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614630}
- m_MasterData:
- m_Owner: {fileID: 8926484042661614628}
- m_Value:
- m_Type:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_SerializableObject: 1.2
- m_Space: -1
- m_Property:
- name: B
- m_serializedType:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614631
-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: b4c11ff25089a324daf359f4b0629b33, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 0}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614631}
- m_MasterData:
- m_Owner: {fileID: 8926484042661614628}
- m_Value:
- m_Type:
- m_SerializableType: System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_SerializableObject: True
- m_Space: -1
- m_Property:
- name: _vfx_enabled
- m_serializedType:
- m_SerializableType: System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614662
-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: 2dc095764ededfa4bb32fa602511ea4b, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 114350483966674976}
- m_Children:
- - {fileID: 8926484042661614820}
- - {fileID: 8926484042661614827}
- m_UIPosition: {x: 877, y: -187}
- m_UICollapsed: 0
- m_UISuperCollapsed: 0
- m_InputSlots: []
- m_OutputSlots: []
- m_Label: Update Particles
- m_Data: {fileID: 8926484042661614627}
- m_InputFlowSlot:
- - link:
- - context: {fileID: 8926484042661614613}
- slotIndex: 0
- m_OutputFlowSlot:
- - link:
- - context: {fileID: 8926484042661614681}
- slotIndex: 0
- integration: 0
- angularIntegration: 0
- ageParticles: 1
- reapParticles: 1
- skipZeroDeltaUpdate: 0
---- !u!114 &8926484042661614681
-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: a0b9e6b9139e58d4c957ec54595da7d3, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 114350483966674976}
- m_Children:
- - {fileID: 8926484042661614685}
- - {fileID: 8926484042661614830}
- - {fileID: 8926484042661614722}
- m_UIPosition: {x: 877, y: 214}
- m_UICollapsed: 0
- m_UISuperCollapsed: 0
- m_InputSlots:
- - {fileID: 8926484042661614682}
- m_OutputSlots: []
- m_Label: Render Quad
- m_Data: {fileID: 8926484042661614627}
- m_InputFlowSlot:
- - link:
- - context: {fileID: 8926484042661614662}
- slotIndex: 0
- m_OutputFlowSlot:
- - link: []
- blendMode: 1
- cullMode: 0
- zWriteMode: 0
- zTestMode: 0
- useAlphaClipping: 0
- generateMotionVector: 0
- excludeFromTUAndAA: 0
- sortingPriority: 0
- m_SubOutputs:
- - {fileID: 8926484042661614687}
- - {fileID: 8926484042661614833}
- useBaseColorMap: 3
- colorMapping: 0
- uvMode: 0
- flipbookLayout: 0
- flipbookBlendFrames: 0
- flipbookMotionVectors: 0
- useSoftParticle: 0
- vfxSystemSortPriority: 0
- sort: 0
- sortMode: 0
- revertSorting: 0
- indirectDraw: 0
- computeCulling: 0
- frustumCulling: 0
- castShadows: 0
- useExposureWeight: 0
- enableRayTracing: 0
- decimationFactor: 1
- raytracedScaleMode: 0
- needsOwnSort: 1
- needsOwnAabbBuffer: 0
- shaderGraph: {fileID: 0}
- materialSettings:
- m_PropertyNames: []
- m_PropertyValues: []
- renderQueue: -1
- primitiveType: 1
- useGeometryShader: 0
---- !u!114 &8926484042661614682
-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: 70a331b1d86cc8d4aa106ccbe0da5852, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 0}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614682}
- m_MasterData:
- m_Owner: {fileID: 8926484042661614681}
- m_Value:
- m_Type:
- m_SerializableType: UnityEngine.Texture2D, UnityEngine.CoreModule, Version=0.0.0.0,
- Culture=neutral, PublicKeyToken=null
- m_SerializableObject: '{"obj":{"fileID":2800000,"guid":"8aafaa78fe944854997fef757ff4ba72","type":3}}'
- m_Space: -1
- m_Property:
- name: mainTexture
- m_serializedType:
- m_SerializableType: UnityEngine.Texture2D, UnityEngine.CoreModule, Version=0.0.0.0,
- Culture=neutral, PublicKeyToken=null
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614685
-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: d16c6aeaef944094b9a1633041804207, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614681}
- m_Children: []
- m_UIPosition: {x: 0, y: 2}
- m_UICollapsed: 0
- m_UISuperCollapsed: 0
- m_InputSlots: []
- m_OutputSlots: []
- m_Disabled: 0
- m_ActivationSlot: {fileID: 8926484042661614686}
- mode: 0
- axes: 4
- faceRay: 1
---- !u!114 &8926484042661614686
-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: b4c11ff25089a324daf359f4b0629b33, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 0}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614686}
- m_MasterData:
- m_Owner: {fileID: 8926484042661614685}
- m_Value:
- m_Type:
- m_SerializableType: System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_SerializableObject: True
- m_Space: -1
- m_Property:
- name: _vfx_enabled
- m_serializedType:
- m_SerializableType: System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614687
-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: 081ffb0090424ba4cb05370a42ead6b9, type: 3}
- m_Name:
- m_EditorClassIdentifier:
---- !u!114 &8926484042661614722
-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: 01ec2c1930009b04ea08905b47262415, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614681}
- m_Children: []
- m_UIPosition: {x: 508.1385, y: 271.34802}
- m_UICollapsed: 0
- m_UISuperCollapsed: 0
- m_InputSlots:
- - {fileID: 8926484042661614723}
- m_OutputSlots: []
- m_Disabled: 0
- m_ActivationSlot: {fileID: 8926484042661614724}
- attribute: color
- Composition: 2
- AlphaComposition: 0
- SampleMode: 0
- Mode: 1
- ColorMode: 3
- channels: 6
---- !u!114 &8926484042661614723
-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: 76f778ff57c4e8145b9681fe3268d8e9, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 0}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614723}
- m_MasterData:
- m_Owner: {fileID: 8926484042661614722}
- m_Value:
- m_Type:
- m_SerializableType: UnityEngine.Gradient, UnityEngine.CoreModule, Version=0.0.0.0,
- Culture=neutral, PublicKeyToken=null
- m_SerializableObject: '{"colorKeys":[{"color":{"r":0.003921564668416977,"g":0.7162883877754211,"b":0.9725490212440491,"a":1.0},"time":0.0},{"color":{"r":0.2158605307340622,"g":0.6866854429244995,"b":1.0,"a":1.0},"time":1.0}],"alphaKeys":[{"alpha":1.0,"time":0.0},{"alpha":1.0,"time":0.800000011920929},{"alpha":0.0,"time":1.0}],"gradientMode":0}'
- m_Space: -1
- m_Property:
- name: Color
- m_serializedType:
- m_SerializableType: UnityEngine.Gradient, UnityEngine.CoreModule, Version=0.0.0.0,
- Culture=neutral, PublicKeyToken=null
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614724
-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: b4c11ff25089a324daf359f4b0629b33, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 0}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614724}
- m_MasterData:
- m_Owner: {fileID: 8926484042661614722}
- m_Value:
- m_Type:
- m_SerializableType: System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_SerializableObject: True
- m_Space: -1
- m_Property:
- name: _vfx_enabled
- m_serializedType:
- m_SerializableType: System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614774
-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: 1b605c022ee79394a8a776c0869b3f9a, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 0}
- m_Children:
- - {fileID: 8926484042661614775}
- - {fileID: 8926484042661614779}
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 0
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614774}
- m_MasterData:
- m_Owner: {fileID: 8926484042661614613}
- m_Value:
- m_Type:
- m_SerializableType: UnityEditor.VFX.AABox, Unity.VisualEffectGraph.Editor,
- Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
- m_SerializableObject: '{"center":{"x":0.0,"y":1.0,"z":0.0},"size":{"x":3.5,"y":4.0,"z":3.5}}'
- m_Space: 0
- m_Property:
- name: bounds
- m_serializedType:
- m_SerializableType: UnityEditor.VFX.AABox, Unity.VisualEffectGraph.Editor,
- Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614775
-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: ac39bd03fca81b849929b9c966f1836a, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614774}
- m_Children:
- - {fileID: 8926484042661614776}
- - {fileID: 8926484042661614777}
- - {fileID: 8926484042661614778}
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614774}
- m_MasterData:
- m_Owner: {fileID: 0}
- m_Value:
- m_Type:
- m_SerializableType:
- m_SerializableObject:
- m_Space: -1
- m_Property:
- name: center
- m_serializedType:
- m_SerializableType: UnityEngine.Vector3, UnityEngine.CoreModule, Version=0.0.0.0,
- Culture=neutral, PublicKeyToken=null
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614776
-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: f780aa281814f9842a7c076d436932e7, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614775}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614774}
- m_MasterData:
- m_Owner: {fileID: 0}
- m_Value:
- m_Type:
- m_SerializableType:
- m_SerializableObject:
- m_Space: -1
- m_Property:
- name: x
- m_serializedType:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614777
-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: f780aa281814f9842a7c076d436932e7, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614775}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614774}
- m_MasterData:
- m_Owner: {fileID: 0}
- m_Value:
- m_Type:
- m_SerializableType:
- m_SerializableObject:
- m_Space: -1
- m_Property:
- name: y
- m_serializedType:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614778
-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: f780aa281814f9842a7c076d436932e7, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614775}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614774}
- m_MasterData:
- m_Owner: {fileID: 0}
- m_Value:
- m_Type:
- m_SerializableType:
- m_SerializableObject:
- m_Space: -1
- m_Property:
- name: z
- m_serializedType:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614779
-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: ac39bd03fca81b849929b9c966f1836a, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614774}
- m_Children:
- - {fileID: 8926484042661614780}
- - {fileID: 8926484042661614781}
- - {fileID: 8926484042661614782}
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614774}
- m_MasterData:
- m_Owner: {fileID: 0}
- m_Value:
- m_Type:
- m_SerializableType:
- m_SerializableObject:
- m_Space: -1
- m_Property:
- name: size
- m_serializedType:
- m_SerializableType: UnityEngine.Vector3, UnityEngine.CoreModule, Version=0.0.0.0,
- Culture=neutral, PublicKeyToken=null
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614780
-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: f780aa281814f9842a7c076d436932e7, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614779}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614774}
- m_MasterData:
- m_Owner: {fileID: 0}
- m_Value:
- m_Type:
- m_SerializableType:
- m_SerializableObject:
- m_Space: -1
- m_Property:
- name: x
- m_serializedType:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614781
-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: f780aa281814f9842a7c076d436932e7, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614779}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614774}
- m_MasterData:
- m_Owner: {fileID: 0}
- m_Value:
- m_Type:
- m_SerializableType:
- m_SerializableObject:
- m_Space: -1
- m_Property:
- name: y
- m_serializedType:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614782
-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: f780aa281814f9842a7c076d436932e7, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614779}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614774}
- m_MasterData:
- m_Owner: {fileID: 0}
- m_Value:
- m_Type:
- m_SerializableType:
- m_SerializableObject:
- m_Space: -1
- m_Property:
- name: z
- m_serializedType:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614783
-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: ac39bd03fca81b849929b9c966f1836a, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 0}
- m_Children:
- - {fileID: 8926484042661614784}
- - {fileID: 8926484042661614785}
- - {fileID: 8926484042661614786}
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614783}
- m_MasterData:
- m_Owner: {fileID: 8926484042661614613}
- m_Value:
- m_Type:
- m_SerializableType: UnityEngine.Vector3, UnityEngine.CoreModule, Version=0.0.0.0,
- Culture=neutral, PublicKeyToken=null
- m_SerializableObject: '{"x":0.5,"y":0.5,"z":0.5}'
- m_Space: -1
- m_Property:
- name: boundsPadding
- m_serializedType:
- m_SerializableType: UnityEngine.Vector3, UnityEngine.CoreModule, Version=0.0.0.0,
- Culture=neutral, PublicKeyToken=null
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614784
-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: f780aa281814f9842a7c076d436932e7, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614783}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614783}
- m_MasterData:
- m_Owner: {fileID: 0}
- m_Value:
- m_Type:
- m_SerializableType:
- m_SerializableObject:
- m_Space: -1
- m_Property:
- name: x
- m_serializedType:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614785
-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: f780aa281814f9842a7c076d436932e7, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614783}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614783}
- m_MasterData:
- m_Owner: {fileID: 0}
- m_Value:
- m_Type:
- m_SerializableType:
- m_SerializableObject:
- m_Space: -1
- m_Property:
- name: y
- m_serializedType:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614786
-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: f780aa281814f9842a7c076d436932e7, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614783}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614783}
- m_MasterData:
- m_Owner: {fileID: 0}
- m_Value:
- m_Type:
- m_SerializableType:
- m_SerializableObject:
- m_Space: -1
- m_Property:
- name: z
- m_serializedType:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614806
-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: 26096dfac7c062b4b94c293605ba085e, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614613}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 0
- m_UISuperCollapsed: 0
- m_InputSlots:
- - {fileID: 8926484042661614807}
- - {fileID: 8926484042661614818}
- - {fileID: 8926484042661614819}
- - {fileID: 8926484042661614813}
- m_OutputSlots: []
- m_Disabled: 0
- m_ActivationSlot: {fileID: 8926484042661614814}
- composition: 0
- speedMode: 1
---- !u!114 &8926484042661614807
-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: e8f2b4a846fd4c14a893cde576ad172b, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 0}
- m_Children:
- - {fileID: 8926484042661614808}
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 0
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614807}
- m_MasterData:
- m_Owner: {fileID: 8926484042661614806}
- m_Value:
- m_Type:
- m_SerializableType: UnityEditor.VFX.DirectionType, Unity.VisualEffectGraph.Editor,
- Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
- m_SerializableObject: '{"direction":{"x":0.0,"y":1.0,"z":0.0}}'
- m_Space: 0
- m_Property:
- name: Direction
- m_serializedType:
- m_SerializableType: UnityEditor.VFX.DirectionType, Unity.VisualEffectGraph.Editor,
- Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614808
-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: ac39bd03fca81b849929b9c966f1836a, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614807}
- m_Children:
- - {fileID: 8926484042661614809}
- - {fileID: 8926484042661614810}
- - {fileID: 8926484042661614811}
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 0
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614807}
- m_MasterData:
- m_Owner: {fileID: 0}
- m_Value:
- m_Type:
- m_SerializableType:
- m_SerializableObject:
- m_Space: -1
- m_Property:
- name: direction
- m_serializedType:
- m_SerializableType: UnityEngine.Vector3, UnityEngine.CoreModule, Version=0.0.0.0,
- Culture=neutral, PublicKeyToken=null
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614809
-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: f780aa281814f9842a7c076d436932e7, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614808}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614807}
- m_MasterData:
- m_Owner: {fileID: 0}
- m_Value:
- m_Type:
- m_SerializableType:
- m_SerializableObject:
- m_Space: -1
- m_Property:
- name: x
- m_serializedType:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614810
-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: f780aa281814f9842a7c076d436932e7, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614808}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614807}
- m_MasterData:
- m_Owner: {fileID: 0}
- m_Value:
- m_Type:
- m_SerializableType:
- m_SerializableObject:
- m_Space: -1
- m_Property:
- name: y
- m_serializedType:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614811
-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: f780aa281814f9842a7c076d436932e7, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614808}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614807}
- m_MasterData:
- m_Owner: {fileID: 0}
- m_Value:
- m_Type:
- m_SerializableType:
- m_SerializableObject:
- m_Space: -1
- m_Property:
- name: z
- m_serializedType:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614813
-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: f780aa281814f9842a7c076d436932e7, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 0}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614813}
- m_MasterData:
- m_Owner: {fileID: 8926484042661614806}
- m_Value:
- m_Type:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_SerializableObject: 0.85
- m_Space: -1
- m_Property:
- name: DirectionBlend
- m_serializedType:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614814
-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: b4c11ff25089a324daf359f4b0629b33, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 0}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614814}
- m_MasterData:
- m_Owner: {fileID: 8926484042661614806}
- m_Value:
- m_Type:
- m_SerializableType: System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_SerializableObject: True
- m_Space: -1
- m_Property:
- name: _vfx_enabled
- m_serializedType:
- m_SerializableType: System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614818
-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: f780aa281814f9842a7c076d436932e7, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 0}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614818}
- m_MasterData:
- m_Owner: {fileID: 8926484042661614806}
- m_Value:
- m_Type:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_SerializableObject: 4.8
- m_Space: -1
- m_Property:
- name: MinSpeed
- m_serializedType:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614819
-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: f780aa281814f9842a7c076d436932e7, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 0}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614819}
- m_MasterData:
- m_Owner: {fileID: 8926484042661614806}
- m_Value:
- m_Type:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_SerializableObject: 7.5
- m_Space: -1
- m_Property:
- name: MaxSpeed
- m_serializedType:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614820
-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: e5dce54ae3368c042b26ab1f305e15b2, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614662}
- m_Children: []
- m_UIPosition: {x: 0, y: 2}
- m_UICollapsed: 0
- m_UISuperCollapsed: 0
- m_InputSlots:
- - {fileID: 8926484042661614821}
- m_OutputSlots: []
- m_Disabled: 0
- m_ActivationSlot: {fileID: 8926484042661614826}
---- !u!114 &8926484042661614821
-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: a9f9544b71b7dab44a4644b6807e8bf6, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 0}
- m_Children:
- - {fileID: 8926484042661614822}
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614821}
- m_MasterData:
- m_Owner: {fileID: 8926484042661614820}
- m_Value:
- m_Type:
- m_SerializableType: UnityEditor.VFX.Vector, Unity.VisualEffectGraph.Editor,
- Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
- m_SerializableObject: '{"vector":{"x":0.0,"y":-9.8100004196167,"z":0.0}}'
- m_Space: 1
- m_Property:
- name: Force
- m_serializedType:
- m_SerializableType: UnityEditor.VFX.Vector, Unity.VisualEffectGraph.Editor,
- Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614822
-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: ac39bd03fca81b849929b9c966f1836a, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614821}
- m_Children:
- - {fileID: 8926484042661614823}
- - {fileID: 8926484042661614824}
- - {fileID: 8926484042661614825}
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614821}
- m_MasterData:
- m_Owner: {fileID: 0}
- m_Value:
- m_Type:
- m_SerializableType:
- m_SerializableObject:
- m_Space: -1
- m_Property:
- name: vector
- m_serializedType:
- m_SerializableType: UnityEngine.Vector3, UnityEngine.CoreModule, Version=0.0.0.0,
- Culture=neutral, PublicKeyToken=null
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614823
-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: f780aa281814f9842a7c076d436932e7, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614822}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614821}
- m_MasterData:
- m_Owner: {fileID: 0}
- m_Value:
- m_Type:
- m_SerializableType:
- m_SerializableObject:
- m_Space: -1
- m_Property:
- name: x
- m_serializedType:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614824
-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: f780aa281814f9842a7c076d436932e7, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614822}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614821}
- m_MasterData:
- m_Owner: {fileID: 0}
- m_Value:
- m_Type:
- m_SerializableType:
- m_SerializableObject:
- m_Space: -1
- m_Property:
- name: y
- m_serializedType:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614825
-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: f780aa281814f9842a7c076d436932e7, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614822}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614821}
- m_MasterData:
- m_Owner: {fileID: 0}
- m_Value:
- m_Type:
- m_SerializableType:
- m_SerializableObject:
- m_Space: -1
- m_Property:
- name: z
- m_serializedType:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614826
-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: b4c11ff25089a324daf359f4b0629b33, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 0}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614826}
- m_MasterData:
- m_Owner: {fileID: 8926484042661614820}
- m_Value:
- m_Type:
- m_SerializableType: System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_SerializableObject: False
- m_Space: -1
- m_Property:
- name: _vfx_enabled
- m_serializedType:
- m_SerializableType: System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614827
-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: b294673e879f9cf449cc9de536818ea9, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614662}
- m_Children: []
- m_UIPosition: {x: 0, y: 77}
- m_UICollapsed: 0
- m_UISuperCollapsed: 0
- m_InputSlots:
- - {fileID: 8926484042661614828}
- m_OutputSlots: []
- m_Disabled: 0
- m_ActivationSlot: {fileID: 8926484042661614829}
- UseParticleSize: 0
---- !u!114 &8926484042661614828
-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: f780aa281814f9842a7c076d436932e7, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 0}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614828}
- m_MasterData:
- m_Owner: {fileID: 8926484042661614827}
- m_Value:
- m_Type:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_SerializableObject: 0.5
- m_Space: -1
- m_Property:
- name: dragCoefficient
- m_serializedType:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614829
-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: b4c11ff25089a324daf359f4b0629b33, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 0}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614829}
- m_MasterData:
- m_Owner: {fileID: 8926484042661614827}
- m_Value:
- m_Type:
- m_SerializableType: System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_SerializableObject: True
- m_Space: -1
- m_Property:
- name: _vfx_enabled
- m_serializedType:
- m_SerializableType: System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614830
-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: 01ec2c1930009b04ea08905b47262415, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614681}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 0
- m_UISuperCollapsed: 0
- m_InputSlots:
- - {fileID: 8926484042661614831}
- m_OutputSlots: []
- m_Disabled: 0
- m_ActivationSlot: {fileID: 8926484042661614832}
- attribute: size
- Composition: 2
- AlphaComposition: 0
- SampleMode: 0
- Mode: 1
- ColorMode: 3
- channels: 6
---- !u!114 &8926484042661614831
-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: c117b74c5c58db542bffe25c78fe92db, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 0}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614831}
- m_MasterData:
- m_Owner: {fileID: 8926484042661614830}
- m_Value:
- m_Type:
- m_SerializableType: UnityEngine.AnimationCurve, UnityEngine.CoreModule, Version=0.0.0.0,
- Culture=neutral, PublicKeyToken=null
- m_SerializableObject: '{"frames":[{"time":0.0,"value":0.75,"inTangent":0.0,"outTangent":1.25,"tangentMode":0,"leftTangentMode":2,"rightTangentMode":2,"broken":true},{"time":1.0,"value":2.0,"inTangent":1.25,"outTangent":2.0,"tangentMode":0,"leftTangentMode":2,"rightTangentMode":2,"broken":true}],"preWrapMode":8,"postWrapMode":8,"version":1}'
- m_Space: -1
- m_Property:
- name: Size
- m_serializedType:
- m_SerializableType: UnityEngine.AnimationCurve, UnityEngine.CoreModule, Version=0.0.0.0,
- Culture=neutral, PublicKeyToken=null
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614832
-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: b4c11ff25089a324daf359f4b0629b33, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 0}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614832}
- m_MasterData:
- m_Owner: {fileID: 8926484042661614830}
- m_Value:
- m_Type:
- m_SerializableType: System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_SerializableObject: True
- m_Space: -1
- m_Property:
- name: _vfx_enabled
- m_serializedType:
- m_SerializableType: System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614833
-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: 388ad3b1dc9c6ae45b630f914fab638f, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 0}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
---- !u!114 &8926484042661614834
-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: fb1f6794ace8b0c4592af9c5604cddbf, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614613}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 0
- m_UISuperCollapsed: 0
- m_InputSlots:
- - {fileID: 8926484042661614871}
- m_OutputSlots: []
- m_Disabled: 0
- m_ActivationSlot: {fileID: 8926484042661614852}
- compositionPosition: 0
- compositionAxes: 0
- compositionDirection: 0
- positionMode: 1
- spawnMode: 0
- shape: 0
- heightMode: 1
- applyOrientation: 1
- killOutliers: 0
- projectionSteps: 2
---- !u!114 &8926484042661614852
-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: b4c11ff25089a324daf359f4b0629b33, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 0}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614852}
- m_MasterData:
- m_Owner: {fileID: 8926484042661614834}
- m_Value:
- m_Type:
- m_SerializableType: System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_SerializableObject: True
- m_Space: -1
- m_Property:
- name: _vfx_enabled
- m_serializedType:
- m_SerializableType: System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614871
-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: 1b605c022ee79394a8a776c0869b3f9a, type: 3}
- m_Name:
- m_EditorClassIdentifier: Unity.VisualEffectGraph.Editor::UnityEditor.VFX.VFXSlot
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 0}
- m_Children:
- - {fileID: 8926484042661614872}
- - {fileID: 8926484042661614887}
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614871}
- m_MasterData:
- m_Owner: {fileID: 8926484042661614834}
- m_Value:
- m_Type:
- m_SerializableType: UnityEditor.VFX.TArcSphere, Unity.VisualEffectGraph.Editor,
- Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
- m_SerializableObject: '{"sphere":{"transform":{"position":{"x":0.0,"y":0.0,"z":0.0},"angles":{"x":0.0,"y":0.0,"z":0.0},"scale":{"x":1.0,"y":1.0,"z":1.0}},"radius":1.0},"arc":6.2831854820251469}'
- m_Space: 0
- m_Property:
- name: arcSphere
- m_serializedType:
- m_SerializableType: UnityEditor.VFX.TArcSphere, Unity.VisualEffectGraph.Editor,
- Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614872
-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: 1b605c022ee79394a8a776c0869b3f9a, type: 3}
- m_Name:
- m_EditorClassIdentifier: Unity.VisualEffectGraph.Editor::UnityEditor.VFX.VFXSlot
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614871}
- m_Children:
- - {fileID: 8926484042661614873}
- - {fileID: 8926484042661614886}
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614871}
- m_MasterData:
- m_Owner: {fileID: 0}
- m_Value:
- m_Type:
- m_SerializableType:
- m_SerializableObject:
- m_Space: -1
- m_Property:
- name: sphere
- m_serializedType:
- m_SerializableType: UnityEditor.VFX.TSphere, Unity.VisualEffectGraph.Editor,
- Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614873
-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: 3e3f628d80ffceb489beac74258f9cf7, type: 3}
- m_Name:
- m_EditorClassIdentifier: Unity.VisualEffectGraph.Editor::UnityEditor.VFX.VFXSlotTransform
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614872}
- m_Children:
- - {fileID: 8926484042661614874}
- - {fileID: 8926484042661614878}
- - {fileID: 8926484042661614882}
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614871}
- m_MasterData:
- m_Owner: {fileID: 0}
- m_Value:
- m_Type:
- m_SerializableType:
- m_SerializableObject:
- m_Space: -1
- m_Property:
- name: transform
- m_serializedType:
- m_SerializableType: UnityEditor.VFX.Transform, Unity.VisualEffectGraph.Editor,
- Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614874
-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: ac39bd03fca81b849929b9c966f1836a, type: 3}
- m_Name:
- m_EditorClassIdentifier: Unity.VisualEffectGraph.Editor::UnityEditor.VFX.VFXSlotFloat3
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614873}
- m_Children:
- - {fileID: 8926484042661614875}
- - {fileID: 8926484042661614876}
- - {fileID: 8926484042661614877}
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614871}
- m_MasterData:
- m_Owner: {fileID: 0}
- m_Value:
- m_Type:
- m_SerializableType:
- m_SerializableObject:
- m_Space: -1
- m_Property:
- name: position
- m_serializedType:
- m_SerializableType: UnityEngine.Vector3, UnityEngine.CoreModule, Version=0.0.0.0,
- Culture=neutral, PublicKeyToken=null
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614875
-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: f780aa281814f9842a7c076d436932e7, type: 3}
- m_Name:
- m_EditorClassIdentifier: Unity.VisualEffectGraph.Editor::UnityEditor.VFX.VFXSlotFloat
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614874}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614871}
- m_MasterData:
- m_Owner: {fileID: 0}
- m_Value:
- m_Type:
- m_SerializableType:
- m_SerializableObject:
- m_Space: -1
- m_Property:
- name: x
- m_serializedType:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614876
-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: f780aa281814f9842a7c076d436932e7, type: 3}
- m_Name:
- m_EditorClassIdentifier: Unity.VisualEffectGraph.Editor::UnityEditor.VFX.VFXSlotFloat
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614874}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614871}
- m_MasterData:
- m_Owner: {fileID: 0}
- m_Value:
- m_Type:
- m_SerializableType:
- m_SerializableObject:
- m_Space: -1
- m_Property:
- name: y
- m_serializedType:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614877
-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: f780aa281814f9842a7c076d436932e7, type: 3}
- m_Name:
- m_EditorClassIdentifier: Unity.VisualEffectGraph.Editor::UnityEditor.VFX.VFXSlotFloat
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614874}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614871}
- m_MasterData:
- m_Owner: {fileID: 0}
- m_Value:
- m_Type:
- m_SerializableType:
- m_SerializableObject:
- m_Space: -1
- m_Property:
- name: z
- m_serializedType:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614878
-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: ac39bd03fca81b849929b9c966f1836a, type: 3}
- m_Name:
- m_EditorClassIdentifier: Unity.VisualEffectGraph.Editor::UnityEditor.VFX.VFXSlotFloat3
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614873}
- m_Children:
- - {fileID: 8926484042661614879}
- - {fileID: 8926484042661614880}
- - {fileID: 8926484042661614881}
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614871}
- m_MasterData:
- m_Owner: {fileID: 0}
- m_Value:
- m_Type:
- m_SerializableType:
- m_SerializableObject:
- m_Space: -1
- m_Property:
- name: angles
- m_serializedType:
- m_SerializableType: UnityEngine.Vector3, UnityEngine.CoreModule, Version=0.0.0.0,
- Culture=neutral, PublicKeyToken=null
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614879
-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: f780aa281814f9842a7c076d436932e7, type: 3}
- m_Name:
- m_EditorClassIdentifier: Unity.VisualEffectGraph.Editor::UnityEditor.VFX.VFXSlotFloat
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614878}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614871}
- m_MasterData:
- m_Owner: {fileID: 0}
- m_Value:
- m_Type:
- m_SerializableType:
- m_SerializableObject:
- m_Space: -1
- m_Property:
- name: x
- m_serializedType:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614880
-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: f780aa281814f9842a7c076d436932e7, type: 3}
- m_Name:
- m_EditorClassIdentifier: Unity.VisualEffectGraph.Editor::UnityEditor.VFX.VFXSlotFloat
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614878}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614871}
- m_MasterData:
- m_Owner: {fileID: 0}
- m_Value:
- m_Type:
- m_SerializableType:
- m_SerializableObject:
- m_Space: -1
- m_Property:
- name: y
- m_serializedType:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614881
-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: f780aa281814f9842a7c076d436932e7, type: 3}
- m_Name:
- m_EditorClassIdentifier: Unity.VisualEffectGraph.Editor::UnityEditor.VFX.VFXSlotFloat
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614878}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614871}
- m_MasterData:
- m_Owner: {fileID: 0}
- m_Value:
- m_Type:
- m_SerializableType:
- m_SerializableObject:
- m_Space: -1
- m_Property:
- name: z
- m_serializedType:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614882
-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: ac39bd03fca81b849929b9c966f1836a, type: 3}
- m_Name:
- m_EditorClassIdentifier: Unity.VisualEffectGraph.Editor::UnityEditor.VFX.VFXSlotFloat3
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614873}
- m_Children:
- - {fileID: 8926484042661614883}
- - {fileID: 8926484042661614884}
- - {fileID: 8926484042661614885}
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614871}
- m_MasterData:
- m_Owner: {fileID: 0}
- m_Value:
- m_Type:
- m_SerializableType:
- m_SerializableObject:
- m_Space: -1
- m_Property:
- name: scale
- m_serializedType:
- m_SerializableType: UnityEngine.Vector3, UnityEngine.CoreModule, Version=0.0.0.0,
- Culture=neutral, PublicKeyToken=null
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614883
-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: f780aa281814f9842a7c076d436932e7, type: 3}
- m_Name:
- m_EditorClassIdentifier: Unity.VisualEffectGraph.Editor::UnityEditor.VFX.VFXSlotFloat
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614882}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614871}
- m_MasterData:
- m_Owner: {fileID: 0}
- m_Value:
- m_Type:
- m_SerializableType:
- m_SerializableObject:
- m_Space: -1
- m_Property:
- name: x
- m_serializedType:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614884
-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: f780aa281814f9842a7c076d436932e7, type: 3}
- m_Name:
- m_EditorClassIdentifier: Unity.VisualEffectGraph.Editor::UnityEditor.VFX.VFXSlotFloat
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614882}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614871}
- m_MasterData:
- m_Owner: {fileID: 0}
- m_Value:
- m_Type:
- m_SerializableType:
- m_SerializableObject:
- m_Space: -1
- m_Property:
- name: y
- m_serializedType:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614885
-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: f780aa281814f9842a7c076d436932e7, type: 3}
- m_Name:
- m_EditorClassIdentifier: Unity.VisualEffectGraph.Editor::UnityEditor.VFX.VFXSlotFloat
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614882}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614871}
- m_MasterData:
- m_Owner: {fileID: 0}
- m_Value:
- m_Type:
- m_SerializableType:
- m_SerializableObject:
- m_Space: -1
- m_Property:
- name: z
- m_serializedType:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614886
-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: f780aa281814f9842a7c076d436932e7, type: 3}
- m_Name:
- m_EditorClassIdentifier: Unity.VisualEffectGraph.Editor::UnityEditor.VFX.VFXSlotFloat
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614872}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614871}
- m_MasterData:
- m_Owner: {fileID: 0}
- m_Value:
- m_Type:
- m_SerializableType:
- m_SerializableObject:
- m_Space: -1
- m_Property:
- name: radius
- m_serializedType:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
---- !u!114 &8926484042661614887
-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: f780aa281814f9842a7c076d436932e7, type: 3}
- m_Name:
- m_EditorClassIdentifier: Unity.VisualEffectGraph.Editor::UnityEditor.VFX.VFXSlotFloat
- m_UIIgnoredErrors: []
- m_Parent: {fileID: 8926484042661614871}
- m_Children: []
- m_UIPosition: {x: 0, y: 0}
- m_UICollapsed: 1
- m_UISuperCollapsed: 0
- m_MasterSlot: {fileID: 8926484042661614871}
- m_MasterData:
- m_Owner: {fileID: 0}
- m_Value:
- m_Type:
- m_SerializableType:
- m_SerializableObject:
- m_Space: -1
- m_Property:
- name: arc
- m_serializedType:
- m_SerializableType: System.Single, mscorlib, Version=4.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
- m_Direction: 0
- m_LinkedSlots: []
diff --git a/Assets/_Project/Art/VFX/spells/VFX_slowarea.vfx.meta b/Assets/_Project/Art/VFX/spells/VFX_slowarea.vfx.meta
deleted file mode 100644
index b32e89c..0000000
--- a/Assets/_Project/Art/VFX/spells/VFX_slowarea.vfx.meta
+++ /dev/null
@@ -1,15 +0,0 @@
-fileFormatVersion: 2
-guid: 397a5bed36b1e63a0a3eef17b04f65bd
-VisualEffectImporter:
- externalObjects: {}
- serializedVersion: 2
- template:
- name:
- category:
- description:
- icon: {instanceID: 0}
- thumbnail: {instanceID: 0}
- useAsTemplate: 0
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Audio/sound_effects.meta b/Assets/_Project/Audio/sound_effects.meta
deleted file mode 100644
index f7e97b8..0000000
--- a/Assets/_Project/Audio/sound_effects.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: d1508e070d3666a4282a219e30ffdbf6
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Audio/sound_effects/FireballExplosion.ogg b/Assets/_Project/Audio/sound_effects/FireballExplosion.ogg
deleted file mode 100644
index 77a3f9b..0000000
--- a/Assets/_Project/Audio/sound_effects/FireballExplosion.ogg
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:2263f258fb816411ff3c118c796cf622bf8327c597c2037f628d5b7dee32ab2a
-size 54249
diff --git a/Assets/_Project/Audio/sound_effects/FireballExplosion.ogg.meta b/Assets/_Project/Audio/sound_effects/FireballExplosion.ogg.meta
deleted file mode 100644
index e3b4013..0000000
--- a/Assets/_Project/Audio/sound_effects/FireballExplosion.ogg.meta
+++ /dev/null
@@ -1,23 +0,0 @@
-fileFormatVersion: 2
-guid: e54448243d0478045b6566412c54e4a7
-AudioImporter:
- externalObjects: {}
- serializedVersion: 8
- defaultSettings:
- serializedVersion: 2
- loadType: 0
- sampleRateSetting: 0
- sampleRateOverride: 44100
- compressionFormat: 1
- quality: 1
- conversionMode: 0
- preloadAudioData: 0
- platformSettingOverrides: {}
- forceToMono: 0
- normalize: 1
- loadInBackground: 0
- ambisonic: 0
- 3D: 1
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Audio/sound_effects/SellSoundEffect.ogg b/Assets/_Project/Audio/sound_effects/SellSoundEffect.ogg
deleted file mode 100644
index 437bd8c..0000000
--- a/Assets/_Project/Audio/sound_effects/SellSoundEffect.ogg
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:6047efab82ffe96cf15c078196b289076daae1875220b99f77596a06e7823808
-size 21467
diff --git a/Assets/_Project/Audio/sound_effects/SellSoundEffect.ogg.meta b/Assets/_Project/Audio/sound_effects/SellSoundEffect.ogg.meta
deleted file mode 100644
index f4b28da..0000000
--- a/Assets/_Project/Audio/sound_effects/SellSoundEffect.ogg.meta
+++ /dev/null
@@ -1,23 +0,0 @@
-fileFormatVersion: 2
-guid: 778b3d5981f4c3045a475794435f43cc
-AudioImporter:
- externalObjects: {}
- serializedVersion: 8
- defaultSettings:
- serializedVersion: 2
- loadType: 0
- sampleRateSetting: 0
- sampleRateOverride: 44100
- compressionFormat: 1
- quality: 1
- conversionMode: 0
- preloadAudioData: 0
- platformSettingOverrides: {}
- forceToMono: 0
- normalize: 1
- loadInBackground: 0
- ambisonic: 0
- 3D: 1
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Audio/sound_effects/TimeSlow.ogg b/Assets/_Project/Audio/sound_effects/TimeSlow.ogg
deleted file mode 100644
index 6540f44..0000000
--- a/Assets/_Project/Audio/sound_effects/TimeSlow.ogg
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:e4d6c70f74ae50b15bcbcf1b53cdf63f521f671b119aa6c32ea9a41c9e07e563
-size 30845
diff --git a/Assets/_Project/Audio/sound_effects/TimeSlow.ogg.meta b/Assets/_Project/Audio/sound_effects/TimeSlow.ogg.meta
deleted file mode 100644
index fe80db5..0000000
--- a/Assets/_Project/Audio/sound_effects/TimeSlow.ogg.meta
+++ /dev/null
@@ -1,23 +0,0 @@
-fileFormatVersion: 2
-guid: 038ff3eab9aa0034bac26ccb648ef2a1
-AudioImporter:
- externalObjects: {}
- serializedVersion: 8
- defaultSettings:
- serializedVersion: 2
- loadType: 0
- sampleRateSetting: 0
- sampleRateOverride: 44100
- compressionFormat: 1
- quality: 1
- conversionMode: 0
- preloadAudioData: 0
- platformSettingOverrides: {}
- forceToMono: 0
- normalize: 1
- loadInBackground: 0
- ambisonic: 0
- 3D: 1
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Art/Sprites/EnemyDraftIcons.meta b/Assets/_Project/Definitions/Buffs.meta
similarity index 77%
rename from Assets/_Project/Art/Sprites/EnemyDraftIcons.meta
rename to Assets/_Project/Definitions/Buffs.meta
index 4257bdb..98de849 100644
--- a/Assets/_Project/Art/Sprites/EnemyDraftIcons.meta
+++ b/Assets/_Project/Definitions/Buffs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
-guid: 42ccb4a3b6aaaab47b73147c8be319bb
+guid: eef6329e4294d86c6978ff9453b56956
folderAsset: yes
DefaultImporter:
externalObjects: {}
diff --git a/Assets/_Project/Definitions/RunDefinitions/EnemyScalingConfig.asset b/Assets/_Project/Definitions/Buffs/ExtraDamage25.asset
similarity index 51%
rename from Assets/_Project/Definitions/RunDefinitions/EnemyScalingConfig.asset
rename to Assets/_Project/Definitions/Buffs/ExtraDamage25.asset
index 509a039..c707eae 100644
--- a/Assets/_Project/Definitions/RunDefinitions/EnemyScalingConfig.asset
+++ b/Assets/_Project/Definitions/Buffs/ExtraDamage25.asset
@@ -9,9 +9,9 @@ MonoBehaviour:
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
- m_Script: {fileID: 11500000, guid: de25a6333a11d074280b5ade557aa8b9, type: 3}
- m_Name: EnemyScalingConfig
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyScalingConfig
- BaseHpBudget: 5000
- GrowthPerEncounter: 1.18
- BossHpMultiplier: 1.2
+ 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/BuilderSpells/SlowAreaUpgradedSpell.asset.meta b/Assets/_Project/Definitions/Buffs/ExtraDamage25.asset.meta
similarity index 79%
rename from Assets/_Project/Definitions/BuilderSpells/SlowAreaUpgradedSpell.asset.meta
rename to Assets/_Project/Definitions/Buffs/ExtraDamage25.asset.meta
index 16266b6..b240b6c 100644
--- a/Assets/_Project/Definitions/BuilderSpells/SlowAreaUpgradedSpell.asset.meta
+++ b/Assets/_Project/Definitions/Buffs/ExtraDamage25.asset.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
-guid: 364edf6abc3c31e33b53d0a01142b8ab
+guid: 9ec170e540b1f91cca762dcebe81a856
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
diff --git a/Assets/_Project/Definitions/Buffs/OffensiveBuffs.asset b/Assets/_Project/Definitions/Buffs/OffensiveBuffs.asset
new file mode 100644
index 0000000..737c5ab
--- /dev/null
+++ b/Assets/_Project/Definitions/Buffs/OffensiveBuffs.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: 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/RunDefinitions/Draft/EnemyDraft/EnemyUpgrade_Flight.asset.meta b/Assets/_Project/Definitions/Buffs/OffensiveBuffs.asset.meta
similarity index 79%
rename from Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft/EnemyUpgrade_Flight.asset.meta
rename to Assets/_Project/Definitions/Buffs/OffensiveBuffs.asset.meta
index c49dfc4..ced41c2 100644
--- a/Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft/EnemyUpgrade_Flight.asset.meta
+++ b/Assets/_Project/Definitions/Buffs/OffensiveBuffs.asset.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
-guid: 80fb851b702ff304183cb6e89c4aa245
+guid: d8ed3b9535538f7fc82be878cc307d26
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
diff --git a/Assets/_Project/Definitions/BuilderSpells/FireballSpell.asset b/Assets/_Project/Definitions/BuilderSpells/FireballSpell.asset
index e713412..5f45c46 100644
--- a/Assets/_Project/Definitions/BuilderSpells/FireballSpell.asset
+++ b/Assets/_Project/Definitions/BuilderSpells/FireballSpell.asset
@@ -14,19 +14,18 @@ MonoBehaviour:
m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.BuilderSpells.FireballSpellDefinition
DisplayName: Fireball
Description: Shoot a fireball
- Icon: {fileID: 21300000, guid: eff45e5e3dab24f438c054a095d34578, type: 3}
- Cooldown: 15
+ Icon: {fileID: 0}
+ Cooldown: 5
TargetType: 0
- Radius: 5
+ Radius: 3
enemyLayerMask:
serializedVersion: 2
m_Bits: 1024
impactVfxPrefab: {fileID: 158073311046191680, guid: 80eaad6d0b024835482b64625b63053c, type: 3}
- impactVfxLifetime: 3
- impactDelay: 0.9
+ impactVfxLifetime: 2
impactSound:
- clip: {fileID: 8300000, guid: e54448243d0478045b6566412c54e4a7, type: 3}
- volume: 0.603
+ clip: {fileID: 8300000, guid: 4ee1b63f27ea04451802bafd35ecef89, type: 3}
+ volume: 0.5
minPitch: 1.048
maxPitch: 0.969
- Damage: 500
+ Damage: 50
diff --git a/Assets/_Project/Definitions/BuilderSpells/SlowAreaSpell.asset b/Assets/_Project/Definitions/BuilderSpells/SlowAreaSpell.asset
index cb2746a..74694ff 100644
--- a/Assets/_Project/Definitions/BuilderSpells/SlowAreaSpell.asset
+++ b/Assets/_Project/Definitions/BuilderSpells/SlowAreaSpell.asset
@@ -14,18 +14,12 @@ MonoBehaviour:
m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.BuilderSpells.SlowAreaSpellDefinition
DisplayName: Slow Area
Description: Slows enemies in an area
- Icon: {fileID: 21300000, guid: 5f5072d8c9f626c4690a7b231c7488f9, type: 3}
- Cooldown: 10
- TargetType: 1
+ Icon: {fileID: 0}
+ Cooldown: 5
+ TargetType: 0
Radius: 5
enemyLayerMask:
serializedVersion: 2
- m_Bits: 1152
- areaVfxPrefab: {fileID: 1462673130280185047, guid: bf2f1f2a9a54e9162b80673e3f7eeaf6, type: 3}
- areaSound:
- clip: {fileID: 8300000, guid: 038ff3eab9aa0034bac26ccb648ef2a1, type: 3}
- volume: 0.632
- minPitch: 1.04
- maxPitch: 0.96
+ m_Bits: 1024
SlowFactor: 0.5
- EffectDuration: 8
+ EffectDuration: 3
diff --git a/Assets/_Project/Definitions/BuilderSpells/SlowAreaUpgradedSpell.asset b/Assets/_Project/Definitions/BuilderSpells/SlowAreaUpgradedSpell.asset
deleted file mode 100644
index 90d9579..0000000
--- a/Assets/_Project/Definitions/BuilderSpells/SlowAreaUpgradedSpell.asset
+++ /dev/null
@@ -1,31 +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: 37cc60a4a6e53e24cb6d1c0be310868a, type: 3}
- m_Name: SlowAreaUpgradedSpell
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.BuilderSpells.SlowAreaUpgradedSpellDefinition
- DisplayName: Slow Area II
- Description: Upgraded Slow Area. Slows for 50% longer.
- Icon: {fileID: 21300000, guid: 5f5072d8c9f626c4690a7b231c7488f9, type: 3}
- Cooldown: 10
- TargetType: 1
- Radius: 5
- enemyLayerMask:
- serializedVersion: 2
- m_Bits: 1152
- areaVfxPrefab: {fileID: 1462673130280185047, guid: bf2f1f2a9a54e9162b80673e3f7eeaf6, type: 3}
- areaSound:
- clip: {fileID: 8300000, guid: 038ff3eab9aa0034bac26ccb648ef2a1, type: 3}
- volume: 0.632
- minPitch: 1.04
- maxPitch: 0.96
- SlowFactor: 0.5
- EffectDuration: 12
diff --git a/Assets/_Project/Definitions/RunDefinitions/Draft.meta b/Assets/_Project/Definitions/Draft.meta
similarity index 100%
rename from Assets/_Project/Definitions/RunDefinitions/Draft.meta
rename to Assets/_Project/Definitions/Draft.meta
diff --git a/Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft/Draft_BuilderEffect_GoldPerKill.asset b/Assets/_Project/Definitions/Draft/Draft_BuilderEffect_GoldPerKill.asset
similarity index 100%
rename from Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft/Draft_BuilderEffect_GoldPerKill.asset
rename to Assets/_Project/Definitions/Draft/Draft_BuilderEffect_GoldPerKill.asset
diff --git a/Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft/Draft_BuilderEffect_GoldPerKill.asset.meta b/Assets/_Project/Definitions/Draft/Draft_BuilderEffect_GoldPerKill.asset.meta
similarity index 100%
rename from Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft/Draft_BuilderEffect_GoldPerKill.asset.meta
rename to Assets/_Project/Definitions/Draft/Draft_BuilderEffect_GoldPerKill.asset.meta
diff --git a/Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft/Draft_BuilderSpellOption_Fireball.asset b/Assets/_Project/Definitions/Draft/Draft_BuilderSpellOption_Fireball.asset
similarity index 100%
rename from Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft/Draft_BuilderSpellOption_Fireball.asset
rename to Assets/_Project/Definitions/Draft/Draft_BuilderSpellOption_Fireball.asset
diff --git a/Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft/Draft_BuilderSpellOption_Fireball.asset.meta b/Assets/_Project/Definitions/Draft/Draft_BuilderSpellOption_Fireball.asset.meta
similarity index 100%
rename from Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft/Draft_BuilderSpellOption_Fireball.asset.meta
rename to Assets/_Project/Definitions/Draft/Draft_BuilderSpellOption_Fireball.asset.meta
diff --git a/Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft/Draft_BuilderSpellOption_SlowArea.asset b/Assets/_Project/Definitions/Draft/Draft_BuilderSpellOption_SlowArea.asset
similarity index 100%
rename from Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft/Draft_BuilderSpellOption_SlowArea.asset
rename to Assets/_Project/Definitions/Draft/Draft_BuilderSpellOption_SlowArea.asset
diff --git a/Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft/Draft_BuilderSpellOption_SlowArea.asset.meta b/Assets/_Project/Definitions/Draft/Draft_BuilderSpellOption_SlowArea.asset.meta
similarity index 100%
rename from Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft/Draft_BuilderSpellOption_SlowArea.asset.meta
rename to Assets/_Project/Definitions/Draft/Draft_BuilderSpellOption_SlowArea.asset.meta
diff --git a/Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft/Draft_NewTower_SiegeCannon.asset b/Assets/_Project/Definitions/Draft/Draft_NewTower_SiegeCannon.asset
similarity index 100%
rename from Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft/Draft_NewTower_SiegeCannon.asset
rename to Assets/_Project/Definitions/Draft/Draft_NewTower_SiegeCannon.asset
diff --git a/Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft/Draft_NewTower_SiegeCannon.asset.meta b/Assets/_Project/Definitions/Draft/Draft_NewTower_SiegeCannon.asset.meta
similarity index 100%
rename from Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft/Draft_NewTower_SiegeCannon.asset.meta
rename to Assets/_Project/Definitions/Draft/Draft_NewTower_SiegeCannon.asset.meta
diff --git a/Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft/Draft_NewTower_TeslaCoil.asset b/Assets/_Project/Definitions/Draft/Draft_NewTower_TeslaCoil.asset
similarity index 100%
rename from Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft/Draft_NewTower_TeslaCoil.asset
rename to Assets/_Project/Definitions/Draft/Draft_NewTower_TeslaCoil.asset
diff --git a/Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft/Draft_NewTower_TeslaCoil.asset.meta b/Assets/_Project/Definitions/Draft/Draft_NewTower_TeslaCoil.asset.meta
similarity index 100%
rename from Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft/Draft_NewTower_TeslaCoil.asset.meta
rename to Assets/_Project/Definitions/Draft/Draft_NewTower_TeslaCoil.asset.meta
diff --git a/Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft/Draft_NewTower_Wall.asset b/Assets/_Project/Definitions/Draft/Draft_NewTower_Wall.asset
similarity index 100%
rename from Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft/Draft_NewTower_Wall.asset
rename to Assets/_Project/Definitions/Draft/Draft_NewTower_Wall.asset
diff --git a/Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft/Draft_NewTower_Wall.asset.meta b/Assets/_Project/Definitions/Draft/Draft_NewTower_Wall.asset.meta
similarity index 100%
rename from Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft/Draft_NewTower_Wall.asset.meta
rename to Assets/_Project/Definitions/Draft/Draft_NewTower_Wall.asset.meta
diff --git a/Assets/_Project/Definitions/Enemies/01_Enemy_CrystalGolemBlue_Definition.asset b/Assets/_Project/Definitions/Enemies/01_Enemy_CrystalGolemBlue_Definition.asset
index 39b2af6..5a7e5c4 100644
--- a/Assets/_Project/Definitions/Enemies/01_Enemy_CrystalGolemBlue_Definition.asset
+++ b/Assets/_Project/Definitions/Enemies/01_Enemy_CrystalGolemBlue_Definition.asset
@@ -13,8 +13,8 @@ MonoBehaviour:
m_Name: 01_Enemy_CrystalGolemBlue_Definition
m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyDefinition
DisplayName: Crystal Elemental (Young)
- HpMultiplier: 1.15
- MoveSpeed: 2.6
+ MaxHp: 100
+ MoveSpeed: 3
IsFlying: 0
LivesCost: 1
EnemyPrefab: {fileID: 1455822126534880203, guid: 5839dc36140d67d4db94c4370a98ea2c, type: 3}
diff --git a/Assets/_Project/Definitions/Enemies/02_Enemy_CyclopsGrey_Definition.asset b/Assets/_Project/Definitions/Enemies/02_Enemy_CyclopsGrey_Definition.asset
index 2bad6f2..b616fca 100644
--- a/Assets/_Project/Definitions/Enemies/02_Enemy_CyclopsGrey_Definition.asset
+++ b/Assets/_Project/Definitions/Enemies/02_Enemy_CyclopsGrey_Definition.asset
@@ -13,7 +13,7 @@ MonoBehaviour:
m_Name: 02_Enemy_CyclopsGrey_Definition
m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyDefinition
DisplayName: Cyclops (Young)
- HpMultiplier: 1.05
+ MaxHp: 200
MoveSpeed: 3
IsFlying: 0
LivesCost: 1
diff --git a/Assets/_Project/Definitions/Enemies/03_Enemy_EntGreen_Definition.asset b/Assets/_Project/Definitions/Enemies/03_Enemy_EntGreen_Definition.asset
index 6ca1cb8..48c0a11 100644
--- a/Assets/_Project/Definitions/Enemies/03_Enemy_EntGreen_Definition.asset
+++ b/Assets/_Project/Definitions/Enemies/03_Enemy_EntGreen_Definition.asset
@@ -13,8 +13,8 @@ MonoBehaviour:
m_Name: 03_Enemy_EntGreen_Definition
m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyDefinition
DisplayName: Ent (Juvenile)
- HpMultiplier: 0.85
- MoveSpeed: 3.4
+ MaxHp: 400
+ MoveSpeed: 3
IsFlying: 0
LivesCost: 1
EnemyPrefab: {fileID: 1455822126534880203, guid: dc462b86bdb8136488da56eb84d88ab5, type: 3}
diff --git a/Assets/_Project/Definitions/Enemies/04_Enemy_CrystalGolemYellow_Definition.asset b/Assets/_Project/Definitions/Enemies/04_Enemy_CrystalGolemYellow_Definition.asset
index ed14151..5129b58 100644
--- a/Assets/_Project/Definitions/Enemies/04_Enemy_CrystalGolemYellow_Definition.asset
+++ b/Assets/_Project/Definitions/Enemies/04_Enemy_CrystalGolemYellow_Definition.asset
@@ -13,8 +13,8 @@ MonoBehaviour:
m_Name: 04_Enemy_CrystalGolemYellow_Definition
m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyDefinition
DisplayName: Crystal Elemental (Adolescent)
- HpMultiplier: 1.25
- MoveSpeed: 2.4
+ MaxHp: 500
+ MoveSpeed: 3
IsFlying: 0
LivesCost: 1
EnemyPrefab: {fileID: 1455822126534880203, guid: d4ca85138dd41244c9b0ab29c945f064, type: 3}
diff --git a/Assets/_Project/Definitions/Enemies/05_Enemy_CyclopsGreen_Definition.asset b/Assets/_Project/Definitions/Enemies/05_Enemy_CyclopsGreen_Definition.asset
index 29dbef6..d516ea5 100644
--- a/Assets/_Project/Definitions/Enemies/05_Enemy_CyclopsGreen_Definition.asset
+++ b/Assets/_Project/Definitions/Enemies/05_Enemy_CyclopsGreen_Definition.asset
@@ -13,8 +13,8 @@ MonoBehaviour:
m_Name: 05_Enemy_CyclopsGreen_Definition
m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyDefinition
DisplayName: Cyclops (Adult)
- HpMultiplier: 1.1
- MoveSpeed: 2.9
+ MaxHp: 600
+ MoveSpeed: 3
IsFlying: 0
LivesCost: 1
EnemyPrefab: {fileID: 1455822126534880203, guid: 525e51e0dfac75744b39e4609b9245f9, type: 3}
diff --git a/Assets/_Project/Definitions/Enemies/06_Enemy_EntBlack_Definition.asset b/Assets/_Project/Definitions/Enemies/06_Enemy_EntBlack_Definition.asset
index cd153bb..dc74753 100644
--- a/Assets/_Project/Definitions/Enemies/06_Enemy_EntBlack_Definition.asset
+++ b/Assets/_Project/Definitions/Enemies/06_Enemy_EntBlack_Definition.asset
@@ -13,8 +13,8 @@ MonoBehaviour:
m_Name: 06_Enemy_EntBlack_Definition
m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyDefinition
DisplayName: Ent (Mature)
- HpMultiplier: 0.9
- MoveSpeed: 3.3
+ MaxHp: 700
+ MoveSpeed: 3
IsFlying: 0
LivesCost: 1
EnemyPrefab: {fileID: 1455822126534880203, guid: c3cae952216a68b458b9a934d89b8be8, type: 3}
diff --git a/Assets/_Project/Definitions/Enemies/07_Enemy_CrystalGolemRed_Definition.asset b/Assets/_Project/Definitions/Enemies/07_Enemy_CrystalGolemRed_Definition.asset
index c0b1005..9b8a5b3 100644
--- a/Assets/_Project/Definitions/Enemies/07_Enemy_CrystalGolemRed_Definition.asset
+++ b/Assets/_Project/Definitions/Enemies/07_Enemy_CrystalGolemRed_Definition.asset
@@ -13,8 +13,8 @@ MonoBehaviour:
m_Name: 07_Enemy_CrystalGolemRed_Definition
m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyDefinition
DisplayName: Crystal Elemental (Mature)
- HpMultiplier: 1.35
- MoveSpeed: 2.2
+ MaxHp: 800
+ MoveSpeed: 3
IsFlying: 0
LivesCost: 1
EnemyPrefab: {fileID: 1455822126534880203, guid: 2f13fef371bd3464694526ff99edef03, type: 3}
diff --git a/Assets/_Project/Definitions/Enemies/08_Enemy_CyclopsRed_Definition.asset b/Assets/_Project/Definitions/Enemies/08_Enemy_CyclopsRed_Definition.asset
index a71ed1a..7ca2962 100644
--- a/Assets/_Project/Definitions/Enemies/08_Enemy_CyclopsRed_Definition.asset
+++ b/Assets/_Project/Definitions/Enemies/08_Enemy_CyclopsRed_Definition.asset
@@ -13,8 +13,8 @@ MonoBehaviour:
m_Name: 08_Enemy_CyclopsRed_Definition
m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyDefinition
DisplayName: Cyclops (Enraged)
- HpMultiplier: 1.2
- MoveSpeed: 2.8
+ MaxHp: 900
+ MoveSpeed: 3
IsFlying: 0
LivesCost: 1
EnemyPrefab: {fileID: 1455822126534880203, guid: 729e11f4ecd916d4fbeaf26dbbae8b7d, type: 3}
diff --git a/Assets/_Project/Definitions/Enemies/09_Enemy_EntOrange_Definition.asset b/Assets/_Project/Definitions/Enemies/09_Enemy_EntOrange_Definition.asset
index e2d0b3b..e9d57fa 100644
--- a/Assets/_Project/Definitions/Enemies/09_Enemy_EntOrange_Definition.asset
+++ b/Assets/_Project/Definitions/Enemies/09_Enemy_EntOrange_Definition.asset
@@ -13,8 +13,8 @@ MonoBehaviour:
m_Name: 09_Enemy_EntOrange_Definition
m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyDefinition
DisplayName: Ent (Ancient)
- HpMultiplier: 0.95
- MoveSpeed: 3.2
+ MaxHp: 1000
+ MoveSpeed: 3
IsFlying: 0
LivesCost: 1
EnemyPrefab: {fileID: 1455822126534880203, guid: d6eed12cf96ab194ab8d35359e3bc8ee, type: 3}
diff --git a/Assets/_Project/Definitions/Enemies/10_Enemy_UndeadDrakeBone_Definition.asset b/Assets/_Project/Definitions/Enemies/10_Enemy_UndeadDrakeBone_Definition.asset
index af46c2c..31d1eec 100644
--- a/Assets/_Project/Definitions/Enemies/10_Enemy_UndeadDrakeBone_Definition.asset
+++ b/Assets/_Project/Definitions/Enemies/10_Enemy_UndeadDrakeBone_Definition.asset
@@ -12,9 +12,9 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: c0d2521d49d21fe4380434f5951944d1, type: 3}
m_Name: 10_Enemy_UndeadDrakeBone_Definition
m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyDefinition
- DisplayName: Drake
- HpMultiplier: 0.7
- MoveSpeed: 4.5
+ DisplayName: Ent (Ancient)
+ MaxHp: 800
+ MoveSpeed: 4
IsFlying: 1
FlightHeight: 3
LivesCost: 1
diff --git a/Assets/_Project/Definitions/RunDefinitions/GoldConfig.asset b/Assets/_Project/Definitions/GoldConfig.asset
similarity index 82%
rename from Assets/_Project/Definitions/RunDefinitions/GoldConfig.asset
rename to Assets/_Project/Definitions/GoldConfig.asset
index abf9261..58f29f6 100644
--- a/Assets/_Project/Definitions/RunDefinitions/GoldConfig.asset
+++ b/Assets/_Project/Definitions/GoldConfig.asset
@@ -17,40 +17,44 @@ MonoBehaviour:
- Wave: {fileID: 11400000, guid: 65f66289ea1233b4897f46cd997d9c7a, type: 2}
GoldPerEnemy: 5
CompletionBonus: 25
- NoLeaksBonus: 10
+ NoLeaksBonus: 50
- Wave: {fileID: 11400000, guid: 190e39db44aa0794aa808fd60976f7c4, type: 2}
GoldPerEnemy: 7
CompletionBonus: 30
- NoLeaksBonus: 10
+ NoLeaksBonus: 50
- Wave: {fileID: 11400000, guid: 39921b44a1a0a56478200028940c5202, type: 2}
GoldPerEnemy: 10
CompletionBonus: 35
- NoLeaksBonus: 10
+ NoLeaksBonus: 50
- Wave: {fileID: 11400000, guid: 50f498bc5bfc46e44b064cc96403e2cb, type: 2}
GoldPerEnemy: 15
CompletionBonus: 40
- NoLeaksBonus: 10
+ NoLeaksBonus: 50
- Wave: {fileID: 11400000, guid: 9ee6dee12f0660844b4d46880f01c02f, type: 2}
GoldPerEnemy: 15
CompletionBonus: 45
- NoLeaksBonus: 10
+ NoLeaksBonus: 50
- Wave: {fileID: 11400000, guid: 41231de63e8f25d448f19f3816e0c22f, type: 2}
GoldPerEnemy: 20
CompletionBonus: 50
- NoLeaksBonus: 10
+ NoLeaksBonus: 100
- Wave: {fileID: 11400000, guid: 6290df178cbd3144aa92a0f09833a7be, type: 2}
GoldPerEnemy: 20
CompletionBonus: 55
- NoLeaksBonus: 20
+ NoLeaksBonus: 100
- Wave: {fileID: 11400000, guid: 86736fd52c18fa84e8ced40f30b514fa, type: 2}
GoldPerEnemy: 25
CompletionBonus: 60
- NoLeaksBonus: 20
+ NoLeaksBonus: 100
- Wave: {fileID: 11400000, guid: 8fdf53cfc405a5f41a00f376198b8d84, type: 2}
GoldPerEnemy: 25
CompletionBonus: 65
- NoLeaksBonus: 20
+ NoLeaksBonus: 100
- Wave: {fileID: 11400000, guid: 4db677d2940202340841471f90a5b73a, type: 2}
GoldPerEnemy: 25
CompletionBonus: 70
- NoLeaksBonus: 20
+ NoLeaksBonus: 100
+ - Wave: {fileID: 11400000, guid: fc1a55fab4ce81047b4925f53a3a8b8d, type: 2}
+ GoldPerEnemy: 50
+ CompletionBonus: 75
+ NoLeaksBonus: 500
diff --git a/Assets/_Project/Definitions/RunDefinitions/GoldConfig.asset.meta b/Assets/_Project/Definitions/GoldConfig.asset.meta
similarity index 100%
rename from Assets/_Project/Definitions/RunDefinitions/GoldConfig.asset.meta
rename to Assets/_Project/Definitions/GoldConfig.asset.meta
diff --git a/Assets/_Project/Definitions/RunDefinitions.meta b/Assets/_Project/Definitions/RunDefinitions.meta
deleted file mode 100644
index d07862f..0000000
--- a/Assets/_Project/Definitions/RunDefinitions.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: d1d72155425add944a007932c121c951
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft.meta b/Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft.meta
deleted file mode 100644
index 359e56c..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 465572754aa5c7e48b1c00843f1b5d7c
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft/EnemyUpgrade_Blink.asset b/Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft/EnemyUpgrade_Blink.asset
deleted file mode 100644
index 98bd078..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft/EnemyUpgrade_Blink.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: d075c66b381872a4781b3916296ccdc9, type: 3}
- m_Name: EnemyUpgrade_Blink
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyUpgrades.AbilityEnemyUpgradeOption
- DisplayName: Blink
- Description: Enemies occasionally teleport a short distance ahead.
- Icon: {fileID: 21300000, guid: df5903159b157404d8e92942029f08f7, type: 3}
- Ability: {fileID: 11400000, guid: 030f10c9a264e544ea3e0f4fdb4171bd, type: 2}
diff --git a/Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft/EnemyUpgrade_Blink.asset.meta b/Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft/EnemyUpgrade_Blink.asset.meta
deleted file mode 100644
index 6570ea6..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft/EnemyUpgrade_Blink.asset.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: eef28ca7b8844504e9d75ff903a3bb61
-NativeFormatImporter:
- externalObjects: {}
- mainObjectFileID: 11400000
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft/EnemyUpgrade_DoubleUp.asset b/Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft/EnemyUpgrade_DoubleUp.asset
deleted file mode 100644
index daa0979..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft/EnemyUpgrade_DoubleUp.asset
+++ /dev/null
@@ -1,19 +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: f5e90fe6b8e439e4f99769a6923c2262, type: 3}
- m_Name: EnemyUpgrade_DoubleUp
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyUpgrades.DoubleUpEnemyUpgradeOption
- DisplayName: Double Dip
- Description: Enemies from this wave gain no upgrades, but enemies from the next
- wave have two upgrades selected automatically
- Icon: {fileID: 21300000, guid: e81c6ebff6b98ac44b884d3006611ae6, type: 3}
- BuffsToAutoApply: 2
diff --git a/Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft/EnemyUpgrade_DoubleUp.asset.meta b/Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft/EnemyUpgrade_DoubleUp.asset.meta
deleted file mode 100644
index db74d57..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft/EnemyUpgrade_DoubleUp.asset.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 46e2a6a59c0a3034f9fa44681d9ba422
-NativeFormatImporter:
- externalObjects: {}
- mainObjectFileID: 11400000
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft/EnemyUpgrade_Flight.asset b/Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft/EnemyUpgrade_Flight.asset
deleted file mode 100644
index 5379694..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft/EnemyUpgrade_Flight.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: d075c66b381872a4781b3916296ccdc9, type: 3}
- m_Name: EnemyUpgrade_Flight
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyUpgrades.AbilityEnemyUpgradeOption
- DisplayName: Flight
- Description: Enemies gain the power of flight
- Icon: {fileID: 21300000, guid: 39ac5850677a092479550234de13e868, type: 3}
- Ability: {fileID: 11400000, guid: cb2ec6bb70f57d0429b99b7cd4c7db73, type: 2}
diff --git a/Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft/EnemyUpgrade_NoBounty.asset b/Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft/EnemyUpgrade_NoBounty.asset
deleted file mode 100644
index 58af079..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft/EnemyUpgrade_NoBounty.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: d075c66b381872a4781b3916296ccdc9, type: 3}
- m_Name: EnemyUpgrade_NoBounty
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyUpgrades.AbilityEnemyUpgradeOption
- DisplayName: Peniless
- Description: Enemies no longer drop gold upon death
- Icon: {fileID: 21300000, guid: ea9802d37489c9442ba2ffd4a41c6cb2, type: 3}
- Ability: {fileID: 11400000, guid: c64c7121b0456bb4a873acec9e0abf91, type: 2}
diff --git a/Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft/EnemyUpgrade_NoBounty.asset.meta b/Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft/EnemyUpgrade_NoBounty.asset.meta
deleted file mode 100644
index bea9c35..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft/EnemyUpgrade_NoBounty.asset.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: a67aa3644d2ad4847a47040f53fea26f
-NativeFormatImporter:
- externalObjects: {}
- mainObjectFileID: 11400000
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft/EnemyUpgrade_Split.asset b/Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft/EnemyUpgrade_Split.asset
deleted file mode 100644
index 2351102..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft/EnemyUpgrade_Split.asset
+++ /dev/null
@@ -1,19 +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: d075c66b381872a4781b3916296ccdc9, type: 3}
- m_Name: EnemyUpgrade_Split
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyUpgrades.AbilityEnemyUpgradeOption
- DisplayName: Split on Death
- Description: Enemies killed will spawn two more smaller, faster, weaker versions
- of themselves. (This only triggers a single time per base enemy)
- Icon: {fileID: 21300000, guid: 43490fdb6e2477643a275956ce5f4596, type: 3}
- Ability: {fileID: 11400000, guid: 0305468bc8f912d429a8b20bf23f947c, type: 2}
diff --git a/Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft/EnemyUpgrade_Split.asset.meta b/Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft/EnemyUpgrade_Split.asset.meta
deleted file mode 100644
index ca2e9c2..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft/EnemyUpgrade_Split.asset.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: f6bfb033a15b6d0419587b80a8286bd1
-NativeFormatImporter:
- externalObjects: {}
- mainObjectFileID: 11400000
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft/EnemyUpgrade_Theft.asset b/Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft/EnemyUpgrade_Theft.asset
deleted file mode 100644
index e0f0515..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft/EnemyUpgrade_Theft.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: d075c66b381872a4781b3916296ccdc9, type: 3}
- m_Name: EnemyUpgrade_Theft
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyUpgrades.AbilityEnemyUpgradeOption
- DisplayName: Thief
- Description: Enemies that leak steal 15 gold on their way out
- Icon: {fileID: 21300000, guid: 315be3feae908714a911f596b8533317, type: 3}
- Ability: {fileID: 11400000, guid: a18691a20d75f17479a83b3e171759e7, type: 2}
diff --git a/Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft/EnemyUpgrade_Theft.asset.meta b/Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft/EnemyUpgrade_Theft.asset.meta
deleted file mode 100644
index f4847ca..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/Draft/EnemyDraft/EnemyUpgrade_Theft.asset.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 37d761966a349ba4cb4141f913224d5c
-NativeFormatImporter:
- externalObjects: {}
- mainObjectFileID: 11400000
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft.meta b/Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft.meta
deleted file mode 100644
index 18e2f50..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 4a6f4d6ffa10ef44da3ac0dd7c7734db
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft/Draft_BuilderSpellOption_SlowAreaUpgrade.asset b/Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft/Draft_BuilderSpellOption_SlowAreaUpgrade.asset
deleted file mode 100644
index 743c46d..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft/Draft_BuilderSpellOption_SlowAreaUpgrade.asset
+++ /dev/null
@@ -1,20 +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: 7b1c5b562f39c72eea3bafd0165af078, type: 3}
- m_Name: Draft_BuilderSpellOption_SlowAreaUpgrade
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.Draft.BuilderSpellUpgradeDraftOption
- DisplayName: Slow Area II
- Description: Slows enemies in the area for 50% longer than the default slow area.
- Icon: {fileID: 0}
- Weight: 1
- BaseKind: 1
- UpgradedKind: 2
diff --git a/Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft/Draft_BuilderSpellOption_SlowAreaUpgrade.asset.meta b/Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft/Draft_BuilderSpellOption_SlowAreaUpgrade.asset.meta
deleted file mode 100644
index 13ca9fd..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft/Draft_BuilderSpellOption_SlowAreaUpgrade.asset.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: d180030b68a9a5e1da497bf6f83451d4
-NativeFormatImporter:
- externalObjects: {}
- mainObjectFileID: 11400000
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft/Draft_TowerUpgrade_Arrow_1.asset b/Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft/Draft_TowerUpgrade_Arrow_1.asset
deleted file mode 100644
index 016e045..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft/Draft_TowerUpgrade_Arrow_1.asset
+++ /dev/null
@@ -1,21 +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: aeec22f152a57ef5798eae035c091543, type: 3}
- m_Name: Draft_TowerUpgrade_Arrow_1
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.Draft.TowerUpgradeDraftOption
- DisplayName: Arrow Tower Upgrade 1
- Description: Unlock the ability to upgrade Arrow Towers with more damage and a
- faster attack speed
- Icon: {fileID: 21300000, guid: a140f62a10b8dc04e84625f8008e0c24, type: 3}
- Weight: 1
- BaseTower: {fileID: 11400000, guid: 0f693e29ca953e1439e10cb8f12e4b30, type: 2}
- UpgradedTower: {fileID: 11400000, guid: 86d4c3ab00d4c654eb4c255280ee5701, type: 2}
diff --git a/Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft/Draft_TowerUpgrade_Arrow_1.asset.meta b/Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft/Draft_TowerUpgrade_Arrow_1.asset.meta
deleted file mode 100644
index 1009def..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/Draft/PlayerDraft/Draft_TowerUpgrade_Arrow_1.asset.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 0b9f4905752751f44b9959b84b620266
-NativeFormatImporter:
- externalObjects: {}
- mainObjectFileID: 11400000
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Definitions/RunDefinitions/EnemyAbilities.meta b/Assets/_Project/Definitions/RunDefinitions/EnemyAbilities.meta
deleted file mode 100644
index d927cf3..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/EnemyAbilities.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 37cbc8b63ce7ae2e4b4abcfd7274bee7
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Definitions/RunDefinitions/EnemyAbilities/BlinkAbility.asset b/Assets/_Project/Definitions/RunDefinitions/EnemyAbilities/BlinkAbility.asset
deleted file mode 100644
index ec1b7b5..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/EnemyAbilities/BlinkAbility.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: 4fbb09f55384ac44399ac4655b2b53ec, type: 3}
- m_Name: BlinkAbility
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyAbilities.BlinkAbilityDefinition
- DisplayName: Blink
- Description: Enemies occasionally teleport a short distance ahead.
- IntervalSeconds: 8
- BlinkDistanceMeters: 4
diff --git a/Assets/_Project/Definitions/RunDefinitions/EnemyAbilities/BlinkAbility.asset.meta b/Assets/_Project/Definitions/RunDefinitions/EnemyAbilities/BlinkAbility.asset.meta
deleted file mode 100644
index 78c2c0b..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/EnemyAbilities/BlinkAbility.asset.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 030f10c9a264e544ea3e0f4fdb4171bd
-NativeFormatImporter:
- externalObjects: {}
- mainObjectFileID: 11400000
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Definitions/RunDefinitions/EnemyAbilities/FlightAbility.asset b/Assets/_Project/Definitions/RunDefinitions/EnemyAbilities/FlightAbility.asset
deleted file mode 100644
index 725338e..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/EnemyAbilities/FlightAbility.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: be77d353621f07e40bbdab2e9cc37f51, type: 3}
- m_Name: FlightAbility
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyAbilities.FlightAbilityDefinition
- DisplayName: Flight
- Description: Enemies gain flying
- FlightHeight: 3
- SpeedMultiplier: 1
diff --git a/Assets/_Project/Definitions/RunDefinitions/EnemyAbilities/FlightAbility.asset.meta b/Assets/_Project/Definitions/RunDefinitions/EnemyAbilities/FlightAbility.asset.meta
deleted file mode 100644
index 0b5d052..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/EnemyAbilities/FlightAbility.asset.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: cb2ec6bb70f57d0429b99b7cd4c7db73
-NativeFormatImporter:
- externalObjects: {}
- mainObjectFileID: 11400000
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Definitions/RunDefinitions/EnemyAbilities/GoldTheftAbility.asset b/Assets/_Project/Definitions/RunDefinitions/EnemyAbilities/GoldTheftAbility.asset
deleted file mode 100644
index fc16c66..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/EnemyAbilities/GoldTheftAbility.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: 3b770ce042b78f44d9a3df537fe25db0, type: 3}
- m_Name: GoldTheftAbility
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyAbilities.GoldTheftAbilityDefinition
- DisplayName: Thief
- Description: Enemies that leak out of a player's maze steal 15 gold as they go.
- GoldStolen: 15
diff --git a/Assets/_Project/Definitions/RunDefinitions/EnemyAbilities/GoldTheftAbility.asset.meta b/Assets/_Project/Definitions/RunDefinitions/EnemyAbilities/GoldTheftAbility.asset.meta
deleted file mode 100644
index 11247d1..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/EnemyAbilities/GoldTheftAbility.asset.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: a18691a20d75f17479a83b3e171759e7
-NativeFormatImporter:
- externalObjects: {}
- mainObjectFileID: 11400000
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Definitions/RunDefinitions/EnemyAbilities/NoBountyAbility.asset b/Assets/_Project/Definitions/RunDefinitions/EnemyAbilities/NoBountyAbility.asset
deleted file mode 100644
index 0231479..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/EnemyAbilities/NoBountyAbility.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: fd5b12948b24e9c4c8840ef3470b437e, type: 3}
- m_Name: NoBountyAbility
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyAbilities.NoBountyAbilityDefinition
- DisplayName: Penniless
- Description: Enemies no longer drop gold on death
- BountyMultiplier: 0
diff --git a/Assets/_Project/Definitions/RunDefinitions/EnemyAbilities/NoBountyAbility.asset.meta b/Assets/_Project/Definitions/RunDefinitions/EnemyAbilities/NoBountyAbility.asset.meta
deleted file mode 100644
index 8aede82..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/EnemyAbilities/NoBountyAbility.asset.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: c64c7121b0456bb4a873acec9e0abf91
-NativeFormatImporter:
- externalObjects: {}
- mainObjectFileID: 11400000
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Definitions/RunDefinitions/EnemyAbilities/SplitOnDeathAbility.asset b/Assets/_Project/Definitions/RunDefinitions/EnemyAbilities/SplitOnDeathAbility.asset
deleted file mode 100644
index 6ca1f9e..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/EnemyAbilities/SplitOnDeathAbility.asset
+++ /dev/null
@@ -1,20 +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: 21543cb1b54f0b16e9c80584f68988b3, type: 3}
- m_Name: SplitOnDeathAbility
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyAbilities.SplitOnDeathAbilityDefinition
- DisplayName: Split on Death
- Description: The enemy splits when it dies.
- Weight: 100
- SplitInto: {fileID: 11400000, guid: a05af37e2256f164d80a66cc2a582466, type: 2}
- SplitCount: 2
- ScatterRadius: 0.5
diff --git a/Assets/_Project/Definitions/RunDefinitions/EnemyAbilities/SplitOnDeathAbility.asset.meta b/Assets/_Project/Definitions/RunDefinitions/EnemyAbilities/SplitOnDeathAbility.asset.meta
deleted file mode 100644
index fae7041..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/EnemyAbilities/SplitOnDeathAbility.asset.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 0305468bc8f912d429a8b20bf23f947c
-NativeFormatImporter:
- externalObjects: {}
- mainObjectFileID: 11400000
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Definitions/RunDefinitions/EnemyScalingConfig.asset.meta b/Assets/_Project/Definitions/RunDefinitions/EnemyScalingConfig.asset.meta
deleted file mode 100644
index 377b8a5..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/EnemyScalingConfig.asset.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: ae9fde0b564e20440ac2e20861fa1ca6
-NativeFormatImporter:
- externalObjects: {}
- mainObjectFileID: 11400000
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Definitions/RunDefinitions/RunStructure.meta b/Assets/_Project/Definitions/RunDefinitions/RunStructure.meta
deleted file mode 100644
index 5a3abf7..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/RunStructure.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 11ba3723e03690b47a4f2518bfc68a62
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Definitions/RunDefinitions/RunStructure/EnemyUpgradeGroup_Phase1_Basics.asset b/Assets/_Project/Definitions/RunDefinitions/RunStructure/EnemyUpgradeGroup_Phase1_Basics.asset
deleted file mode 100644
index 8691b9a..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/RunStructure/EnemyUpgradeGroup_Phase1_Basics.asset
+++ /dev/null
@@ -1,28 +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: 4202a767d96e70f41b6a7e84eb08a832, type: 3}
- m_Name: EnemyUpgradeGroup_Phase1_Basics
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyUpgrades.EnemyUpgradeGroup
- DisplayName: Upgrade Group Phase 1
- Options:
- - Option: {fileID: 11400000, guid: eef28ca7b8844504e9d75ff903a3bb61, type: 2}
- Weight: 1
- - Option: {fileID: 11400000, guid: 46e2a6a59c0a3034f9fa44681d9ba422, type: 2}
- Weight: 1
- - Option: {fileID: 11400000, guid: 80fb851b702ff304183cb6e89c4aa245, type: 2}
- Weight: 1
- - Option: {fileID: 11400000, guid: a67aa3644d2ad4847a47040f53fea26f, type: 2}
- Weight: 1
- - Option: {fileID: 11400000, guid: f6bfb033a15b6d0419587b80a8286bd1, type: 2}
- Weight: 1
- - Option: {fileID: 11400000, guid: 37d761966a349ba4cb4141f913224d5c, type: 2}
- Weight: 1
diff --git a/Assets/_Project/Definitions/RunDefinitions/RunStructure/EnemyUpgradeGroup_Phase1_Basics.asset.meta b/Assets/_Project/Definitions/RunDefinitions/RunStructure/EnemyUpgradeGroup_Phase1_Basics.asset.meta
deleted file mode 100644
index 04f87c2..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/RunStructure/EnemyUpgradeGroup_Phase1_Basics.asset.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 3dfcbf5323c0a184c84f6b9e80e37257
-NativeFormatImporter:
- externalObjects: {}
- mainObjectFileID: 11400000
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Definitions/RunDefinitions/RunStructure/Phase1.asset b/Assets/_Project/Definitions/RunDefinitions/RunStructure/Phase1.asset
deleted file mode 100644
index 34c811a..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/RunStructure/Phase1.asset
+++ /dev/null
@@ -1,21 +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: 8f73fcf0261654d44a6d0939bdc9b88f, type: 3}
- m_Name: Phase1
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.Waves.PhaseDefinition
- DisplayName: Phase 1
- WaveGroups:
- - {fileID: 11400000, guid: 80eaa5ec98fd5df4e950131a8984fd31, type: 2}
- BossGroups:
- - {fileID: 11400000, guid: 2981ffffb36dfc440aa4517c17cfbbf4, type: 2}
- EnemyUpgradeGroups:
- - {fileID: 11400000, guid: 3dfcbf5323c0a184c84f6b9e80e37257, type: 2}
diff --git a/Assets/_Project/Definitions/RunDefinitions/RunStructure/Phase1.asset.meta b/Assets/_Project/Definitions/RunDefinitions/RunStructure/Phase1.asset.meta
deleted file mode 100644
index 7fea787..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/RunStructure/Phase1.asset.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 1d4f04f995c4c3b44be6e5f9d0f6e3f8
-NativeFormatImporter:
- externalObjects: {}
- mainObjectFileID: 11400000
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Definitions/RunDefinitions/RunStructure/Test_Run_Definition.asset b/Assets/_Project/Definitions/RunDefinitions/RunStructure/Test_Run_Definition.asset
deleted file mode 100644
index c77b032..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/RunStructure/Test_Run_Definition.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: 2b73bb9876b7fdb4480b13ff6bb8bd67, type: 3}
- m_Name: Test_Run_Definition
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.Waves.RunDefinition
- WavesPerCycle: 5
- CyclesPerPhase: 3
- Phases:
- - {fileID: 11400000, guid: 1d4f04f995c4c3b44be6e5f9d0f6e3f8, type: 2}
diff --git a/Assets/_Project/Definitions/RunDefinitions/RunStructure/Test_Run_Definition.asset.meta b/Assets/_Project/Definitions/RunDefinitions/RunStructure/Test_Run_Definition.asset.meta
deleted file mode 100644
index bcf6074..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/RunStructure/Test_Run_Definition.asset.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 1bb81a966192c344d94cce2628c1832b
-NativeFormatImporter:
- externalObjects: {}
- mainObjectFileID: 11400000
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Definitions/RunDefinitions/RunStructure/WaveGroup_Phase1_Bosses.asset b/Assets/_Project/Definitions/RunDefinitions/RunStructure/WaveGroup_Phase1_Bosses.asset
deleted file mode 100644
index 2e739af..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/RunStructure/WaveGroup_Phase1_Bosses.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: 5625bd25f378bda429666820a4940cee, type: 3}
- m_Name: WaveGroup_Phase1_Bosses
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.Waves.WaveGroup
- DisplayName: Phase 1 Bosses
- Waves:
- - Wave: {fileID: 11400000, guid: 4db677d2940202340841471f90a5b73a, type: 2}
- Weight: 1
diff --git a/Assets/_Project/Definitions/RunDefinitions/RunStructure/WaveGroup_Phase1_Bosses.asset.meta b/Assets/_Project/Definitions/RunDefinitions/RunStructure/WaveGroup_Phase1_Bosses.asset.meta
deleted file mode 100644
index 174e980..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/RunStructure/WaveGroup_Phase1_Bosses.asset.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 2981ffffb36dfc440aa4517c17cfbbf4
-NativeFormatImporter:
- externalObjects: {}
- mainObjectFileID: 11400000
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Definitions/RunDefinitions/RunStructure/WaveGroup_Phase1_Normal.asset b/Assets/_Project/Definitions/RunDefinitions/RunStructure/WaveGroup_Phase1_Normal.asset
deleted file mode 100644
index 265c492..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/RunStructure/WaveGroup_Phase1_Normal.asset
+++ /dev/null
@@ -1,34 +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: 5625bd25f378bda429666820a4940cee, type: 3}
- m_Name: WaveGroup_Phase1_Normal
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.Waves.WaveGroup
- DisplayName:
- Waves:
- - Wave: {fileID: 11400000, guid: 65f66289ea1233b4897f46cd997d9c7a, type: 2}
- Weight: 1
- - Wave: {fileID: 11400000, guid: 190e39db44aa0794aa808fd60976f7c4, type: 2}
- Weight: 1
- - Wave: {fileID: 11400000, guid: 39921b44a1a0a56478200028940c5202, type: 2}
- Weight: 1
- - Wave: {fileID: 11400000, guid: 50f498bc5bfc46e44b064cc96403e2cb, type: 2}
- Weight: 1
- - Wave: {fileID: 11400000, guid: 9ee6dee12f0660844b4d46880f01c02f, type: 2}
- Weight: 0.5
- - Wave: {fileID: 11400000, guid: 41231de63e8f25d448f19f3816e0c22f, type: 2}
- Weight: 0.5
- - Wave: {fileID: 11400000, guid: 6290df178cbd3144aa92a0f09833a7be, type: 2}
- Weight: 0.5
- - Wave: {fileID: 11400000, guid: 86736fd52c18fa84e8ced40f30b514fa, type: 2}
- Weight: 0.5
- - Wave: {fileID: 11400000, guid: 8fdf53cfc405a5f41a00f376198b8d84, type: 2}
- Weight: 0.5
diff --git a/Assets/_Project/Definitions/RunDefinitions/RunStructure/WaveGroup_Phase1_Normal.asset.meta b/Assets/_Project/Definitions/RunDefinitions/RunStructure/WaveGroup_Phase1_Normal.asset.meta
deleted file mode 100644
index c2de13d..0000000
--- a/Assets/_Project/Definitions/RunDefinitions/RunStructure/WaveGroup_Phase1_Normal.asset.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 80eaa5ec98fd5df4e950131a8984fd31
-NativeFormatImporter:
- externalObjects: {}
- mainObjectFileID: 11400000
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Definitions/Towers/BasicArrowTower.asset b/Assets/_Project/Definitions/Towers/BasicArrowTower.asset
index d39d8d9..3a1c5c4 100644
--- a/Assets/_Project/Definitions/Towers/BasicArrowTower.asset
+++ b/Assets/_Project/Definitions/Towers/BasicArrowTower.asset
@@ -17,12 +17,8 @@ MonoBehaviour:
backbone of any maze.
FootprintSize: {x: 2, y: 2}
GoldCost: 25
- SellRefundPercent: 0.75
- FullRefundIfUnupgraded: 0
BuildTime: 0.5
TowerPrefab: {fileID: 6482414459531823157, guid: 1511641f145758b469e64376d2a0d434, type: 3}
- ConstructionPhases: {fileID: 0}
- Icon: {fileID: 21300000, guid: afe216737d1b46e783889299bda1c252, type: 3}
DamageType: 0
TargetPriority: 0
TargetType: 0
@@ -38,5 +34,5 @@ MonoBehaviour:
EffectDuration: 0
ProjectilePrefab: {fileID: 2664719039363295382, guid: dc2e4a4108e03874a8b2dab88dcc8fba, type: 3}
ProjectileSpeed: 15
- UpgradePaths:
- - {fileID: 11400000, guid: 86d4c3ab00d4c654eb4c255280ee5701, type: 2}
+ UpgradePaths: []
+ Icon: {fileID: 21300000, guid: afe216737d1b46e783889299bda1c252, type: 3}
diff --git a/Assets/_Project/Definitions/Towers/BasicArrowTower_Upgrade_1.asset b/Assets/_Project/Definitions/Towers/BasicArrowTower_Upgrade_1.asset
deleted file mode 100644
index 226afff..0000000
--- a/Assets/_Project/Definitions/Towers/BasicArrowTower_Upgrade_1.asset
+++ /dev/null
@@ -1,41 +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: 7b353a757b6e6774d97e6fb8ba138fcc, type: 3}
- m_Name: BasicArrowTower_Upgrade_1
- m_EditorClassIdentifier: Assembly-CSharp::TD.Towers.TowerDefinition
- DisplayName: Basic Arrow Tower
- Description: Single-target tower that fires arrows at ground and air enemies. The
- backbone of any maze.
- FootprintSize: {x: 2, y: 2}
- GoldCost: 50
- SellRefundPercent: 0.75
- FullRefundIfUnupgraded: 0
- BuildTime: 1.5
- TowerPrefab: {fileID: 6482414459531823157, guid: 1511641f145758b469e64376d2a0d434, type: 3}
- ConstructionPhases: {fileID: 0}
- Icon: {fileID: 21300000, guid: a140f62a10b8dc04e84625f8008e0c24, type: 3}
- DamageType: 0
- TargetPriority: 0
- TargetType: 0
- GroundedOnly: 0
- Damage: 15
- Range: 20
- AttacksPerSecond: 6
- SplashRadius: 0
- ChainCount: 0
- ChainRange: 0
- SlowFactor: 0
- DotDamagePerSecond: 0
- EffectDuration: 0
- ProjectilePrefab: {fileID: 2664719039363295382, guid: dc2e4a4108e03874a8b2dab88dcc8fba, type: 3}
- ProjectileSpeed: 15
- UpgradePaths: []
diff --git a/Assets/_Project/Definitions/Towers/BasicArrowTower_Upgrade_1.asset.meta b/Assets/_Project/Definitions/Towers/BasicArrowTower_Upgrade_1.asset.meta
deleted file mode 100644
index facefe3..0000000
--- a/Assets/_Project/Definitions/Towers/BasicArrowTower_Upgrade_1.asset.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 86d4c3ab00d4c654eb4c255280ee5701
-NativeFormatImporter:
- externalObjects: {}
- mainObjectFileID: 11400000
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Definitions/RunDefinitions/Waves.meta b/Assets/_Project/Definitions/Waves.meta
similarity index 100%
rename from Assets/_Project/Definitions/RunDefinitions/Waves.meta
rename to Assets/_Project/Definitions/Waves.meta
diff --git a/Assets/_Project/Definitions/RunDefinitions/Waves/Wave10Definition.asset b/Assets/_Project/Definitions/Waves/Wave10Definition.asset
similarity index 93%
rename from Assets/_Project/Definitions/RunDefinitions/Waves/Wave10Definition.asset
rename to Assets/_Project/Definitions/Waves/Wave10Definition.asset
index 1bc8617..eb49ceb 100644
--- a/Assets/_Project/Definitions/RunDefinitions/Waves/Wave10Definition.asset
+++ b/Assets/_Project/Definitions/Waves/Wave10Definition.asset
@@ -13,7 +13,7 @@ MonoBehaviour:
m_Name: Wave10Definition
m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.WaveDefinition
PrepTime: 10
- ReleaseInterval: 2
Entries:
- EnemyType: {fileID: 11400000, guid: 1d9809612c00ed049848a955613a4619, type: 2}
- Count: 5
+ Count: 50
+ SpawnInterval: 0.5
diff --git a/Assets/_Project/Definitions/RunDefinitions/Waves/Wave10Definition.asset.meta b/Assets/_Project/Definitions/Waves/Wave10Definition.asset.meta
similarity index 100%
rename from Assets/_Project/Definitions/RunDefinitions/Waves/Wave10Definition.asset.meta
rename to Assets/_Project/Definitions/Waves/Wave10Definition.asset.meta
diff --git a/Assets/_Project/Definitions/RunDefinitions/Waves/Wave1Definition.asset b/Assets/_Project/Definitions/Waves/Wave1Definition.asset
similarity index 100%
rename from Assets/_Project/Definitions/RunDefinitions/Waves/Wave1Definition.asset
rename to Assets/_Project/Definitions/Waves/Wave1Definition.asset
diff --git a/Assets/_Project/Definitions/RunDefinitions/Waves/Wave1Definition.asset.meta b/Assets/_Project/Definitions/Waves/Wave1Definition.asset.meta
similarity index 100%
rename from Assets/_Project/Definitions/RunDefinitions/Waves/Wave1Definition.asset.meta
rename to Assets/_Project/Definitions/Waves/Wave1Definition.asset.meta
diff --git a/Assets/_Project/Definitions/RunDefinitions/Waves/Wave2Definition.asset b/Assets/_Project/Definitions/Waves/Wave2Definition.asset
similarity index 100%
rename from Assets/_Project/Definitions/RunDefinitions/Waves/Wave2Definition.asset
rename to Assets/_Project/Definitions/Waves/Wave2Definition.asset
diff --git a/Assets/_Project/Definitions/RunDefinitions/Waves/Wave2Definition.asset.meta b/Assets/_Project/Definitions/Waves/Wave2Definition.asset.meta
similarity index 100%
rename from Assets/_Project/Definitions/RunDefinitions/Waves/Wave2Definition.asset.meta
rename to Assets/_Project/Definitions/Waves/Wave2Definition.asset.meta
diff --git a/Assets/_Project/Definitions/RunDefinitions/Waves/Wave3Definition.asset b/Assets/_Project/Definitions/Waves/Wave3Definition.asset
similarity index 100%
rename from Assets/_Project/Definitions/RunDefinitions/Waves/Wave3Definition.asset
rename to Assets/_Project/Definitions/Waves/Wave3Definition.asset
diff --git a/Assets/_Project/Definitions/RunDefinitions/Waves/Wave3Definition.asset.meta b/Assets/_Project/Definitions/Waves/Wave3Definition.asset.meta
similarity index 100%
rename from Assets/_Project/Definitions/RunDefinitions/Waves/Wave3Definition.asset.meta
rename to Assets/_Project/Definitions/Waves/Wave3Definition.asset.meta
diff --git a/Assets/_Project/Definitions/RunDefinitions/Waves/Wave4Definition.asset b/Assets/_Project/Definitions/Waves/Wave4Definition.asset
similarity index 100%
rename from Assets/_Project/Definitions/RunDefinitions/Waves/Wave4Definition.asset
rename to Assets/_Project/Definitions/Waves/Wave4Definition.asset
diff --git a/Assets/_Project/Definitions/RunDefinitions/Waves/Wave4Definition.asset.meta b/Assets/_Project/Definitions/Waves/Wave4Definition.asset.meta
similarity index 100%
rename from Assets/_Project/Definitions/RunDefinitions/Waves/Wave4Definition.asset.meta
rename to Assets/_Project/Definitions/Waves/Wave4Definition.asset.meta
diff --git a/Assets/_Project/Definitions/RunDefinitions/Waves/Wave5Definition.asset b/Assets/_Project/Definitions/Waves/Wave5Definition.asset
similarity index 100%
rename from Assets/_Project/Definitions/RunDefinitions/Waves/Wave5Definition.asset
rename to Assets/_Project/Definitions/Waves/Wave5Definition.asset
diff --git a/Assets/_Project/Definitions/RunDefinitions/Waves/Wave5Definition.asset.meta b/Assets/_Project/Definitions/Waves/Wave5Definition.asset.meta
similarity index 100%
rename from Assets/_Project/Definitions/RunDefinitions/Waves/Wave5Definition.asset.meta
rename to Assets/_Project/Definitions/Waves/Wave5Definition.asset.meta
diff --git a/Assets/_Project/Definitions/RunDefinitions/Waves/Wave6Definition.asset b/Assets/_Project/Definitions/Waves/Wave6Definition.asset
similarity index 100%
rename from Assets/_Project/Definitions/RunDefinitions/Waves/Wave6Definition.asset
rename to Assets/_Project/Definitions/Waves/Wave6Definition.asset
diff --git a/Assets/_Project/Definitions/RunDefinitions/Waves/Wave6Definition.asset.meta b/Assets/_Project/Definitions/Waves/Wave6Definition.asset.meta
similarity index 100%
rename from Assets/_Project/Definitions/RunDefinitions/Waves/Wave6Definition.asset.meta
rename to Assets/_Project/Definitions/Waves/Wave6Definition.asset.meta
diff --git a/Assets/_Project/Definitions/RunDefinitions/Waves/Wave7Definition.asset b/Assets/_Project/Definitions/Waves/Wave7Definition.asset
similarity index 100%
rename from Assets/_Project/Definitions/RunDefinitions/Waves/Wave7Definition.asset
rename to Assets/_Project/Definitions/Waves/Wave7Definition.asset
diff --git a/Assets/_Project/Definitions/RunDefinitions/Waves/Wave7Definition.asset.meta b/Assets/_Project/Definitions/Waves/Wave7Definition.asset.meta
similarity index 100%
rename from Assets/_Project/Definitions/RunDefinitions/Waves/Wave7Definition.asset.meta
rename to Assets/_Project/Definitions/Waves/Wave7Definition.asset.meta
diff --git a/Assets/_Project/Definitions/RunDefinitions/Waves/Wave8Definition.asset b/Assets/_Project/Definitions/Waves/Wave8Definition.asset
similarity index 100%
rename from Assets/_Project/Definitions/RunDefinitions/Waves/Wave8Definition.asset
rename to Assets/_Project/Definitions/Waves/Wave8Definition.asset
diff --git a/Assets/_Project/Definitions/RunDefinitions/Waves/Wave8Definition.asset.meta b/Assets/_Project/Definitions/Waves/Wave8Definition.asset.meta
similarity index 100%
rename from Assets/_Project/Definitions/RunDefinitions/Waves/Wave8Definition.asset.meta
rename to Assets/_Project/Definitions/Waves/Wave8Definition.asset.meta
diff --git a/Assets/_Project/Definitions/RunDefinitions/Waves/Wave9Definition.asset b/Assets/_Project/Definitions/Waves/Wave9Definition.asset
similarity index 100%
rename from Assets/_Project/Definitions/RunDefinitions/Waves/Wave9Definition.asset
rename to Assets/_Project/Definitions/Waves/Wave9Definition.asset
diff --git a/Assets/_Project/Definitions/RunDefinitions/Waves/Wave9Definition.asset.meta b/Assets/_Project/Definitions/Waves/Wave9Definition.asset.meta
similarity index 100%
rename from Assets/_Project/Definitions/RunDefinitions/Waves/Wave9Definition.asset.meta
rename to Assets/_Project/Definitions/Waves/Wave9Definition.asset.meta
diff --git a/Assets/_Project/Prefabs/Enemies/Enemy_CrystalGolem_Blue.prefab b/Assets/_Project/Prefabs/Enemies/Enemy_CrystalGolem_Blue.prefab
index b4613dd..546fd71 100644
--- a/Assets/_Project/Prefabs/Enemies/Enemy_CrystalGolem_Blue.prefab
+++ b/Assets/_Project/Prefabs/Enemies/Enemy_CrystalGolem_Blue.prefab
@@ -17,7 +17,6 @@ GameObject:
- component: {fileID: 3756613737869398948}
- component: {fileID: 6036713598566100742}
- component: {fileID: -1575784752119152190}
- - component: {fileID: 154042129325690845}
m_Layer: 10
m_Name: Enemy_CrystalGolem_Blue
m_TagString: Untagged
@@ -126,7 +125,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyHealth
ShowTopMostFoldoutHeaderGroup: 1
- definition: {fileID: 11400000, guid: a05af37e2256f164d80a66cc2a582466, type: 2}
+ definition: {fileID: 0}
deathAnimator: {fileID: 1819643351848917197}
deathAnimationDuration: 1.5
sinkDuration: 1.5
@@ -225,19 +224,6 @@ MonoBehaviour:
volume: 0.75
minPitch: 0.95
maxPitch: 1.05
---- !u!114 &154042129325690845
-MonoBehaviour:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 1455822126534880203}
- m_Enabled: 1
- m_EditorHideFlags: 0
- m_Script: {fileID: 11500000, guid: 9d1a26a32e8969cd29bce6010126a5f6, type: 3}
- m_Name:
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyAbility
- ShowTopMostFoldoutHeaderGroup: 1
--- !u!1 &5361867751622119598
GameObject:
m_ObjectHideFlags: 0
diff --git a/Assets/_Project/Prefabs/Enemies/Enemy_CrystalGolem_Red.prefab b/Assets/_Project/Prefabs/Enemies/Enemy_CrystalGolem_Red.prefab
index 4f54da4..f183f6e 100644
--- a/Assets/_Project/Prefabs/Enemies/Enemy_CrystalGolem_Red.prefab
+++ b/Assets/_Project/Prefabs/Enemies/Enemy_CrystalGolem_Red.prefab
@@ -16,7 +16,6 @@ GameObject:
- component: {fileID: 3283904430289710888}
- component: {fileID: 3756613737869398948}
- component: {fileID: 6036713598566100742}
- - component: {fileID: 7515565462997402737}
m_Layer: 10
m_Name: Enemy_CrystalGolem_Red
m_TagString: Untagged
@@ -125,7 +124,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyHealth
ShowTopMostFoldoutHeaderGroup: 1
- definition: {fileID: 11400000, guid: edc686813aef43743bb15d356b511dff, type: 2}
+ definition: {fileID: 0}
deathAnimator: {fileID: 8642767442976997525}
deathAnimationDuration: 1.5
sinkDuration: 1.5
@@ -203,19 +202,6 @@ CapsuleCollider:
m_Height: 2
m_Direction: 1
m_Center: {x: 0, y: 1, z: 0}
---- !u!114 &7515565462997402737
-MonoBehaviour:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 1455822126534880203}
- m_Enabled: 1
- m_EditorHideFlags: 0
- m_Script: {fileID: 11500000, guid: 9d1a26a32e8969cd29bce6010126a5f6, type: 3}
- m_Name:
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyAbility
- ShowTopMostFoldoutHeaderGroup: 1
--- !u!1 &5361867751622119598
GameObject:
m_ObjectHideFlags: 0
diff --git a/Assets/_Project/Prefabs/Enemies/Enemy_CrystalGolem_Yellow.prefab b/Assets/_Project/Prefabs/Enemies/Enemy_CrystalGolem_Yellow.prefab
index fb8210c..3144e35 100644
--- a/Assets/_Project/Prefabs/Enemies/Enemy_CrystalGolem_Yellow.prefab
+++ b/Assets/_Project/Prefabs/Enemies/Enemy_CrystalGolem_Yellow.prefab
@@ -16,7 +16,6 @@ GameObject:
- component: {fileID: 3283904430289710888}
- component: {fileID: 3756613737869398948}
- component: {fileID: 6036713598566100742}
- - component: {fileID: -5265869660159140516}
m_Layer: 10
m_Name: Enemy_CrystalGolem_Yellow
m_TagString: Untagged
@@ -125,7 +124,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyHealth
ShowTopMostFoldoutHeaderGroup: 1
- definition: {fileID: 11400000, guid: fe31ccfc7a429b24fb8f57279ba96c95, type: 2}
+ definition: {fileID: 0}
deathAnimator: {fileID: 7317567842465160949}
deathAnimationDuration: 1.5
sinkDuration: 1.5
@@ -203,19 +202,6 @@ CapsuleCollider:
m_Height: 2
m_Direction: 1
m_Center: {x: 0, y: 1, z: 0}
---- !u!114 &-5265869660159140516
-MonoBehaviour:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 1455822126534880203}
- m_Enabled: 1
- m_EditorHideFlags: 0
- m_Script: {fileID: 11500000, guid: 9d1a26a32e8969cd29bce6010126a5f6, type: 3}
- m_Name:
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyAbility
- ShowTopMostFoldoutHeaderGroup: 1
--- !u!1 &5361867751622119598
GameObject:
m_ObjectHideFlags: 0
diff --git a/Assets/_Project/Prefabs/Enemies/Enemy_Cyclops_Green.prefab b/Assets/_Project/Prefabs/Enemies/Enemy_Cyclops_Green.prefab
index e11218f..dfb934a 100644
--- a/Assets/_Project/Prefabs/Enemies/Enemy_Cyclops_Green.prefab
+++ b/Assets/_Project/Prefabs/Enemies/Enemy_Cyclops_Green.prefab
@@ -17,7 +17,6 @@ GameObject:
- component: {fileID: 3756613737869398948}
- component: {fileID: 6036713598566100742}
- component: {fileID: -4695266156693656620}
- - component: {fileID: -7800194977638654489}
m_Layer: 10
m_Name: Enemy_Cyclops_Green
m_TagString: Untagged
@@ -126,7 +125,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyHealth
ShowTopMostFoldoutHeaderGroup: 1
- definition: {fileID: 11400000, guid: 21c69bd4b2c55cd4fa9cd9b551c6e3fe, type: 2}
+ definition: {fileID: 0}
deathAnimator: {fileID: 6910253555362952583}
deathAnimationDuration: 1.5
sinkDuration: 1.5
@@ -216,20 +215,9 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 9df6fea9a86daafae998f8eecc44ee7d, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::TD.Combat.EnemyDeathSound
- deathSounds: []
---- !u!114 &-7800194977638654489
-MonoBehaviour:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 1455822126534880203}
- m_Enabled: 1
- m_EditorHideFlags: 0
- m_Script: {fileID: 11500000, guid: 9d1a26a32e8969cd29bce6010126a5f6, type: 3}
- m_Name:
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyAbility
- ShowTopMostFoldoutHeaderGroup: 1
+ deathClips:
+ - {fileID: 8300000, guid: e007e19b827137ce395021d825927dcc, type: 3}
+ - {fileID: 8300000, guid: 6cea30689732db2f0a8989fe8f1a07a9, type: 3}
--- !u!1 &5361867751622119598
GameObject:
m_ObjectHideFlags: 0
diff --git a/Assets/_Project/Prefabs/Enemies/Enemy_Cyclops_Grey.prefab b/Assets/_Project/Prefabs/Enemies/Enemy_Cyclops_Grey.prefab
index d943a11..055ef54 100644
--- a/Assets/_Project/Prefabs/Enemies/Enemy_Cyclops_Grey.prefab
+++ b/Assets/_Project/Prefabs/Enemies/Enemy_Cyclops_Grey.prefab
@@ -16,7 +16,6 @@ GameObject:
- component: {fileID: 3283904430289710888}
- component: {fileID: 3756613737869398948}
- component: {fileID: 6036713598566100742}
- - component: {fileID: -5218620239817511880}
m_Layer: 10
m_Name: Enemy_Cyclops_Grey
m_TagString: Untagged
@@ -125,7 +124,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyHealth
ShowTopMostFoldoutHeaderGroup: 1
- definition: {fileID: 11400000, guid: 52d9af0c680bca64a86d66fce2a73668, type: 2}
+ definition: {fileID: 0}
deathAnimator: {fileID: 5079596453572633521}
deathAnimationDuration: 1.5
sinkDuration: 1.5
@@ -203,19 +202,6 @@ CapsuleCollider:
m_Height: 2
m_Direction: 1
m_Center: {x: 0, y: 1, z: 0}
---- !u!114 &-5218620239817511880
-MonoBehaviour:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 1455822126534880203}
- m_Enabled: 1
- m_EditorHideFlags: 0
- m_Script: {fileID: 11500000, guid: 9d1a26a32e8969cd29bce6010126a5f6, type: 3}
- m_Name:
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyAbility
- ShowTopMostFoldoutHeaderGroup: 1
--- !u!1 &5361867751622119598
GameObject:
m_ObjectHideFlags: 0
diff --git a/Assets/_Project/Prefabs/Enemies/Enemy_Cyclops_Red.prefab b/Assets/_Project/Prefabs/Enemies/Enemy_Cyclops_Red.prefab
index 06a8723..5a0e904 100644
--- a/Assets/_Project/Prefabs/Enemies/Enemy_Cyclops_Red.prefab
+++ b/Assets/_Project/Prefabs/Enemies/Enemy_Cyclops_Red.prefab
@@ -16,7 +16,6 @@ GameObject:
- component: {fileID: 3283904430289710888}
- component: {fileID: 3756613737869398948}
- component: {fileID: 6036713598566100742}
- - component: {fileID: -7009934292190590155}
m_Layer: 10
m_Name: Enemy_Cyclops_Red
m_TagString: Untagged
@@ -125,7 +124,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyHealth
ShowTopMostFoldoutHeaderGroup: 1
- definition: {fileID: 11400000, guid: 4b981d8b44098894197507508f655072, type: 2}
+ definition: {fileID: 0}
deathAnimator: {fileID: 162562342023078655}
deathAnimationDuration: 1.5
sinkDuration: 1.5
@@ -203,19 +202,6 @@ CapsuleCollider:
m_Height: 2
m_Direction: 1
m_Center: {x: 0, y: 1, z: 0}
---- !u!114 &-7009934292190590155
-MonoBehaviour:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 1455822126534880203}
- m_Enabled: 1
- m_EditorHideFlags: 0
- m_Script: {fileID: 11500000, guid: 9d1a26a32e8969cd29bce6010126a5f6, type: 3}
- m_Name:
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyAbility
- ShowTopMostFoldoutHeaderGroup: 1
--- !u!1 &5361867751622119598
GameObject:
m_ObjectHideFlags: 0
diff --git a/Assets/_Project/Prefabs/Enemies/Enemy_Ent_Black.prefab b/Assets/_Project/Prefabs/Enemies/Enemy_Ent_Black.prefab
index 829e743..0e12a01 100644
--- a/Assets/_Project/Prefabs/Enemies/Enemy_Ent_Black.prefab
+++ b/Assets/_Project/Prefabs/Enemies/Enemy_Ent_Black.prefab
@@ -16,7 +16,6 @@ GameObject:
- component: {fileID: 3283904430289710888}
- component: {fileID: 3756613737869398948}
- component: {fileID: 6036713598566100742}
- - component: {fileID: 3080180597758813642}
m_Layer: 10
m_Name: Enemy_Ent_Black
m_TagString: Untagged
@@ -125,7 +124,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyHealth
ShowTopMostFoldoutHeaderGroup: 1
- definition: {fileID: 11400000, guid: bed87a08deeb7e048ba06c8268b0f959, type: 2}
+ definition: {fileID: 0}
deathAnimator: {fileID: 6938810086909213800}
deathAnimationDuration: 1.5
sinkDuration: 1.5
@@ -203,19 +202,6 @@ CapsuleCollider:
m_Height: 2
m_Direction: 1
m_Center: {x: 0, y: 1, z: 0}
---- !u!114 &3080180597758813642
-MonoBehaviour:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 1455822126534880203}
- m_Enabled: 1
- m_EditorHideFlags: 0
- m_Script: {fileID: 11500000, guid: 9d1a26a32e8969cd29bce6010126a5f6, type: 3}
- m_Name:
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyAbility
- ShowTopMostFoldoutHeaderGroup: 1
--- !u!1 &5361867751622119598
GameObject:
m_ObjectHideFlags: 0
diff --git a/Assets/_Project/Prefabs/Enemies/Enemy_Ent_Green.prefab b/Assets/_Project/Prefabs/Enemies/Enemy_Ent_Green.prefab
index b37096d..eeaca4c 100644
--- a/Assets/_Project/Prefabs/Enemies/Enemy_Ent_Green.prefab
+++ b/Assets/_Project/Prefabs/Enemies/Enemy_Ent_Green.prefab
@@ -16,7 +16,6 @@ GameObject:
- component: {fileID: 3283904430289710888}
- component: {fileID: 3756613737869398948}
- component: {fileID: 6036713598566100742}
- - component: {fileID: -2371779262468771447}
m_Layer: 10
m_Name: Enemy_Ent_Green
m_TagString: Untagged
@@ -125,7 +124,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyHealth
ShowTopMostFoldoutHeaderGroup: 1
- definition: {fileID: 11400000, guid: 77f4545654c5b7b41b2760a580da6068, type: 2}
+ definition: {fileID: 0}
deathAnimator: {fileID: 2830498994423139172}
deathAnimationDuration: 1.5
sinkDuration: 1.5
@@ -203,19 +202,6 @@ CapsuleCollider:
m_Height: 2
m_Direction: 1
m_Center: {x: 0, y: 1, z: 0}
---- !u!114 &-2371779262468771447
-MonoBehaviour:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 1455822126534880203}
- m_Enabled: 1
- m_EditorHideFlags: 0
- m_Script: {fileID: 11500000, guid: 9d1a26a32e8969cd29bce6010126a5f6, type: 3}
- m_Name:
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyAbility
- ShowTopMostFoldoutHeaderGroup: 1
--- !u!1 &5361867751622119598
GameObject:
m_ObjectHideFlags: 0
diff --git a/Assets/_Project/Prefabs/Enemies/Enemy_Ent_Orange.prefab b/Assets/_Project/Prefabs/Enemies/Enemy_Ent_Orange.prefab
index 063f5d2..9b366bb 100644
--- a/Assets/_Project/Prefabs/Enemies/Enemy_Ent_Orange.prefab
+++ b/Assets/_Project/Prefabs/Enemies/Enemy_Ent_Orange.prefab
@@ -16,7 +16,6 @@ GameObject:
- component: {fileID: 3283904430289710888}
- component: {fileID: 3756613737869398948}
- component: {fileID: 6036713598566100742}
- - component: {fileID: 2883007611589499530}
m_Layer: 10
m_Name: Enemy_Ent_Orange
m_TagString: Untagged
@@ -125,7 +124,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyHealth
ShowTopMostFoldoutHeaderGroup: 1
- definition: {fileID: 11400000, guid: 097d7ba78591cd443a06d637e0822a46, type: 2}
+ definition: {fileID: 0}
deathAnimator: {fileID: 471366370285235598}
deathAnimationDuration: 1.5
sinkDuration: 1.5
@@ -203,19 +202,6 @@ CapsuleCollider:
m_Height: 2
m_Direction: 1
m_Center: {x: 0, y: 1, z: 0}
---- !u!114 &2883007611589499530
-MonoBehaviour:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 1455822126534880203}
- m_Enabled: 1
- m_EditorHideFlags: 0
- m_Script: {fileID: 11500000, guid: 9d1a26a32e8969cd29bce6010126a5f6, type: 3}
- m_Name:
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyAbility
- ShowTopMostFoldoutHeaderGroup: 1
--- !u!1 &5361867751622119598
GameObject:
m_ObjectHideFlags: 0
diff --git a/Assets/_Project/Prefabs/Enemies/Enemy_UndeadDrake_Bone.prefab b/Assets/_Project/Prefabs/Enemies/Enemy_UndeadDrake_Bone.prefab
index 7994b3a..8acc7db 100644
--- a/Assets/_Project/Prefabs/Enemies/Enemy_UndeadDrake_Bone.prefab
+++ b/Assets/_Project/Prefabs/Enemies/Enemy_UndeadDrake_Bone.prefab
@@ -16,7 +16,6 @@ GameObject:
- component: {fileID: 3283904430289710888}
- component: {fileID: 3756613737869398948}
- component: {fileID: 6036713598566100742}
- - component: {fileID: 5647470509798016640}
m_Layer: 10
m_Name: Enemy_UndeadDrake_Bone
m_TagString: Untagged
@@ -125,7 +124,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyHealth
ShowTopMostFoldoutHeaderGroup: 1
- definition: {fileID: 11400000, guid: 1d9809612c00ed049848a955613a4619, type: 2}
+ definition: {fileID: 0}
deathAnimator: {fileID: 3345626123719039665}
deathAnimationDuration: 1.5
sinkDuration: 1.5
@@ -203,19 +202,6 @@ CapsuleCollider:
m_Height: 2
m_Direction: 1
m_Center: {x: 0, y: 1, z: 0}
---- !u!114 &5647470509798016640
-MonoBehaviour:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 1455822126534880203}
- m_Enabled: 1
- m_EditorHideFlags: 0
- m_Script: {fileID: 11500000, guid: 9d1a26a32e8969cd29bce6010126a5f6, type: 3}
- m_Name:
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyAbility
- ShowTopMostFoldoutHeaderGroup: 1
--- !u!1 &5361867751622119598
GameObject:
m_ObjectHideFlags: 0
diff --git a/Assets/_Project/Prefabs/Player/Player.prefab b/Assets/_Project/Prefabs/Player/Player.prefab
index 240ec88..08614f3 100644
--- a/Assets/_Project/Prefabs/Player/Player.prefab
+++ b/Assets/_Project/Prefabs/Player/Player.prefab
@@ -13,14 +13,11 @@ GameObject:
- component: {fileID: 2918837822014987993}
- component: {fileID: 7845089877743661692}
- component: {fileID: 4336209376377567030}
+ - component: {fileID: 2806524246861401760}
- component: {fileID: 2806524246861401799}
- component: {fileID: 2806524246861401801}
- component: {fileID: 5683786710272852339}
- component: {fileID: 6856236869205671849}
- - component: {fileID: -4438158474568445526}
- - component: {fileID: 2871545571532934715}
- - component: {fileID: 1298503032983562452}
- - component: {fileID: 8755797571419628899}
m_Layer: 0
m_Name: Player
m_TagString: Untagged
@@ -109,6 +106,21 @@ 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}
--- !u!114 &2806524246861401799
MonoBehaviour:
m_ObjectHideFlags: 0
@@ -161,55 +173,3 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.PlayerSpellLoadout
ShowTopMostFoldoutHeaderGroup: 1
---- !u!114 &-4438158474568445526
-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: 65825b3189ae873948659b37b219f5e6, type: 3}
- m_Name:
- m_EditorClassIdentifier: Assembly-CSharp::TD.Dev.DebugCommandRelay
- ShowTopMostFoldoutHeaderGroup: 1
---- !u!114 &2871545571532934715
-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: f60f6928562351f4891142ad59ab6352, type: 3}
- m_Name:
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.PlayerTowerUpgrades
- ShowTopMostFoldoutHeaderGroup: 1
---- !u!114 &1298503032983562452
-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: b7e4c91a08f5d2461a3f8e6c25b09d74, type: 3}
- m_Name:
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.PlayerTowerDeck
- ShowTopMostFoldoutHeaderGroup: 1
---- !u!114 &8755797571419628899
-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: 2b8f4d1e6c3a497051d8e2f7a9c1b063, type: 3}
- m_Name:
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.Draft.PlayerDraft
- ShowTopMostFoldoutHeaderGroup: 1
diff --git a/Assets/_Project/Scenes/Levels/9Player.unity b/Assets/_Project/Scenes/Levels/9Player.unity
index a08f1d5..98a6d5e 100644
--- a/Assets/_Project/Scenes/Levels/9Player.unity
+++ b/Assets/_Project/Scenes/Levels/9Player.unity
@@ -2253,7 +2253,7 @@ MonoBehaviour:
m_EditorClassIdentifier: Assembly-CSharp::TD.Audio.AudioManager
categories:
- category: 0
- maxVoices: 6
+ maxVoices: 4
volume: 0.5
- category: 1
maxVoices: 4
@@ -2276,56 +2276,6 @@ Transform:
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
---- !u!1 &181002639
-GameObject:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- serializedVersion: 6
- m_Component:
- - component: {fileID: 181002641}
- - component: {fileID: 181002640}
- m_Layer: 0
- m_Name: EnemyAbilityPool
- m_TagString: Untagged
- m_Icon: {fileID: 0}
- m_NavMeshLayer: 0
- m_StaticEditorFlags: 0
- m_IsActive: 1
---- !u!114 &181002640
-MonoBehaviour:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 181002639}
- m_Enabled: 1
- m_EditorHideFlags: 0
- m_Script: {fileID: 11500000, guid: 4409eb25a8be555de8dc3a73d4ed95db, type: 3}
- m_Name:
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyAbilities.EnemyAbilityPool
- abilities:
- - {fileID: 11400000, guid: 0305468bc8f912d429a8b20bf23f947c, type: 2}
- - {fileID: 11400000, guid: 030f10c9a264e544ea3e0f4fdb4171bd, type: 2}
- - {fileID: 11400000, guid: cb2ec6bb70f57d0429b99b7cd4c7db73, type: 2}
- - {fileID: 11400000, guid: a18691a20d75f17479a83b3e171759e7, type: 2}
- - {fileID: 11400000, guid: c64c7121b0456bb4a873acec9e0abf91, type: 2}
---- !u!4 &181002641
-Transform:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 181002639}
- serializedVersion: 2
- m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
- m_LocalPosition: {x: 15.94346, y: 0, z: 194.22044}
- m_LocalScale: {x: 1, y: 1, z: 1}
- m_ConstrainProportionsScale: 0
- m_Children: []
- m_Father: {fileID: 0}
- m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!43 &203092944
Mesh:
m_ObjectHideFlags: 0
@@ -11584,57 +11534,6 @@ Mesh:
- serializedVersion: 1
m_IndexStart: 0
m_IndexCount: 0
---- !u!1 &759470733
-GameObject:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- serializedVersion: 6
- m_Component:
- - component: {fileID: 759470735}
- - component: {fileID: 759470734}
- m_Layer: 0
- m_Name: SellEffectSpawner
- m_TagString: Untagged
- m_Icon: {fileID: 0}
- m_NavMeshLayer: 0
- m_StaticEditorFlags: 0
- m_IsActive: 1
---- !u!114 &759470734
-MonoBehaviour:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 759470733}
- m_Enabled: 1
- m_EditorHideFlags: 0
- m_Script: {fileID: 11500000, guid: e2708c6bac8136f45b48c899d3d40630, type: 3}
- m_Name:
- m_EditorClassIdentifier: Assembly-CSharp::TD.VFX.SellEffectSpawner
- coinBurstPrefab: {fileID: 2313150535691989867, guid: 31f8d8c320b42c54d886d610fb13a1e7, type: 3}
- sellSound:
- clip: {fileID: 8300000, guid: 778b3d5981f4c3045a475794435f43cc, type: 3}
- volume: 1
- minPitch: 0.95
- maxPitch: 1.05
- verticalOffset: 0.75
---- !u!4 &759470735
-Transform:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 759470733}
- serializedVersion: 2
- m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
- m_LocalPosition: {x: 35.63582, y: 0.5, z: 163.67152}
- m_LocalScale: {x: 1, y: 1, z: 1}
- m_ConstrainProportionsScale: 0
- m_Children: []
- m_Father: {fileID: 0}
- m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &759520883
GameObject:
m_ObjectHideFlags: 0
@@ -13019,57 +12918,6 @@ BoxCollider:
serializedVersion: 3
m_Size: {x: 24, y: 1, z: 30}
m_Center: {x: 0, y: 0, z: 0}
---- !u!1 &875833597
-GameObject:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- serializedVersion: 6
- m_Component:
- - component: {fileID: 875833599}
- - component: {fileID: 875833598}
- m_Layer: 0
- m_Name: EnemyUpgradePool
- m_TagString: Untagged
- m_Icon: {fileID: 0}
- m_NavMeshLayer: 0
- m_StaticEditorFlags: 0
- m_IsActive: 1
---- !u!114 &875833598
-MonoBehaviour:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 875833597}
- m_Enabled: 1
- m_EditorHideFlags: 0
- m_Script: {fileID: 11500000, guid: c4d98a01d9b9a7b438a6f91b0b7f498f, type: 3}
- m_Name:
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyUpgrades.EnemyUpgradePool
- options:
- - {fileID: 11400000, guid: eef28ca7b8844504e9d75ff903a3bb61, type: 2}
- - {fileID: 11400000, guid: 46e2a6a59c0a3034f9fa44681d9ba422, type: 2}
- - {fileID: 11400000, guid: 80fb851b702ff304183cb6e89c4aa245, type: 2}
- - {fileID: 11400000, guid: a67aa3644d2ad4847a47040f53fea26f, type: 2}
- - {fileID: 11400000, guid: 37d761966a349ba4cb4141f913224d5c, type: 2}
- - {fileID: 11400000, guid: f6bfb033a15b6d0419587b80a8286bd1, type: 2}
---- !u!4 &875833599
-Transform:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 875833597}
- serializedVersion: 2
- m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
- m_LocalPosition: {x: 38.30481, y: 0.5, z: 155.93135}
- m_LocalScale: {x: 1, y: 1, z: 1}
- m_ConstrainProportionsScale: 0
- m_Children: []
- m_Father: {fileID: 0}
- m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &902199259
GameObject:
m_ObjectHideFlags: 0
@@ -13529,7 +13377,6 @@ MonoBehaviour:
spells:
- {fileID: 11400000, guid: 051c48f416e5366b59d49b0e062333ac, type: 2}
- {fileID: 11400000, guid: d893f46b037536b4ba91307e184cecfe, type: 2}
- - {fileID: 11400000, guid: 364edf6abc3c31e33b53d0a01142b8ab, type: 2}
--- !u!4 &914832726
Transform:
m_ObjectHideFlags: 0
@@ -13859,75 +13706,6 @@ Mesh:
- serializedVersion: 1
m_IndexStart: 0
m_IndexCount: 0
---- !u!1 &954434158
-GameObject:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- serializedVersion: 6
- m_Component:
- - component: {fileID: 954434161}
- - component: {fileID: 954434160}
- - component: {fileID: 954434159}
- m_Layer: 0
- m_Name: DebugConsole
- m_TagString: Untagged
- m_Icon: {fileID: 0}
- m_NavMeshLayer: 0
- m_StaticEditorFlags: 0
- m_IsActive: 1
---- !u!114 &954434159
-MonoBehaviour:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 954434158}
- m_Enabled: 1
- m_EditorHideFlags: 0
- m_Script: {fileID: 11500000, guid: 7c6a808e8291552d0b64e36a210879c4, type: 3}
- m_Name:
- m_EditorClassIdentifier: Assembly-CSharp::TD.Dev.DebugConsole
- toggleKey: 4
---- !u!114 &954434160
-MonoBehaviour:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 954434158}
- m_Enabled: 1
- m_EditorHideFlags: 0
- m_Script: {fileID: 19102, guid: 0000000000000000e000000000000000, type: 0}
- m_Name:
- m_EditorClassIdentifier: UnityEngine.dll::UnityEngine.UIElements.UIDocument
- m_PanelSettings: {fileID: 11400000, guid: 6aa0af71585acea4db4995c3931dc946, type: 2}
- m_ParentUI: {fileID: 0}
- sourceAsset: {fileID: 0}
- m_SortingOrder: 1
- m_Position: 0
- m_WorldSpaceSizeMode: 1
- m_WorldSpaceWidth: 1920
- m_WorldSpaceHeight: 1080
- m_PivotReferenceSize: 0
- m_Pivot: 0
- m_WorldSpaceCollider: {fileID: 0}
---- !u!4 &954434161
-Transform:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 954434158}
- serializedVersion: 2
- m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
- m_LocalPosition: {x: -52.51635, y: 0, z: -27.6311}
- m_LocalScale: {x: 1, y: 1, z: 1}
- m_ConstrainProportionsScale: 0
- m_Children: []
- m_Father: {fileID: 0}
- m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &955672358
GameObject:
m_ObjectHideFlags: 0
@@ -13996,78 +13774,6 @@ BoxCollider:
serializedVersion: 3
m_Size: {x: 17, y: 1, z: 13}
m_Center: {x: 0, y: 0, z: 0}
---- !u!1 &961493951
-GameObject:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- serializedVersion: 6
- m_Component:
- - component: {fileID: 961493954}
- - component: {fileID: 961493952}
- - component: {fileID: 961493953}
- m_Layer: 0
- m_Name: RunState
- m_TagString: Untagged
- m_Icon: {fileID: 0}
- m_NavMeshLayer: 0
- m_StaticEditorFlags: 0
- m_IsActive: 1
---- !u!114 &961493952
-MonoBehaviour:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 961493951}
- m_Enabled: 1
- m_EditorHideFlags: 0
- m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
- m_Name:
- m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.NetworkObject
- GlobalObjectIdHash: 3660389945
- InScenePlacedSourceGlobalObjectIdHash: 0
- DeferredDespawnTick: 0
- Ownership: 1
- AlwaysReplicateAsRoot: 0
- SynchronizeTransform: 1
- ActiveSceneSynchronization: 0
- SceneMigrationSynchronization: 0
- SpawnWithObservers: 1
- DontDestroyWithOwner: 0
- AutoObjectParentSync: 1
- SyncOwnerTransformWhenParented: 1
- AllowOwnerToParent: 0
---- !u!114 &961493953
-MonoBehaviour:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 961493951}
- m_Enabled: 1
- m_EditorHideFlags: 0
- m_Script: {fileID: 11500000, guid: a293cf6dca91dd2478c9f4e799cf9e0d, type: 3}
- m_Name:
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.Waves.RunState
- ShowTopMostFoldoutHeaderGroup: 1
- runDefinition: {fileID: 11400000, guid: 1bb81a966192c344d94cce2628c1832b, type: 2}
---- !u!4 &961493954
-Transform:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 961493951}
- serializedVersion: 2
- m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
- m_LocalPosition: {x: 38.30481, y: 0.5, z: 155.93135}
- m_LocalScale: {x: 1, y: 1, z: 1}
- m_ConstrainProportionsScale: 0
- m_Children: []
- m_Father: {fileID: 0}
- m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &993870443
GameObject:
m_ObjectHideFlags: 0
@@ -14648,7 +14354,6 @@ MonoBehaviour:
placementManager: {fileID: 1507514108}
cameraController: {fileID: 1239994223}
rejectionMessageDuration: 2.5
- menuButtonIcon: {fileID: 0}
chatMaxHeight: 280
chatMaxMessages: 2147483647
chatSystemColor: {r: 1, g: 0.7, b: 0.2, a: 1}
@@ -15787,7 +15492,6 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: de7d013503af0f74c950f215f8dae1c0, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.PathfindingService
- recomputeBudgetMs: 1.5
--- !u!4 &1149980841
Transform:
m_ObjectHideFlags: 0
@@ -18434,11 +18138,19 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.WaveManager
ShowTopMostFoldoutHeaderGroup: 1
+ waveDefinitions:
+ - {fileID: 11400000, guid: 65f66289ea1233b4897f46cd997d9c7a, type: 2}
+ - {fileID: 11400000, guid: 190e39db44aa0794aa808fd60976f7c4, type: 2}
+ - {fileID: 11400000, guid: 39921b44a1a0a56478200028940c5202, type: 2}
+ - {fileID: 11400000, guid: 50f498bc5bfc46e44b064cc96403e2cb, type: 2}
+ - {fileID: 11400000, guid: 9ee6dee12f0660844b4d46880f01c02f, type: 2}
+ - {fileID: 11400000, guid: 41231de63e8f25d448f19f3816e0c22f, type: 2}
+ - {fileID: 11400000, guid: 6290df178cbd3144aa92a0f09833a7be, type: 2}
+ - {fileID: 11400000, guid: 86736fd52c18fa84e8ced40f30b514fa, type: 2}
+ - {fileID: 11400000, guid: 8fdf53cfc405a5f41a00f376198b8d84, type: 2}
+ - {fileID: 11400000, guid: 4db677d2940202340841471f90a5b73a, type: 2}
startingLives: 40
- draftTimeSeconds: 30
- voteTimeSeconds: 25
goldConfig: {fileID: 11400000, guid: d5e01c919f14a1e4888ad494a159241b, type: 2}
- enemyScalingConfig: {fileID: 11400000, guid: ae9fde0b564e20440ac2e20861fa1ca6, type: 2}
--- !u!43 &1393870843
Mesh:
m_ObjectHideFlags: 0
@@ -20308,7 +20020,6 @@ MonoBehaviour:
- {fileID: 0}
- {fileID: 11400000, guid: d5b2e8fa3c7e9b5f4a1d0c2e7f6b3a91, type: 2}
- {fileID: 11400000, guid: 0f693e29ca953e1439e10cb8f12e4b30, type: 2}
- - {fileID: 11400000, guid: 86d4c3ab00d4c654eb4c255280ee5701, type: 2}
- {fileID: 11400000, guid: 04219895bbea4fb9a94fd62fef35bf2a, type: 2}
- {fileID: 11400000, guid: e3f1a9c2b5d84e76a0c3f1e8d27b4905, type: 2}
startingDeck:
@@ -23017,7 +22728,6 @@ MonoBehaviour:
m_EditorClassIdentifier: Assembly-CSharp::TD.Dev.DevWaveControls
hotkey: 94
grantTowerHotkey: 101
- skipToBossHotkey: 100
--- !u!4 &1731269687
Transform:
m_ObjectHideFlags: 0
@@ -28077,78 +27787,6 @@ Mesh:
- serializedVersion: 1
m_IndexStart: 0
m_IndexCount: 0
---- !u!1 &2021000666
-GameObject:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- serializedVersion: 6
- m_Component:
- - component: {fileID: 2021000669}
- - component: {fileID: 2021000667}
- - component: {fileID: 2021000668}
- m_Layer: 0
- m_Name: WaveVote
- m_TagString: Untagged
- m_Icon: {fileID: 0}
- m_NavMeshLayer: 0
- m_StaticEditorFlags: 0
- m_IsActive: 1
---- !u!114 &2021000667
-MonoBehaviour:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 2021000666}
- m_Enabled: 1
- m_EditorHideFlags: 0
- m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
- m_Name:
- m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.NetworkObject
- GlobalObjectIdHash: 3520766009
- InScenePlacedSourceGlobalObjectIdHash: 0
- DeferredDespawnTick: 0
- Ownership: 1
- AlwaysReplicateAsRoot: 0
- SynchronizeTransform: 1
- ActiveSceneSynchronization: 0
- SceneMigrationSynchronization: 0
- SpawnWithObservers: 1
- DontDestroyWithOwner: 0
- AutoObjectParentSync: 1
- SyncOwnerTransformWhenParented: 1
- AllowOwnerToParent: 0
---- !u!114 &2021000668
-MonoBehaviour:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 2021000666}
- m_Enabled: 1
- m_EditorHideFlags: 0
- m_Script: {fileID: 11500000, guid: a18321dc2d138034fbdb5f705f19f893, type: 3}
- m_Name:
- m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.EnemyUpgrades.WaveVote
- ShowTopMostFoldoutHeaderGroup: 1
- optionsPerVote: 3
---- !u!4 &2021000669
-Transform:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 2021000666}
- serializedVersion: 2
- m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
- m_LocalPosition: {x: 38.30481, y: 0.5, z: 155.93135}
- m_LocalScale: {x: 1, y: 1, z: 1}
- m_ConstrainProportionsScale: 0
- m_Children: []
- m_Father: {fileID: 0}
- m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!43 &2024389666
Mesh:
m_ObjectHideFlags: 0
@@ -29024,11 +28662,10 @@ MonoBehaviour:
options:
- {fileID: 11400000, guid: 6b1d3f8a2c5e4097b8a1d0c3e6f9b240, type: 2}
- {fileID: 11400000, guid: f4a2b8d1c6e93f05a7b2d4c8e1f6a930, type: 2}
- - {fileID: 11400000, guid: 0b9f4905752751f44b9959b84b620266, type: 2}
- {fileID: 11400000, guid: 65ff25c1c8a89f7df8e88f71968c1c98, type: 2}
- {fileID: 11400000, guid: 5f390de69ba43b2a6a0d89cf09580320, type: 2}
+ - {fileID: 11400000, guid: 65ff25c1c8a89f7df8e88f71968c1c98, type: 2}
- {fileID: 11400000, guid: 29b35841f7e5db454903798cb5d83434, type: 2}
- - {fileID: 11400000, guid: d180030b68a9a5e1da497bf6f83451d4, type: 2}
--- !u!4 &2139601601
Transform:
m_ObjectHideFlags: 0
@@ -29428,9 +29065,3 @@ SceneRoots:
- {fileID: 2079088113}
- {fileID: 914832726}
- {fileID: 993870445}
- - {fileID: 759470735}
- - {fileID: 954434161}
- - {fileID: 181002641}
- - {fileID: 961493954}
- - {fileID: 2021000669}
- - {fileID: 875833599}
diff --git a/Assets/_Project/Scripts/Audio/AudioCategory.cs b/Assets/_Project/Scripts/Audio/AudioCategory.cs
index 539e9ba..9a446aa 100644
--- a/Assets/_Project/Scripts/Audio/AudioCategory.cs
+++ b/Assets/_Project/Scripts/Audio/AudioCategory.cs
@@ -1,5 +1,5 @@
// Assets/_Project/Scripts/Audio/AudioCategory.cs
namespace TD.Audio
{
- public enum AudioCategory { Combat, EnemyHitDeath, UI }
+ public enum AudioCategory { TowerFire, EnemyHitDeath, UI }
}
diff --git a/Assets/_Project/Scripts/Audio/AudioManager.cs b/Assets/_Project/Scripts/Audio/AudioManager.cs
index 5fbc61b..21099bb 100644
--- a/Assets/_Project/Scripts/Audio/AudioManager.cs
+++ b/Assets/_Project/Scripts/Audio/AudioManager.cs
@@ -11,12 +11,6 @@ namespace TD.Audio
{
public AudioCategory category;
public int maxVoices;
-
- // NOTE: currently inert. It seeds each pooled AudioSource at init, but every
- // Play() call overwrites src.volume with (per-sound volume × SFX slider), so
- // this value never reaches the mix. Left as-is rather than "fixed" because
- // multiplying it in would halve every existing sound (authored scenes use 0.5).
- // Wire it up only alongside a pass to re-level the per-sound volumes.
[Range(0f, 1f)]
public float volume;
}
@@ -64,16 +58,6 @@ namespace TD.Audio
}
}
- ///
- /// Plays a one-shot on the given category's voice pool. is
- /// the per-sound authored level; it's scaled by the player's SFX slider
- /// () on the way in.
- ///
- ///
- /// The scalar is sampled at play time, so a sound already in flight keeps the volume
- /// it started with when the slider moves. Every SFX here is short, so that's
- /// imperceptible and avoids having to track live voices.
- ///
public void Play(AudioClip clip, AudioCategory category, float pitch = 1f, float volume = 1f)
{
if (clip == null) return;
@@ -82,7 +66,7 @@ namespace TD.Audio
int idx = indices[category];
var src = pool[idx % pool.Length];
src.pitch = pitch;
- src.volume = volume * AudioVolumeSettings.SfxScalar;
+ src.volume = volume;
src.clip = clip;
src.Play();
indices[category] = idx + 1;
diff --git a/Assets/_Project/Scripts/Audio/AudioVolumeSettings.cs b/Assets/_Project/Scripts/Audio/AudioVolumeSettings.cs
deleted file mode 100644
index 5df40a6..0000000
--- a/Assets/_Project/Scripts/Audio/AudioVolumeSettings.cs
+++ /dev/null
@@ -1,97 +0,0 @@
-// Assets/_Project/Scripts/Audio/AudioVolumeSettings.cs
-using UnityEngine;
-
-namespace TD.Audio
-{
- ///
- /// Player-facing volume mix, persisted in and shared by
- /// every audio consumer in the game. Two buses:
- ///
- /// - Music — the looping scene track played by .
- /// - SFX — everything routed through
- /// (spells, tower fire, build/land, enemy death, sell, UI clicks).
- ///
- ///
- ///
- /// Why not named AudioSettings. UnityEngine.AudioSettings already
- /// exists, so a TD.Audio.AudioSettings would be an ambiguous reference in any
- /// file that has both using UnityEngine; and using TD.Audio;.
- ///
- /// Units. Public values are ints 0..100 — the numbers the settings slider
- /// shows. Consumers multiply by / ,
- /// which apply a squared taper so the slider's lower half isn't perceptually dead
- /// (linear amplitude drops off much faster than perceived loudness). At 100 the
- /// scalar is exactly 1, so the default mix is byte-identical to the pre-settings
- /// behaviour.
- ///
- /// Live updates. fires on every mutation.
- /// Continuous sources (music) subscribe and re-apply; one-shots read the scalar at
- /// play time, so a mid-flight SFX keeps the volume it started with. That's fine —
- /// they're all short.
- ///
- public static class AudioVolumeSettings
- {
- public const int MinVolume = 0;
- public const int MaxVolume = 100;
-
- private const int DefaultVolume = 100;
- private const string MusicKey = "td.audio.music";
- private const string SfxKey = "td.audio.sfx";
-
- /// Raised whenever music or SFX volume changes.
- public static event System.Action OnChanged;
-
- // -1 = not yet read from PlayerPrefs. Statics are cleared on domain reload
- // (entering play mode in the editor), so the lazy load re-runs each session.
- private static int music = -1;
- private static int sfx = -1;
-
- public static int MusicVolume
- {
- get
- {
- if (music < 0) music = PlayerPrefs.GetInt(MusicKey, DefaultVolume);
- return music;
- }
- set => Set(ref music, MusicKey, value);
- }
-
- public static int SfxVolume
- {
- get
- {
- if (sfx < 0) sfx = PlayerPrefs.GetInt(SfxKey, DefaultVolume);
- return sfx;
- }
- set => Set(ref sfx, SfxKey, value);
- }
-
- /// Amplitude multiplier (0..1) for the music bus.
- public static float MusicScalar => ToScalar(MusicVolume);
-
- /// Amplitude multiplier (0..1) for the sound-effects bus.
- public static float SfxScalar => ToScalar(SfxVolume);
-
- ///
- /// Flushes PlayerPrefs to disk. Setters only write the in-memory pref (cheap
- /// enough to call on every frame of a slider drag); call this once when the
- /// settings UI closes so the choice survives a crash.
- ///
- public static void Flush() => PlayerPrefs.Save();
-
- private static void Set(ref int field, string key, int value)
- {
- int clamped = Mathf.Clamp(value, MinVolume, MaxVolume);
- if (field == clamped) return;
- field = clamped;
- PlayerPrefs.SetInt(key, clamped);
- OnChanged?.Invoke();
- }
-
- private static float ToScalar(int volume)
- {
- float linear = Mathf.Clamp01(volume / (float)MaxVolume);
- return linear * linear;
- }
- }
-}
diff --git a/Assets/_Project/Scripts/Audio/AudioVolumeSettings.cs.meta b/Assets/_Project/Scripts/Audio/AudioVolumeSettings.cs.meta
deleted file mode 100644
index 2b1f334..0000000
--- a/Assets/_Project/Scripts/Audio/AudioVolumeSettings.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 4d2f94f5f8df24e8aa1f47d9f0e3ecf4
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Audio/MusicPlayer.cs b/Assets/_Project/Scripts/Audio/MusicPlayer.cs
index 781e36e..325b15c 100644
--- a/Assets/_Project/Scripts/Audio/MusicPlayer.cs
+++ b/Assets/_Project/Scripts/Audio/MusicPlayer.cs
@@ -7,12 +7,6 @@ namespace TD.Audio
/// Plays a single looping music track for the scene it lives in.
/// Add to a GameObject in the scene, assign a clip, and it starts on load.
///
- ///
- /// Volume is the authored scaled by the player's music slider
- /// (). Unlike one-shot SFX, the track is
- /// continuous, so this subscribes to and
- /// re-applies live while the slider is dragged.
- ///
[RequireComponent(typeof(AudioSource))]
public class MusicPlayer : MonoBehaviour
{
@@ -20,36 +14,14 @@ namespace TD.Audio
[Range(0f, 1f)]
[SerializeField] private float volume = 1f;
- private AudioSource source;
-
- private void Awake()
- {
- source = GetComponent();
- }
-
- private void OnEnable()
- {
- AudioVolumeSettings.OnChanged += ApplyVolume;
- }
-
- private void OnDisable()
- {
- AudioVolumeSettings.OnChanged -= ApplyVolume;
- }
-
private void Start()
{
- source.clip = clip;
- source.loop = true;
- source.playOnAwake = false;
- ApplyVolume();
- source.Play();
- }
-
- private void ApplyVolume()
- {
- if (source == null) return;
- source.volume = volume * AudioVolumeSettings.MusicScalar;
+ var src = GetComponent();
+ src.clip = clip;
+ src.volume = volume;
+ src.loop = true;
+ src.playOnAwake = false;
+ src.Play();
}
}
}
diff --git a/Assets/_Project/Scripts/Combat/TeslaTowerFireSound.cs b/Assets/_Project/Scripts/Combat/TeslaTowerFireSound.cs
index eddc323..3334523 100644
--- a/Assets/_Project/Scripts/Combat/TeslaTowerFireSound.cs
+++ b/Assets/_Project/Scripts/Combat/TeslaTowerFireSound.cs
@@ -38,7 +38,7 @@ namespace TD.Combat
private void HandleAreaFired(Vector3[] positions)
{
- AudioManager.Instance?.Play(fireSound.clip, AudioCategory.Combat, fireSound.RandomPitch(), fireSound.volume);
+ AudioManager.Instance?.Play(fireSound.clip, AudioCategory.TowerFire, fireSound.RandomPitch(), fireSound.volume);
}
}
}
diff --git a/Assets/_Project/Scripts/Combat/TowerCombat.cs b/Assets/_Project/Scripts/Combat/TowerCombat.cs
index 3001be9..6c1a53f 100644
--- a/Assets/_Project/Scripts/Combat/TowerCombat.cs
+++ b/Assets/_Project/Scripts/Combat/TowerCombat.cs
@@ -67,6 +67,11 @@ 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];
@@ -234,6 +239,25 @@ 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)
@@ -241,7 +265,9 @@ namespace TD.Combat
attackCooldown -= Time.deltaTime;
if (attackCooldown > 0f) return;
- attackCooldown = 1f / def.AttacksPerSecond;
+ float effectiveFireRate = def.AttacksPerSecond
+ * (GetOwnerBuffManager()?.GetMultiplier(BuffStat.AttackSpeed) ?? 1f);
+ attackCooldown = 1f / effectiveFireRate;
Fire(def);
}
@@ -458,7 +484,9 @@ namespace TD.Combat
private void HitEnemy(in CombatProfile p, EnemyHealth target, PlayerSlot owner)
{
- target.TakeDamage(p.Damage, p.DamageType, owner);
+ var multiplier = GetOwnerBuffManager()?.GetMultiplier(BuffStat.Damage) ?? 1f;
+ float effectiveDamage = p.Damage * multiplier;
+ target.TakeDamage(effectiveDamage, p.DamageType, owner);
ApplyStatusEffect(p, target, owner);
}
@@ -497,9 +525,11 @@ namespace TD.Combat
return;
}
+ float effectiveDamage = def.Damage
+ * (GetOwnerBuffManager()?.GetMultiplier(BuffStat.Damage) ?? 1f);
proj.InitializeServer(
target,
- def.Damage,
+ effectiveDamage,
p.DamageType,
p.TargetType,
p.SplashRadius,
diff --git a/Assets/_Project/Scripts/Core/BuffStat.cs b/Assets/_Project/Scripts/Core/BuffStat.cs
new file mode 100644
index 0000000..3e2132e
--- /dev/null
+++ b/Assets/_Project/Scripts/Core/BuffStat.cs
@@ -0,0 +1,12 @@
+// 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
new file mode 100644
index 0000000..b91fc16
--- /dev/null
+++ b/Assets/_Project/Scripts/Core/BuffStat.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: fc84fec503f24bc7693ee29558f45d27
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Core/BuilderSpellKind.cs b/Assets/_Project/Scripts/Core/BuilderSpellKind.cs
index 91c56e9..58fcb5d 100644
--- a/Assets/_Project/Scripts/Core/BuilderSpellKind.cs
+++ b/Assets/_Project/Scripts/Core/BuilderSpellKind.cs
@@ -11,6 +11,5 @@ namespace TD.Core
{
Fireball = 0,
SlowArea = 1,
- SlowAreaUpgraded = 2,
}
}
diff --git a/Assets/_Project/Scripts/Core/EnemyAbilityKind.cs b/Assets/_Project/Scripts/Core/EnemyAbilityKind.cs
deleted file mode 100644
index 3356956..0000000
--- a/Assets/_Project/Scripts/Core/EnemyAbilityKind.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-namespace TD.Core
-{
- ///
- /// Identifies which enemy ability a
- /// represents. Mirrors
- /// — a stable, enum-indexed identifier used by
- /// instead of an asset reference.
- ///
- public enum EnemyAbilityKind : byte
- {
- /// Sentinel for "no ability". Never a real pool entry.
- None = 0,
-
- /// Dies into several smaller, faster copies of itself.
- SplitOnDeath = 1,
-
- /// Grounded enemies take to the air, ignoring the maze entirely.
- Flight = 2,
-
- /// Teleports a short distance further along its path on a timer.
- Blink = 3,
-
- /// Grants no kill bounty.
- NoBounty = 4,
-
- /// Steals gold from the player whose zone it escaped, on top of the life cost.
- GoldTheft = 5,
- }
-}
diff --git a/Assets/_Project/Scripts/Core/EnemyAbilityKind.cs.meta b/Assets/_Project/Scripts/Core/EnemyAbilityKind.cs.meta
deleted file mode 100644
index 18dd995..0000000
--- a/Assets/_Project/Scripts/Core/EnemyAbilityKind.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: ec52a6b404fed65dfa48a63b5535373d
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Core/Enums.cs b/Assets/_Project/Scripts/Core/Enums.cs
index 7d49400..95f979a 100644
--- a/Assets/_Project/Scripts/Core/Enums.cs
+++ b/Assets/_Project/Scripts/Core/Enums.cs
@@ -68,12 +68,8 @@ namespace TD.Core
///
///
/// None is a sentinel value used in OwnerGrid to mark tiles not owned by any player zone.
- /// Maps use a contiguous prefix of the player values.
- ///
- /// The enum runs to 9, but only slots are ever
- /// allocated. Lobbies capped at 3 for the 2.0 design; the surplus values are kept because
- /// they are baked into existing LevelData owner grids, and renumbering the enum would
- /// force a re-bake of every map to no runtime benefit.
+ /// Player1..Player9 cover the maximum supported player count. Maps using fewer players use a
+ /// contiguous prefix (e.g., a 3-player map uses Player1, Player2, Player3 only).
///
///
/// Global phase of a match, driven by MatchState.
@@ -96,28 +92,6 @@ namespace TD.Core
Defeat = 4,
}
- ///
- /// Which step of the between-encounters sequence is currently running. Replicated by
- /// WaveManager so every HUD can label the shared countdown correctly.
- ///
- ///
- /// These run strictly in order and never overlap: players take their personal draft, then vote
- /// on the buff for the wave they just cleared, then build. The build timer deliberately does
- /// NOT run concurrently with the choices — "after all players have chosen" only means
- /// something if it isn't racing a clock players also want to spend on their maze.
- ///
- public enum InterWaveStage : byte
- {
- /// No inter-wave step running — a wave is spawning or being fought.
- None = 0,
- /// Players are picking their personal draft reward.
- Draft = 1,
- /// Players are voting on the buff for the wave they just cleared.
- Vote = 2,
- /// Build countdown before the next wave spawns.
- Build = 3,
- }
-
///
/// Stable identifier per race. Values 1-16 reserve slots for the planned
/// 16-race grid in the lobby; only races with a corresponding
diff --git a/Assets/_Project/Scripts/Core/MatchRules.cs b/Assets/_Project/Scripts/Core/MatchRules.cs
deleted file mode 100644
index d698547..0000000
--- a/Assets/_Project/Scripts/Core/MatchRules.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-// Assets/_Project/Scripts/Core/MatchRules.cs
-namespace TD.Core
-{
- ///
- /// Match-wide constants that several unrelated systems have to agree on.
- ///
- public static class MatchRules
- {
- ///
- /// Maximum players in one lobby.
- ///
- ///
- /// Reduced from 9 to 3 for the 2.0 design as a deliberate scope cut: three players
- /// is enough for the shared-lives and shared-vote mechanics to matter, and it shrinks map
- /// authoring, balance surface, and network load all at once.
- ///
- /// Why still runs to 9. Shrinking the enum would
- /// invalidate the owner grids baked into existing LevelData assets and force a
- /// re-bake of every map, for no runtime benefit — the extra values are simply never
- /// allocated. Slot allocation caps here instead, which is the only place that decides who
- /// gets a slot at all. Per-slot arrays stay sized to the enum, so they hold a few unused
- /// entries; that is intentional and costs nothing.
- ///
- public const int MaxPlayers = 3;
- }
-}
diff --git a/Assets/_Project/Scripts/Core/MatchRules.cs.meta b/Assets/_Project/Scripts/Core/MatchRules.cs.meta
deleted file mode 100644
index 74bb050..0000000
--- a/Assets/_Project/Scripts/Core/MatchRules.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: ac169573e8a2fd046a3ebac7d2e03c8c
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Core/PoolWeightAttribute.cs b/Assets/_Project/Scripts/Core/PoolWeightAttribute.cs
deleted file mode 100644
index 0bb29d0..0000000
--- a/Assets/_Project/Scripts/Core/PoolWeightAttribute.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-// Assets/_Project/Scripts/Core/PoolWeightAttribute.cs
-using UnityEngine;
-
-namespace TD.Core
-{
- ///
- /// Marks a float field as a 0–1 relative draw weight inside an authored pool. The
- /// inspector renders it as a slider and, when the value is zero, shows a warning directly
- /// beneath explaining that the entry can never be drawn.
- ///
- ///
- /// Relative, not absolute. Weights are normalized against the summed weight of the
- /// candidate set at draw time, so entries at equal weight are equally likely and an entry at
- /// 0.5 is half as likely as one at 1.0. This is why every weight field defaults to 1 —
- /// adding an entry to a pool then never silently re-weights the entries already in it.
- ///
- /// The warning is advisory. Muting an entry while tuning is a legitimate
- /// workflow, so the drawer never clamps the value or blocks the edit. It exists because a
- /// zero-weight entry is otherwise indistinguishable, in play, from one that simply hasn't come
- /// up yet — the failure is silent, so the authoring surface has to be loud.
- ///
- public class PoolWeightAttribute : PropertyAttribute
- {
- ///
- /// Noun used in the zero-weight warning ("this wave can never be drawn"). Keep it
- /// singular and lowercase.
- ///
- public readonly string Noun;
-
- public PoolWeightAttribute(string noun = "entry")
- {
- Noun = string.IsNullOrWhiteSpace(noun) ? "entry" : noun;
- }
- }
-}
diff --git a/Assets/_Project/Scripts/Core/PoolWeightAttribute.cs.meta b/Assets/_Project/Scripts/Core/PoolWeightAttribute.cs.meta
deleted file mode 100644
index 2ce9e75..0000000
--- a/Assets/_Project/Scripts/Core/PoolWeightAttribute.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: e171856d7a8fe194f97091853672c19d
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Dev/DebugCommandRelay.cs b/Assets/_Project/Scripts/Dev/DebugCommandRelay.cs
deleted file mode 100644
index 360b2a3..0000000
--- a/Assets/_Project/Scripts/Dev/DebugCommandRelay.cs
+++ /dev/null
@@ -1,117 +0,0 @@
-// Assets/_Project/Scripts/Dev/DebugCommandRelay.cs
-using System.Collections.Generic;
-using Unity.Netcode;
-using UnityEngine;
-using TD.Gameplay.Draft;
-using TD.Gameplay.EnemyUpgrades;
-using TD.Gameplay.Waves;
-
-namespace TD.Dev
-{
- ///
- /// Debug-only network relay: exposes one RPC that force-applies a
- /// by its index, bypassing PlayerDraft's "must currently be
- /// offered" check.
- ///
- ///
- /// Lives entirely separate from PlayerDraft/DraftOption/DraftPool — none of those files
- /// know this component exists. Removing this component (and TD.Dev.DebugConsole, which
- /// calls it) leaves the shipping draft system completely untouched.
- ///
- /// Added to the Player prefab alongside PlayerDraft/PlayerGoldManager/etc. so it spawns
- /// with a per-player OwnerClientId. Static per-client registry mirrors PlayerDraft's
- /// GetForClient/Local pattern.
- ///
- public class DebugCommandRelay : NetworkBehaviour
- {
- private static readonly Dictionary s_byClientId
- = new Dictionary();
-
- public static DebugCommandRelay GetForClient(ulong clientId)
- {
- s_byClientId.TryGetValue(clientId, out var relay);
- return relay;
- }
-
- public static DebugCommandRelay Local
- {
- get
- {
- var nm = NetworkManager.Singleton;
- if (nm == null || !nm.IsClient) return null;
- return GetForClient(nm.LocalClientId);
- }
- }
-
- 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);
- }
-
- ///
- /// Owning client: force-apply the DraftOption at (its
- /// DraftPool index) immediately, skipping the normal "must be currently offered" gate.
- /// Debug/testing only — any connected client can grant themselves anything via this
- /// RPC, same tradeoff DevWaveControls already accepts for its hotkeys.
- ///
- [Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Owner)]
- public void DebugGrantDraftOptionRpc(int optionId)
- {
- var option = DraftPool.Instance != null ? DraftPool.Instance.Get(optionId) : null;
- if (option == null)
- {
- Debug.LogWarning($"[DebugCommandRelay] Grant requested for unknown option id {optionId}.");
- return;
- }
-
- if (option.ServerApply(OwnerClientId))
- Debug.Log($"[DebugCommandRelay] Granted '{option.DisplayName}' to client {OwnerClientId}.");
- else
- Debug.LogWarning($"[DebugCommandRelay] '{option.DisplayName}' could not be applied " +
- $"to client {OwnerClientId}.");
- }
-
- ///
- /// Force-apply the at (its
- /// index) to the run's current wave slot — the same
- /// recording WaveVote.ServerResolve does for a vote winner, skipping the vote
- /// entirely. Not player-scoped: the effect lands on the run's current wave slot rather
- /// than the caller, so any connected client's relay can trigger it.
- ///
- ///
- /// Takes effect the next time this wave slot's enemies spawn, not retroactively on
- /// enemies already alive — see 's remarks on why
- /// applying is "record on the slot," not a direct mutation.
- ///
- [Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Owner)]
- public void DebugGrantEnemyUpgradeRpc(int optionId)
- {
- var pool = EnemyUpgradePool.Instance;
- var option = pool != null ? pool.Get(optionId) : null;
- if (option == null)
- {
- Debug.LogWarning($"[DebugCommandRelay] Grant requested for unknown enemy upgrade id {optionId}.");
- return;
- }
-
- var run = RunState.Instance;
- if (run == null)
- {
- Debug.LogWarning("[DebugCommandRelay] No RunState in this scene.");
- return;
- }
-
- int slot = run.CurrentUpgradeSlot;
- run.ServerAddUpgrade(slot, optionId);
- option.ServerOnApplied(run, slot);
-
- Debug.Log($"[DebugCommandRelay] Granted '{option.DisplayName}' to wave slot {slot}.");
- }
- }
-}
diff --git a/Assets/_Project/Scripts/Dev/DebugCommandRelay.cs.meta b/Assets/_Project/Scripts/Dev/DebugCommandRelay.cs.meta
deleted file mode 100644
index d7a2598..0000000
--- a/Assets/_Project/Scripts/Dev/DebugCommandRelay.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 65825b3189ae873948659b37b219f5e6
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Dev/DebugConsole.cs b/Assets/_Project/Scripts/Dev/DebugConsole.cs
deleted file mode 100644
index 6ffbf2b..0000000
--- a/Assets/_Project/Scripts/Dev/DebugConsole.cs
+++ /dev/null
@@ -1,760 +0,0 @@
-// Assets/_Project/Scripts/Dev/DebugConsole.cs
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Text;
-using UnityEngine;
-using UnityEngine.InputSystem;
-using UnityEngine.UIElements;
-using TD.UI;
-using TD.Gameplay.Draft;
-using TD.Gameplay.EnemyUpgrades;
-
-namespace TD.Dev
-{
- ///
- /// Standalone debug console overlay: press the toggle key to open a text
- /// input, type a command, Enter to submit, Escape to cancel.
- ///
- ///
- /// Deliberately decoupled from — own GameObject,
- /// own UIDocument (it shares HUDPanelSettings, drawn above the HUD via a
- /// higher sorting order) — so it can be dropped out of a build entirely by
- /// not including that GameObject, the same way
- /// is kept separate from shipping systems.
- ///
- /// The grammar lives in a tree (see
- /// ) rather than in hardcoded string compares,
- /// so the same definition drives both execution and the autocomplete list.
- /// Supports one command so far: "get <type> <name>" grants a
- /// to the local player via
- /// .
- ///
- /// Keys while open: Up/Down move the suggestion highlight, Tab accepts it,
- /// Enter accepts it while the command is still incomplete and submits once
- /// it is complete, Escape closes without submitting.
- ///
- [RequireComponent(typeof(UIDocument))]
- public class DebugConsole : MonoBehaviour
- {
- public static DebugConsole Instance { get; private set; }
-
- ///
- /// True while the console overlay is showing. Legacy IMGUI (OnGUI) dev tools
- /// draw after every runtime UI Toolkit panel regardless of sorting order, so they
- /// have to check this and skip drawing rather than rely on layering.
- ///
- public static bool IsOpen => Instance != null && Instance.consoleOpen;
-
- [Tooltip("Keyboard shortcut that opens and closes the console. Uses the new Input " +
- "System (UnityEngine.InputSystem.Key). Backquote is the physical ` / ~ key.")]
- [SerializeField] private Key toggleKey = Key.Backquote;
-
- private DebugConsoleUI ui;
- private bool consoleOpen;
-
- // Frame on which the console was opened or closed. The toggle key and Enter
- // are ignored on that frame and the next so the keypress that opened/closed
- // the console doesn't also get typed into the field or immediately submit it.
- // Mirrors HUDController's chatToggleSuppressFrame.
- private int toggleSuppressFrame = -1;
-
- // Current completion candidates for what's typed, the highlighted one, and the
- // index in the input text where accepting a candidate starts overwriting.
- private List suggestions = new List();
- private int selectedIndex;
- private int fragmentStart;
-
- private void Awake()
- {
- if (Instance != null && Instance != this)
- {
- Debug.LogError("[DebugConsole] Duplicate instance detected. Destroying the new one.");
- Destroy(gameObject);
- return;
- }
- Instance = this;
- }
-
- private void Start()
- {
- var doc = GetComponent();
- if (doc == null)
- {
- Debug.LogError("[DebugConsole] No UIDocument component found.");
- return;
- }
-
- var root = doc.rootVisualElement;
- if (root == null)
- {
- Debug.LogError("[DebugConsole] rootVisualElement is null. " +
- "Check that Panel Settings is assigned.");
- return;
- }
-
- ui = new DebugConsoleUI(root);
-
- // Focus tracking — contributes to the same flag HUDController's chat
- // input sets, so camera/builder/spell input all stay gated correctly
- // regardless of which text field currently has focus.
- ui.CommandInput.RegisterCallback(_ => HUDController.IsTextInputActive = true);
- ui.CommandInput.RegisterCallback(_ => HUDController.IsTextInputActive = false);
-
- ui.CommandInput.RegisterValueChangedCallback(_ => RefreshSuggestions());
-
- // Tab and the arrows can't be polled from Keyboard.current the way Enter and
- // Escape are (below): UI Toolkit would still insert a tab character and move
- // the focus ring before Update runs. Intercept them on the field in the
- // trickle-down phase, before the text engine and the focus ring see them.
- ui.CommandInput.RegisterCallback(OnConsoleKeyDown, TrickleDown.TrickleDown);
- ui.CommandInput.RegisterCallback(OnConsoleNavigationMove,
- TrickleDown.TrickleDown);
- }
-
- private void Update()
- {
- var kb = Keyboard.current;
- if (kb == null) return; // no keyboard connected (e.g. headless server)
-
- if (Time.frameCount <= toggleSuppressFrame) return;
-
- if (toggleKey != Key.None && kb[toggleKey].wasPressedThisFrame)
- {
- if (consoleOpen) CloseConsole();
- else OpenConsole();
- return;
- }
-
- if (!consoleOpen) return;
-
- bool enterDown = kb.enterKey.wasPressedThisFrame || kb.numpadEnterKey.wasPressedThisFrame;
- bool escDown = kb.escapeKey.wasPressedThisFrame;
-
- if (enterDown) SubmitCommand();
- else if (escDown) CloseConsole();
- }
-
- private void OnConsoleKeyDown(KeyDownEvent evt)
- {
- if (!consoleOpen) return;
-
- switch (evt.keyCode)
- {
- case KeyCode.Tab:
- AcceptSelectedSuggestion();
- Swallow(evt);
- return;
- case KeyCode.DownArrow:
- MoveSelection(1);
- Swallow(evt);
- return;
- case KeyCode.UpArrow:
- MoveSelection(-1);
- Swallow(evt);
- return;
- }
-
- // Tab reaches the field twice — once carrying keyCode, once carrying only the
- // '\t' character. Swallow the second one too or a literal tab lands in the text.
- if (evt.character == '\t') Swallow(evt);
- }
-
- // UI Toolkit also translates Tab into a focus-ring navigation event, separately
- // from the key event above. Block it so Tab can't move focus off the console.
- private void OnConsoleNavigationMove(NavigationMoveEvent evt)
- {
- if (!consoleOpen) return;
- Swallow(evt);
- }
-
- // Consume an event so neither the text engine nor the focus ring acts on it.
- // EventBase.PreventDefault is obsolete in Unity 6; FocusController.IgnoreEvent is
- // its replacement for the focus-navigation half.
- private void Swallow(EventBase evt)
- {
- evt.StopImmediatePropagation();
- ui.CommandInput?.panel?.focusController?.IgnoreEvent(evt);
- }
-
- private void OpenConsole()
- {
- if (ui.CommandInput == null) return;
- ui.Show();
- ui.CommandInput.SetValueWithoutNotify(string.Empty);
- RefreshSuggestions();
-
- // Suppress the toggle key and Enter for this frame and the next so the
- // keypress that opened the console doesn't also get typed into the field.
- // Focus is deferred a frame for the same reason — UI Toolkit would
- // otherwise route the open keypress to the freshly-focused TextField.
- toggleSuppressFrame = Time.frameCount + 1;
- consoleOpen = true;
- StartCoroutine(FocusNextFrame());
- }
-
- private IEnumerator FocusNextFrame()
- {
- yield return null;
- if (consoleOpen && ui.CommandInput != null)
- {
- ui.CommandInput.Focus();
- ui.CommandInput.SelectAll();
- }
- }
-
- private void CloseConsole()
- {
- if (ui.CommandInput != null)
- {
- ui.CommandInput.SetValueWithoutNotify(string.Empty);
- ui.CommandInput.Blur();
- }
- ui.Hide();
-
- suggestions.Clear();
- selectedIndex = 0;
- ui.ClearSuggestions();
-
- consoleOpen = false;
- toggleSuppressFrame = Time.frameCount + 1;
- SuppressHudChatOpen();
- }
-
- // The Enter that submitted/accepted in this console also gets polled by
- // HUDController's chat this same frame — suppress its "Enter opens chat" branch
- // (HUDController.HandleChatInput, which gates only on this frame counter) so
- // using the debug console never pops chat open too.
- private static void SuppressHudChatOpen()
- {
- HUDController.SuppressChatOpenUntilFrame = Time.frameCount + 1;
- }
-
- private void SubmitCommand()
- {
- string text = ui.CommandInput?.value ?? string.Empty;
-
- // Enter doubles as "accept the highlighted suggestion" while the typed
- // command isn't runnable yet, so you can drive the whole thing with Enter
- // and only ever hit Tab when you want to skip past the highlighted row.
- if (!string.IsNullOrWhiteSpace(text) && suggestions.Count > 0 && !IsComplete(text))
- {
- AcceptSelectedSuggestion();
- SuppressHudChatOpen();
- return;
- }
-
- CloseConsole();
-
- if (string.IsNullOrWhiteSpace(text)) return;
- ExecuteCommand(text.Trim());
- }
-
- // ---------------------------------------------------------------- grammar
-
- ///
- /// One token level of the command grammar. Literal levels list their
- /// ; a level that takes a free-form final argument sets
- /// to enumerate the valid values for completion.
- ///
- private sealed class CommandNode
- {
- public string Token; // literal token; null for the root
- public string Hint; // dim one-liner shown beside the token
- public Func> Values; // non-null => this level takes a final value
- public bool Executable; // a complete command can end here
- public Action Execute; // "Values" level: run the completed command
- public readonly List Children = new List();
- }
-
- private const string UsageHint = "Try: get , grant enemy ";
-
- // Maps the type token in "get " to the DraftOption subclass it
- // should match against. Add an entry here whenever a new DraftOption subclass
- // should be gettable from the console — the command tree and its autocomplete
- // list are both built from this.
- private static readonly Dictionary DraftTypeAliases =
- new Dictionary(StringComparer.OrdinalIgnoreCase)
- {
- { "spell", typeof(BuilderSpellDraftOption) },
- { "tower", typeof(NewTowerDraftOption) },
- { "buff", typeof(BuilderEffectDraftOption) },
- { "effect", typeof(BuilderEffectDraftOption) },
- };
-
- private CommandNode commandTree;
- private CommandNode CommandTree => commandTree ?? (commandTree = BuildCommandTree());
-
- private static CommandNode BuildCommandTree()
- {
- var root = new CommandNode();
-
- var get = new CommandNode
- {
- Token = "get",
- Hint = "grant a draft option to the local player",
- };
- root.Children.Add(get);
-
- foreach (var pair in DraftTypeAliases)
- {
- string typeToken = pair.Key;
- var optionType = pair.Value;
- get.Children.Add(new CommandNode
- {
- Token = typeToken,
- Hint = "",
- Executable = true,
- Values = () => DraftOptionNames(optionType),
- Execute = name => ExecuteGet(typeToken, optionType, name),
- });
- }
-
- var grant = new CommandNode
- {
- Token = "grant",
- Hint = "grant a buff to the enemies",
- };
- root.Children.Add(grant);
-
- grant.Children.Add(new CommandNode
- {
- Token = "enemy",
- Hint = "",
- Executable = true,
- Values = EnemyUpgradeNames,
- Execute = ExecuteGrantEnemyAbility,
- });
-
- return root;
- }
-
- // Display names of every pooled DraftOption of the given subclass, alphabetically.
- // Recomputed per keystroke rather than cached: DraftPool is a scene singleton that
- // may not exist yet when the console is built, and the pool is only a handful of
- // entries. Uses DraftPool's existing Count/Get surface — the same one ExecuteGet
- // walks — so completion can never offer something the command can't resolve.
- private static List DraftOptionNames(Type optionType)
- {
- var names = new List();
-
- var pool = DraftPool.Instance;
- if (pool == null) return names;
-
- for (int i = 0; i < pool.Count; i++)
- {
- var option = pool.Get(i);
- if (option == null || !optionType.IsInstanceOfType(option)) continue;
-
- string name = option.DisplayName;
- if (string.IsNullOrWhiteSpace(name)) continue;
- if (names.Contains(name)) continue;
-
- names.Add(name);
- }
-
- names.Sort(StringComparer.OrdinalIgnoreCase);
- return names;
- }
-
- // Display names of every pooled EnemyUpgradeOption, alphabetically. Mirrors
- // DraftOptionNames above — EnemyUpgradePool is the enemy-side equivalent of DraftPool,
- // and this is the same Count/Get surface ExecuteGrantEnemyAbility walks. Includes
- // non-ability cards too (e.g. the vote-meta "Double Dip") — DebugGrantEnemyUpgradeRpc
- // resolves through EnemyUpgradeOption.ServerOnApplied regardless of subclass, so
- // there's nothing this command can't actually grant.
- private static List EnemyUpgradeNames()
- {
- var names = new List();
-
- var pool = EnemyUpgradePool.Instance;
- if (pool == null) return names;
-
- for (int i = 0; i < pool.Count; i++)
- {
- var option = pool.Get(i);
- if (option == null) continue;
-
- string name = option.DisplayName;
- if (string.IsNullOrWhiteSpace(name)) continue;
- if (names.Contains(name)) continue;
-
- names.Add(name);
- }
-
- names.Sort(StringComparer.OrdinalIgnoreCase);
- return names;
- }
-
- // Valid literal tokens at a level, for error messages ("Expected one of: ...").
- private static string ChildTokens(CommandNode node)
- {
- var tokens = new List();
- for (int i = 0; i < node.Children.Count; i++) tokens.Add(node.Children[i].Token);
- tokens.Sort(StringComparer.OrdinalIgnoreCase);
- return string.Join(", ", tokens);
- }
-
- private static CommandNode FindChild(CommandNode node, string token)
- {
- for (int i = 0; i < node.Children.Count; i++)
- if (string.Equals(node.Children[i].Token, token, StringComparison.OrdinalIgnoreCase))
- return node.Children[i];
- return null;
- }
-
- // Whitespace tokenizer that also reports where each token starts, so completion
- // knows which span of the raw input a candidate would replace.
- private static void Tokenize(string text, List tokens, List starts)
- {
- tokens.Clear();
- starts.Clear();
-
- int i = 0;
- while (i < text.Length)
- {
- while (i < text.Length && char.IsWhiteSpace(text[i])) i++;
- if (i >= text.Length) break;
-
- int start = i;
- while (i < text.Length && !char.IsWhiteSpace(text[i])) i++;
- tokens.Add(text.Substring(start, i - start));
- starts.Add(start);
- }
- }
-
- // ------------------------------------------------------------- completion
-
- private struct Suggestion
- {
- public string Text; // what replaces the current fragment
- public string Hint; // dim trailing text, display only
- public bool AppendSpace; // true when more tokens follow this one
- }
-
- private readonly List walkTokens = new List();
- private readonly List walkStarts = new List();
-
- ///
- /// Candidates for the token currently being typed, plus (via
- /// ) the index in where
- /// accepting one starts overwriting.
- ///
- private List ComputeSuggestions(string text, out int fragStart)
- {
- text = text ?? string.Empty;
- Tokenize(text, walkTokens, walkStarts);
-
- bool endsWithSpace = text.Length > 0 && char.IsWhiteSpace(text[text.Length - 1]);
-
- var node = CommandTree;
- int i = 0;
-
- while (true)
- {
- // Free-form final argument: everything from here to the end of the line is
- // one value, so multi-word names ("Slow Area") complete as a single unit —
- // matching how ExecuteCommand rejoins the trailing tokens.
- if (node.Values != null)
- {
- fragStart = i < walkTokens.Count ? walkStarts[i] : text.Length;
- string valueFragment = i < walkTokens.Count
- ? text.Substring(fragStart).Trim()
- : string.Empty;
- return FilterValues(node.Values(), valueFragment);
- }
-
- // Nothing typed at this level yet — offer everything it accepts.
- if (i >= walkTokens.Count)
- {
- fragStart = text.Length;
- return FilterChildren(node, string.Empty);
- }
-
- // Last token with no trailing space is still being typed: complete it
- // rather than trying to descend through it.
- if (i == walkTokens.Count - 1 && !endsWithSpace)
- {
- fragStart = walkStarts[i];
- return FilterChildren(node, walkTokens[i]);
- }
-
- var child = FindChild(node, walkTokens[i]);
- if (child == null)
- {
- // A finished token that matches nothing — the rest of the line is
- // unreachable, so there's nothing useful to suggest.
- fragStart = walkStarts[i];
- return new List();
- }
-
- node = child;
- i++;
- }
- }
-
- private static List FilterChildren(CommandNode node, string fragment)
- {
- var result = new List();
- for (int i = 0; i < node.Children.Count; i++)
- {
- var child = node.Children[i];
- if (fragment.Length > 0 &&
- !child.Token.StartsWith(fragment, StringComparison.OrdinalIgnoreCase))
- continue;
-
- result.Add(new Suggestion
- {
- Text = child.Token,
- Hint = child.Hint,
- AppendSpace = child.Children.Count > 0 || child.Values != null,
- });
- }
- result.Sort((a, b) => string.Compare(a.Text, b.Text, StringComparison.OrdinalIgnoreCase));
- return result;
- }
-
- // Values are matched on the same normalized form execution uses, so anything the
- // list offers for "slow_ar" is something ExecuteGet would also accept.
- private static List FilterValues(List values, string fragment)
- {
- var result = new List();
- string normalizedFragment = Normalize(fragment);
-
- for (int i = 0; i < values.Count; i++)
- {
- if (normalizedFragment.Length > 0 &&
- !Normalize(values[i]).StartsWith(normalizedFragment, StringComparison.Ordinal))
- continue;
-
- result.Add(new Suggestion { Text = values[i], AppendSpace = false });
- }
- return result;
- }
-
- /// True when is a runnable command as typed.
- private bool IsComplete(string text)
- {
- Tokenize(text ?? string.Empty, walkTokens, walkStarts);
-
- var node = CommandTree;
- int i = 0;
-
- while (i < walkTokens.Count)
- {
- if (node.Values != null) break;
-
- var child = FindChild(node, walkTokens[i]);
- if (child == null) return false;
-
- node = child;
- i++;
- }
-
- if (!node.Executable) return false;
-
- if (node.Values != null)
- {
- if (i >= walkTokens.Count) return false; // value level, nothing typed for it
-
- string normalized = Normalize(string.Join(" ", walkTokens.GetRange(i, walkTokens.Count - i)));
- var values = node.Values();
- for (int v = 0; v < values.Count; v++)
- if (Normalize(values[v]) == normalized) return true;
-
- return false;
- }
-
- return true;
- }
-
- // --------------------------------------------------------- suggestion UI
-
- private void RefreshSuggestions()
- {
- if (ui.CommandInput == null) return;
-
- // Keep the highlight on the same candidate when it survives the new filter,
- // so typing another character doesn't silently move what Enter would accept.
- string previouslySelected = selectedIndex >= 0 && selectedIndex < suggestions.Count
- ? suggestions[selectedIndex].Text
- : null;
-
- suggestions = ComputeSuggestions(ui.CommandInput.value, out fragmentStart);
-
- selectedIndex = 0;
- if (previouslySelected != null)
- {
- for (int i = 0; i < suggestions.Count; i++)
- {
- if (!string.Equals(suggestions[i].Text, previouslySelected, StringComparison.Ordinal))
- continue;
- selectedIndex = i;
- break;
- }
- }
-
- ui.RenderSuggestions(ToSuggestionRows(suggestions), selectedIndex);
- }
-
- private static List ToSuggestionRows(List suggestions)
- {
- var rows = new List(suggestions.Count);
- for (int i = 0; i < suggestions.Count; i++)
- rows.Add(new DebugConsoleUI.SuggestionRow(suggestions[i].Text, suggestions[i].Hint));
- return rows;
- }
-
- private void MoveSelection(int delta)
- {
- if (suggestions.Count == 0) return;
-
- selectedIndex = (selectedIndex + delta) % suggestions.Count;
- if (selectedIndex < 0) selectedIndex += suggestions.Count;
-
- ui.RenderSuggestions(ToSuggestionRows(suggestions), selectedIndex);
- }
-
- private bool AcceptSelectedSuggestion()
- {
- if (ui.CommandInput == null) return false;
- if (selectedIndex < 0 || selectedIndex >= suggestions.Count) return false;
-
- var suggestion = suggestions[selectedIndex];
- string text = ui.CommandInput.value ?? string.Empty;
- int start = Mathf.Clamp(fragmentStart, 0, text.Length);
-
- string completed = text.Substring(0, start) + suggestion.Text +
- (suggestion.AppendSpace ? " " : string.Empty);
-
- // SetValueWithoutNotify + an explicit refresh instead of assigning value:
- // one deterministic recompute, and the caret placement below can't race the
- // change callback.
- ui.CommandInput.SetValueWithoutNotify(completed);
- ui.CommandInput.SelectRange(completed.Length, completed.Length);
- RefreshSuggestions();
- return true;
- }
-
- // ------------------------------------------------------------- execution
-
- private void ExecuteCommand(string text)
- {
- Tokenize(text, walkTokens, walkStarts);
-
- var node = CommandTree;
- int i = 0;
-
- while (i < walkTokens.Count && node.Values == null)
- {
- var child = FindChild(node, walkTokens[i]);
- if (child == null)
- {
- Debug.Log($"[DebugConsole] Unrecognized token \"{walkTokens[i]}\" in " +
- $"\"{text}\". Expected one of: {ChildTokens(node)}. {UsageHint}");
- return;
- }
-
- node = child;
- i++;
- }
-
- if (node.Values == null || !node.Executable || i >= walkTokens.Count)
- {
- Debug.Log($"[DebugConsole] Incomplete command: \"{text}\". {UsageHint}");
- return;
- }
-
- string nameToken = string.Join(" ", walkTokens.GetRange(i, walkTokens.Count - i));
- node.Execute(nameToken);
- }
-
- // "get ": grants any DraftOption of the given type whose DisplayName
- // matches , via the local player's DebugCommandRelay. Reuses DraftOption's
- // existing type-agnostic ServerApply dispatch instead of hardcoding a grant path
- // per option type.
- private static void ExecuteGet(string typeToken, Type optionType, string nameToken)
- {
- var pool = DraftPool.Instance;
- if (pool == null)
- {
- Debug.LogWarning("[DebugConsole] No DraftPool in this scene.");
- return;
- }
-
- string normalizedTarget = Normalize(nameToken);
- for (int i = 0; i < pool.Count; i++)
- {
- var option = pool.Get(i);
- if (option == null || !optionType.IsInstanceOfType(option)) continue;
- if (Normalize(option.DisplayName) != normalizedTarget) continue;
-
- var relay = DebugCommandRelay.Local;
- if (relay == null)
- {
- Debug.LogWarning("[DebugConsole] No local DebugCommandRelay found — " +
- "is it on the Player prefab?");
- return;
- }
-
- relay.DebugGrantDraftOptionRpc(i);
- return;
- }
-
- Debug.LogWarning($"[DebugConsole] No {typeToken} option named \"{nameToken}\" found.");
- }
-
- // "grant enemy ": the same effect as the post-wave vote picking that ability
- // card for the current wave slot — records it via RunState.ServerAddUpgrade and runs
- // its ServerOnApplied, mirroring WaveVote.ServerResolve exactly (see DebugCommandRelay.
- // DebugGrantEnemyUpgradeRpc). Takes effect the next time this wave slot's enemies
- // spawn, not retroactively on enemies already alive.
- private static void ExecuteGrantEnemyAbility(string nameToken)
- {
- var pool = EnemyUpgradePool.Instance;
- if (pool == null)
- {
- Debug.LogWarning("[DebugConsole] No EnemyUpgradePool in this scene.");
- return;
- }
-
- string normalizedTarget = Normalize(nameToken);
- for (int i = 0; i < pool.Count; i++)
- {
- var option = pool.Get(i);
- if (option == null || Normalize(option.DisplayName) != normalizedTarget) continue;
-
- var relay = DebugCommandRelay.Local;
- if (relay == null)
- {
- Debug.LogWarning("[DebugConsole] No local DebugCommandRelay found — " +
- "is it on the Player prefab?");
- return;
- }
-
- relay.DebugGrantEnemyUpgradeRpc(i);
- return;
- }
-
- Debug.LogWarning($"[DebugConsole] No enemy ability named \"{nameToken}\" found.");
- }
-
- // DisplayName is player-facing draft card text (its spacing/casing is chosen for
- // presentation, not for typing), so command matching strips everything but letters
- // and digits before comparing — "Slow Area", "slow_area", and "SlowArea" all match
- // the same option.
- private static string Normalize(string s)
- {
- if (string.IsNullOrEmpty(s)) return string.Empty;
- var sb = new StringBuilder(s.Length);
- foreach (char c in s)
- if (char.IsLetterOrDigit(c)) sb.Append(char.ToLowerInvariant(c));
- return sb.ToString();
- }
-
- private void OnDestroy()
- {
- if (Instance == this) Instance = null;
- }
- }
-}
diff --git a/Assets/_Project/Scripts/Dev/DebugConsole.cs.meta b/Assets/_Project/Scripts/Dev/DebugConsole.cs.meta
deleted file mode 100644
index c9a3292..0000000
--- a/Assets/_Project/Scripts/Dev/DebugConsole.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 7c6a808e8291552d0b64e36a210879c4
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Dev/DebugConsoleUI.cs b/Assets/_Project/Scripts/Dev/DebugConsoleUI.cs
deleted file mode 100644
index 808dd29..0000000
--- a/Assets/_Project/Scripts/Dev/DebugConsoleUI.cs
+++ /dev/null
@@ -1,185 +0,0 @@
-// Assets/_Project/Scripts/Dev/DebugConsoleUI.cs
-using System.Collections.Generic;
-using UnityEngine;
-using UnityEngine.UIElements;
-
-namespace TD.Dev
-{
- ///
- /// Builds and draws 's VisualElement tree: the input bar and
- /// its autocomplete list. Owns presentation only — element construction, styling, and
- /// row rendering — no command grammar, keybinding, or suggestion-selection logic; that
- /// stays in , which drives this class through its public
- /// members and reads/writes directly for focus and value.
- ///
- internal sealed class DebugConsoleUI
- {
- // Beyond this many candidates the list is truncated with a "+N more" row so a
- // large DraftPool can't grow the console bar off the top of the screen.
- private const int MaxVisibleSuggestions = 12;
-
- /// One row of the autocomplete list: a candidate token plus an optional dim hint.
- public readonly struct SuggestionRow
- {
- public readonly string Text;
- public readonly string Hint;
-
- public SuggestionRow(string text, string hint)
- {
- Text = text;
- Hint = hint;
- }
- }
-
- public TextField CommandInput { get; }
-
- private readonly VisualElement consoleContainer;
- private readonly VisualElement suggestionList;
-
- public DebugConsoleUI(VisualElement uiRoot)
- {
- // Top-anchored bar, closed by default. Distinct position from the HUD's
- // bottom-left chat panel so the two never overlap.
- consoleContainer = new VisualElement();
- consoleContainer.pickingMode = PickingMode.Ignore;
- consoleContainer.style.position = Position.Absolute;
- consoleContainer.style.left = 0;
- consoleContainer.style.right = 0;
- consoleContainer.style.top = 0;
- consoleContainer.style.paddingTop = 8;
- consoleContainer.style.paddingBottom = 8;
- consoleContainer.style.paddingLeft = 12;
- consoleContainer.style.paddingRight = 12;
- // Fully opaque: the console is a text-entry overlay that has to stay readable
- // over the level, the HUD, and whatever else is on screen.
- consoleContainer.style.backgroundColor = new Color(0f, 0f, 0f, 1f);
- consoleContainer.style.display = DisplayStyle.None;
-
- CommandInput = new TextField();
- CommandInput.style.minHeight = 28;
- CommandInput.style.width = Length.Percent(100);
- CommandInput.maxLength = 200;
- CommandInput.isDelayed = false;
- StyleConsoleInput(CommandInput);
-
- suggestionList = new VisualElement();
- suggestionList.pickingMode = PickingMode.Ignore;
- suggestionList.style.flexDirection = FlexDirection.Column;
- suggestionList.style.marginTop = 4;
- suggestionList.style.display = DisplayStyle.None;
-
- consoleContainer.Add(CommandInput);
- consoleContainer.Add(suggestionList);
- uiRoot.Add(consoleContainer);
- }
-
- // Mirrors HUDController.StyleChatInputDark — dark background, white text,
- // a thin border, and inner padding so ascenders/descenders don't clip.
- // Green border (vs. chat's neutral gray) as a quick visual "this is the
- // debug console, not chat" cue.
- private static void StyleConsoleInput(TextField field)
- {
- var darkBg = new Color(0.05f, 0.05f, 0.05f, 1f);
- var borderClr = new Color(0.35f, 0.75f, 0.35f);
-
- field.style.backgroundColor = darkBg;
- field.style.color = Color.white;
- field.style.borderTopWidth = field.style.borderBottomWidth =
- field.style.borderLeftWidth = field.style.borderRightWidth = 1;
- field.style.borderTopColor = field.style.borderBottomColor =
- field.style.borderLeftColor = field.style.borderRightColor = borderClr;
-
- var inner = field.Q("unity-text-input");
- if (inner != null)
- {
- inner.style.backgroundColor = darkBg;
- inner.style.color = Color.white;
- inner.style.paddingTop = 4;
- inner.style.paddingBottom = 4;
- inner.style.paddingLeft = 6;
- inner.style.paddingRight = 6;
- }
- }
-
- // One autocomplete row: the candidate token plus an optional dim hint. Rows are
- // pickingMode Ignore and keyboard-driven only — nothing here is clickable, which
- // avoids the recursive picking-mode problem documented in HUDController's chat
- // panel (clicks falling through to the game world via a ScrollView's internal
- // wrapper element).
- private static VisualElement BuildSuggestionRow(string text, string hint, bool selected)
- {
- var row = new VisualElement();
- row.pickingMode = PickingMode.Ignore;
- row.style.flexDirection = FlexDirection.Row;
- row.style.minHeight = 20;
- row.style.paddingLeft = 6;
- row.style.paddingRight = 6;
- row.style.backgroundColor = selected
- ? new Color(0.16f, 0.38f, 0.16f, 1f) // matches the green console cue
- : new Color(0.05f, 0.05f, 0.05f, 1f);
-
- var label = new Label(text);
- label.pickingMode = PickingMode.Ignore;
- label.style.color = Color.white;
- label.style.unityFontStyleAndWeight = selected ? FontStyle.Bold : FontStyle.Normal;
- row.Add(label);
-
- if (!string.IsNullOrEmpty(hint))
- {
- var hintLabel = new Label(hint);
- hintLabel.pickingMode = PickingMode.Ignore;
- hintLabel.style.color = new Color(0.6f, 0.6f, 0.6f);
- hintLabel.style.marginLeft = 12;
- row.Add(hintLabel);
- }
-
- return row;
- }
-
- public void Show()
- {
- consoleContainer.style.display = DisplayStyle.Flex;
- consoleContainer.pickingMode = PickingMode.Position;
- }
-
- public void Hide()
- {
- consoleContainer.style.display = DisplayStyle.None;
- consoleContainer.pickingMode = PickingMode.Ignore;
- }
-
- // Windows the list around selectedIndex so arrowing past MaxVisibleSuggestions
- // keeps the selected row on screen instead of scrolling it out of the truncated view.
- public void RenderSuggestions(List rows, int selectedIndex)
- {
- suggestionList.Clear();
-
- if (rows == null || rows.Count == 0)
- {
- suggestionList.style.display = DisplayStyle.None;
- return;
- }
-
- suggestionList.style.display = DisplayStyle.Flex;
-
- int start = rows.Count > MaxVisibleSuggestions
- ? Mathf.Clamp(selectedIndex - MaxVisibleSuggestions / 2, 0,
- rows.Count - MaxVisibleSuggestions)
- : 0;
- int end = Mathf.Min(start + MaxVisibleSuggestions, rows.Count);
-
- for (int i = start; i < end; i++)
- suggestionList.Add(BuildSuggestionRow(rows[i].Text, rows[i].Hint, i == selectedIndex));
-
- int hidden = rows.Count - (end - start);
- if (hidden > 0)
- suggestionList.Add(BuildSuggestionRow($"+{hidden} more", null, false));
- }
-
- public void ClearSuggestions()
- {
- suggestionList.Clear();
- suggestionList.style.display = DisplayStyle.None;
- }
- }
-}
diff --git a/Assets/_Project/Scripts/Dev/DebugConsoleUI.cs.meta b/Assets/_Project/Scripts/Dev/DebugConsoleUI.cs.meta
deleted file mode 100644
index 66e439e..0000000
--- a/Assets/_Project/Scripts/Dev/DebugConsoleUI.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: fce39b2f68ef0f3f9bec299aa46d353a
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Dev/DevWaveControls.cs b/Assets/_Project/Scripts/Dev/DevWaveControls.cs
index 9ffa05e..2dfd6ff 100644
--- a/Assets/_Project/Scripts/Dev/DevWaveControls.cs
+++ b/Assets/_Project/Scripts/Dev/DevWaveControls.cs
@@ -33,10 +33,6 @@ namespace TD.Dev
"verified now. Set to Key.None to use the OnGUI button only.")]
[SerializeField] private Key grantTowerHotkey = Key.F8;
- [Tooltip("Keyboard shortcut to skip every remaining encounter in the current phase and " +
- "go straight to its boss. Set to Key.None to use the OnGUI button only.")]
- [SerializeField] private Key skipToBossHotkey = Key.F7;
-
private void Update()
{
var kb = Keyboard.current;
@@ -47,9 +43,6 @@ namespace TD.Dev
if (grantTowerHotkey != Key.None && kb[grantTowerHotkey].wasPressedThisFrame)
TryGrantNextTower();
-
- if (skipToBossHotkey != Key.None && kb[skipToBossHotkey].wasPressedThisFrame)
- TrySkipToBoss();
}
private void OnGUI()
@@ -59,20 +52,13 @@ namespace TD.Dev
if (NetworkManager.Singleton == null || !NetworkManager.Singleton.IsServer)
return;
- // IMGUI draws after every runtime UI Toolkit panel, so this box would cover the
- // debug console's autocomplete list no matter what sorting order the console's
- // PanelSettings uses. Yield to the console while it's open.
- if (DebugConsole.IsOpen) return;
-
// Anchored below the top HUD bar so it doesn't overlap gold/wave/lives.
const float topOffset = 90f;
- GUI.Box(new Rect(10, topOffset, 180, 128), "Dev: Wave Controls");
+ GUI.Box(new Rect(10, topOffset, 180, 95), "Dev: Wave Controls");
if (GUI.Button(new Rect(20, topOffset + 25, 160, 28), "Force Next Wave"))
TryForceNextWave();
if (GUI.Button(new Rect(20, topOffset + 58, 160, 28), "Grant Next Tower"))
TryGrantNextTower();
- if (GUI.Button(new Rect(20, topOffset + 91, 160, 28), "Skip To Boss"))
- TrySkipToBoss();
}
// Dev stand-in for the draft system: grants the local player the first catalog
@@ -127,27 +113,5 @@ namespace TD.Dev
Debug.Log("[DevWaveControls] Forcing next wave.");
wm.ForceAdvanceToNextWave();
}
-
- // Skips straight to the phase boss so boss content can be tested without playing
- // every cycle first. Server-only, like the other cheats.
- private void TrySkipToBoss()
- {
- if (NetworkManager.Singleton == null || !NetworkManager.Singleton.IsServer)
- {
- Debug.LogWarning("[DevWaveControls] Skip-to-boss requested off the server. Ignored.");
- return;
- }
-
- var wm = WaveManager.Instance;
- if (wm == null)
- {
- Debug.LogWarning("[DevWaveControls] WaveManager.Instance is null. " +
- "Is the scene running with a WaveManager network-spawned?");
- return;
- }
-
- Debug.Log("[DevWaveControls] Skipping to boss.");
- wm.ForceAdvanceToBoss();
- }
}
}
diff --git a/Assets/_Project/Scripts/Editor/Core.meta b/Assets/_Project/Scripts/Editor/Core.meta
deleted file mode 100644
index 7db3e99..0000000
--- a/Assets/_Project/Scripts/Editor/Core.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 75cc168bfd0b91b41a313a4389d31910
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Scripts/Editor/Core/PoolWeightDrawer.cs b/Assets/_Project/Scripts/Editor/Core/PoolWeightDrawer.cs
deleted file mode 100644
index 3321b16..0000000
--- a/Assets/_Project/Scripts/Editor/Core/PoolWeightDrawer.cs
+++ /dev/null
@@ -1,66 +0,0 @@
-// Assets/_Project/Scripts/Editor/Core/PoolWeightDrawer.cs
-using UnityEditor;
-using UnityEngine;
-using TD.Core;
-
-namespace TD.Editor.Core
-{
- ///
- /// Draws a field as a 0–1 slider, with an inline warning
- /// rendered directly beneath it whenever the weight is zero.
- ///
- ///
- /// Deliberately does not clamp, revert, or disable the field — a designer muting an entry
- /// while tuning is normal. The drawer's only job is to make the consequence visible at the
- /// point of editing, since a zero-weight entry fails silently at runtime.
- ///
- [CustomPropertyDrawer(typeof(PoolWeightAttribute))]
- public class PoolWeightDrawer : PropertyDrawer
- {
- // Two lines of text alongside the icon column need more than one line of box height.
- private const float HelpBoxLines = 2.4f;
-
- public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
- {
- float h = EditorGUIUtility.singleLineHeight;
-
- if (IsZero(property))
- h += EditorGUIUtility.standardVerticalSpacing + HelpBoxHeight();
-
- return h;
- }
-
- public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
- {
- if (property.propertyType != SerializedPropertyType.Float)
- {
- EditorGUI.LabelField(position, label.text, "[PoolWeight] only supports float fields.");
- return;
- }
-
- var sliderRect = new Rect(position.x, position.y, position.width,
- EditorGUIUtility.singleLineHeight);
- EditorGUI.Slider(sliderRect, property, 0f, 1f, label);
-
- if (!IsZero(property)) return;
-
- var attr = (PoolWeightAttribute)attribute;
- var boxRect = new Rect(
- position.x,
- position.y + EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing,
- position.width,
- HelpBoxHeight());
-
- EditorGUI.HelpBox(
- boxRect,
- $"Weight is 0 — this {attr.Noun} can never be drawn and will not appear in game. " +
- $"Raise it above 0 to put it back in the pool.",
- MessageType.Warning);
- }
-
- private static bool IsZero(SerializedProperty property)
- => property.propertyType == SerializedPropertyType.Float && property.floatValue <= 0f;
-
- private static float HelpBoxHeight() => EditorGUIUtility.singleLineHeight * HelpBoxLines;
- }
-}
diff --git a/Assets/_Project/Scripts/Editor/Core/PoolWeightDrawer.cs.meta b/Assets/_Project/Scripts/Editor/Core/PoolWeightDrawer.cs.meta
deleted file mode 100644
index 3ec645e..0000000
--- a/Assets/_Project/Scripts/Editor/Core/PoolWeightDrawer.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 8ba57381fc7e6a3479e59d84e45720d9
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Editor/Gameplay/EncounterGoldEntryDrawer.cs b/Assets/_Project/Scripts/Editor/Gameplay/EncounterGoldEntryDrawer.cs
deleted file mode 100644
index 737b34a..0000000
--- a/Assets/_Project/Scripts/Editor/Gameplay/EncounterGoldEntryDrawer.cs
+++ /dev/null
@@ -1,122 +0,0 @@
-// Assets/_Project/Scripts/Editor/Gameplay/EncounterGoldEntryDrawer.cs
-using UnityEditor;
-using UnityEngine;
-using TD.Gameplay;
-
-namespace TD.Editor.Gameplay
-{
- ///
- /// Custom property drawer for . Labels each element by its
- /// run position ("Encounter 3") rather than "Element 2", and renders a projected-earnings
- /// line beneath the fields so the economy can be tuned without arithmetic.
- ///
- ///
- /// Projections are estimates by necessity. Kill income depends on how many enemies the
- /// encounter spawns, and the wave filling each slot is drawn at runtime from a phase pool —
- /// so the drawer multiplies against
- /// instead. The cumulative figure is the number worth watching: it answers "how much could a
- /// player have banked by this point in the run", which is what tower prices get balanced
- /// against.
- ///
- [CustomPropertyDrawer(typeof(EncounterGoldEntry))]
- public class EncounterGoldEntryDrawer : PropertyDrawer
- {
- private const float PreviewLineExtraHeight = 4f;
-
- public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
- {
- if (!property.isExpanded) return EditorGUIUtility.singleLineHeight;
-
- float h = EditorGUIUtility.singleLineHeight; // foldout
- h += SpacedLineHeight() * 3; // the three gold fields
- h += SpacedLineHeight() * 2 + PreviewLineExtraHeight; // projection + cumulative
- return h;
- }
-
- public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
- {
- int encounterNumber = EncounterNumberFrom(property);
-
- // Re-label the foldout by run position. Element indices are 0-based and read as
- // off-by-one against everything else in the run (RunState, the HUD, the checklist),
- // which made miscounting the boss slot easy.
- var header = new GUIContent(encounterNumber > 0
- ? $"Encounter {encounterNumber}"
- : label.text);
-
- Rect headerRect = new Rect(position.x, position.y, position.width,
- EditorGUIUtility.singleLineHeight);
- property.isExpanded = EditorGUI.Foldout(headerRect, property.isExpanded, header,
- toggleOnLabelClick: true);
- if (!property.isExpanded) return;
-
- float y = position.y + SpacedLineHeight();
- const float indent = 14f;
- Rect Row()
- {
- Rect r = new Rect(position.x + indent, y, position.width - indent,
- EditorGUIUtility.singleLineHeight);
- y += SpacedLineHeight();
- return r;
- }
-
- var perEnemyProp = property.FindPropertyRelative("GoldPerEnemy");
- var completionProp = property.FindPropertyRelative("CompletionBonus");
- var noLeaksProp = property.FindPropertyRelative("NoLeaksBonus");
-
- EditorGUI.PropertyField(Row(), perEnemyProp);
- EditorGUI.PropertyField(Row(), completionProp);
- EditorGUI.PropertyField(Row(), noLeaksProp);
-
- // Read values off the SerializedProperties rather than the backing object: edits
- // aren't applied to the instance until ApplyModifiedProperties runs at end of frame,
- // so the object would show stale numbers while the designer is still typing.
- int perEnemy = perEnemyProp.intValue;
- int completion = completionProp.intValue;
- int noLeaks = noLeaksProp.intValue;
-
- var config = property.serializedObject.targetObject as GoldConfig;
- int assumed = config != null ? config.PreviewEnemiesPerEncounter : 0;
-
- int projected = perEnemy * assumed + completion + noLeaks;
-
- y += PreviewLineExtraHeight;
- var style = new GUIStyle(EditorStyles.miniLabel) { fontStyle = FontStyle.Italic };
-
- using (new EditorGUI.DisabledScope(true))
- {
- EditorGUI.LabelField(
- Row(),
- $"Projected: {projected} g ({perEnemy} × {assumed} enemies " +
- $"+ {completion} clear + {noLeaks} no-leak)",
- style);
-
- // Cumulative only makes sense once we know where this entry sits in the run.
- if (config != null && encounterNumber > 0)
- {
- EditorGUI.LabelField(
- Row(),
- $"Cumulative by here: {config.ProjectedCumulativeThrough(encounterNumber)} g " +
- $"(incl. {config.StartingGold} starting)",
- style);
- }
- }
- }
-
- // Recovers the 1-based run position from a path like "Encounters.Array.data[2]".
- // Returns 0 when the entry isn't being drawn as an array element.
- private static int EncounterNumberFrom(SerializedProperty property)
- {
- string path = property.propertyPath;
- int open = path.LastIndexOf('[');
- int close = path.LastIndexOf(']');
- if (open < 0 || close < open) return 0;
-
- string inner = path.Substring(open + 1, close - open - 1);
- return int.TryParse(inner, out int index) ? index + 1 : 0;
- }
-
- private static float SpacedLineHeight()
- => EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
- }
-}
diff --git a/Assets/_Project/Scripts/Editor/Gameplay/EncounterGoldEntryDrawer.cs.meta b/Assets/_Project/Scripts/Editor/Gameplay/EncounterGoldEntryDrawer.cs.meta
deleted file mode 100644
index 74b7ba7..0000000
--- a/Assets/_Project/Scripts/Editor/Gameplay/EncounterGoldEntryDrawer.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 62f688cf14ba23b42bd0edd63a11ba62
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Editor/Gameplay/RunDefinitionEditor.cs b/Assets/_Project/Scripts/Editor/Gameplay/RunDefinitionEditor.cs
deleted file mode 100644
index 0c08333..0000000
--- a/Assets/_Project/Scripts/Editor/Gameplay/RunDefinitionEditor.cs
+++ /dev/null
@@ -1,69 +0,0 @@
-// Assets/_Project/Scripts/Editor/Gameplay/RunDefinitionEditor.cs
-using UnityEditor;
-using UnityEngine;
-using TD.Gameplay.Waves;
-
-namespace TD.Editor.Gameplay
-{
- ///
- /// Inspector for . Draws the normal fields, then a live
- /// validation panel reporting whether the authored pools can actually produce a run.
- ///
- ///
- /// The check that matters here is the distinct-enemy-type one: because enemy upgrades are
- /// keyed to a wave slot, a phase must be able to fill its cycle with waves that use different
- /// enemies. A pool can look healthy (plenty of entries) and still fail that. Reporting it in
- /// the inspector turns a startup error into something a designer sees while authoring.
- ///
- [CustomEditor(typeof(RunDefinition))]
- public class RunDefinitionEditor : UnityEditor.Editor
- {
- public override void OnInspectorGUI()
- {
- DrawDefaultInspector();
-
- var run = (RunDefinition)target;
-
- EditorGUILayout.Space();
- EditorGUILayout.LabelField("Validation", EditorStyles.boldLabel);
-
- bool ok = run.Validate(out string report);
- EditorGUILayout.HelpBox(report, ok ? MessageType.Info : MessageType.Error);
-
- // Per-phase capacity readout — the fastest way to see how much headroom a pool has
- // before the distinct-type requirement starts biting.
- if (run.Phases != null)
- {
- EditorGUILayout.Space();
- EditorGUILayout.LabelField("Phase capacity", EditorStyles.boldLabel);
-
- for (int i = 0; i < run.Phases.Length; i++)
- {
- var phase = run.Phases[i];
- if (phase == null)
- {
- EditorGUILayout.LabelField($"Phase {i + 1}", "— empty —");
- continue;
- }
-
- int distinct = phase.CountDistinctEnemyTypes();
- string value = $"{distinct} distinct enemy type(s) / {run.WavesPerCycle} needed";
- var style = new GUIStyle(EditorStyles.label)
- {
- normal = { textColor = distinct >= run.WavesPerCycle
- ? EditorStyles.label.normal.textColor
- : new Color(0.9f, 0.4f, 0.3f) }
- };
- EditorGUILayout.LabelField(phase.Label, value, style);
- }
- }
-
- EditorGUILayout.Space();
- if (GUILayout.Button("Rebuild Wave Index"))
- {
- run.RebuildIndex();
- Debug.Log($"[RunDefinition] Rebuilt wave index: {run.WaveCount} distinct wave(s).");
- }
- }
- }
-}
diff --git a/Assets/_Project/Scripts/Editor/Gameplay/RunDefinitionEditor.cs.meta b/Assets/_Project/Scripts/Editor/Gameplay/RunDefinitionEditor.cs.meta
deleted file mode 100644
index 2b46f6d..0000000
--- a/Assets/_Project/Scripts/Editor/Gameplay/RunDefinitionEditor.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 5f5e8e0197efd154b92817bbb3028b35
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Editor/Gameplay/WaveGoldEntryDrawer.cs b/Assets/_Project/Scripts/Editor/Gameplay/WaveGoldEntryDrawer.cs
new file mode 100644
index 0000000..dff5ca8
--- /dev/null
+++ b/Assets/_Project/Scripts/Editor/Gameplay/WaveGoldEntryDrawer.cs
@@ -0,0 +1,112 @@
+// Assets/_Project/Scripts/Editor/Gameplay/WaveGoldEntryDrawer.cs
+using UnityEditor;
+using UnityEngine;
+using TD.Gameplay;
+
+namespace TD.Editor.Gameplay
+{
+ ///
+ /// Custom property drawer for . Renders the standard fields
+ /// followed by a read-only "Preview Total" line computed from the entry's values, so
+ /// designers can see at a glance how much a single player could earn from a wave.
+ ///
+ ///
+ /// The preview includes the kill-reward portion only when the entry's Wave
+ /// reference is assigned (it's needed to count enemies in that wave). When unset, the
+ /// preview shows just CompletionBonus + NoLeaksBonus and labels itself so the
+ /// designer knows to drop a WaveDefinition in for a complete number.
+ ///
+ [CustomPropertyDrawer(typeof(WaveGoldEntry))]
+ public class WaveGoldEntryDrawer : PropertyDrawer
+ {
+ private const float PreviewLineExtraHeight = 4f;
+
+ public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
+ {
+ // Sum the children + one extra line for the preview label. We render children
+ // ourselves rather than using EditorGUI.PropertyField default since we want a
+ // foldout with our custom preview tacked onto the end.
+ if (!property.isExpanded) return EditorGUIUtility.singleLineHeight;
+
+ float h = EditorGUIUtility.singleLineHeight; // foldout
+ h += SpacedLineHeight() * 4; // Wave + GoldPerEnemy + Completion + NoLeaks
+ h += SpacedLineHeight() + PreviewLineExtraHeight; // preview label
+ return h;
+ }
+
+ public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
+ {
+ // Foldout header.
+ Rect headerRect = new Rect(position.x, position.y, position.width,
+ EditorGUIUtility.singleLineHeight);
+ property.isExpanded = EditorGUI.Foldout(headerRect, property.isExpanded, label,
+ toggleOnLabelClick: true);
+ if (!property.isExpanded) return;
+
+ float y = position.y + SpacedLineHeight();
+ float indent = 14f;
+ Rect Row()
+ {
+ Rect r = new Rect(position.x + indent, y, position.width - indent,
+ EditorGUIUtility.singleLineHeight);
+ y += SpacedLineHeight();
+ return r;
+ }
+
+ // Standard property fields.
+ var waveProp = property.FindPropertyRelative("Wave");
+ var perEnemyProp = property.FindPropertyRelative("GoldPerEnemy");
+ var completionProp = property.FindPropertyRelative("CompletionBonus");
+ var noLeaksProp = property.FindPropertyRelative("NoLeaksBonus");
+
+ EditorGUI.PropertyField(Row(), waveProp);
+ EditorGUI.PropertyField(Row(), perEnemyProp);
+ EditorGUI.PropertyField(Row(), completionProp);
+ EditorGUI.PropertyField(Row(), noLeaksProp);
+
+ // Compute the preview total. We instantiate the same logic as the runtime
+ // property — read the serialized values, count enemies in the optional Wave,
+ // sum it all up. Done inline (rather than calling WaveGoldEntry.PreviewTotalGold
+ // directly) because SerializedProperty edits aren't applied to the target object
+ // until ApplyModifiedProperties runs at the end of the frame, so reading the
+ // backing class instance here would show stale data while the user is typing.
+ int perEnemy = perEnemyProp.intValue;
+ int completion = completionProp.intValue;
+ int noLeaks = noLeaksProp.intValue;
+ var waveAsset = waveProp.objectReferenceValue as WaveDefinition;
+
+ int enemyCount = 0;
+ if (waveAsset != null && waveAsset.Entries != null)
+ {
+ foreach (var e in waveAsset.Entries)
+ {
+ if (e.EnemyType != null && e.Count > 0)
+ enemyCount += e.Count;
+ }
+ }
+
+ int total = perEnemy * enemyCount + completion + noLeaks;
+
+ // Preview line. Slightly dimmed style, italic, non-editable. Includes a
+ // breakdown so the designer can see where the number came from.
+ y += PreviewLineExtraHeight;
+ Rect previewRect = new Rect(position.x + indent, y, position.width - indent,
+ EditorGUIUtility.singleLineHeight);
+
+ string breakdown = waveAsset != null
+ ? $"Preview Total: {total} g ({perEnemy} × {enemyCount} enemies " +
+ $"+ {completion} completion + {noLeaks} no-leak)"
+ : $"Preview Total: {completion + noLeaks} g (bonuses only — assign Wave " +
+ $"to include kill rewards)";
+
+ var prevStyle = new GUIStyle(EditorStyles.miniLabel) { fontStyle = FontStyle.Italic };
+ using (new EditorGUI.DisabledScope(true))
+ {
+ EditorGUI.LabelField(previewRect, breakdown, prevStyle);
+ }
+ }
+
+ private static float SpacedLineHeight()
+ => EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
+ }
+}
diff --git a/Assets/_Project/Scripts/Editor/Gameplay/WaveGoldEntryDrawer.cs.meta b/Assets/_Project/Scripts/Editor/Gameplay/WaveGoldEntryDrawer.cs.meta
new file mode 100644
index 0000000..b9c6f5d
--- /dev/null
+++ b/Assets/_Project/Scripts/Editor/Gameplay/WaveGoldEntryDrawer.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: cc17000144b098340a210921594babca
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Gameplay/ActiveBuff.cs b/Assets/_Project/Scripts/Gameplay/ActiveBuff.cs
new file mode 100644
index 0000000..3a54012
--- /dev/null
+++ b/Assets/_Project/Scripts/Gameplay/ActiveBuff.cs
@@ -0,0 +1,61 @@
+// 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
new file mode 100644
index 0000000..d6868bd
--- /dev/null
+++ b/Assets/_Project/Scripts/Gameplay/ActiveBuff.cs.meta
@@ -0,0 +1,2 @@
+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
new file mode 100644
index 0000000..bad6ee3
--- /dev/null
+++ b/Assets/_Project/Scripts/Gameplay/BuffCategory.cs
@@ -0,0 +1,23 @@
+// 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
new file mode 100644
index 0000000..c2d766d
--- /dev/null
+++ b/Assets/_Project/Scripts/Gameplay/BuffCategory.cs.meta
@@ -0,0 +1,2 @@
+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
new file mode 100644
index 0000000..951972e
--- /dev/null
+++ b/Assets/_Project/Scripts/Gameplay/BuffDefinition.cs
@@ -0,0 +1,24 @@
+// 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
new file mode 100644
index 0000000..12b38db
--- /dev/null
+++ b/Assets/_Project/Scripts/Gameplay/BuffDefinition.cs.meta
@@ -0,0 +1,2 @@
+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 955f301..ce1e258 100644
--- a/Assets/_Project/Scripts/Gameplay/BuilderInputController.cs
+++ b/Assets/_Project/Scripts/Gameplay/BuilderInputController.cs
@@ -85,7 +85,6 @@ namespace TD.Gameplay
// Cached reference to the local TowerPaintController, looked up lazily (same
// rationale as the placement controller).
private TowerPaintController cachedPaintController;
- private BuilderSpellCastController cachedSpellCastController;
// ----- Lifecycle --------------------------------------------------
@@ -121,7 +120,7 @@ namespace TD.Gameplay
// Placement and paint are both modal: while either is active, the local
// controller for that mode owns left-/right-click, so selection here yields.
- bool isModal = IsLocalPlayerPlacing() || IsLocalPlayerPainting() || IsLocalPlayerAimingSpell();
+ bool isModal = IsLocalPlayerPlacing() || IsLocalPlayerPainting();
Vector2 mousePos = mouse.position.ReadValue();
// UI Toolkit dispatches button click events AFTER Update runs, but raw mouse
@@ -139,11 +138,10 @@ namespace TD.Gameplay
// Escape: clear selection. Allowed during placement mode too — Escape never
// means anything else here, and clearing selection during placement is fine.
- // Suppressed while chat (or any HUD text field) has focus or the gear menu is
- // open, since Escape there means "cancel typing" / "back out of the menu" and
- // should not also clear the unit selection.
+ // Suppressed while chat (or any HUD text field) has focus, since Escape there
+ // means "cancel typing" and should not also clear the unit selection.
if (keyboard != null && keyboard.escapeKey.wasPressedThisFrame
- && !HUDController.IsUiCapturingInput)
+ && !HUDController.IsTextInputActive)
{
SelectionState.Instance?.Clear();
}
@@ -152,21 +150,13 @@ namespace TD.Gameplay
// controllers handle right-click as cancel there) and when over HUD.
if (isModal) return;
- // Tab: select the builder, or (if already selected) recenter the camera on it.
- if (keyboard != null && keyboard.tabKey.wasPressedThisFrame
- && !HUDController.IsUiCapturingInput)
+ // B: toggle buff menu.
+ if (keyboard != null && keyboard.bKey.wasPressedThisFrame
+ && !HUDController.IsTextInputActive)
{
- var selection = SelectionState.Instance;
- if (selection != null && selection.IsSelected(builder))
- {
- HUDController.Instance?.CenterCameraOnSelection();
- }
- else
- {
- selection?.Select(builder);
- }
+ HUDController.Instance?.ToggleBuffMenu();
}
-
+
if (pointerOverHud) return;
if (!mouse.rightButton.wasPressedThisFrame) return;
@@ -287,21 +277,5 @@ namespace TD.Gameplay
}
return cachedPaintController.IsPainting;
}
-
- private bool IsLocalPlayerAimingSpell()
- {
- if (cachedSpellCastController == null)
- {
- // Find lazily — controller may have been added after this component spawned.
- cachedSpellCastController =
- UnityEngine.Object.FindAnyObjectByType();
- if (cachedSpellCastController == null) return false;
- }
- // IsAiming covers "aiming, click not yet made"; ConsumedCastClickThisFrame covers the
- // frame the confirming click casts and exits aim mode — together they suppress the
- // selection click regardless of Update order between the two controllers.
- return cachedSpellCastController.IsAiming
- || cachedSpellCastController.ConsumedCastClickThisFrame;
- }
}
}
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Gameplay/BuilderSpellCastController.cs b/Assets/_Project/Scripts/Gameplay/BuilderSpellCastController.cs
index 557fe3d..49160da 100644
--- a/Assets/_Project/Scripts/Gameplay/BuilderSpellCastController.cs
+++ b/Assets/_Project/Scripts/Gameplay/BuilderSpellCastController.cs
@@ -56,21 +56,6 @@ namespace TD.Gameplay
// -1 when not aiming.
private int activeSlot = -1;
-
- // Frame on which the confirming cast click was consumed. Lets the selection controller
- // ignore that click order-independently — see ConsumedCastClickThisFrame.
- private int lastCastClickFrame = -1;
-
- /// True while a spell is being aimed (awaiting the confirming click). The
- /// selection input controller treats this as a modal state, so the click that confirms
- /// the cast doesn't also fall through to selection and deselect the builder.
- public bool IsAiming => activeSlot >= 0;
-
- /// True during the frame the confirming cast click was consumed. Aim mode exits
- /// the same frame the cast is submitted, flipping false; this stays
- /// true for the rest of that frame so the selection controller ignores the click no matter
- /// which controller's Update ran first.
- public bool ConsumedCastClickThisFrame => lastCastClickFrame == Time.frameCount;
private BuilderSpellDefinition activeDefinition;
private bool lastHitValid;
@@ -95,19 +80,11 @@ namespace TD.Gameplay
private void Update()
{
- if (!HUDController.IsUiCapturingInput)
+ if (!HUDController.IsTextInputActive)
ScanHotkeys();
if (activeSlot < 0) return; // idle — nothing to aim
- // Deselecting the builder mid-aim cancels the cast, so you can't start aiming while
- // selected and then deselect to sneak a cast past the gate above.
- if (!LocalBuilderSelected())
- {
- ExitAimMode();
- return;
- }
-
var keyboard = Keyboard.current;
if (keyboard != null && keyboard.escapeKey.wasPressedThisFrame)
{
@@ -138,32 +115,16 @@ namespace TD.Gameplay
if (mouse.leftButton.wasPressedThisFrame && lastHitValid)
{
- // Record the frame so the selection controller ignores this click even though
- // TrySubmitCast exits aim mode this same frame (IsAiming flips false). Without
- // this, whether the click also deselected the builder depended on Update order.
- lastCastClickFrame = Time.frameCount;
TrySubmitCast();
}
}
// ----- Hotkey scan --------------------------------------------------
- // True when the local player's own builder is the current selection. Casting is gated
- // on this so it matches the spell HUD (shown only for the selected builder).
- private static bool LocalBuilderSelected()
- {
- var selected = SelectionState.Instance?.SelectedObject;
- return selected is Builder builder && builder.IsOwner;
- }
-
private void ScanHotkeys()
{
if (activeSlot >= 0) return; // already aiming — hotkeys re-scanned only when idle
- // Spells cast only while the local player's OWN builder is selected — keeps casting
- // in sync with the spell HUD, which is shown only for the selected builder.
- if (!LocalBuilderSelected()) return;
-
var loadout = PlayerSpellLoadout.Local;
if (loadout == null) return;
diff --git a/Assets/_Project/Scripts/Gameplay/BuilderSpells/BuilderSpellDefinition.cs b/Assets/_Project/Scripts/Gameplay/BuilderSpells/BuilderSpellDefinition.cs
index 2bf7c9e..e1ca3b2 100644
--- a/Assets/_Project/Scripts/Gameplay/BuilderSpells/BuilderSpellDefinition.cs
+++ b/Assets/_Project/Scripts/Gameplay/BuilderSpells/BuilderSpellDefinition.cs
@@ -24,9 +24,8 @@ namespace TD.Gameplay.BuilderSpells
/// Server-only resolution, client-only visual. runs on
/// the server and applies damage/status via the same Physics.OverlapSphereNonAlloc +
/// EnemyHealth/EnemyStatus pattern TowerCombat already uses.
- /// Presentation is split into (on cast) and
- /// (on contact, seconds later for a
- /// projectile spell), both running on every peer including the server.
+ /// runs on every peer (including the server) after a successful
+ /// cast, purely for presentation.
///
public abstract class BuilderSpellDefinition : ScriptableObject
{
@@ -83,28 +82,10 @@ namespace TD.Gameplay.BuilderSpells
public abstract bool ServerCast(ulong clientId, Vector3 targetPoint);
///
- /// Seconds between the cast and the effect landing. 0 = instant: damage and the impact
- /// sound resolve immediately on cast. A projectile-style spell (e.g. the Fireball meteor)
- /// overrides this with its fall/travel time, so spawns the
- /// visual on cast but holds (damage) and
- /// (sound) until the projectile reaches the ground. Set it to match the VFX's fall time.
+ /// Runs on every peer (via 's ClientRpc) after a
+ /// successful . Default no-op; override to spawn an impact VFX
+ /// prefab and self-destroy it, the same idiom used elsewhere for one-off visuals.
///
- public virtual float ImpactDelay => 0f;
-
- ///
- /// Runs on every peer (via 's ClientRpc) the moment the
- /// spell is cast. Spawns the visual: for an instant spell that's the whole effect; for a
- /// delayed spell it's the projectile/travel visual that lands after .
- /// Default no-op.
- ///
- public virtual void ClientSpawnVfx(Vector3 targetPoint) { }
-
- ///
- /// Runs on every peer when the spell makes contact — immediately for an instant spell, or
- /// seconds after cast for a delayed one. Play the impact sound
- /// (and any impact-moment visual) here so it lands with the effect, not the throw.
- /// Default no-op.
- ///
- public virtual void ClientPlayImpact(Vector3 targetPoint) { }
+ public virtual void ClientPlayVfx(Vector3 targetPoint) { }
}
}
diff --git a/Assets/_Project/Scripts/Gameplay/BuilderSpells/BuilderSpellPool.cs b/Assets/_Project/Scripts/Gameplay/BuilderSpells/BuilderSpellPool.cs
index 1327be9..2f44df6 100644
--- a/Assets/_Project/Scripts/Gameplay/BuilderSpells/BuilderSpellPool.cs
+++ b/Assets/_Project/Scripts/Gameplay/BuilderSpells/BuilderSpellPool.cs
@@ -14,8 +14,7 @@ namespace TD.Gameplay.BuilderSpells
///
/// Plain MonoBehaviour: identical on every peer (same assets), so there is nothing to sync.
/// The server reads it to resolve casts; clients read it to resolve / and to render the cast hotbar. Mirrors
+ /// cref="BuilderSpellDefinition.ClientPlayVfx"/> and to render the cast hotbar. Mirrors
/// exactly.
///
public class BuilderSpellPool : MonoBehaviour
diff --git a/Assets/_Project/Scripts/Gameplay/BuilderSpells/FireballSpellDefinition.cs b/Assets/_Project/Scripts/Gameplay/BuilderSpells/FireballSpellDefinition.cs
index b4917ca..b5dae1f 100644
--- a/Assets/_Project/Scripts/Gameplay/BuilderSpells/FireballSpellDefinition.cs
+++ b/Assets/_Project/Scripts/Gameplay/BuilderSpells/FireballSpellDefinition.cs
@@ -28,14 +28,6 @@ namespace TD.Gameplay.BuilderSpells
[Min(0f)]
[SerializeField] private float impactVfxLifetime = 2f;
- [Tooltip("Seconds from cast until the falling fireball reaches the ground. Damage and the " +
- "impact sound are held until then so they land with the visual — SET THIS TO MATCH " +
- "the meteor VFX's fall time. 0 = everything resolves instantly on cast.")]
- [Min(0f)]
- [SerializeField] private float impactDelay = 0.8f;
-
- public override float ImpactDelay => impactDelay;
-
[Header("Impact Sound")]
[Tooltip("Sound played on every peer when this spell resolves successfully. Same " +
"SoundConfig struct TowerBuiltSound uses — reuse a tower's clip directly if " +
@@ -67,18 +59,13 @@ namespace TD.Gameplay.BuilderSpells
return hitAny;
}
- public override void ClientSpawnVfx(Vector3 targetPoint)
+ public override void ClientPlayVfx(Vector3 targetPoint)
{
- // The meteor VFX plays its full fall-and-crash animation from here; it reaches the
- // ground after ImpactDelay, which is when ClientPlayImpact and ServerCast fire.
if (impactVfxPrefab != null)
Destroy(Instantiate(impactVfxPrefab, targetPoint, Quaternion.identity), impactVfxLifetime);
- }
- public override void ClientPlayImpact(Vector3 targetPoint)
- {
if (impactSound.clip != null)
- AudioManager.Instance?.Play(impactSound.clip, AudioCategory.Combat,
+ AudioManager.Instance?.Play(impactSound.clip, AudioCategory.TowerFire,
impactSound.RandomPitch(), impactSound.volume);
}
}
diff --git a/Assets/_Project/Scripts/Gameplay/BuilderSpells/SlowAreaSpellDefinition.cs b/Assets/_Project/Scripts/Gameplay/BuilderSpells/SlowAreaSpellDefinition.cs
index 4885848..4bee6bd 100644
--- a/Assets/_Project/Scripts/Gameplay/BuilderSpells/SlowAreaSpellDefinition.cs
+++ b/Assets/_Project/Scripts/Gameplay/BuilderSpells/SlowAreaSpellDefinition.cs
@@ -1,6 +1,5 @@
// Assets/_Project/Scripts/Gameplay/BuilderSpells/SlowAreaSpellDefinition.cs
using UnityEngine;
-using TD.Audio;
using TD.Core;
namespace TD.Gameplay.BuilderSpells
@@ -14,36 +13,19 @@ namespace TD.Gameplay.BuilderSpells
/// while aiming, and this spell resolves against that same radius (no separate field).
/// Reuses the existing slow/DoT system () rather than
/// introducing a new status mechanism.
- ///
- /// The cast spawns a that lingers for
- /// and keeps re-applying the slow to anything standing in
- /// the radius, so enemies walking through the area get slowed rather than only whatever
- /// happened to be standing there at the exact instant of cast.
- ///
///
[CreateAssetMenu(fileName = "SlowAreaSpell", menuName = "TD/Builder Spells/Slow Area")]
public class SlowAreaSpellDefinition : BuilderSpellDefinition
{
public override BuilderSpellKind Kind => BuilderSpellKind.SlowArea;
- [Header("Area VFX")]
- [Tooltip("Prefab spawned at the target point on a successful cast, authored at a 1-unit " +
- "radius — scaled up to match Radius here. Lives for EffectDuration, matching " +
- "how long the slow itself lasts. Optional — leave empty for no visual.")]
- [SerializeField] private GameObject areaVfxPrefab;
-
- [Tooltip("Sound played on every peer when this spell resolves successfully.")]
- [SerializeField] private SoundConfig areaSound;
-
[Header("Slow Area")]
[Tooltip("Speed multiplier applied to affected enemies for EffectDuration " +
"(e.g. 0.5 = half speed).")]
[Range(0f, 1f)]
public float SlowFactor = 0.5f;
- [Tooltip("Seconds the slow lasts on an affected enemy, and how long the area itself " +
- "lingers, re-slowing anything that walks through it. Re-casting on an " +
- "already-slowed enemy refreshes it.")]
+ [Tooltip("Seconds the slow lasts. Re-casting on an already-slowed enemy refreshes it.")]
[Min(0f)]
public float EffectDuration = 3f;
@@ -51,28 +33,23 @@ namespace TD.Gameplay.BuilderSpells
{
PlayerSlot owner = PlayerMatchState.SlotForClient(clientId);
- SlowAreaZone.Spawn(targetPoint, Mathf.Max(Radius, 0.01f), SlowFactor,
- EffectDuration, enemyLayerMask, owner);
+ int count = Physics.OverlapSphereNonAlloc(
+ targetPoint, Mathf.Max(Radius, 0.01f), s_overlapBuffer, enemyLayerMask);
- return true;
- }
-
- public override void ClientSpawnVfx(Vector3 targetPoint)
- {
- if (areaVfxPrefab != null)
+ bool hitAny = false;
+ for (int i = 0; i < count; i++)
{
- var instance = Instantiate(areaVfxPrefab, targetPoint, Quaternion.identity);
- instance.transform.localScale *= Mathf.Max(Radius, 0.01f);
- Destroy(instance, EffectDuration);
- }
- }
+ var enemyHealth = s_overlapBuffer[i].GetComponent();
+ if (enemyHealth == null || enemyHealth.IsDead) continue;
- public override void ClientPlayImpact(Vector3 targetPoint)
- {
- // Instant spell (ImpactDelay = 0), so this fires on cast alongside ClientSpawnVfx.
- if (areaSound.clip != null)
- AudioManager.Instance?.Play(areaSound.clip, AudioCategory.Combat,
- areaSound.RandomPitch(), areaSound.volume);
+ var enemyStatus = s_overlapBuffer[i].GetComponent();
+ if (enemyStatus == null) continue;
+
+ enemyStatus.ApplyEffect(DamageType.Cold, SlowFactor, EffectDuration, owner);
+ hitAny = true;
+ }
+
+ return hitAny;
}
}
}
diff --git a/Assets/_Project/Scripts/Gameplay/BuilderSpells/SlowAreaUpgradedSpellDefinition.cs b/Assets/_Project/Scripts/Gameplay/BuilderSpells/SlowAreaUpgradedSpellDefinition.cs
deleted file mode 100644
index dd339f7..0000000
--- a/Assets/_Project/Scripts/Gameplay/BuilderSpells/SlowAreaUpgradedSpellDefinition.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-// Assets/_Project/Scripts/Gameplay/BuilderSpells/SlowAreaUpgradedSpellDefinition.cs
-
-using TD.Core;
-using UnityEngine;
-
-namespace TD.Gameplay.BuilderSpells
-{
- ///
- /// Draft upgrade for — identical behavior, just a
- /// distinct so can replace the base spell
- /// with this one in the player's loadout. All fields (SlowFactor, EffectDuration, etc.)
- /// are inherited; author this asset with a longer EffectDuration than the base spell.
- ///
- [CreateAssetMenu(fileName = "SlowAreaUpgradedSpell",
- menuName = "TD/Builder Spells/Slow Area (Upgraded)")]
- public class SlowAreaUpgradedSpellDefinition : SlowAreaSpellDefinition
- {
- public override BuilderSpellKind Kind => BuilderSpellKind.SlowAreaUpgraded;
- }
-}
diff --git a/Assets/_Project/Scripts/Gameplay/BuilderSpells/SlowAreaUpgradedSpellDefinition.cs.meta b/Assets/_Project/Scripts/Gameplay/BuilderSpells/SlowAreaUpgradedSpellDefinition.cs.meta
deleted file mode 100644
index af8991f..0000000
--- a/Assets/_Project/Scripts/Gameplay/BuilderSpells/SlowAreaUpgradedSpellDefinition.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 37cc60a4a6e53e24cb6d1c0be310868a
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Gameplay/BuilderSpells/SlowAreaZone.cs b/Assets/_Project/Scripts/Gameplay/BuilderSpells/SlowAreaZone.cs
deleted file mode 100644
index de1c76a..0000000
--- a/Assets/_Project/Scripts/Gameplay/BuilderSpells/SlowAreaZone.cs
+++ /dev/null
@@ -1,77 +0,0 @@
-// Assets/_Project/Scripts/Gameplay/BuilderSpells/SlowAreaZone.cs
-using UnityEngine;
-using TD.Core;
-
-namespace TD.Gameplay.BuilderSpells
-{
- ///
- /// Server-only, non-networked lingering area spawned by
- /// . Re-scans its radius on an
- /// interval for its lifetime and keeps refreshing the slow on anything standing
- /// inside, so enemies get slowed as they walk through rather than only at the
- /// instant of cast.
- ///
- ///
- /// Never spawned as a — it runs
- /// server-side only, and the slow it applies already reaches clients through
- /// 's own NetworkVariable, the same as an instant hit.
- ///
- public class SlowAreaZone : MonoBehaviour
- {
- private const float TickInterval = 0.2f;
-
- private float radius;
- private float slowFactor;
- private float effectDuration;
- private LayerMask enemyLayerMask;
- private PlayerSlot owner;
-
- private readonly Collider[] overlapBuffer = new Collider[64];
- private float tickTimer;
-
- public static SlowAreaZone Spawn(Vector3 position, float radius, float slowFactor,
- float effectDuration, LayerMask enemyLayerMask, PlayerSlot owner)
- {
- var zoneObject = new GameObject("SlowAreaZone (Server)");
- zoneObject.transform.position = position;
-
- var zone = zoneObject.AddComponent();
- zone.radius = radius;
- zone.slowFactor = slowFactor;
- zone.effectDuration = effectDuration;
- zone.enemyLayerMask = enemyLayerMask;
- zone.owner = owner;
- zone.ApplyToOverlapping();
-
- Destroy(zoneObject, effectDuration);
- return zone;
- }
-
- private void Update()
- {
- tickTimer -= Time.deltaTime;
- if (tickTimer > 0f) return;
-
- ApplyToOverlapping();
- }
-
- private void ApplyToOverlapping()
- {
- tickTimer = TickInterval;
-
- int count = Physics.OverlapSphereNonAlloc(
- transform.position, radius, overlapBuffer, enemyLayerMask);
-
- for (int i = 0; i < count; i++)
- {
- var enemyHealth = overlapBuffer[i].GetComponent();
- if (enemyHealth == null || enemyHealth.IsDead) continue;
-
- var enemyStatus = overlapBuffer[i].GetComponent();
- if (enemyStatus == null) continue;
-
- enemyStatus.ApplyEffect(DamageType.Cold, slowFactor, effectDuration, owner);
- }
- }
- }
-}
diff --git a/Assets/_Project/Scripts/Gameplay/BuilderSpells/SlowAreaZone.cs.meta b/Assets/_Project/Scripts/Gameplay/BuilderSpells/SlowAreaZone.cs.meta
deleted file mode 100644
index 7c880a9..0000000
--- a/Assets/_Project/Scripts/Gameplay/BuilderSpells/SlowAreaZone.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 7b00a1f57e8be13cd91c0ed5f515bfc8
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Gameplay/BuilderUpgradeManager.cs b/Assets/_Project/Scripts/Gameplay/BuilderUpgradeManager.cs
index 6b81aea..5f07d31 100644
--- a/Assets/_Project/Scripts/Gameplay/BuilderUpgradeManager.cs
+++ b/Assets/_Project/Scripts/Gameplay/BuilderUpgradeManager.cs
@@ -9,7 +9,8 @@ namespace TD.Gameplay
{
///
/// Per-player set of granted builder effects. Lives on the Player prefab alongside
- /// and .
+ /// , , and
+ /// .
///
///
/// List of granted kinds. A builder effect carries no per-grant state (no
@@ -20,9 +21,8 @@ namespace TD.Gameplay
/// same constraint on a struct); byte already does, for free.
///
/// Granted via draft.
- /// calls when a player picks it. Effects are otherwise
- /// permanent for the match — the only way one leaves the granted set is
- /// swapping it for its upgraded replacement.
+ /// calls when a player picks it. Effects are permanent for
+ /// the match — there is no revoke.
///
/// Query, don't snapshot. Consumers (e.g. awarding
/// kill gold) query this manager live at the point of use rather than towers caching their
@@ -118,24 +118,5 @@ namespace TD.Gameplay
grantedKinds.Add(value);
return true;
}
-
- ///
- /// Server-only: replaces a previously granted effect with a different one (an upgrade
- /// pick). No-op if the old effect isn't currently granted or the new one already is.
- /// Returns true if the swap happened.
- ///
- public bool ServerUpgradeEffect(BuilderEffectKind oldKind, BuilderEffectKind newKind)
- {
- if (!IsServer) return false;
-
- byte newValue = (byte)newKind;
- if (grantedKinds.Contains(newValue)) return false;
-
- byte oldValue = (byte)oldKind;
- if (!grantedKinds.Remove(oldValue)) return false;
-
- grantedKinds.Add(newValue);
- return true;
- }
}
}
diff --git a/Assets/_Project/Scripts/Gameplay/CameraController.cs b/Assets/_Project/Scripts/Gameplay/CameraController.cs
index 4a2009a..b80f33e 100644
--- a/Assets/_Project/Scripts/Gameplay/CameraController.cs
+++ b/Assets/_Project/Scripts/Gameplay/CameraController.cs
@@ -227,18 +227,16 @@ namespace TD.Gameplay
{
Vector2 dir = Vector2.zero;
- // Keyboard: arrow keys only. WASD is reserved for tower-build hotkeys (the command
- // grid) — camera panning is arrow keys plus the mouse edge-pan below. Suppressed
- // entirely while the player is typing (so arrow keys in chat navigate text instead
- // of panning) or while the gear menu is open. Edge-pan below is mouse-driven and
- // gated separately by the pointer-over-HUD check, which the menu overlay satisfies.
- var kb = HUDController.IsUiCapturingInput ? null : Keyboard.current;
+ // Keyboard: WASD + arrow keys. Suppressed entirely while the player
+ // is typing — pressing 'a' or 'w' into chat should not pan the camera.
+ // (Edge-pan below stays active since it's mouse-driven.)
+ var kb = HUDController.IsTextInputActive ? null : Keyboard.current;
if (kb != null)
{
- if (kb.leftArrowKey.isPressed) dir.x -= 1f;
- if (kb.rightArrowKey.isPressed) dir.x += 1f;
- if (kb.downArrowKey.isPressed) dir.y -= 1f;
- if (kb.upArrowKey.isPressed) dir.y += 1f;
+ if (kb.aKey.isPressed || kb.leftArrowKey.isPressed) dir.x -= 1f;
+ if (kb.dKey.isPressed || kb.rightArrowKey.isPressed) dir.x += 1f;
+ if (kb.sKey.isPressed || kb.downArrowKey.isPressed) dir.y -= 1f;
+ if (kb.wKey.isPressed || kb.upArrowKey.isPressed) dir.y += 1f;
}
// Edge-pan: mouse near screen edge adds to keyboard direction.
diff --git a/Assets/_Project/Scripts/Gameplay/Draft/BuilderEffectUpgradeDraftOption.cs b/Assets/_Project/Scripts/Gameplay/Draft/BuilderEffectUpgradeDraftOption.cs
deleted file mode 100644
index 46022b5..0000000
--- a/Assets/_Project/Scripts/Gameplay/Draft/BuilderEffectUpgradeDraftOption.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-// Assets/_Project/Scripts/Gameplay/Draft/BuilderEffectUpgradeDraftOption.cs
-using UnityEngine;
-using TD.Core;
-using TD.Gameplay.BuilderEffects;
-
-namespace TD.Gameplay.Draft
-{
- ///
- /// Draft choice — "upgrade a builder effect you already have". Replaces
- /// with in the player's
- /// .
- ///
- ///
- /// Mirrors , but swaps rather than adds. Only offered
- /// while the player currently has granted — once taken,
- /// is removed, which automatically makes this option (and any sibling
- /// upgrade branching off the same base) invalid for future drafts.
- ///
- [CreateAssetMenu(fileName = "BuilderEffectUpgradeOption",
- menuName = "TD/Draft/Builder Effect Upgrade Option")]
- public class BuilderEffectUpgradeDraftOption : DraftOption
- {
- [Header("Payload")]
- [Tooltip("The builder effect this option upgrades from.")]
- public BuilderEffectKind BaseKind;
-
- [Tooltip("The builder effect this option upgrades to.")]
- public BuilderEffectKind UpgradedKind;
-
- public override bool IsValidFor(ulong clientId)
- {
- var upgrades = BuilderUpgradeManager.GetForClient(clientId);
- return upgrades != null && upgrades.PlayerHasEffect(BaseKind);
- }
-
- public override bool ServerApply(ulong clientId)
- {
- var upgrades = BuilderUpgradeManager.GetForClient(clientId);
- return upgrades != null && upgrades.ServerUpgradeEffect(BaseKind, UpgradedKind);
- }
- }
-}
diff --git a/Assets/_Project/Scripts/Gameplay/Draft/BuilderEffectUpgradeDraftOption.cs.meta b/Assets/_Project/Scripts/Gameplay/Draft/BuilderEffectUpgradeDraftOption.cs.meta
deleted file mode 100644
index 393a4c1..0000000
--- a/Assets/_Project/Scripts/Gameplay/Draft/BuilderEffectUpgradeDraftOption.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 717648113e8090fc6856744d898ab375
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Gameplay/Draft/BuilderSpellDraftOption.cs b/Assets/_Project/Scripts/Gameplay/Draft/BuilderSpellDraftOption.cs
index 360a566..416fc03 100644
--- a/Assets/_Project/Scripts/Gameplay/Draft/BuilderSpellDraftOption.cs
+++ b/Assets/_Project/Scripts/Gameplay/Draft/BuilderSpellDraftOption.cs
@@ -15,9 +15,7 @@ namespace TD.Gameplay.Draft
/// and already key off of.
/// also checks slot capacity — once
/// is reached, no further spell options are
- /// offered. Gates on rather than
- /// current possession, so a base spell already upgraded past (see
- /// ) is never re-offered.
+ /// offered.
///
[CreateAssetMenu(fileName = "BuilderSpellOption", menuName = "TD/Draft/Builder Spell Option")]
public class BuilderSpellDraftOption : DraftOption
@@ -31,7 +29,7 @@ namespace TD.Gameplay.Draft
var loadout = PlayerSpellLoadout.GetForClient(clientId);
if (loadout == null) return false;
if (loadout.SlotCount >= PlayerSpellLoadout.MaxSpellSlots) return false;
- return !loadout.PlayerHasEverGranted(Kind);
+ return !loadout.PlayerHasSpell(Kind);
}
public override bool ServerApply(ulong clientId)
@@ -39,14 +37,5 @@ namespace TD.Gameplay.Draft
var loadout = PlayerSpellLoadout.GetForClient(clientId);
return loadout != null && loadout.ServerGrantSpell(Kind);
}
-
- /// Inherits the granted spell's icon (resolved from the pool by
- /// ) unless this option assigns an override.
- public override Sprite ResolveIcon()
- {
- if (Icon != null) return Icon;
- var def = BuilderSpellPool.Instance != null ? BuilderSpellPool.Instance.Get(Kind) : null;
- return def != null ? def.Icon : null;
- }
}
}
diff --git a/Assets/_Project/Scripts/Gameplay/Draft/BuilderSpellUpgradeDraftOption.cs b/Assets/_Project/Scripts/Gameplay/Draft/BuilderSpellUpgradeDraftOption.cs
deleted file mode 100644
index a9d56bd..0000000
--- a/Assets/_Project/Scripts/Gameplay/Draft/BuilderSpellUpgradeDraftOption.cs
+++ /dev/null
@@ -1,52 +0,0 @@
-// Assets/_Project/Scripts/Gameplay/Draft/BuilderSpellUpgradeDraftOption.cs
-using UnityEngine;
-using TD.Core;
-using TD.Gameplay.BuilderSpells;
-
-namespace TD.Gameplay.Draft
-{
- ///
- /// Draft choice — "upgrade a builder spell you already have". Replaces
- /// with in the player's
- /// , in place — the upgraded spell keeps the same hotkey
- /// slot the base spell occupied.
- ///
- ///
- /// Mirrors , but swaps rather than adds (so it doesn't
- /// consume a slot). Only offered while the player currently has
- /// granted — once taken, is removed, which automatically makes this
- /// option (and any sibling upgrade branching off the same base) invalid for future drafts.
- ///
- [CreateAssetMenu(fileName = "BuilderSpellUpgradeOption",
- menuName = "TD/Draft/Builder Spell Upgrade Option")]
- public class BuilderSpellUpgradeDraftOption : DraftOption
- {
- [Header("Payload")]
- [Tooltip("The builder spell this option upgrades from.")]
- public BuilderSpellKind BaseKind;
-
- [Tooltip("The builder spell this option upgrades to.")]
- public BuilderSpellKind UpgradedKind;
-
- public override bool IsValidFor(ulong clientId)
- {
- var loadout = PlayerSpellLoadout.GetForClient(clientId);
- return loadout != null && loadout.PlayerHasSpell(BaseKind);
- }
-
- public override bool ServerApply(ulong clientId)
- {
- var loadout = PlayerSpellLoadout.GetForClient(clientId);
- return loadout != null && loadout.ServerUpgradeSpell(BaseKind, UpgradedKind);
- }
-
- /// Inherits the upgraded spell's icon (resolved from the pool by
- /// ) unless this option assigns an override.
- public override Sprite ResolveIcon()
- {
- if (Icon != null) return Icon;
- var def = BuilderSpellPool.Instance != null ? BuilderSpellPool.Instance.Get(UpgradedKind) : null;
- return def != null ? def.Icon : null;
- }
- }
-}
diff --git a/Assets/_Project/Scripts/Gameplay/Draft/BuilderSpellUpgradeDraftOption.cs.meta b/Assets/_Project/Scripts/Gameplay/Draft/BuilderSpellUpgradeDraftOption.cs.meta
deleted file mode 100644
index 2d6304f..0000000
--- a/Assets/_Project/Scripts/Gameplay/Draft/BuilderSpellUpgradeDraftOption.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 7b1c5b562f39c72eea3bafd0165af078
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Gameplay/Draft/DraftOption.cs b/Assets/_Project/Scripts/Gameplay/Draft/DraftOption.cs
index c098caf..8ae8e94 100644
--- a/Assets/_Project/Scripts/Gameplay/Draft/DraftOption.cs
+++ b/Assets/_Project/Scripts/Gameplay/Draft/DraftOption.cs
@@ -34,19 +34,9 @@ namespace TD.Gameplay.Draft
[TextArea(2, 4)]
public string Description;
- [Tooltip("OPTIONAL override for the draft card icon. Leave empty to inherit the icon of " +
- "the item this option grants (its tower / spell); assign one only when you want " +
- "to override that inherited icon.")]
+ [Tooltip("Icon shown on the draft card. Optional for placeholder content.")]
public Sprite Icon;
- ///
- /// The icon actually shown on the draft card. Returns the explicit
- /// override when one is assigned; otherwise subclasses fall back to the icon of the item
- /// they grant (tower, spell). The base class has no referenced item, so it just returns
- /// (which may be null).
- ///
- public virtual Sprite ResolveIcon() => Icon;
-
[Header("Generation")]
[Tooltip("Relative draw weight. Higher = offered more often. Rarer rewards use " +
"lower weights. Must be > 0.")]
diff --git a/Assets/_Project/Scripts/Gameplay/Draft/DraftService.cs b/Assets/_Project/Scripts/Gameplay/Draft/DraftService.cs
index f95a423..3b9ea5f 100644
--- a/Assets/_Project/Scripts/Gameplay/Draft/DraftService.cs
+++ b/Assets/_Project/Scripts/Gameplay/Draft/DraftService.cs
@@ -82,7 +82,7 @@ namespace TD.Gameplay.Draft
draft.ServerOffer(resultScratch);
}
- /// Server-only: auto-resolve any unpicked drafts (called when the timer expires).
+ /// Server-only: auto-resolve any unpicked drafts (called at prep end).
public void ServerAutoResolveAll()
{
if (!IsServer) return;
@@ -90,29 +90,6 @@ namespace TD.Gameplay.Draft
PlayerDraft.GetForClient(pms.OwnerClientId)?.ServerAutoResolve();
}
- ///
- /// True once every connected player has resolved their draft. The inter-wave step polls
- /// this to end the draft timer early instead of making everyone wait out the clock.
- ///
- ///
- /// Returns false when nobody is connected, so an empty lobby can't trip the barrier and
- /// race the sequence forward.
- ///
- public static bool AllPlayersPicked
- {
- get
- {
- bool any = false;
- foreach (var pms in PlayerMatchState.AllPlayers)
- {
- any = true;
- var draft = PlayerDraft.GetForClient(pms.OwnerClientId);
- if (draft != null && draft.HasActiveDraft) return false;
- }
- return any;
- }
- }
-
// ----- Generation -------------------------------------------------
// Weighted random draw WITHOUT replacement: picks `count` distinct option ids from
diff --git a/Assets/_Project/Scripts/Gameplay/Draft/NewTowerDraftOption.cs b/Assets/_Project/Scripts/Gameplay/Draft/NewTowerDraftOption.cs
index e7af9f6..aab2a98 100644
--- a/Assets/_Project/Scripts/Gameplay/Draft/NewTowerDraftOption.cs
+++ b/Assets/_Project/Scripts/Gameplay/Draft/NewTowerDraftOption.cs
@@ -48,9 +48,5 @@ namespace TD.Gameplay.Draft
return deck.ServerGrantTower(typeId);
}
-
- /// Inherits the granted tower's icon unless this option assigns an override.
- public override Sprite ResolveIcon()
- => Icon != null ? Icon : (Tower != null ? Tower.Icon : null);
}
}
diff --git a/Assets/_Project/Scripts/Gameplay/Draft/PlayerDraft.cs b/Assets/_Project/Scripts/Gameplay/Draft/PlayerDraft.cs
index 1ac355b..3fd7350 100644
--- a/Assets/_Project/Scripts/Gameplay/Draft/PlayerDraft.cs
+++ b/Assets/_Project/Scripts/Gameplay/Draft/PlayerDraft.cs
@@ -18,10 +18,7 @@ namespace TD.Gameplay.Draft
/// Authority. The server offers (), resolves
/// ( / ), and applies the
/// chosen option. The owning client only sends intent via
- /// .
- ///
- /// The paid reroll is disabled for the 2.0 MVP — see the commented-out block at
- /// the bottom of this file for why it was kept rather than removed.
+ /// and .
///
/// Generation lives in ; this component is just
/// the replicated state + the pick/buy entry points.
@@ -140,44 +137,31 @@ namespace TD.Gameplay.Draft
// ----- Owner → server RPCs ----------------------------------------
/// Owning client: pick one of the offered options by id.
- [Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Owner)]
+ [Rpc(SendTo.Server, RequireOwnership = true)]
public void RequestPickRpc(int optionId)
{
ServerResolve(optionId);
}
- // ----- Extra-draft shop (DISABLED for the 2.0 MVP) --------------------
- //
- // Kept rather than deleted: the shop is expected back, but as a SHARED gold sink — the
- // whole lobby pools money toward one collective extra draft for everyone — rather than
- // the per-player reroll implemented here. When it returns, this method is the wrong shape
- // (per-player deduction, per-player offer) but the surrounding plumbing is right, so it
- // stays as the reference for what to rebuild against.
- //
- // Re-enabling this alone would also break the inter-wave barrier: DraftService.
- // AllPlayersPicked ends the draft stage the moment everyone has resolved, so a player
- // buying a roll after that point would be offered cards the sequence has already moved
- // past.
- //
- // ///
- // /// Owning client: buy a fresh roll of options for gold. Rejected if the player can't
- // /// afford it or already has an unresolved draft (resolve the current one first so a
- // /// free pick is never silently overwritten).
- // ///
- // [Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Owner)]
- // public void RequestBuyRerollRpc()
- // {
- // if (HasActiveDraft) return; // resolve the pending draft before buying another
- //
- // var service = DraftService.Instance;
- // if (service == null) return;
- //
- // var gold = PlayerGoldManager.GetForClient(OwnerClientId);
- // int cost = service.RerollCost;
- // if (gold == null || gold.CurrentGold < cost) return;
- //
- // gold.DeductGold(cost);
- // service.ServerOfferTo(OwnerClientId);
- // }
+ ///
+ /// Owning client: buy a fresh roll of options for gold. Rejected if the player can't
+ /// afford it or already has an unresolved draft (resolve the current one first so a
+ /// free pick is never silently overwritten).
+ ///
+ [Rpc(SendTo.Server, RequireOwnership = true)]
+ public void RequestBuyRerollRpc()
+ {
+ if (HasActiveDraft) return; // resolve the pending draft before buying another
+
+ var service = DraftService.Instance;
+ if (service == null) return;
+
+ var gold = PlayerGoldManager.GetForClient(OwnerClientId);
+ int cost = service.RerollCost;
+ if (gold == null || gold.CurrentGold < cost) return;
+
+ gold.DeductGold(cost);
+ service.ServerOfferTo(OwnerClientId);
+ }
}
}
diff --git a/Assets/_Project/Scripts/Gameplay/Draft/TowerUpgradeDraftOption.cs b/Assets/_Project/Scripts/Gameplay/Draft/TowerUpgradeDraftOption.cs
deleted file mode 100644
index 841d627..0000000
--- a/Assets/_Project/Scripts/Gameplay/Draft/TowerUpgradeDraftOption.cs
+++ /dev/null
@@ -1,80 +0,0 @@
-// Assets/_Project/Scripts/Gameplay/Draft/TowerUpgradeDraftOption.cs
-using UnityEngine;
-using TD.Towers;
-
-namespace TD.Gameplay.Draft
-{
- ///
- /// Draft choice — "unlock an upgrade". Adds to the player's
- /// set, letting them spend gold converting a placed
- /// into it. The base tower stays buildable.
- ///
- ///
- /// Unlock, not swap. This option used to replace the base tower in the player's
- /// deck, which made the draft pick itself the whole upgrade — free, instant, and retroactive to
- /// every tower built afterward. The 2.0 model splits it: the draft grants access, gold
- /// grants the tower. So the deck is untouched here and already-placed towers are
- /// upgraded one at a time, each paid for.
- ///
- /// Prerequisites fall out of the tree. An upgrade is offerable only once its
- /// parent is reachable — either the parent is a base tower in the player's deck, or it's an
- /// upgrade node they've already unlocked. That single rule is what gates deep nodes behind
- /// shallow ones without any explicit prerequisite list: authoring the tree in
- /// is enough.
- ///
- [CreateAssetMenu(fileName = "TowerUpgradeOption", menuName = "TD/Draft/Tower Upgrade Option")]
- public class TowerUpgradeDraftOption : DraftOption
- {
- [Header("Payload")]
- [Tooltip("The tower this upgrade branches from — the tree node that must already be " +
- "reachable (in the player's deck, or itself an unlocked upgrade) for this " +
- "option to be offered.")]
- public TowerDefinition BaseTower;
-
- [Tooltip("The tower this option unlocks as an upgrade target. Must be in the " +
- "TowerPlacementManager catalog (that's where its TowerTypeId comes from) and " +
- "should be listed in BaseTower's UpgradePaths so the conversion is legal.")]
- public TowerDefinition UpgradedTower;
-
- public override bool IsValidFor(ulong clientId)
- {
- var deck = PlayerTowerDeck.GetForClient(clientId);
- var upgrades = PlayerTowerUpgrades.GetForClient(clientId);
- var pm = TowerPlacementManager.Instance;
-
- if (deck == null || upgrades == null || pm == null) return false;
- if (BaseTower == null || UpgradedTower == null) return false;
-
- if (!pm.TryGetTypeId(BaseTower, out int baseTypeId)) return false;
- if (!pm.TryGetTypeId(UpgradedTower, out int upgradedTypeId)) return false;
-
- // Already unlocked — offering it again would waste the pick.
- if (upgrades.Contains(upgradedTypeId)) return false;
-
- // The parent must be reachable: a base tower they can build, or an upgrade they've
- // already taken. This is the whole prerequisite mechanism.
- return deck.Contains(baseTypeId) || upgrades.Contains(baseTypeId);
- }
-
- public override bool ServerApply(ulong clientId)
- {
- var upgrades = PlayerTowerUpgrades.GetForClient(clientId);
- var pm = TowerPlacementManager.Instance;
- if (upgrades == null || pm == null || UpgradedTower == null) return false;
-
- if (!pm.TryGetTypeId(UpgradedTower, out int upgradedTypeId))
- {
- Debug.LogError($"[TowerUpgradeDraftOption] '{UpgradedTower.name}' is not in the " +
- $"tower catalog; cannot apply. Add it to " +
- $"TowerPlacementManager.towerDefinitions.");
- return false;
- }
-
- return upgrades.ServerUnlock(upgradedTypeId);
- }
-
- /// Inherits the upgraded tower's icon unless this option assigns an override.
- public override Sprite ResolveIcon()
- => Icon != null ? Icon : (UpgradedTower != null ? UpgradedTower.Icon : null);
- }
-}
diff --git a/Assets/_Project/Scripts/Gameplay/Draft/TowerUpgradeDraftOption.cs.meta b/Assets/_Project/Scripts/Gameplay/Draft/TowerUpgradeDraftOption.cs.meta
deleted file mode 100644
index a35a5d2..0000000
--- a/Assets/_Project/Scripts/Gameplay/Draft/TowerUpgradeDraftOption.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: aeec22f152a57ef5798eae035c091543
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Gameplay/EnemyAbilities.meta b/Assets/_Project/Scripts/Gameplay/EnemyAbilities.meta
deleted file mode 100644
index 099a1ab..0000000
--- a/Assets/_Project/Scripts/Gameplay/EnemyAbilities.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 1c918164b61faf1d5af9f782c5237a60
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Scripts/Gameplay/EnemyAbilities/BlinkAbilityDefinition.cs b/Assets/_Project/Scripts/Gameplay/EnemyAbilities/BlinkAbilityDefinition.cs
deleted file mode 100644
index c00904c..0000000
--- a/Assets/_Project/Scripts/Gameplay/EnemyAbilities/BlinkAbilityDefinition.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-// Assets/_Project/Scripts/Gameplay/EnemyAbilities/BlinkAbilityDefinition.cs
-using UnityEngine;
-using TD.Core;
-
-namespace TD.Gameplay.EnemyAbilities
-{
- ///
- /// Every few seconds, the enemy teleports a short distance further along its path — skipping
- /// tiles, and the towers covering them.
- ///
- ///
- /// Blinks follow the path rather than cutting toward the goal, so the maze still shapes the
- /// route; the buff shortens time-under-fire rather than bypassing walls. See
- /// EnemyMovement.ServerBlinkForward.
- ///
- /// The cooldown lives in this enemy's own timer slot rather than on the asset. A field
- /// here would be shared by every enemy carrying the buff, so the whole wave would blink in
- /// perfect unison — which looks like a bug even when the timing is right.
- ///
- [CreateAssetMenu(fileName = "BlinkAbility", menuName = "TD/Enemy Abilities/Blink")]
- public class BlinkAbilityDefinition : EnemyAbilityDefinition
- {
- public override EnemyAbilityKind Kind => EnemyAbilityKind.Blink;
-
- [Header("Blink")]
- [Tooltip("Seconds between blinks.")]
- [Min(0.1f)]
- public float IntervalSeconds = 8f;
-
- [Tooltip("World units to advance along the path per blink.")]
- [Min(0.1f)]
- public float BlinkDistanceMeters = 4f;
-
- public override void ServerOnSpawn(EnemyAbility instance, int abilityIndex)
- {
- // Seed each enemy's cooldown with a random point within the full interval, as if it
- // spawned already partway through a cooldown. A small jitter window near zero would
- // still let a burst of enemies spawned together sync up and blink as a visible cluster
- // every cycle; spreading across the whole interval avoids that regardless of how
- // IntervalSeconds is tuned. Done once, here, rather than on the first tick — a tick-time
- // check would have to distinguish "never started" from "just blinked", and both
- // states read as a zero timer.
- instance.TimerFor(abilityIndex) = -Random.Range(0f, IntervalSeconds);
- }
-
- public override void ServerTick(EnemyAbility instance, int abilityIndex, float dt)
- {
- ref float timer = ref instance.TimerFor(abilityIndex);
- timer += dt;
- if (timer < IntervalSeconds) return;
-
- timer = 0f;
- instance.GetComponent()?.ServerBlinkForward(BlinkDistanceMeters);
- }
- }
-}
diff --git a/Assets/_Project/Scripts/Gameplay/EnemyAbilities/BlinkAbilityDefinition.cs.meta b/Assets/_Project/Scripts/Gameplay/EnemyAbilities/BlinkAbilityDefinition.cs.meta
deleted file mode 100644
index b7af88b..0000000
--- a/Assets/_Project/Scripts/Gameplay/EnemyAbilities/BlinkAbilityDefinition.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 4fbb09f55384ac44399ac4655b2b53ec
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Gameplay/EnemyAbilities/EnemyAbilityDefinition.cs b/Assets/_Project/Scripts/Gameplay/EnemyAbilities/EnemyAbilityDefinition.cs
deleted file mode 100644
index d80b430..0000000
--- a/Assets/_Project/Scripts/Gameplay/EnemyAbilities/EnemyAbilityDefinition.cs
+++ /dev/null
@@ -1,107 +0,0 @@
-// Assets/_Project/Scripts/Gameplay/EnemyAbilities/EnemyAbilityDefinition.cs
-using UnityEngine;
-using TD.Core;
-
-namespace TD.Gameplay.EnemyAbilities
-{
- ///
- /// Base class for one enemy ability (e.g. "split into smaller enemies on death"). Abilities
- /// are attached to a wave slot by the post-wave player vote, and every enemy that wave
- /// spawns from then on carries the whole accumulated set.
- ///
- ///
- /// Assignment is deterministic, not random. This system originally rolled an
- /// ability per individual enemy against a no-ability weight. Under the 2.0 design the players
- /// choose it, it applies to every enemy in the wave, and it persists for the rest of the phase
- /// — so the roll is gone and WaveManager reads the slot's set instead. What survived is
- /// the shape: one asset per kind, server-only hooks.
- ///
- /// Abilities stack. A wave can collect several across a phase's cycles, so an
- /// enemy holds a list and every hook below runs once per ability. Implementations must not
- /// assume they are the only ability on the enemy.
- ///
- /// Server-only hooks. All hooks run on the server only —
- /// gates every call on IsServer. They default to no-ops so a new card only overrides the
- /// one or two it actually needs.
- ///
- /// No per-enemy state on the asset. A single asset is shared by every enemy
- /// carrying the ability, across every wave and match. Anything per-instance belongs on the
- /// component or the enemy itself.
- ///
- public abstract class EnemyAbilityDefinition : ScriptableObject
- {
- /// Which enemy ability this asset's data belongs to.
- public abstract EnemyAbilityKind Kind { get; }
-
- [Header("Presentation")]
- [Tooltip("Name shown in debug logs and the enemy-info panel.")]
- public string DisplayName;
-
- [Tooltip("Short description shown in the enemy-info panel.")]
- [TextArea(2, 4)]
- public string Description;
-
- // ----- Spawn-time stat modification --------------------------------
-
- ///
- /// Server-only: alter what this enemy spawns as, before it is built. Runs for every enemy
- /// of the buffed wave. Default no-op.
- ///
- ///
- /// This is the hook for cards that change what an enemy fundamentally is — flight,
- /// health, speed, size. It must run before the enemy exists, because spawn position
- /// (flyers are raised) and EnemyHealth/EnemyMovement initialization all read
- /// these values once and keep them.
- ///
- /// When several abilities stack, they see each other's edits in application order,
- /// so multiplicative modifiers compose naturally. Prefer multiplying over assigning for
- /// anything numeric, or the last card to run silently wins.
- ///
- public virtual void ServerModifySpawn(ref EnemySpawnContext context) { }
-
- // ----- Lifetime hooks ----------------------------------------------
-
- /// Server-only: called once, right after the ability set is assigned (before the
- /// enemy's NetworkObject is spawned to clients). Default no-op.
- /// This ability's slot on — the
- /// same index receives, so per-enemy timers can be seeded here.
- public virtual void ServerOnSpawn(EnemyAbility instance, int abilityIndex) { }
-
- ///
- /// Server-only: called every frame this ability is active on a live enemy. Default no-op.
- ///
- /// This ability's slot on . Pass it
- /// to to reach a per-enemy float this ability owns.
- ///
- /// The asset holds no per-enemy state. One
- /// instance is shared by every enemy carrying the ability, so a cooldown stored in a field
- /// here would be shared by the entire wave — every enemy would blink in lockstep, or worse,
- /// race each other's writes. gives each enemy its own
- /// slot by reference, which is enough for the timer-driven abilities and costs no
- /// allocation.
- ///
- public virtual void ServerTick(EnemyAbility instance, int abilityIndex, float dt) { }
-
- /// Server-only: called the instant the enemy's HP reaches zero, before the
- /// death animation/despawn sequence plays. Default no-op.
- public virtual void ServerOnDeath(EnemyAbility instance, EnemyHealth health) { }
-
- // ----- Economy hooks -----------------------------------------------
-
- ///
- /// Server-only: adjust the gold a player earns for killing this enemy. Returns the reward
- /// to pass on; default is unchanged.
- ///
- ///
- /// Chained across a stacked ability set, each ability receiving the previous one's result.
- /// Callers clamp the final value at zero, so returning a negative is safe but pointless.
- ///
- public virtual int ServerModifyKillReward(EnemyAbility instance, int reward) => reward;
-
- ///
- /// Server-only: called when this enemy reaches the defense point, after lives have been
- /// deducted. The hook for leak-punishing cards that cost more than lives. Default no-op.
- ///
- public virtual void ServerOnReachedGoal(EnemyAbility instance, PlayerSlot originZone) { }
- }
-}
diff --git a/Assets/_Project/Scripts/Gameplay/EnemyAbilities/EnemyAbilityDefinition.cs.meta b/Assets/_Project/Scripts/Gameplay/EnemyAbilities/EnemyAbilityDefinition.cs.meta
deleted file mode 100644
index b94f8dd..0000000
--- a/Assets/_Project/Scripts/Gameplay/EnemyAbilities/EnemyAbilityDefinition.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 855f7f0848d3086868816d25295fcdd6
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Gameplay/EnemyAbilities/EnemyAbilityPool.cs b/Assets/_Project/Scripts/Gameplay/EnemyAbilities/EnemyAbilityPool.cs
deleted file mode 100644
index 09a0880..0000000
--- a/Assets/_Project/Scripts/Gameplay/EnemyAbilities/EnemyAbilityPool.cs
+++ /dev/null
@@ -1,71 +0,0 @@
-// Assets/_Project/Scripts/Gameplay/EnemyAbilities/EnemyAbilityPool.cs
-using System;
-using UnityEngine;
-using TD.Core;
-
-namespace TD.Gameplay.EnemyAbilities
-{
- ///
- /// Scene singleton holding every available this match,
- /// authored as a flat array in the inspector for convenience. Internally it builds a
- /// fixed-size table indexed by so lookups are a single array
- /// access, not a scan. Mirrors exactly.
- ///
- ///
- /// Plain MonoBehaviour: identical on every peer (same assets), so there is nothing to sync.
- ///
- /// Lookup only — no rolling. This pool used to draw a random ability per spawned
- /// enemy against a "no ability" weight. Under the 2.0 design abilities are chosen by the
- /// players' post-wave vote and apply to every enemy in the wave, so the draw moved to
- /// WaveVote (over cards, not abilities) and this is purely the kind→asset table
- /// WaveManager resolves against at spawn time.
- ///
- public class EnemyAbilityPool : MonoBehaviour
- {
- public static EnemyAbilityPool Instance { get; private set; }
-
- [Tooltip("Every EnemyAbilityDefinition asset available this match. One entry per " +
- "EnemyAbilityKind — order doesn't matter, Kind on the asset itself decides " +
- "its slot.")]
- [SerializeField] private EnemyAbilityDefinition[] abilities;
-
- // Fixed-size, enum-indexed lookup built once in Awake. Sized to the enum's entry count,
- // not authored count, so an out-of-range Kind is a compile-time impossibility rather
- // than a bounds check we'd otherwise need on every Get().
- private EnemyAbilityDefinition[] byKind;
-
- private void Awake()
- {
- if (Instance != null && Instance != this)
- {
- Debug.LogError("[EnemyAbilityPool] Multiple instances detected. Only one per scene.");
- return;
- }
- Instance = this;
-
- int kindCount = Enum.GetValues(typeof(EnemyAbilityKind)).Length;
- byKind = new EnemyAbilityDefinition[kindCount];
- if (abilities == null) return;
-
- for (int i = 0; i < abilities.Length; i++)
- {
- var def = abilities[i];
- if (def == null) continue;
- byKind[(int)def.Kind] = def;
- }
- }
-
- private void OnDestroy()
- {
- if (Instance == this) Instance = null;
- }
-
- /// Returns the ability asset for , or null if none is
- /// authored in this pool.
- public EnemyAbilityDefinition Get(EnemyAbilityKind kind)
- {
- int i = (int)kind;
- return (byKind != null && i >= 0 && i < byKind.Length) ? byKind[i] : null;
- }
- }
-}
diff --git a/Assets/_Project/Scripts/Gameplay/EnemyAbilities/EnemyAbilityPool.cs.meta b/Assets/_Project/Scripts/Gameplay/EnemyAbilities/EnemyAbilityPool.cs.meta
deleted file mode 100644
index a951ada..0000000
--- a/Assets/_Project/Scripts/Gameplay/EnemyAbilities/EnemyAbilityPool.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 4409eb25a8be555de8dc3a73d4ed95db
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Gameplay/EnemyAbilities/EnemySpawnContext.cs b/Assets/_Project/Scripts/Gameplay/EnemyAbilities/EnemySpawnContext.cs
deleted file mode 100644
index 67fda7b..0000000
--- a/Assets/_Project/Scripts/Gameplay/EnemyAbilities/EnemySpawnContext.cs
+++ /dev/null
@@ -1,52 +0,0 @@
-// Assets/_Project/Scripts/Gameplay/EnemyAbilities/EnemySpawnContext.cs
-namespace TD.Gameplay.EnemyAbilities
-{
- ///
- /// Mutable per-spawn copy of an enemy's stats, handed to each of its abilities before the
- /// enemy is built so they can alter what it spawns as.
- ///
- ///
- /// Why a context rather than more parameters. Wave buffs like "grounded enemies become
- /// flying" or "enemies spawn at half health" have to land before
- /// EnemyHealth.InitializeServer and before the spawn position is computed (flyers are
- /// raised by their flight height). Threading each new stat through SpawnEnemy as another
- /// argument was already unwieldy at two; this keeps the signature flat and gives abilities one
- /// obvious place to write.
- ///
- /// Seeded from the , never written back to it — the
- /// definition is a shared asset and a buff that mutated it would leak across waves, matches,
- /// and (in the editor) sessions.
- ///
- public struct EnemySpawnContext
- {
- public float MaxHp;
- public float MoveSpeed;
- public bool IsFlying;
- public float FlightHeight;
- public int LivesCost;
-
- /// Uniform transform scale relative to the prefab's authored scale.
- public float VisualScale;
-
- ///
- /// Seeds a context from an enemy definition's authored stats, with health supplied by the
- /// caller.
- ///
- /// The type being spawned. Supplies everything except health.
- /// Health this enemy spawns with, already resolved from the
- /// encounter's budget and the type's — see
- /// . Passed in rather than read off the
- /// definition because it depends on run position and the wave's enemy count, neither of
- /// which the asset knows about.
- public static EnemySpawnContext FromDefinition(EnemyDefinition def, float resolvedMaxHp)
- => new EnemySpawnContext
- {
- MaxHp = resolvedMaxHp,
- MoveSpeed = def.MoveSpeed,
- IsFlying = def.IsFlying,
- FlightHeight = def.FlightHeight,
- LivesCost = def.LivesCost,
- VisualScale = 1f,
- };
- }
-}
diff --git a/Assets/_Project/Scripts/Gameplay/EnemyAbilities/EnemySpawnContext.cs.meta b/Assets/_Project/Scripts/Gameplay/EnemyAbilities/EnemySpawnContext.cs.meta
deleted file mode 100644
index d812b43..0000000
--- a/Assets/_Project/Scripts/Gameplay/EnemyAbilities/EnemySpawnContext.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 2f4b0f77b422a474cbe82f3967e6195b
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Gameplay/EnemyAbilities/FlightAbilityDefinition.cs b/Assets/_Project/Scripts/Gameplay/EnemyAbilities/FlightAbilityDefinition.cs
deleted file mode 100644
index 322b2cf..0000000
--- a/Assets/_Project/Scripts/Gameplay/EnemyAbilities/FlightAbilityDefinition.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-// Assets/_Project/Scripts/Gameplay/EnemyAbilities/FlightAbilityDefinition.cs
-using UnityEngine;
-using TD.Core;
-
-namespace TD.Gameplay.EnemyAbilities
-{
- ///
- /// Grounded enemies take to the air: they path over the baked terrain grid instead of through
- /// the maze, and towers flagged ground-only can no longer touch them.
- ///
- ///
- /// The single most punishing card in the set, because it doesn't weaken the maze — it
- /// deletes it. Everything a player built to lengthen the route stops mattering for that wave,
- /// and only their anti-air coverage counts. Weight it accordingly in the pools.
- ///
- /// Purely a spawn-time change: EnemyMovement reads the flying flag once at
- /// initialization to pick which grid to path on and to opt out of re-path scheduling, and
- /// TowerCombat reads the replicated flag on EnemyHealth when filtering targets.
- /// Both are already wired for flight — this card only has to flip the bit before the enemy is
- /// built.
- ///
- [CreateAssetMenu(fileName = "FlightAbility", menuName = "TD/Enemy Abilities/Flight")]
- public class FlightAbilityDefinition : EnemyAbilityDefinition
- {
- public override EnemyAbilityKind Kind => EnemyAbilityKind.Flight;
-
- [Header("Flight")]
- [Tooltip("Height above the ground these enemies hover at. Keep modest — tower targeting " +
- "uses 3D range, so altitude eats into the effective reach of anything that CAN " +
- "shoot them.")]
- [Min(0f)]
- public float FlightHeight = 3f;
-
- [Tooltip("Speed multiplier applied when the enemy takes flight. Flying enemies travel a " +
- "much shorter route, so values below 1 are a reasonable counterweight.")]
- [Range(0.1f, 2f)]
- public float SpeedMultiplier = 1f;
-
- public override void ServerModifySpawn(ref EnemySpawnContext context)
- {
- // Already-flying enemies keep their authored height rather than being overwritten by
- // this card's — the wave was designed around it, and stacking Flight onto a flier
- // shouldn't quietly relocate it.
- if (!context.IsFlying)
- {
- context.IsFlying = true;
- context.FlightHeight = FlightHeight;
- }
-
- context.MoveSpeed *= SpeedMultiplier;
- }
- }
-}
diff --git a/Assets/_Project/Scripts/Gameplay/EnemyAbilities/FlightAbilityDefinition.cs.meta b/Assets/_Project/Scripts/Gameplay/EnemyAbilities/FlightAbilityDefinition.cs.meta
deleted file mode 100644
index 82e8217..0000000
--- a/Assets/_Project/Scripts/Gameplay/EnemyAbilities/FlightAbilityDefinition.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: be77d353621f07e40bbdab2e9cc37f51
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Gameplay/EnemyAbilities/GoldTheftAbilityDefinition.cs b/Assets/_Project/Scripts/Gameplay/EnemyAbilities/GoldTheftAbilityDefinition.cs
deleted file mode 100644
index 974b59f..0000000
--- a/Assets/_Project/Scripts/Gameplay/EnemyAbilities/GoldTheftAbilityDefinition.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-// Assets/_Project/Scripts/Gameplay/EnemyAbilities/GoldTheftAbilityDefinition.cs
-using UnityEngine;
-using TD.Core;
-
-namespace TD.Gameplay.EnemyAbilities
-{
- ///
- /// An enemy that reaches the defense point doesn't just cost a life — it robs the player whose
- /// maze it escaped.
- ///
- ///
- /// Charged to the origin zone, not the whole team. Lives are a shared pool, so a leak
- /// already punishes everyone equally; billing the gold to whoever let it through is the part
- /// that makes this card bite differently from "enemies cost 2 lives". It also keeps the blame
- /// legible — the player who leaked is the player who pays.
- ///
- /// Deducting more gold than a player has simply empties them; there is no debt. Going
- /// negative would silently disable building until they clawed back to zero, which reads as a
- /// broken HUD rather than a penalty.
- ///
- [CreateAssetMenu(fileName = "GoldTheftAbility", menuName = "TD/Enemy Abilities/Gold Theft")]
- public class GoldTheftAbilityDefinition : EnemyAbilityDefinition
- {
- public override EnemyAbilityKind Kind => EnemyAbilityKind.GoldTheft;
-
- [Header("Theft")]
- [Tooltip("Flat gold taken from the leaking player, on top of the normal life cost.")]
- [Min(0)]
- public int GoldStolen = 15;
-
- public override void ServerOnReachedGoal(EnemyAbility instance, PlayerSlot originZone)
- {
- if (GoldStolen <= 0 || originZone == PlayerSlot.None) return;
-
- var pms = PlayerMatchState.GetForSlot(originZone);
- if (pms == null) return;
-
- var gold = PlayerGoldManager.GetForClient(pms.OwnerClientId);
- if (gold == null) return;
-
- // Clamp to what they actually hold — a leak shouldn't be able to put a player in debt.
- int taken = Mathf.Min(GoldStolen, gold.CurrentGold);
- if (taken <= 0) return;
-
- gold.DeductGold(taken);
-
- // Surface it in-world on every peer so the loss isn't just a number quietly ticking
- // down in the corner. Routed through WaveManager because the popup has to reach
- // clients and a ScriptableObject has no NetworkBehaviour to send from.
- WaveManager.Instance?.ServerBroadcastGoldLoss(instance.transform.position, taken);
- }
- }
-}
diff --git a/Assets/_Project/Scripts/Gameplay/EnemyAbilities/GoldTheftAbilityDefinition.cs.meta b/Assets/_Project/Scripts/Gameplay/EnemyAbilities/GoldTheftAbilityDefinition.cs.meta
deleted file mode 100644
index aa23919..0000000
--- a/Assets/_Project/Scripts/Gameplay/EnemyAbilities/GoldTheftAbilityDefinition.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 3b770ce042b78f44d9a3df537fe25db0
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Gameplay/EnemyAbilities/NoBountyAbilityDefinition.cs b/Assets/_Project/Scripts/Gameplay/EnemyAbilities/NoBountyAbilityDefinition.cs
deleted file mode 100644
index 42d2bab..0000000
--- a/Assets/_Project/Scripts/Gameplay/EnemyAbilities/NoBountyAbilityDefinition.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-// Assets/_Project/Scripts/Gameplay/EnemyAbilities/NoBountyAbilityDefinition.cs
-using UnityEngine;
-using TD.Core;
-
-namespace TD.Gameplay.EnemyAbilities
-{
- ///
- /// Killing these enemies pays little or nothing. The wave gets no harder to survive — it gets
- /// harder to profit from.
- ///
- ///
- /// Attacks the economy, not the maze. Every other card makes a wave more dangerous;
- /// this one makes clearing it worthless, which compounds instead across cycles — a wave that
- /// pays nothing on cycle 1 also pays nothing on cycles 2 and 3, so the players who voted it in
- /// are choosing to be poorer for the rest of the phase. That slow squeeze is the point, and
- /// it's why the card reads as mild and isn't.
- ///
- /// Wave-completion and no-leak bonuses are untouched — those are paid per player by
- /// WaveManager, not per kill, so a wave carrying this still rewards clearing it.
- ///
- [CreateAssetMenu(fileName = "NoBountyAbility", menuName = "TD/Enemy Abilities/No Bounty")]
- public class NoBountyAbilityDefinition : EnemyAbilityDefinition
- {
- public override EnemyAbilityKind Kind => EnemyAbilityKind.NoBounty;
-
- [Header("Bounty")]
- [Tooltip("Fraction of the normal kill bounty these enemies still pay. 0 = nothing at all.")]
- [Range(0f, 1f)]
- public float BountyMultiplier = 0f;
-
- public override int ServerModifyKillReward(EnemyAbility instance, int reward)
- => Mathf.FloorToInt(reward * BountyMultiplier);
- }
-}
diff --git a/Assets/_Project/Scripts/Gameplay/EnemyAbilities/NoBountyAbilityDefinition.cs.meta b/Assets/_Project/Scripts/Gameplay/EnemyAbilities/NoBountyAbilityDefinition.cs.meta
deleted file mode 100644
index a2f48ff..0000000
--- a/Assets/_Project/Scripts/Gameplay/EnemyAbilities/NoBountyAbilityDefinition.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: fd5b12948b24e9c4c8840ef3470b437e
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Gameplay/EnemyAbilities/SplitOnDeathAbilityDefinition.cs b/Assets/_Project/Scripts/Gameplay/EnemyAbilities/SplitOnDeathAbilityDefinition.cs
deleted file mode 100644
index 1d96391..0000000
--- a/Assets/_Project/Scripts/Gameplay/EnemyAbilities/SplitOnDeathAbilityDefinition.cs
+++ /dev/null
@@ -1,92 +0,0 @@
-// Assets/_Project/Scripts/Gameplay/EnemyAbilities/SplitOnDeathAbilityDefinition.cs
-using UnityEngine;
-using TD.Core;
-
-namespace TD.Gameplay.EnemyAbilities
-{
- ///
- /// On death, spawns smaller copies of the dying enemy's own type at
- /// the corpse's position, continuing on toward the goal instead of restarting from the wave's
- /// spawner.
- ///
- ///
- /// No separate minion asset. The copies reuse whichever
- /// and prefab the dying enemy was spawned from; every stat is expressed here as a percentage
- /// of what the parent actually spawned with, so one card covers any enemy it lands on.
- ///
- /// Percentages are of the PARENT, not the asset. If other cards on the same wave
- /// have already modified the enemy, the minions scale off the modified values — a split of a
- /// buffed enemy produces buffed minions, which is what players will expect from watching it.
- ///
- /// Minions inherit no abilities at all — not even this one. That is what makes the
- /// recursion terminate: a minion of a splitting enemy is a plain enemy, so a wave carrying
- /// Split produces exactly one extra generation regardless of how many other cards it has
- /// collected. Enforced at the spawn site, not here.
- ///
- [CreateAssetMenu(fileName = "SplitOnDeathAbility", menuName = "TD/Enemy Abilities/Split On Death")]
- public class SplitOnDeathAbilityDefinition : EnemyAbilityDefinition
- {
- public override EnemyAbilityKind Kind => EnemyAbilityKind.SplitOnDeath;
-
- [Header("Split")]
- [Tooltip("How many copies spawn when the parent dies.")]
- [Min(1)]
- public int SplitCount = 2;
-
- [Header("Minion stats (percent of the parent's spawned values)")]
- [Tooltip("Minion max HP as a fraction of the parent's max HP.")]
- [Range(0.01f, 2f)]
- public float HpPercent = 0.5f;
-
- [Tooltip("Minion move speed as a fraction of the parent's speed. Above 1 makes the split " +
- "faster than what died — the classic 'smaller and quicker' read.")]
- [Range(0.01f, 3f)]
- public float SpeedPercent = 1.25f;
-
- [Tooltip("Minion transform scale as a fraction of the parent's scale. Requires the enemy " +
- "prefab's NetworkTransform to sync scale, or minions render full-size on remote peers.")]
- [Range(0.1f, 2f)]
- public float ScalePercent = 0.6f;
-
- [Tooltip("Minion lives cost as a fraction of the parent's, rounded down but never below 1 " +
- "— a leak always has to hurt, or splits become a way to farm free ground.")]
- [Range(0f, 2f)]
- public float LivesCostPercent = 1f;
-
- [Tooltip("Minion flight height as a fraction of the parent's. Only meaningful when the " +
- "parent is flying; ground splits ignore it.")]
- [Range(0.1f, 2f)]
- public float FlightHeightPercent = 1f;
-
- [Header("Placement")]
- [Tooltip("Random scatter radius (world units) applied to each spawn so minions don't " +
- "stack exactly on top of each other.")]
- [Min(0f)]
- public float ScatterRadius = 0.5f;
-
- public override void ServerOnDeath(EnemyAbility instance, EnemyHealth health)
- {
- var def = health.Definition;
- if (def == null) return;
-
- var movement = instance.GetComponent();
- var atTile = GridCoordinates.WorldToGrid(instance.transform.position);
- var ownerSlot = movement != null ? movement.OriginZone : PlayerSlot.None;
-
- // Derive the minion's stats from what the parent actually spawned as.
- var parent = instance.SpawnContext;
- var minion = new EnemySpawnContext
- {
- MaxHp = Mathf.Max(1f, parent.MaxHp * HpPercent),
- MoveSpeed = Mathf.Max(0.01f, parent.MoveSpeed * SpeedPercent),
- IsFlying = parent.IsFlying,
- FlightHeight = parent.FlightHeight * FlightHeightPercent,
- LivesCost = Mathf.Max(1, Mathf.FloorToInt(parent.LivesCost * LivesCostPercent)),
- VisualScale = parent.VisualScale * ScalePercent,
- };
-
- WaveManager.Instance?.ServerSpawnSplitEnemies(
- def, SplitCount, atTile, ownerSlot, ScatterRadius, minion);
- }
- }
-}
diff --git a/Assets/_Project/Scripts/Gameplay/EnemyAbilities/SplitOnDeathAbilityDefinition.cs.meta b/Assets/_Project/Scripts/Gameplay/EnemyAbilities/SplitOnDeathAbilityDefinition.cs.meta
deleted file mode 100644
index 8c2a4f3..0000000
--- a/Assets/_Project/Scripts/Gameplay/EnemyAbilities/SplitOnDeathAbilityDefinition.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 21543cb1b54f0b16e9c80584f68988b3
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Gameplay/EnemyAbility.cs b/Assets/_Project/Scripts/Gameplay/EnemyAbility.cs
deleted file mode 100644
index 8913543..0000000
--- a/Assets/_Project/Scripts/Gameplay/EnemyAbility.cs
+++ /dev/null
@@ -1,199 +0,0 @@
-// Assets/_Project/Scripts/Gameplay/EnemyAbility.cs
-using System.Collections.Generic;
-using Unity.Netcode;
-using UnityEngine;
-using TD.Core;
-using TD.Gameplay.EnemyAbilities;
-
-namespace TD.Gameplay
-{
- ///
- /// Per-enemy ability set. Holds every its wave has
- /// accumulated and drives their server-only hooks.
- ///
- ///
- /// Initialization: Call on the server immediately after
- /// Instantiate and before NetworkObject.Spawn(), following the same pattern as
- /// EnemyHealth.InitializeServer / EnemyMovement.InitializeServer.
- ///
- /// A set, not a slot. This used to hold one ability rolled at random per enemy. Wave
- /// buffs stack across a phase's cycles, so it now holds a list and fans every hook out over
- /// all of them. replicates the set so clients can show players what
- /// they're facing.
- ///
- /// Optional component: Not required by EnemyHealth or EnemyMovement —
- /// WaveManager.SpawnEnemy only assigns abilities when this component is present on the
- /// prefab, so enemy prefabs without it simply never carry wave buffs.
- ///
- /// On-death hook is not event-driven: WaveManager.HandleEnemyKilled calls
- /// directly rather than this component subscribing to
- /// EnemyHealth.OnDied itself — that keeps a split-spawn's activeEnemyCount
- /// increment ordered before the triggering kill's decrement, regardless of NetworkBehaviour
- /// subscription order.
- ///
- [RequireComponent(typeof(NetworkObject))]
- public class EnemyAbility : NetworkBehaviour
- {
- // Replicated ability kinds carried by this enemy. Empty means no wave buffs. Exists so
- // clients can render "this wave splits and flies" in the enemy-info panel without
- // re-deriving it from run state.
- private NetworkList kinds;
-
- // ----- Pre-spawn init (server-local) ----------------------------------
-
- private readonly List pendingDefinitions = new List();
- private bool hasPendingInit;
-
- // ----- Public state -----------------------------------------------------
-
- private readonly List definitions = new List();
-
- /// The abilities this enemy carries. Server-side; empty on clients.
- public IReadOnlyList Definitions => definitions;
-
- /// True if this enemy carries no abilities at all.
- public bool IsPlain => definitions.Count == 0;
-
- ///
- /// The stats this enemy actually spawned with, after every ability's spawn modification.
- /// Server-side.
- ///
- ///
- /// Kept so on-death abilities can derive from what the enemy really was rather than from
- /// its . Split-on-death needs this: its minions are a
- /// percentage of "the enemy that died", which is not the authored asset once other cards
- /// on the same wave have modified it.
- ///
- public EnemySpawnContext SpawnContext { get; private set; }
-
- private void Awake()
- {
- kinds = new NetworkList();
- }
-
- /// Replicated check for a specific ability. Safe on any peer.
- public bool HasKind(EnemyAbilityKind kind)
- {
- byte target = (byte)kind;
- for (int i = 0; i < kinds.Count; i++)
- if (kinds[i] == target) return true;
- return false;
- }
-
- // ----- Server-only pre-spawn init -------------------------------------
-
- ///
- /// Called by WaveManager on the server after Instantiate and before
- /// NetworkObject.Spawn(). may be null or empty,
- /// meaning this enemy carries no wave buffs.
- ///
- ///
- /// Copies rather than retains the caller's list — WaveManager reuses one scratch
- /// buffer across every enemy in a wave, so holding the reference would alias every enemy
- /// to the same (later mutated) contents.
- ///
- public void InitializeServer(IReadOnlyList abilities,
- EnemySpawnContext spawnContext)
- {
- pendingDefinitions.Clear();
- if (abilities != null)
- {
- for (int i = 0; i < abilities.Count; i++)
- if (abilities[i] != null) pendingDefinitions.Add(abilities[i]);
- }
- SpawnContext = spawnContext;
- hasPendingInit = true;
- }
-
- // ----- NGO lifecycle --------------------------------------------------
-
- public override void OnNetworkSpawn()
- {
- if (!IsServer || !hasPendingInit) return;
-
- definitions.Clear();
- definitions.AddRange(pendingDefinitions);
- hasPendingInit = false;
-
- timers = definitions.Count > 0
- ? new float[definitions.Count]
- : System.Array.Empty();
-
- kinds.Clear();
- for (int i = 0; i < definitions.Count; i++)
- kinds.Add((byte)definitions[i].Kind);
-
- for (int i = 0; i < definitions.Count; i++)
- definitions[i].ServerOnSpawn(this, i);
- }
-
- // ----- Server tick ------------------------------------------------
-
- private void Update()
- {
- if (!IsServer || definitions.Count == 0) return;
-
- float dt = Time.deltaTime;
- for (int i = 0; i < definitions.Count; i++)
- definitions[i].ServerTick(this, i, dt);
- }
-
- // ----- Per-enemy ability scratch ----------------------------------
-
- // One float per carried ability, owned by that ability. Exists because the definitions
- // are shared ScriptableObjects: a cooldown stored on the asset would be shared by every
- // enemy in the wave, making them all fire in lockstep. Allocated once at init, sized to
- // the ability count — never resized, since the set is fixed at spawn.
- private float[] timers = System.Array.Empty();
-
- ///
- /// A per-enemy float owned by the ability at , returned by
- /// reference so it can be read and written in place. Typically a cooldown accumulator.
- ///
- public ref float TimerFor(int abilityIndex)
- {
- if (abilityIndex < 0 || abilityIndex >= timers.Length)
- {
- // Out-of-range means a caller passed an index that doesn't match its slot. Hand
- // back a scratch cell rather than throwing mid-tick; the ability just won't
- // accumulate, which is visible in play but not fatal.
- Debug.LogError($"[EnemyAbility] TimerFor({abilityIndex}) is out of range on " +
- $"{name}. The ability will not keep time.");
- return ref timerFallback;
- }
- return ref timers[abilityIndex];
- }
-
- private float timerFallback;
-
- // ----- Server hook fan-out ----------------------------------------
-
- /// Server-only: run every carried ability's on-death hook.
- public void ServerInvokeOnDeath(EnemyHealth health)
- {
- if (!IsServer) return;
- for (int i = 0; i < definitions.Count; i++)
- definitions[i].ServerOnDeath(this, health);
- }
-
- ///
- /// Server-only: chain every carried ability's kill-reward modifier. Result is clamped at
- /// zero so a stack of reward-suppressing cards can't hand out negative gold.
- ///
- public int ServerModifyKillReward(int reward)
- {
- if (!IsServer) return reward;
- for (int i = 0; i < definitions.Count; i++)
- reward = definitions[i].ServerModifyKillReward(this, reward);
- return Mathf.Max(0, reward);
- }
-
- /// Server-only: run every carried ability's reached-goal hook.
- public void ServerInvokeReachedGoal(PlayerSlot originZone)
- {
- if (!IsServer) return;
- for (int i = 0; i < definitions.Count; i++)
- definitions[i].ServerOnReachedGoal(this, originZone);
- }
- }
-}
diff --git a/Assets/_Project/Scripts/Gameplay/EnemyAbility.cs.meta b/Assets/_Project/Scripts/Gameplay/EnemyAbility.cs.meta
deleted file mode 100644
index 3562455..0000000
--- a/Assets/_Project/Scripts/Gameplay/EnemyAbility.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 9d1a26a32e8969cd29bce6010126a5f6
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Gameplay/EnemyDefinition.cs b/Assets/_Project/Scripts/Gameplay/EnemyDefinition.cs
index bda4e45..15f45f8 100644
--- a/Assets/_Project/Scripts/Gameplay/EnemyDefinition.cs
+++ b/Assets/_Project/Scripts/Gameplay/EnemyDefinition.cs
@@ -12,13 +12,6 @@ namespace TD.Gameplay
/// in project assets, only the asset reference (or its fields) crosses runtime code.
/// Replace with a real mesh/animator when art is ready —
/// no code changes required.
- ///
- /// An enemy type is flavour, not a difficulty tier. There is deliberately no
- /// absolute health field: health comes from based on how far
- /// into the run the encounter sits, and this asset only says how the type deviates from that
- /// (). Because waves are drawn at random from a phase pool, any
- /// difficulty baked into the roster would be difficulty handed out by the luck of the draw.
- /// Every type should be a viable occupant of any slot.
///
[CreateAssetMenu(fileName = "EnemyDefinition", menuName = "TD/Enemy Definition", order = 3)]
public class EnemyDefinition : ScriptableObject
@@ -28,16 +21,10 @@ namespace TD.Gameplay
public string DisplayName;
[Header("Stats")]
- [Tooltip("How tough this enemy is RELATIVE to its encounter's budget — not an absolute " +
- "hit-point value. 1.0 = exactly its share of the budget; 1.25 = a quarter " +
- "tougher than the slot calls for. Keep it near 1.0 and let the wave's enemy " +
- "count express tankiness (fewer enemies each get a bigger share).")]
- [Min(0.01f)]
- public float HpMultiplier = 1f;
+ [Tooltip("Maximum hit points for this enemy type.")]
+ public float MaxHp = 100f;
- [Tooltip("Movement speed in world units per second along the A* path. ABSOLUTE and " +
- "unscaled — speed is a fixed character trait, not a progression axis. See " +
- "EnemyScalingConfig for why.")]
+ [Tooltip("Movement speed in world units per second along the A* path.")]
public float MoveSpeed = 3f;
[Tooltip("When true this enemy flies: it paths on the baked terrain grid, ignoring " +
diff --git a/Assets/_Project/Scripts/Gameplay/EnemyHealth.cs b/Assets/_Project/Scripts/Gameplay/EnemyHealth.cs
index 28f6ab9..10256d2 100644
--- a/Assets/_Project/Scripts/Gameplay/EnemyHealth.cs
+++ b/Assets/_Project/Scripts/Gameplay/EnemyHealth.cs
@@ -1,6 +1,5 @@
// Assets/_Project/Scripts/Gameplay/EnemyHealth.cs
using System.Collections;
-using System.Collections.Generic;
using Unity.Netcode;
using UnityEngine;
using TD.Core;
@@ -68,14 +67,13 @@ namespace TD.Gameplay
private int pendingLivesCost = 1;
private bool pendingIsFlying;
private bool pendingIsHeld;
- private bool pendingIsBoss;
private bool hasPendingInit;
// ----- Server-local runtime state -------------------------------------
- // Kill gold is no longer carried per-enemy — it comes from the GoldConfig entry for
- // the current encounter (GoldConfig.GetEncounterEntry) at the moment the kill is
- // registered. See WaveManager.HandleEnemyKilled.
+ // Kill gold is no longer carried per-enemy — it comes from
+ // GoldConfig.Waves[currentWaveIndex].GoldPerEnemy at the moment the kill
+ // is registered. See WaveManager.HandleEnemyKilled.
/// Lives deducted from the shared pool when this enemy reaches the goal.
public int LivesCost { get; private set; } = 1;
@@ -105,13 +103,6 @@ namespace TD.Gameplay
NetworkVariableReadPermission.Everyone,
NetworkVariableWritePermission.Server);
- // Set for enemies spawned by a phase's boss encounter. Replicated because the HUD's boss
- // bar is client-side and has to recognise a boss without asking the server.
- private readonly NetworkVariable isBoss = new NetworkVariable(
- false,
- NetworkVariableReadPermission.Everyone,
- NetworkVariableWritePermission.Server);
-
// ----- Public state ---------------------------------------------------
public float CurrentHp => hp.Value;
@@ -126,19 +117,6 @@ namespace TD.Gameplay
///
public bool IsFlying => isFlying.Value;
- /// True if this enemy was spawned by a phase's boss encounter. Replicated.
- public bool IsBoss => isBoss.Value;
-
- ///
- /// Every live boss, on every peer. Maintained by spawn/despawn so the HUD can render a
- /// boss bar without polling the scene.
- ///
- ///
- /// A list rather than a single reference because the boss encounter spawns one boss per
- /// player zone — there are as many simultaneous bosses as there are players.
- ///
- public static readonly List ActiveBosses = new List();
-
// ----- Events ---------------------------------------------------------
///
@@ -158,14 +136,12 @@ namespace TD.Gameplay
/// and before NetworkObject.Spawn(). Mirrors the
/// TowerInstance.InitializeServer pattern.
///
- public void InitializeServer(float maxHp, int livesCost, bool flying, bool held = false,
- bool boss = false)
+ public void InitializeServer(float maxHp, int livesCost, bool flying, bool held = false)
{
pendingMaxHp = maxHp;
pendingLivesCost = livesCost;
pendingIsFlying = flying;
pendingIsHeld = held;
- pendingIsBoss = boss;
hasPendingInit = true;
// Cache locally on the server immediately — clients resolve via NV.
@@ -182,7 +158,6 @@ namespace TD.Gameplay
hp.Value = pendingMaxHp;
isFlying.Value = pendingIsFlying;
isHeld.Value = pendingIsHeld;
- isBoss.Value = pendingIsBoss;
hasPendingInit = false;
}
@@ -190,9 +165,6 @@ namespace TD.Gameplay
if (!IsServer)
MaxHp = hp.Value;
- if (isBoss.Value && !ActiveBosses.Contains(this))
- ActiveBosses.Add(this);
-
// Apply initial held state on all peers and watch for future changes.
// isHeld.OnValueChanged doesn't fire for the initial replication, so we
// apply it explicitly here as well.
@@ -202,8 +174,6 @@ namespace TD.Gameplay
public override void OnNetworkDespawn()
{
- ActiveBosses.Remove(this);
-
// If this enemy was the locally-selected ISelectable, clear the
// selection so the HUD doesn't keep displaying a stale corpse.
// SelectionState is a local UI singleton, safe to query on any peer.
diff --git a/Assets/_Project/Scripts/Gameplay/EnemyMovement.cs b/Assets/_Project/Scripts/Gameplay/EnemyMovement.cs
index 6815ff2..86195c6 100644
--- a/Assets/_Project/Scripts/Gameplay/EnemyMovement.cs
+++ b/Assets/_Project/Scripts/Gameplay/EnemyMovement.cs
@@ -21,10 +21,8 @@ namespace TD.Gameplay
/// and stores the tile waypoint list.
/// - Each frame: moves toward the world center of remainingPath[0].
/// When within snap distance, pops the waypoint and checks for zone transitions.
- /// - When the maze changes (tower placed / sold), 's
- /// budgeted scheduler calls (registered via
- /// ), rerunning A* from the current
- /// tile — spread across frames so a full wave never spikes.
+ /// - When fires (tower placed /
+ /// sold), reruns A* from the current tile.
/// - When remainingPath is empty after a pop, the enemy has reached the
/// goal — fires and the enemy is despawned.
///
@@ -73,11 +71,6 @@ namespace TD.Gameplay
// the goal). Without this, every zone crossing was counted as a leak; only
// the originating player should be credited a leak per the design.
private PlayerSlot originZone = PlayerSlot.None;
-
- /// The zone (player) this enemy was originally spawned for — see
- /// . Used by on-death abilities (e.g. Split on Death) so
- /// spawned children preserve the same leak attribution as their parent.
- public PlayerSlot OriginZone => originZone;
// Latches once the enemy has crossed its origin zone's leak volume, so we
// never double-count a leak if the enemy re-enters its origin (rare but
// possible if pathing is dynamic).
@@ -175,18 +168,15 @@ namespace TD.Gameplay
// Recompute when a tower is placed or sold — grounded enemies only.
// Flyers path on the static baked grid, so tower changes can never affect
- // their route; registering would just trigger needless recomputes.
- // Registration (not a direct event subscription) lets PathfindingService
- // spread the recomputes across frames under a per-frame budget, so a maze
- // change with a full wave present never spikes a single frame.
+ // their route; subscribing would just trigger needless recomputes.
if (!isFlying && PathfindingService.Instance != null)
- PathfindingService.Instance.RegisterMover(this);
+ PathfindingService.Instance.OnPathsInvalidated += RecomputePath;
}
public override void OnNetworkDespawn()
{
if (PathfindingService.Instance != null)
- PathfindingService.Instance.UnregisterMover(this);
+ PathfindingService.Instance.OnPathsInvalidated -= RecomputePath;
}
// ----- Server update --------------------------------------------------
@@ -282,81 +272,10 @@ namespace TD.Gameplay
NetworkObject.Despawn();
}
- // ----- Ability-driven movement ----------------------------------------
-
- ///
- /// Server-only: teleport this enemy forward along its existing path by up to
- /// world units. Used by the Blink wave buff.
- ///
- ///
- /// Follows the path rather than the straight line to the goal. Jumping toward the
- /// goal as the crow flies would let a grounded enemy blink through walls, which is the
- /// flying buff's job, not this one. Walking the path keeps the enemy inside the maze —
- /// it cuts corners, it doesn't ignore them.
- ///
- /// Distance is measured along the path (waypoint to waypoint), consuming the
- /// budget one segment at a time, so the landing spot can fall mid-segment rather than
- /// snapping to a waypoint. This keeps the blink distance predictable regardless of how
- /// far apart the path's waypoints happen to be — unlike skipping a fixed waypoint count,
- /// where one blink could span a few tiles or the length of the whole map depending on
- /// path smoothing.
- ///
- /// Zone tracking runs once, on the final landing tile, so an enemy that blinks out
- /// of its origin zone still credits its owner a leak. Skipping that would make the buff a
- /// way to launder leaks past the no-leak bonus.
- ///
- /// Blinking onto (or past) the final waypoint resolves as a normal goal arrival,
- /// despawn and life cost included.
- ///
- public void ServerBlinkForward(float meters)
- {
- if (!IsServer || meters <= 0f) return;
- if (hasReachedGoal || remainingPath.Count == 0) return;
- if (health != null && health.IsHeld) return;
-
- float remaining = meters;
- Vector3 position = transform.position;
-
- while (remaining > 0f && remainingPath.Count > 0)
- {
- Vector3 targetWorld = GridCoordinates.GridToWorld(remainingPath[0]);
- Vector3 toTarget = targetWorld - position;
- toTarget.y = 0f;
- float distance = toTarget.magnitude;
-
- if (distance <= remaining)
- {
- // Reach this waypoint fully and keep walking the leftover budget
- // into the next segment.
- remaining -= distance;
- position = new Vector3(targetWorld.x, position.y, targetWorld.z);
- remainingPath.RemoveAt(0);
- }
- else
- {
- // Budget runs out partway along this segment.
- position += toTarget.normalized * remaining;
- remaining = 0f;
- }
- }
-
- // Preserve Y so flyers stay at altitude and grounded pivots don't sink.
- transform.position = position;
-
- if (remainingPath.Count == 0)
- {
- HandleGoalReached();
- return;
- }
-
- CheckZoneTransition(GridCoordinates.WorldToGrid(position));
- }
-
// ----- Path invalidation ----------------------------------------------
- // Called by PathfindingService's budgeted scheduler after a walkability change
- // (tower placed/sold). Public so the scheduler can drive it across frames.
- public void RecomputePath()
+ // Called on server when LevelLoader.OnWalkabilityChanged fires (tower placed/sold).
+ private void RecomputePath()
{
if (!IsServer) return;
diff --git a/Assets/_Project/Scripts/Gameplay/EnemyScalingConfig.cs b/Assets/_Project/Scripts/Gameplay/EnemyScalingConfig.cs
deleted file mode 100644
index 8a833ee..0000000
--- a/Assets/_Project/Scripts/Gameplay/EnemyScalingConfig.cs
+++ /dev/null
@@ -1,85 +0,0 @@
-// Assets/_Project/Scripts/Gameplay/EnemyScalingConfig.cs
-using UnityEngine;
-
-namespace TD.Gameplay
-{
- ///
- /// How enemy health scales across a run. Single source of truth for difficulty progression,
- /// the health counterpart to .
- ///
- ///
- /// The problem this solves. Waves are drawn at random from a phase pool, so difficulty
- /// can no longer live in the assets — an enemy authored at
- /// 1000 HP is brutal at encounter 1 and trivial at encounter 40, and the draw decides which
- /// you get. Progression has to be a property of how far the players have come, exactly
- /// as gold payouts are.
- ///
- /// Budget, not per-enemy HP. This config scales the total health an
- /// encounter presents; per-enemy health is that budget divided by the wave's enemy count. If
- /// the curve set per-enemy health instead, enemy count would silently become a second
- /// difficulty axis and a 150-strong swarm would hit three times harder than a 50-strong wave
- /// in the same slot. Dividing a fixed budget turns count into a texture knob: eight
- /// giants and a hundred rats threaten equally, but demand different mazes.
- ///
- /// Budget is per zone. Every player zone spawns the full wave, so this is the
- /// health one player faces, not the lobby total. It does not change with player count.
- ///
- /// Speed is deliberately absent. Speed stays on the
- /// , unscaled. Escalating speed alongside health compounds into
- /// a difficulty cliff and quietly invalidates tower balance as a run goes — projectile lead,
- /// slow-effect value and time-under-fire all shift. Fast-versus-slow reads best as a fixed
- /// character trait; health and the voted wave buffs carry escalation.
- ///
- [CreateAssetMenu(fileName = "EnemyScalingConfig", menuName = "TD/Enemy Scaling Config", order = 3)]
- public class EnemyScalingConfig : ScriptableObject
- {
- [Header("Health curve")]
- [Tooltip("Total enemy health the run's FIRST encounter presents, per player zone. " +
- "Per-enemy health is this divided by the wave's enemy count.")]
- [Min(1f)]
- public float BaseHpBudget = 5000f;
-
- [Tooltip("Multiplier applied per encounter. 1.18 ≈ +18% health each encounter, so the " +
- "budget roughly doubles every four. Encounters are counted continuously across " +
- "the whole run, so cycle 2 is strictly harder than cycle 1.")]
- [Min(1f)]
- public float GrowthPerEncounter = 1.18f;
-
- [Header("Boss")]
- [Tooltip("Extra multiplier on a boss encounter's budget, on top of the curve. Bosses " +
- "spawn few enemies, so nearly all of this lands on the single boss body.")]
- [Min(1f)]
- public float BossHpMultiplier = 6f;
-
- ///
- /// Total health budget for the given 1-based encounter number
- /// (RunState.GlobalEncounterNumber), per player zone.
- ///
- public float GetEncounterHpBudget(int encounterNumber, bool isBoss)
- {
- int steps = Mathf.Max(0, encounterNumber - 1);
- float budget = BaseHpBudget * Mathf.Pow(GrowthPerEncounter, steps);
- if (isBoss) budget *= BossHpMultiplier;
- return budget;
- }
-
- ///
- /// Health for one enemy in the given encounter: the encounter's budget shared across
- /// bodies, then skewed by the enemy type's
- /// .
- ///
- ///
- /// The archetype multiplier is a deliberate deviation from budget, not a
- /// redistribution of it — a wave of 1.25× enemies really is 25% tougher than its slot
- /// calls for. Keep multipliers near 1.0 and let the wave's enemy count express tankiness;
- /// that is the knob the budget is designed to divide.
- ///
- public float ResolvePerEnemyHp(int encounterNumber, bool isBoss, int waveEnemyCount,
- float archetypeMultiplier)
- {
- int bodies = Mathf.Max(1, waveEnemyCount);
- float share = GetEncounterHpBudget(encounterNumber, isBoss) / bodies;
- return Mathf.Max(1f, share * Mathf.Max(0.01f, archetypeMultiplier));
- }
- }
-}
diff --git a/Assets/_Project/Scripts/Gameplay/EnemyScalingConfig.cs.meta b/Assets/_Project/Scripts/Gameplay/EnemyScalingConfig.cs.meta
deleted file mode 100644
index a428b6f..0000000
--- a/Assets/_Project/Scripts/Gameplay/EnemyScalingConfig.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: de25a6333a11d074280b5ade557aa8b9
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Gameplay/EnemyUpgrades.meta b/Assets/_Project/Scripts/Gameplay/EnemyUpgrades.meta
deleted file mode 100644
index f5ba3d6..0000000
--- a/Assets/_Project/Scripts/Gameplay/EnemyUpgrades.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 95ac4e8d1714a07489dad0c283970bf8
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Scripts/Gameplay/EnemyUpgrades/AbilityEnemyUpgradeOption.cs b/Assets/_Project/Scripts/Gameplay/EnemyUpgrades/AbilityEnemyUpgradeOption.cs
deleted file mode 100644
index 3576d6b..0000000
--- a/Assets/_Project/Scripts/Gameplay/EnemyUpgrades/AbilityEnemyUpgradeOption.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-// Assets/_Project/Scripts/Gameplay/EnemyUpgrades/AbilityEnemyUpgradeOption.cs
-using UnityEngine;
-using TD.Gameplay.EnemyAbilities;
-using TD.Gameplay.Waves;
-
-namespace TD.Gameplay.EnemyUpgrades
-{
- ///
- /// The workhorse enemy-buff card: grants an to a wave
- /// slot. Every enemy spawned by that wave, in every later cycle of the phase, carries the
- /// ability.
- ///
- ///
- /// There is nothing to do at vote-resolution time — recording the option id on the slot IS the
- /// effect, because the spawn path builds each enemy's ability set from the slot's recorded
- /// options. is what that lookup resolves to.
- ///
- [CreateAssetMenu(fileName = "EnemyUpgrade_Ability",
- menuName = "TD/Enemy Upgrades/Ability Card", order = 20)]
- public class AbilityEnemyUpgradeOption : EnemyUpgradeOption
- {
- [Header("Payload")]
- [Tooltip("The ability every enemy in this wave gains. Must also be present in the scene's " +
- "EnemyAbilityPool so it can be resolved at spawn time.")]
- public EnemyAbilityDefinition Ability;
-
- public override bool IsValidForSlot(RunState run, int slot)
- {
- if (Ability == null) return false;
-
- // Don't offer an ability the slot already has by another card's hand. Exact-id repeats
- // are filtered by the caller, but two different cards can grant the same ability, and
- // stacking one twice is a no-op the players would rightly feel cheated by.
- return !SlotAlreadyHasAbility(run, slot);
- }
-
- // Shared scratch: validation runs on the server during vote generation, which is
- // single-threaded, so one reused list beats allocating per candidate per slot.
- private static readonly System.Collections.Generic.List s_idScratch
- = new System.Collections.Generic.List();
-
- private bool SlotAlreadyHasAbility(RunState run, int slot)
- {
- var pool = EnemyUpgradePool.Instance;
- if (run == null || pool == null) return false;
-
- var ids = s_idScratch;
- run.GetUpgradesForSlot(slot, ids);
-
- foreach (int id in ids)
- {
- if (pool.Get(id) is AbilityEnemyUpgradeOption other && other.Ability == Ability)
- return true;
- }
- return false;
- }
-
- /// Inherits nothing automatically — carries no
- /// icon, so the card's own is the only source.
- public override Sprite ResolveIcon() => Icon;
- }
-}
diff --git a/Assets/_Project/Scripts/Gameplay/EnemyUpgrades/AbilityEnemyUpgradeOption.cs.meta b/Assets/_Project/Scripts/Gameplay/EnemyUpgrades/AbilityEnemyUpgradeOption.cs.meta
deleted file mode 100644
index f619df4..0000000
--- a/Assets/_Project/Scripts/Gameplay/EnemyUpgrades/AbilityEnemyUpgradeOption.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: d075c66b381872a4781b3916296ccdc9
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Gameplay/EnemyUpgrades/DoubleUpEnemyUpgradeOption.cs b/Assets/_Project/Scripts/Gameplay/EnemyUpgrades/DoubleUpEnemyUpgradeOption.cs
deleted file mode 100644
index 882dd8f..0000000
--- a/Assets/_Project/Scripts/Gameplay/EnemyUpgrades/DoubleUpEnemyUpgradeOption.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-// Assets/_Project/Scripts/Gameplay/EnemyUpgrades/DoubleUpEnemyUpgradeOption.cs
-using UnityEngine;
-using TD.Gameplay.Waves;
-
-namespace TD.Gameplay.EnemyUpgrades
-{
- ///
- /// Vote-meta card: the next time this wave is cleared, it takes several buffs automatically
- /// and the players get no say in which.
- ///
- ///
- /// Not an enemy ability. Nothing about the enemies changes when this is voted in — it
- /// changes how the next vote on this slot resolves. That's why it derives from
- /// directly instead of wrapping an
- /// EnemyAbilityDefinition: the spawn path builds ability sets only from ability cards,
- /// so this one is recorded on the slot and otherwise invisible to enemies.
- ///
- /// The trade is agency for information. Voting this in is choosing to make one
- /// future wave worse in exchange for it not being the worst option — the auto-draw is weighted
- /// and slot-filtered exactly like a real vote, so it's a gamble on the pool, not a punishment.
- /// It also disarms itself after firing (ServerConsumeAutoApply), so a slot can only be
- /// doubled once per card.
- ///
- /// Repeats are prevented by the normal offer filter: the slot records this card like any
- /// other, so it can't be offered to the same slot twice in a phase.
- ///
- [CreateAssetMenu(fileName = "EnemyUpgrade_DoubleUp",
- menuName = "TD/Enemy Upgrades/Double Up Card", order = 22)]
- public class DoubleUpEnemyUpgradeOption : EnemyUpgradeOption
- {
- [Header("Payload")]
- [Tooltip("How many buffs the wave takes automatically in place of its next vote.")]
- [Min(2)]
- public int BuffsToAutoApply = 2;
-
- public override bool IsValidForSlot(RunState run, int slot)
- {
- // Pointless to offer while the slot is already armed — the player would be trading a
- // vote away for nothing.
- return run != null && run.GetAutoApplyCount(slot) <= 0;
- }
-
- public override void ServerOnApplied(RunState run, int slot)
- {
- run?.ServerSetAutoApply(slot, BuffsToAutoApply);
- }
- }
-}
diff --git a/Assets/_Project/Scripts/Gameplay/EnemyUpgrades/DoubleUpEnemyUpgradeOption.cs.meta b/Assets/_Project/Scripts/Gameplay/EnemyUpgrades/DoubleUpEnemyUpgradeOption.cs.meta
deleted file mode 100644
index ca93bb2..0000000
--- a/Assets/_Project/Scripts/Gameplay/EnemyUpgrades/DoubleUpEnemyUpgradeOption.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: f5e90fe6b8e439e4f99769a6923c2262
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Gameplay/EnemyUpgrades/EnemyUpgradeGroup.cs b/Assets/_Project/Scripts/Gameplay/EnemyUpgrades/EnemyUpgradeGroup.cs
deleted file mode 100644
index 1e0f9f2..0000000
--- a/Assets/_Project/Scripts/Gameplay/EnemyUpgrades/EnemyUpgradeGroup.cs
+++ /dev/null
@@ -1,75 +0,0 @@
-// Assets/_Project/Scripts/Gameplay/EnemyUpgrades/EnemyUpgradeGroup.cs
-using System;
-using System.Collections.Generic;
-using UnityEngine;
-using TD.Core;
-
-namespace TD.Gameplay.EnemyUpgrades
-{
- ///
- /// One weighted candidate inside an .
- ///
- ///
- /// Weight is relative and normalized at draw time — see .
- /// Kept on the entry rather than on the option asset for the same reason wave weights are:
- /// a card can be a staple of one phase's pool and a rarity in another's.
- ///
- [Serializable]
- public class EnemyUpgradePoolEntry
- {
- [Tooltip("The enemy-buff card that may be offered from this group.")]
- public EnemyUpgradeOption Option;
-
- [Tooltip("Relative draw weight within the pool this group contributes to. Entries at " +
- "equal weight are equally likely; 0.5 is half as likely as 1.0.")]
- [PoolWeight("buff")]
- public float Weight = 1f;
- }
-
- ///
- /// A named, reusable bag of enemy-buff cards. Groups are the unit designers move between
- /// phases: dragging a group into Phase 2's list makes every card in it votable from phase 2
- /// onward, without touching the cards themselves.
- ///
- ///
- /// Buffs are gated by phase, not by cycle — every card a phase allows is votable from
- /// its very first wave. Gating by cycle was considered and rejected: the whole point of the
- /// cyclical structure is that players choose how hard to make their own next lap, and holding
- /// the interesting cards back until cycle 3 would flatten that decision into a formality.
- ///
- [CreateAssetMenu(fileName = "EnemyUpgradeGroup",
- menuName = "TD/Enemy Upgrades/Upgrade Group", order = 21)]
- public class EnemyUpgradeGroup : ScriptableObject
- {
- [Tooltip("Designer-facing name, shown in validation messages. Falls back to the asset name.")]
- public string DisplayName;
-
- [Tooltip("Cards in this group, each with a relative draw weight.")]
- public EnemyUpgradePoolEntry[] Options;
-
- /// Name used in validation and log messages. Falls back to the asset name.
- public string Label => string.IsNullOrWhiteSpace(DisplayName) ? name : DisplayName;
-
- ///
- /// Appends every non-null, non-zero-weight entry across into
- /// . Zero-weight entries are filtered here so "weight 0 means it
- /// can never appear" lives in exactly one place.
- ///
- public static void CollectCandidates(EnemyUpgradeGroup[] groups, List into)
- {
- into.Clear();
- if (groups == null) return;
-
- foreach (var group in groups)
- {
- if (group?.Options == null) continue;
- foreach (var entry in group.Options)
- {
- if (entry?.Option == null) continue;
- if (entry.Weight <= 0f) continue;
- into.Add(entry);
- }
- }
- }
- }
-}
diff --git a/Assets/_Project/Scripts/Gameplay/EnemyUpgrades/EnemyUpgradeGroup.cs.meta b/Assets/_Project/Scripts/Gameplay/EnemyUpgrades/EnemyUpgradeGroup.cs.meta
deleted file mode 100644
index 3410908..0000000
--- a/Assets/_Project/Scripts/Gameplay/EnemyUpgrades/EnemyUpgradeGroup.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 4202a767d96e70f41b6a7e84eb08a832
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Gameplay/EnemyUpgrades/EnemyUpgradeOption.cs b/Assets/_Project/Scripts/Gameplay/EnemyUpgrades/EnemyUpgradeOption.cs
deleted file mode 100644
index ac43085..0000000
--- a/Assets/_Project/Scripts/Gameplay/EnemyUpgrades/EnemyUpgradeOption.cs
+++ /dev/null
@@ -1,66 +0,0 @@
-// Assets/_Project/Scripts/Gameplay/EnemyUpgrades/EnemyUpgradeOption.cs
-using UnityEngine;
-using TD.Gameplay.Waves;
-
-namespace TD.Gameplay.EnemyUpgrades
-{
- ///
- /// Base class for one card in the post-wave enemy-buff vote. Players collectively choose one
- /// of these to permanently attach to the wave they just cleared; it takes effect the next time
- /// that wave comes round in the cycle.
- ///
- ///
- /// Mirrors DraftOption deliberately. Same shape — abstract SO, one asset
- /// per card, concrete subclasses carry the payload — so the two authoring surfaces read the
- /// same way. The difference is who it targets: a draft option applies to a player, an enemy
- /// upgrade applies to a wave slot.
- ///
- /// Identity. Options live in and cross the network
- /// as their pool index, the same stable-within-a-match id pattern used by the tower catalog
- /// and draft pool.
- ///
- /// Recording is not this type's job. WaveVote records the winning option id
- /// onto the slot via RunState.ServerAddUpgrade; is only
- /// for cards that need a side effect beyond being recorded (the vote-meta cards).
- /// Ability cards need nothing here — the spawn path reads the slot's recorded set.
- ///
- public abstract class EnemyUpgradeOption : ScriptableObject
- {
- [Header("Presentation")]
- [Tooltip("Name shown on the vote card.")]
- public string DisplayName;
-
- [Tooltip("Short description shown on the vote card. Say what it does to the enemies, in " +
- "the players' terms — this is the only thing they get to judge the vote on.")]
- [TextArea(2, 4)]
- public string Description;
-
- [Tooltip("OPTIONAL override for the vote card icon. Leave empty to inherit the icon of " +
- "whatever this option grants.")]
- public Sprite Icon;
-
- ///
- /// Icon actually shown on the vote card. Subclasses override to fall back to the icon of
- /// the thing they grant; the base has nothing to inherit from, so it returns
- /// (which may be null).
- ///
- public virtual Sprite ResolveIcon() => Icon;
-
- ///
- /// Server-only: may this option be OFFERED for right now? Default
- /// true; override for cards with prerequisites or ones that would be a no-op.
- ///
- ///
- /// Callers already skip options the slot has taken before, so implementations don't need
- /// to re-check that.
- ///
- public virtual bool IsValidForSlot(RunState run, int slot) => true;
-
- ///
- /// Server-only: side effect to run after this option has been recorded onto
- /// . Default no-op — most cards do their work at enemy-spawn time
- /// by virtue of being on the slot, not at vote-resolution time.
- ///
- public virtual void ServerOnApplied(RunState run, int slot) { }
- }
-}
diff --git a/Assets/_Project/Scripts/Gameplay/EnemyUpgrades/EnemyUpgradeOption.cs.meta b/Assets/_Project/Scripts/Gameplay/EnemyUpgrades/EnemyUpgradeOption.cs.meta
deleted file mode 100644
index 37a9369..0000000
--- a/Assets/_Project/Scripts/Gameplay/EnemyUpgrades/EnemyUpgradeOption.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 6b1e9770be72a694a96169d9a9032528
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Gameplay/EnemyUpgrades/EnemyUpgradePool.cs b/Assets/_Project/Scripts/Gameplay/EnemyUpgrades/EnemyUpgradePool.cs
deleted file mode 100644
index f6c51e8..0000000
--- a/Assets/_Project/Scripts/Gameplay/EnemyUpgrades/EnemyUpgradePool.cs
+++ /dev/null
@@ -1,94 +0,0 @@
-// Assets/_Project/Scripts/Gameplay/EnemyUpgrades/EnemyUpgradePool.cs
-using System.Collections.Generic;
-using UnityEngine;
-
-namespace TD.Gameplay.EnemyUpgrades
-{
- ///
- /// Scene singleton holding every that can appear anywhere in
- /// this match. The array index is the option's EnemyUpgradeOptionId — the stable
- /// identifier used to replicate vote offers, ballots, and the upgrades recorded on wave slots.
- ///
- ///
- /// Why a flat pool and per-phase groups. The pool supplies network identity: one
- /// array, one index per card, identical on every peer. PhaseDefinition.EnemyUpgradeGroups
- /// supplies gating: which of those cards a given phase may offer. Folding gating into the pool
- /// would make ids phase-relative and therefore unstable across a phase change, which is
- /// exactly when replicated slot upgrades are being cleared and rewritten.
- ///
- /// Plain MonoBehaviour — identical assets on every peer, so there is nothing to sync.
- /// The server reads it to generate and resolve votes; clients read it to render vote cards
- /// from replicated ids.
- ///
- public class EnemyUpgradePool : MonoBehaviour
- {
- public static EnemyUpgradePool Instance { get; private set; }
-
- [Tooltip("Every EnemyUpgradeOption asset that can appear this match. Array index is the " +
- "id used over the network — reordering mid-development is fine, but not between " +
- "a host and its clients.")]
- [SerializeField] private EnemyUpgradeOption[] options;
-
- private void Awake()
- {
- if (Instance != null && Instance != this)
- {
- Debug.LogError("[EnemyUpgradePool] Multiple instances detected. Only one per scene.");
- return;
- }
- Instance = this;
- }
-
- private void OnDestroy()
- {
- if (Instance == this) Instance = null;
- }
-
- /// Number of options in the pool.
- public int Count => options?.Length ?? 0;
-
- /// Returns the option at , or null if out of range.
- public EnemyUpgradeOption Get(int id)
- => (options != null && id >= 0 && id < options.Length) ? options[id] : null;
-
- /// Resolves an option asset back to its pool id, or -1 if it isn't in the pool.
- public int GetId(EnemyUpgradeOption option)
- {
- if (options == null || option == null) return -1;
- for (int i = 0; i < options.Length; i++)
- if (options[i] == option) return i;
- return -1;
- }
-
- ///
- /// Collects the pool ids of every card the given phase groups allow. Cards missing from
- /// the pool are reported once and skipped — an authored group referencing an unpooled
- /// card is a wiring mistake that would otherwise silently shrink the vote.
- ///
- public void CollectAllowedIds(EnemyUpgradeGroup[] phaseGroups,
- List entryScratch,
- List intoIds,
- List intoWeights)
- {
- intoIds.Clear();
- intoWeights.Clear();
-
- EnemyUpgradeGroup.CollectCandidates(phaseGroups, entryScratch);
-
- foreach (var entry in entryScratch)
- {
- int id = GetId(entry.Option);
- if (id < 0)
- {
- Debug.LogWarning($"[EnemyUpgradePool] '{entry.Option.name}' is referenced by a " +
- $"phase's upgrade groups but is not in the pool — it can " +
- $"never be offered. Add it to the pool's options array.");
- continue;
- }
-
- intoIds.Add(id);
- intoWeights.Add(entry.Weight);
- }
- }
- }
-}
diff --git a/Assets/_Project/Scripts/Gameplay/EnemyUpgrades/EnemyUpgradePool.cs.meta b/Assets/_Project/Scripts/Gameplay/EnemyUpgrades/EnemyUpgradePool.cs.meta
deleted file mode 100644
index 161d578..0000000
--- a/Assets/_Project/Scripts/Gameplay/EnemyUpgrades/EnemyUpgradePool.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: c4d98a01d9b9a7b438a6f91b0b7f498f
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Gameplay/EnemyUpgrades/WaveVote.cs b/Assets/_Project/Scripts/Gameplay/EnemyUpgrades/WaveVote.cs
deleted file mode 100644
index 4d8e872..0000000
--- a/Assets/_Project/Scripts/Gameplay/EnemyUpgrades/WaveVote.cs
+++ /dev/null
@@ -1,419 +0,0 @@
-// Assets/_Project/Scripts/Gameplay/EnemyUpgrades/WaveVote.cs
-using System.Collections.Generic;
-using Unity.Netcode;
-using UnityEngine;
-using TD.Core;
-using TD.Gameplay.Waves;
-
-namespace TD.Gameplay.EnemyUpgrades
-{
- ///
- /// The shared, open-ballot vote held after each wave clears: players collectively pick
- /// which permanent buff the wave they just beat will carry into the next cycle.
- ///
- ///
- /// Ballots are public by design. replicates to everyone the
- /// instant a vote is cast, and the HUD paints each voter's slot badge onto the card they chose.
- /// That visibility is the point of the mechanic — seeing two teammates converge on "Flying" is
- /// what lets the third decide whether to join them or split the vote. A private tally would
- /// reduce this to a dice roll with extra steps.
- ///
- /// Votes stay changeable until the vote resolves, for the same reason. The
- /// consequence is that the last player to vote ends the vote instantly (see
- /// ), locking their choice in with no chance for anyone to
- /// respond. If that reads badly in playtest, the fix is a short grace period after the final
- /// ballot rather than closing the ballots.
- ///
- /// Scene singleton, server-authoritative. Unlike PlayerDraft (one per
- /// player) this is one shared object, so votes arrive as plain server RPCs and the server maps
- /// the sender to a itself rather than trusting an owner claim.
- ///
- public class WaveVote : NetworkBehaviour
- {
- // ----- Singleton ---------------------------------------------------
-
- public static WaveVote Instance { get; private set; }
-
- // ----- Inspector ---------------------------------------------------
-
- [Tooltip("How many buff cards the vote offers. Standard is 3.")]
- [SerializeField] private int optionsPerVote = 3;
-
- // ----- Networked state ---------------------------------------------
-
- // The EnemyUpgradeOptionIds on the table. Empty when no vote is open.
- private NetworkList offeredOptionIds;
-
- // Ballots indexed by (int)PlayerSlot, sized 10 (slots 0-9; index 0 = PlayerSlot.None and
- // is never written). Value is the option id voted for, or NoVote. Public read permission
- // is deliberate — see the class remarks.
- private NetworkList ballots;
-
- /// Sentinel stored in for a player who hasn't voted.
- public const int NoVote = -1;
-
- // Wave slot this vote will upgrade, or -1 when no vote is open.
- private readonly NetworkVariable targetSlot = new NetworkVariable(
- -1, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Server);
-
- /// Fired on every peer when the offered set, any ballot, or the open/closed
- /// state changes. The HUD subscribes to rebuild the vote panel and its voter badges.
- public event System.Action OnVoteChanged;
-
- // ----- Lifecycle ----------------------------------------------------
-
- private void Awake()
- {
- offeredOptionIds = new NetworkList();
- ballots = new NetworkList();
- }
-
- public override void OnNetworkSpawn()
- {
- if (Instance != null && Instance != this)
- {
- Debug.LogError("[WaveVote] Duplicate WaveVote detected. Only one may exist per scene.");
- return;
- }
- Instance = this;
-
- if (IsServer)
- {
- // One entry per PlayerSlot value (None + Player1..Player9).
- for (int i = 0; i < 10; i++) ballots.Add(NoVote);
- }
-
- offeredOptionIds.OnListChanged += HandleListChanged;
- ballots.OnListChanged += HandleListChanged;
- targetSlot.OnValueChanged += HandleTargetChanged;
- }
-
- public override void OnNetworkDespawn()
- {
- offeredOptionIds.OnListChanged -= HandleListChanged;
- ballots.OnListChanged -= HandleListChanged;
- targetSlot.OnValueChanged -= HandleTargetChanged;
-
- if (Instance == this) Instance = null;
- }
-
- private void HandleListChanged(NetworkListEvent _) => OnVoteChanged?.Invoke();
- private void HandleTargetChanged(int _, int __) => OnVoteChanged?.Invoke();
-
- // ----- Read API ------------------------------------------------------
-
- /// True while a vote is open for players to cast or change a ballot.
- public bool IsOpen => targetSlot.Value >= 0;
-
- /// Wave slot this vote will upgrade, or -1 when closed.
- public int TargetSlot => targetSlot.Value;
-
- public int OptionCount => offeredOptionIds.Count;
-
- public int GetOptionId(int index)
- => (index >= 0 && index < offeredOptionIds.Count) ? offeredOptionIds[index] : -1;
-
- /// The option voted for, or .
- public int GetBallot(PlayerSlot slot)
- {
- int i = (int)slot;
- return (i >= 0 && i < ballots.Count) ? ballots[i] : NoVote;
- }
-
- ///
- /// Appends every player slot that voted for . Drives the voter
- /// badges the HUD paints onto each card.
- ///
- public void GetVotersFor(int optionId, List into)
- {
- into.Clear();
- if (optionId < 0) return;
-
- foreach (var pms in PlayerMatchState.AllPlayers)
- {
- if (pms.Slot == PlayerSlot.None) continue;
- if (GetBallot(pms.Slot) == optionId) into.Add(pms.Slot);
- }
- }
-
- ///
- /// True when every connected player with a real slot has cast a ballot. The inter-wave
- /// step uses this to end the vote early instead of burning the whole timer.
- ///
- public bool AllPlayersVoted
- {
- get
- {
- if (!IsOpen) return false;
-
- bool any = false;
- foreach (var pms in PlayerMatchState.AllPlayers)
- {
- if (pms.Slot == PlayerSlot.None) continue;
- any = true;
- if (GetBallot(pms.Slot) == NoVote) return false;
- }
- return any;
- }
- }
-
- // ----- Server: open / resolve ----------------------------------------
-
- // Scratch reused across vote generation; server is single-threaded.
- private readonly List entryScratch = new List();
- private readonly List idScratch = new List();
- private readonly List weightScratch = new List();
- private readonly List tiedScratch = new List();
-
- ///
- /// Server-only: open a vote for , drawing cards from the
- /// current phase's allowed pool. Returns false (and opens nothing) if there is nothing
- /// legal left to offer — a wave that has already collected every card its phase allows
- /// simply gets no vote rather than an empty panel.
- ///
- public bool ServerOpenVote(int waveSlot)
- {
- if (!IsServer) return false;
-
- var run = RunState.Instance;
- var pool = EnemyUpgradePool.Instance;
- if (run == null || pool == null)
- {
- Debug.LogWarning("[WaveVote] No RunState or EnemyUpgradePool in scene — " +
- "skipping the enemy-buff vote.");
- return false;
- }
-
- if (!CollectCandidatesFor(waveSlot, run, pool))
- {
- Debug.Log($"[WaveVote] No enemy-buff cards left to offer for wave slot " +
- $"{waveSlot}; skipping the vote.");
- return false;
- }
-
- // Weighted draw without replacement.
- offeredOptionIds.Clear();
- int draws = Mathf.Min(optionsPerVote, idScratch.Count);
- for (int n = 0; n < draws; n++)
- {
- int pick = DrawWeightedIndex(weightScratch);
- if (pick < 0) break;
-
- offeredOptionIds.Add(idScratch[pick]);
- idScratch.RemoveAt(pick);
- weightScratch.RemoveAt(pick);
- }
-
- ServerClearBallots();
- targetSlot.Value = waveSlot;
- return true;
- }
-
- ///
- /// Server-only: tally the ballots, apply the winner to the target slot, and close the
- /// vote. Returns the winning option id, or -1 if no vote was open.
- ///
- ///
- /// Ties resolve randomly among the tied options. At three players and three cards a 1/1/1
- /// split is common, and with open ballots the players can see it coming and break it
- /// themselves — which is the intended pressure, not a flaw in the tie-break.
- ///
- /// If nobody voted at all (everyone idle), one of the offered cards is chosen at
- /// random. Skipping the buff entirely would quietly make idling the optimal play.
- ///
- public int ServerResolve()
- {
- if (!IsServer || !IsOpen) return -1;
-
- int slot = targetSlot.Value;
- int winner = TallyWinner();
-
- if (winner >= 0)
- {
- var run = RunState.Instance;
- var pool = EnemyUpgradePool.Instance;
-
- run?.ServerAddUpgrade(slot, winner);
- pool?.Get(winner)?.ServerOnApplied(run, slot);
-
- Debug.Log($"[WaveVote] Wave slot {slot} gains " +
- $"'{pool?.Get(winner)?.DisplayName ?? winner.ToString()}'.");
- }
-
- ServerClose();
- return winner;
- }
-
- ///
- /// Server-only: apply buffs to outright,
- /// with no vote. Returns how many actually landed. Used by the vote-meta cards that trade
- /// a future vote away for extra buffs.
- ///
- ///
- /// Deliberately opens no ballot: the card's whole cost is that the players don't get
- /// to choose this time. Drawing from the same weighted, slot-filtered candidate set the
- /// vote would have used keeps the outcome fair rather than worst-case.
- ///
- public int ServerAutoApply(int slot, int count)
- {
- if (!IsServer || count <= 0) return 0;
-
- var run = RunState.Instance;
- var pool = EnemyUpgradePool.Instance;
- if (run == null || pool == null) return 0;
-
- var phase = run.CurrentPhase;
- if (phase == null) return 0;
-
- int applied = 0;
- for (int n = 0; n < count; n++)
- {
- // Re-collect each time: applying one buff can invalidate another (a card the slot
- // now has, or one whose prerequisite just changed).
- if (!CollectCandidatesFor(slot, run, pool)) break;
-
- int pick = DrawWeightedIndex(weightScratch);
- if (pick < 0) break;
-
- int id = idScratch[pick];
- run.ServerAddUpgrade(slot, id);
- pool.Get(id)?.ServerOnApplied(run, slot);
- applied++;
-
- Debug.Log($"[WaveVote] Auto-applied '{pool.Get(id)?.DisplayName ?? id.ToString()}' " +
- $"to wave slot {slot}.");
- }
-
- return applied;
- }
-
- /// Server-only: close the vote without applying anything.
- public void ServerClose()
- {
- if (!IsServer) return;
- targetSlot.Value = -1;
- offeredOptionIds.Clear();
- ServerClearBallots();
- }
-
- private void ServerClearBallots()
- {
- for (int i = 0; i < ballots.Count; i++) ballots[i] = NoVote;
- }
-
- // Highest vote count wins; ties broken at random. No votes cast → random offered card.
- private int TallyWinner()
- {
- if (offeredOptionIds.Count == 0) return -1;
-
- int best = 0;
- tiedScratch.Clear();
-
- for (int i = 0; i < offeredOptionIds.Count; i++)
- {
- int id = offeredOptionIds[i];
- int count = 0;
-
- foreach (var pms in PlayerMatchState.AllPlayers)
- {
- if (pms.Slot == PlayerSlot.None) continue;
- if (GetBallot(pms.Slot) == id) count++;
- }
-
- if (count > best)
- {
- best = count;
- tiedScratch.Clear();
- tiedScratch.Add(id);
- }
- else if (count == best && best > 0)
- {
- tiedScratch.Add(id);
- }
- }
-
- if (tiedScratch.Count == 0)
- {
- // Nobody voted — pick one of the offered cards rather than letting the wave
- // escape unbuffed, which would make going idle the strongest play.
- return offeredOptionIds[Random.Range(0, offeredOptionIds.Count)];
- }
-
- return tiedScratch[Random.Range(0, tiedScratch.Count)];
- }
-
- ///
- /// Fills / with the cards this phase
- /// allows that can still meaningfully take. Returns false when
- /// nothing qualifies.
- ///
- private bool CollectCandidatesFor(int slot, RunState run, EnemyUpgradePool pool)
- {
- var phase = run.CurrentPhase;
- if (phase == null) return false;
-
- pool.CollectAllowedIds(phase.EnemyUpgradeGroups, entryScratch, idScratch, weightScratch);
-
- // Filter to what this slot can actually take: not already applied, and the card itself
- // agrees it's a meaningful offer.
- for (int i = idScratch.Count - 1; i >= 0; i--)
- {
- int id = idScratch[i];
- var option = pool.Get(id);
-
- bool valid = option != null
- && !run.SlotHasUpgrade(slot, id)
- && option.IsValidForSlot(run, slot);
-
- if (!valid)
- {
- idScratch.RemoveAt(i);
- weightScratch.RemoveAt(i);
- }
- }
-
- return idScratch.Count > 0;
- }
-
- private static int DrawWeightedIndex(List weights)
- {
- if (weights == null || weights.Count == 0) return -1;
-
- float total = 0f;
- for (int i = 0; i < weights.Count; i++) total += Mathf.Max(0f, weights[i]);
- if (total <= 0f) return -1;
-
- float roll = Random.value * total;
- for (int i = 0; i < weights.Count; i++)
- {
- roll -= Mathf.Max(0f, weights[i]);
- if (roll <= 0f) return i;
- }
- return weights.Count - 1; // float drift fallback
- }
-
- // ----- Client → server ------------------------------------------------
-
- ///
- /// Cast or change this player's vote. Any client may send; the server resolves the sender
- /// to a slot itself and ignores anything it can't attribute or that isn't on the table.
- ///
- [Rpc(SendTo.Server)]
- public void RequestVoteRpc(int optionId, RpcParams rpcParams = default)
- {
- if (!IsOpen) return;
-
- var senderSlot = PlayerMatchState.SlotForClient(rpcParams.Receive.SenderClientId);
- if (senderSlot == PlayerSlot.None) return;
-
- // Only options actually on the table are accepted — a client can't vote a card in.
- bool offered = false;
- for (int i = 0; i < offeredOptionIds.Count; i++)
- if (offeredOptionIds[i] == optionId) { offered = true; break; }
- if (!offered) return;
-
- int index = (int)senderSlot;
- if (index >= 0 && index < ballots.Count) ballots[index] = optionId;
- }
- }
-}
diff --git a/Assets/_Project/Scripts/Gameplay/EnemyUpgrades/WaveVote.cs.meta b/Assets/_Project/Scripts/Gameplay/EnemyUpgrades/WaveVote.cs.meta
deleted file mode 100644
index 20d621a..0000000
--- a/Assets/_Project/Scripts/Gameplay/EnemyUpgrades/WaveVote.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: a18321dc2d138034fbdb5f705f19f893
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Gameplay/GoldConfig.cs b/Assets/_Project/Scripts/Gameplay/GoldConfig.cs
index 2707d91..965ee7e 100644
--- a/Assets/_Project/Scripts/Gameplay/GoldConfig.cs
+++ b/Assets/_Project/Scripts/Gameplay/GoldConfig.cs
@@ -1,68 +1,91 @@
// Assets/_Project/Scripts/Gameplay/GoldConfig.cs
using System;
using UnityEngine;
-using UnityEngine.Serialization;
namespace TD.Gameplay
{
///
- /// Gold rules for one encounter slot, addressed by position in the run rather than by
- /// which wave happens to occupy it.
+ /// Per-wave gold rules nested under . Each entry maps to one
+ /// wave by array index in .
///
///
- /// Position, not identity. Under the 2.0 run structure the waves filling each slot are
- /// drawn at random from a phase pool, so "the gold for wave 3" means the run's third
- /// encounter, whatever spawns in it. Payouts are a property of how far the players have
- /// progressed — not of which enemy they happened to draw.
+ /// Pairing with WaveDefinition. The runtime pairing of gold entry to wave is by
+ /// array index — entry 0 applies to wave 1, etc. The reference here
+ /// is OPTIONAL and exists purely so the inspector can compute and display the
+ /// read-only value (which needs to know the wave's
+ /// total enemy count to multiply against ). Leaving it
+ /// unset just hides the kill-reward portion of the preview; runtime is unaffected.
///
[Serializable]
- public class EncounterGoldEntry
+ public class WaveGoldEntry
{
- [Tooltip("Gold awarded to the killing player per enemy slain during this encounter. " +
- "Applies uniformly to every enemy type.")]
+ [Tooltip("Optional reference to the WaveDefinition this entry pairs with. " +
+ "Used ONLY for the inspector preview total — runtime pairing is by " +
+ "array index in GoldConfig.Waves. Drag the matching WaveDefinition " +
+ "here to see the live total computed under this entry.")]
+ public WaveDefinition Wave;
+
+ [Tooltip("Gold awarded to the killing player per enemy slain during this wave. " +
+ "Applies uniformly to every enemy type in the wave.")]
public int GoldPerEnemy = 10;
- [Tooltip("Flat bonus gold awarded to every active player when this encounter is fully " +
+ [Tooltip("Flat bonus gold awarded to every active player when this wave is fully " +
"cleared (all enemies dead or reached the goal).")]
public int CompletionBonus = 50;
- [Tooltip("Extra bonus gold awarded to a player who cleared this encounter without any " +
+ [Tooltip("Extra bonus gold awarded to a player who cleared this wave without any " +
"enemy from their own zone escaping their leak volume. Stacks on top of " +
- "CompletionBonus.")]
+ "CompletionBonus for the no-leak achievement.")]
public int NoLeaksBonus = 50;
///
- /// Guaranteed gold for a player who clears this encounter: the two bonuses, with no
- /// kill income counted.
+ /// Read-only preview of the maximum gold a single player could earn in this wave:
+ /// GoldPerEnemy × totalEnemyCount + CompletionBonus + NoLeaksBonus.
///
///
- /// Kill income can't be known here — the wave occupying this slot is drawn at runtime, so
- /// its enemy count varies per run.
- /// supplies an estimate for the inspector's projected totals.
+ /// Returns just CompletionBonus + NoLeaksBonus if is null
+ /// (no enemy count to multiply against). Pure computation — safe at edit time and
+ /// at runtime. The custom inspector under renders this as
+ /// a non-editable label under the entry's fields.
///
- public int GuaranteedBonusGold => CompletionBonus + NoLeaksBonus;
-
- ///
- /// Projected best-case earnings for one player this encounter, given an assumed enemy
- /// count. Editor tuning aid only — nothing at runtime reads it.
- ///
- public int ProjectedTotal(int assumedEnemyCount)
- => GoldPerEnemy * Mathf.Max(0, assumedEnemyCount) + GuaranteedBonusGold;
+ public int PreviewTotalGold
+ {
+ get
+ {
+ int total = CompletionBonus + NoLeaksBonus;
+ if (Wave != null && Wave.Entries != null)
+ {
+ int enemies = 0;
+ foreach (var e in Wave.Entries)
+ {
+ if (e.EnemyType != null && e.Count > 0)
+ enemies += e.Count;
+ }
+ total += GoldPerEnemy * enemies;
+ }
+ return total;
+ }
+ }
}
///
/// Single source of truth for every gold-related tunable in the game.
///
///
- /// Wiring. Assign one GoldConfig asset to WaveManager.goldConfig in the Match
- /// scene. WaveManager seeds per-player starting gold from , reads
- /// for kill rewards, and awards the completion
- /// and no-leak bonuses as each encounter clears.
+ /// Inspector layout. StartingGold is at the top and applies to every player.
+ /// The Waves array follows, each element collapsible in the inspector with the
+ /// per-wave rules and a read-only preview total computed from the entry's data.
///
- /// No per-enemy-type bounties. Every enemy killed in a given encounter grants the
- /// same reward regardless of . With enemy types now drawn at
- /// random per phase, per-type bounties would make income depend on the luck of the draw
- /// rather than on progression.
+ /// Wiring. Assign one GoldConfig asset to WaveManager.goldConfig in
+ /// the Match scene. WaveManager initializes per-player starting gold from
+ /// at match start, uses
+ /// to compute kill rewards, and awards /
+ /// when each wave clears.
+ ///
+ /// No per-enemy-type bounties. Every enemy killed in wave N grants the same
+ /// regardless of .
+ /// If per-type variation becomes a design need, extend WaveGoldEntry with a per-type
+ /// table; the current model intentionally favors simplicity.
///
[CreateAssetMenu(fileName = "GoldConfig", menuName = "TD/Gold Config", order = 2)]
public class GoldConfig : ScriptableObject
@@ -70,58 +93,20 @@ namespace TD.Gameplay
[Tooltip("Gold each player starts the match with. Same for every player.")]
public int StartingGold = 100;
- [Tooltip("Gold rules per encounter, in run order. Element 0 = the run's first encounter. " +
- "Encounters are counted continuously — cycles do NOT restart the count — so one " +
- "phase of 5 waves × 3 cycles + a boss needs 16 entries. Missing entries pay " +
- "zero for that encounter.")]
- [FormerlySerializedAs("Waves")]
- public EncounterGoldEntry[] Encounters;
-
- [Header("Editor preview")]
- [Tooltip("Assumed enemies per encounter, used ONLY to project totals in this inspector. " +
- "Nothing at runtime reads it — the real count comes from whichever wave the run " +
- "draws into each slot.")]
- [Min(0)]
- public int PreviewEnemiesPerEncounter = 40;
+ [Tooltip("Per-wave gold rules. Element 0 = Wave 1. Match the order and length of " +
+ "WaveManager.waveDefinitions; extra entries are ignored, missing entries " +
+ "fall back to zero gold for that wave.")]
+ public WaveGoldEntry[] Waves;
///
- /// Returns the gold rules for the given 1-based encounter number
- /// (RunState.GlobalEncounterNumber), or null if out of range.
+ /// Returns the gold entry for the given 1-based wave number, or null if out of range.
///
- ///
- /// Keyed by run position, not by wave slot within a cycle. The same five waves come
- /// round three times per phase, so a slot-keyed table would pay the same on cycle 3 as on
- /// cycle 1 while the enemies grew steadily stronger from the buffs voted onto them. A
- /// run-long counter lets payouts climb with the difficulty.
- ///
- public EncounterGoldEntry GetEncounterEntry(int encounterNumber)
+ public WaveGoldEntry GetWaveEntry(int waveNumber)
{
- if (Encounters == null) return null;
- int index = encounterNumber - 1;
- if (index < 0 || index >= Encounters.Length) return null;
- return Encounters[index];
- }
-
- /// Number of authored encounter entries.
- public int EncounterCount => Encounters?.Length ?? 0;
-
- ///
- /// Editor tuning aid: cumulative projected earnings for one player from the run's first
- /// encounter through inclusive, assuming
- /// kills each time and no leaks.
- ///
- public int ProjectedCumulativeThrough(int encounterNumber)
- {
- if (Encounters == null) return StartingGold;
-
- int total = StartingGold;
- int last = Mathf.Min(encounterNumber, Encounters.Length);
- for (int i = 0; i < last; i++)
- {
- if (Encounters[i] == null) continue;
- total += Encounters[i].ProjectedTotal(PreviewEnemiesPerEncounter);
- }
- return total;
+ if (Waves == null) return null;
+ int index = waveNumber - 1;
+ if (index < 0 || index >= Waves.Length) return null;
+ return Waves[index];
}
}
}
diff --git a/Assets/_Project/Scripts/Gameplay/PathfindingService.cs b/Assets/_Project/Scripts/Gameplay/PathfindingService.cs
index f9a98a8..d6c99df 100644
--- a/Assets/_Project/Scripts/Gameplay/PathfindingService.cs
+++ b/Assets/_Project/Scripts/Gameplay/PathfindingService.cs
@@ -26,15 +26,13 @@ namespace TD.Gameplay
/// Who calls this:
///
/// - calls once on
- /// spawn, then registers via to be re-pathed when
- /// the maze changes.
+ /// spawn and again whenever fires.
///
///
/// Invalidation: Subscribes to .
- /// When a tower is placed or sold, LevelLoader.SetWalkable fires that event; the
- /// service then enqueues every registered grounded enemy and recomputes them under a
- /// per-frame time budget (recomputeBudgetMs), draining across frames so a maze
- /// change with a full wave present never spikes a single frame.
+ /// When a tower is placed or sold, LevelLoader.SetWalkable fires that event
+ /// and is relayed to all active enemies, which
+ /// each recompute their own path from their current tile.
///
/// Goal tile set: Built once on Start from
/// LevelLoader.LevelData.Goals[].TileArea. Goal tiles never change at
@@ -54,10 +52,7 @@ namespace TD.Gameplay
///
/// Fired on every peer when the walkability grid changes (tower placed/sold).
- /// Enemies no longer subscribe here — they register with the budgeted re-path
- /// scheduler () so recomputes spread across frames.
- /// This event remains as an extension point for any non-enemy listener that wants
- /// immediate notification of a maze change.
+ /// subscribes per-instance to recompute its path.
///
public event System.Action OnPathsInvalidated;
@@ -84,28 +79,6 @@ namespace TD.Gameplay
private readonly Dictionary gScore = new Dictionary();
private readonly SimplePriorityQueue openSet = new SimplePriorityQueue();
- // ----- Deferred re-path scheduler ---------------------------------
- //
- // Grounded enemies register here (flyers never re-path — baked grid). On a
- // walkability change we enqueue every registered enemy and recompute a
- // TIME-BUDGETED number of them per frame, draining the backlog over subsequent
- // frames. Recomputing them all synchronously on the change frame spiked to ~0.5s
- // with a full wave on this large grid whenever a tower was sold/placed — this
- // caps the per-frame cost so no single frame ever hitches. The maze can only be
- // opened (sell) or narrowed with a guaranteed remaining route (placement, BFS-
- // validated), so an enemy briefly following its slightly-stale path for a few
- // frames until its turn comes up is safe and visually negligible.
-
- [Tooltip("Max wall-clock milliseconds spent recomputing enemy paths per frame " +
- "after a maze change. The backlog drains over following frames; at least " +
- "one enemy is always processed per frame so it converges. Lower = smoother " +
- "but slower to fully update; higher = faster to update but larger frame cost.")]
- [SerializeField] private float recomputeBudgetMs = 1.5f;
-
- private readonly HashSet movers = new HashSet();
- private readonly Queue recomputeQueue = new Queue();
- private readonly HashSet queued = new HashSet();
-
// ----- Lifecycle --------------------------------------------------
private void Awake()
@@ -142,48 +115,6 @@ namespace TD.Gameplay
loader.OnWalkabilityChanged -= HandleWalkabilityChanged;
}
- // Drains the deferred re-path backlog under a per-frame time budget. Runs on all
- // peers, but the queue is only ever populated on the server (clients never register
- // movers), so this is a no-op cost on clients. At least one enemy is processed per
- // frame whenever the queue is non-empty, so it always converges.
- private void Update()
- {
- if (recomputeQueue.Count == 0) return;
-
- double startMs = Time.realtimeSinceStartupAsDouble * 1000.0;
- do
- {
- var mover = recomputeQueue.Dequeue();
- if (!queued.Remove(mover)) continue; // unregistered/cancelled after enqueue
- if (mover != null) mover.RecomputePath(); // reads the CURRENT grid state
- }
- while (recomputeQueue.Count > 0
- && Time.realtimeSinceStartupAsDouble * 1000.0 - startMs < recomputeBudgetMs);
- }
-
- // ----- Deferred re-path scheduler API -----------------------------
-
- ///
- /// Registers a grounded enemy to be re-pathed (budgeted, over following frames)
- /// whenever the maze changes. Flyers must NOT register — their baked-grid route
- /// never changes. Called on the server from .
- ///
- public void RegisterMover(EnemyMovement mover)
- {
- if (mover != null) movers.Add(mover);
- }
-
- ///
- /// Removes an enemy from the scheduler (on despawn). Any stale entry still sitting
- /// in the pending queue is skipped when dequeued (the queued membership check).
- ///
- public void UnregisterMover(EnemyMovement mover)
- {
- if (mover == null) return;
- movers.Remove(mover);
- queued.Remove(mover);
- }
-
// ----- Public API -------------------------------------------------
///
@@ -540,16 +471,7 @@ namespace TD.Gameplay
private void HandleWalkabilityChanged()
{
- // Notify any non-enemy listeners immediately (kept for API compatibility).
OnPathsInvalidated?.Invoke();
-
- // Enqueue every registered grounded enemy for a budgeted, deferred recompute
- // instead of recomputing them all on this frame. Already-queued enemies are
- // deduped, so rapid successive maze changes just lengthen the drain rather
- // than compounding into a spike.
- foreach (var mover in movers)
- if (queued.Add(mover))
- recomputeQueue.Enqueue(mover);
}
}
diff --git a/Assets/_Project/Scripts/Gameplay/PlayerBuffManager.cs b/Assets/_Project/Scripts/Gameplay/PlayerBuffManager.cs
new file mode 100644
index 0000000..506791e
--- /dev/null
+++ b/Assets/_Project/Scripts/Gameplay/PlayerBuffManager.cs
@@ -0,0 +1,195 @@
+// 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
new file mode 100644
index 0000000..b1951a5
--- /dev/null
+++ b/Assets/_Project/Scripts/Gameplay/PlayerBuffManager.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 7775ee3a2f441b52480aabf54be6c1b6
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Gameplay/PlayerGoldManager.cs b/Assets/_Project/Scripts/Gameplay/PlayerGoldManager.cs
index 4cbd3a4..1cb3170 100644
--- a/Assets/_Project/Scripts/Gameplay/PlayerGoldManager.cs
+++ b/Assets/_Project/Scripts/Gameplay/PlayerGoldManager.cs
@@ -143,17 +143,11 @@ namespace TD.Gameplay
///
/// Server-side entry point for awarding gold (wave clear, enemy kill).
/// Direct call — not Rpc-wrapped — because awards always originate
- /// from server-authoritative game events. When
- /// is true (the default) it also increments so the
- /// HUD's per-wave counter reflects it; spending does not decrement that counter (it
- /// tracks earnings, not balance).
+ /// from server-authoritative game events. Also increments
+ /// so the HUD's per-wave counter reflects
+ /// it; spending does not decrement that counter (it tracks earnings, not balance).
///
- ///
- /// Pass false for gold returned to the player that isn't income — e.g. a tower-sell
- /// refund. The player's balance still rises, but the per-wave "earned" counter does not,
- /// so refunds don't inflate round-income stats.
- ///
- public void AwardGold(int amount, bool countAsEarned = true)
+ public void AwardGold(int amount)
{
if (!IsServer)
{
@@ -164,7 +158,7 @@ namespace TD.Gameplay
if (amount <= 0) return;
currentGold.Value += amount;
- if (countAsEarned) goldEarnedThisWave.Value += amount;
+ goldEarnedThisWave.Value += amount;
}
///
diff --git a/Assets/_Project/Scripts/Gameplay/PlayerMatchState.cs b/Assets/_Project/Scripts/Gameplay/PlayerMatchState.cs
index d572bd7..212dbd7 100644
--- a/Assets/_Project/Scripts/Gameplay/PlayerMatchState.cs
+++ b/Assets/_Project/Scripts/Gameplay/PlayerMatchState.cs
@@ -210,21 +210,17 @@ namespace TD.Gameplay
// --- Slot allocation (server-only) -------------------------------
- // Caps at MatchRules.MaxPlayers rather than at the PlayerSlot enum's range — the enum
- // still runs to 9 so baked LevelData owner grids stay valid, but only the first
- // MaxPlayers slots are ever handed out. See MatchRules for why.
private static PlayerSlot AllocateNextSlot()
{
- for (int i = 1; i <= MatchRules.MaxPlayers; i++)
+ for (int i = 1; i <= 9; i++)
{
var candidate = (PlayerSlot)i;
if (!s_assignedSlots.Contains(candidate))
return candidate;
}
- Debug.LogError($"[PlayerMatchState] No free PlayerSlot (lobby is full at " +
- $"{MatchRules.MaxPlayers} players). Returning None — this client " +
- $"will be unable to play.");
+ Debug.LogError("[PlayerMatchState] No free PlayerSlot (already 9 players). " +
+ "Returning None — this client will be unable to play.");
return PlayerSlot.None;
}
}
diff --git a/Assets/_Project/Scripts/Gameplay/PlayerSpellLoadout.cs b/Assets/_Project/Scripts/Gameplay/PlayerSpellLoadout.cs
index 4782e9f..a6a1f68 100644
--- a/Assets/_Project/Scripts/Gameplay/PlayerSpellLoadout.cs
+++ b/Assets/_Project/Scripts/Gameplay/PlayerSpellLoadout.cs
@@ -1,5 +1,4 @@
// Assets/_Project/Scripts/Gameplay/PlayerSpellLoadout.cs
-using System.Collections;
using System.Collections.Generic;
using Unity.Netcode;
using UnityEngine;
@@ -21,12 +20,9 @@ namespace TD.Gameplay
/// slot index and an aimed world point. The server re-validates everything (slot exists,
/// off cooldown, ) since client-side checks in
/// are UX-only, resolves the
- /// via . An instant
- /// spell ( == 0) resolves on cast: a hit starts
- /// the cooldown and fires ; a miss is a no-op. A delayed
- /// spell (a projectile, e.g. the Fireball meteor) commits on the throw — cooldown + the falling
- /// visual fire immediately — while its damage and impact sound are held until the projectile
- /// lands seconds later.
+ /// via , and calls
+ /// . A successful cast starts the cooldown
+ /// and fires so every peer plays the same visual.
///
public class PlayerSpellLoadout : NetworkBehaviour
{
@@ -60,10 +56,6 @@ namespace TD.Gameplay
private readonly NetworkList spells = new NetworkList();
- // Every kind ever granted this match, including ones since upgraded away. Unlike
- // `spells`, entries here are never removed — see PlayerHasEverGranted.
- private readonly NetworkList everGrantedKinds = new NetworkList();
-
/// Fired on every peer when a spell is granted or a cooldown changes.
public event System.Action OnLoadoutChanged;
@@ -104,14 +96,6 @@ namespace TD.Gameplay
public BuilderSpellKind? GetKind(int slot)
=> (slot >= 0 && slot < spells.Count) ? spells[slot].Kind : (BuilderSpellKind?)null;
- ///
- /// True if this player has ever been granted the given spell this match, whether or
- /// not it's still occupying a slot (e.g. it may have since been upgraded away). Used
- /// by so a base spell already
- /// upgraded past is never re-offered.
- ///
- public bool PlayerHasEverGranted(BuilderSpellKind kind) => everGrantedKinds.Contains((byte)kind);
-
/// True if is out of range, empty, or still cooling down.
public bool IsSlotOnCooldown(int slot)
{
@@ -148,33 +132,9 @@ namespace TD.Gameplay
if (PlayerHasSpell(kind)) return false;
spells.Add(SpellSlot.CreateReady(kind));
- if (!everGrantedKinds.Contains((byte)kind)) everGrantedKinds.Add((byte)kind);
return true;
}
- ///
- /// Server-only: replaces a previously granted spell with a different one (an upgrade
- /// pick), in place — the upgraded spell keeps the base spell's hotkey slot and starts
- /// off cooldown. No-op if the old spell isn't currently granted or the new one already
- /// is. Returns true if the swap happened.
- ///
- public bool ServerUpgradeSpell(BuilderSpellKind oldKind, BuilderSpellKind newKind)
- {
- if (!IsServer) return false;
- if (PlayerHasSpell(newKind)) return false;
-
- for (int i = 0; i < spells.Count; i++)
- {
- if (spells[i].Kind == oldKind)
- {
- spells[i] = SpellSlot.CreateReady(newKind);
- if (!everGrantedKinds.Contains((byte)newKind)) everGrantedKinds.Add((byte)newKind);
- return true;
- }
- }
- return false;
- }
-
// ----- Cast RPC -----------------------------------------------------
///
@@ -182,7 +142,7 @@ namespace TD.Gameplay
/// at . All validation happens
/// here on the server; the client only uses this to trigger an attempt.
///
- [Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Owner)]
+ [Rpc(SendTo.Server, RequireOwnership = true)]
public void RequestCastSpellRpc(int slot, Vector3 targetPoint)
{
if (MatchState.Instance == null || MatchState.Instance.Phase != MatchPhase.Playing)
@@ -200,69 +160,18 @@ namespace TD.Gameplay
return;
}
- if (definition.ImpactDelay <= 0f)
- {
- // Instant spell (e.g. Slow Area): resolve on cast. A miss (hit nothing) is a no-op
- // and does NOT start the cooldown — unchanged behavior.
- if (!definition.ServerCast(OwnerClientId, targetPoint)) return;
+ if (!definition.ServerCast(OwnerClientId, targetPoint)) return;
- StartCooldown(slot, definition);
- PlayVfxClientRpc(slotValue.Kind, targetPoint);
- }
- else
- {
- // Delayed spell (e.g. the Fireball meteor): the projectile has to fall before it
- // lands, so the cast is COMMITTED on the throw — cooldown starts and the falling
- // visual spawns now — while damage and the impact sound wait until it hits the
- // ground (ImpactDelay later). Enemies are re-scanned at impact, so movement during
- // the fall resolves correctly.
- StartCooldown(slot, definition);
- PlayVfxClientRpc(slotValue.Kind, targetPoint);
- StartCoroutine(ServerResolveImpactAfterDelay(
- slotValue.Kind, OwnerClientId, targetPoint, definition.ImpactDelay));
- }
- }
-
- // Server-only: stamp the slot's cooldown from the definition and replicate it.
- private void StartCooldown(int slot, BuilderSpellDefinition definition)
- {
- var slotValue = spells[slot];
slotValue.CooldownEndServerTime = NetworkManager.ServerTime.Time + definition.Cooldown;
spells[slot] = slotValue;
- }
- // Server-only: apply a delayed spell's effect once its projectile has landed. Re-resolves
- // targets at impact — the enemy set may have shifted during the fall.
- private IEnumerator ServerResolveImpactAfterDelay(
- BuilderSpellKind kind, ulong clientId, Vector3 targetPoint, float delay)
- {
- yield return new WaitForSeconds(delay);
- if (!IsServer) yield break;
- BuilderSpellPool.Instance?.Get(kind)?.ServerCast(clientId, targetPoint);
+ PlayVfxClientRpc(slotValue.Kind, targetPoint);
}
[ClientRpc]
private void PlayVfxClientRpc(BuilderSpellKind kind, Vector3 targetPoint)
{
- var def = BuilderSpellPool.Instance?.Get(kind);
- if (def == null) return;
-
- def.ClientSpawnVfx(targetPoint); // spawn the (possibly falling) visual now
-
- if (def.ImpactDelay <= 0f)
- def.ClientPlayImpact(targetPoint);
- else
- StartCoroutine(ClientPlayImpactAfterDelay(kind, targetPoint, def.ImpactDelay));
- }
-
- // Client-side: play the impact (sound + any contact visual) when the projectile lands.
- // Timed locally off the same ImpactDelay so it stays in lockstep with this peer's own
- // falling visual — no second RPC, no double latency.
- private IEnumerator ClientPlayImpactAfterDelay(
- BuilderSpellKind kind, Vector3 targetPoint, float delay)
- {
- yield return new WaitForSeconds(delay);
- BuilderSpellPool.Instance?.Get(kind)?.ClientPlayImpact(targetPoint);
+ BuilderSpellPool.Instance?.Get(kind)?.ClientPlayVfx(targetPoint);
}
}
}
diff --git a/Assets/_Project/Scripts/Gameplay/PlayerTowerDeck.cs b/Assets/_Project/Scripts/Gameplay/PlayerTowerDeck.cs
index 9d7b54d..eb5687c 100644
--- a/Assets/_Project/Scripts/Gameplay/PlayerTowerDeck.cs
+++ b/Assets/_Project/Scripts/Gameplay/PlayerTowerDeck.cs
@@ -7,8 +7,8 @@ namespace TD.Gameplay
{
///
/// Per-player set of unlocked tower types — the player's "deck". Lives on the
- /// Player prefab alongside and
- /// .
+ /// Player prefab alongside ,
+ /// , and .
///
///
/// Roguelike keystone. Players no longer see every tower in the match
@@ -146,30 +146,5 @@ namespace TD.Gameplay
unlockedTypeIds.Add(towerTypeId);
return true;
}
-
- ///
- /// Server-only: replaces a previously unlocked tower type with a different one (an
- /// upgrade pick) — every tower of that type placed from now on uses the new definition.
- /// Already-placed towers keep whatever type they were placed with; this only changes
- /// what's placeable going forward. No-op if the old type isn't currently unlocked or
- /// the new one already is. Returns true if the swap happened.
- ///
- public bool ServerUpgradeTower(int oldTypeId, int newTypeId)
- {
- if (!IsServer) return false;
- if (newTypeId <= 0) return false;
- if (Contains(newTypeId)) return false;
-
- for (int i = 0; i < unlockedTypeIds.Count; i++)
- {
- if (unlockedTypeIds[i] == oldTypeId)
- {
- unlockedTypeIds.RemoveAt(i);
- unlockedTypeIds.Add(newTypeId);
- return true;
- }
- }
- return false;
- }
}
}
diff --git a/Assets/_Project/Scripts/Gameplay/PlayerTowerUpgrades.cs b/Assets/_Project/Scripts/Gameplay/PlayerTowerUpgrades.cs
deleted file mode 100644
index 0b17d88..0000000
--- a/Assets/_Project/Scripts/Gameplay/PlayerTowerUpgrades.cs
+++ /dev/null
@@ -1,116 +0,0 @@
-// Assets/_Project/Scripts/Gameplay/PlayerTowerUpgrades.cs
-using System.Collections.Generic;
-using Unity.Netcode;
-using UnityEngine;
-
-namespace TD.Gameplay
-{
- ///
- /// Per-player set of unlocked tower upgrade nodes — the tower types this player is
- /// allowed to convert an existing tower into. Lives on the Player prefab alongside
- /// .
- ///
- ///
- /// Distinct from the deck, deliberately. is what you
- /// can build from scratch; this is what you can upgrade into. They have to be
- /// separate sets because the 2.0 model is two-step: a draft pick unlocks a node, and gold spent
- /// on an already-placed tower converts it. Unlocking an upgrade must not make it directly
- /// buildable, or the gold step — the entire cost of the upgrade — is bypassed.
- ///
- /// The tree lives in the data. Edges are TowerDefinition.UpgradePaths; this
- /// component only records which nodes a player has earned the right to use. Whether a
- /// particular tower can take a particular upgrade is a question for the tree (is it a child of
- /// what's placed?) crossed with this set (has the player unlocked it?).
- ///
- /// Append-only within a match, mirroring the deck. Reset happens by re-initializing at
- /// match start, so Retry / return-to-lobby cycles start clean.
- ///
- public class PlayerTowerUpgrades : NetworkBehaviour
- {
- // ----- Static registry (mirrors PlayerTowerDeck / PlayerGoldManager) -----
-
- private static readonly Dictionary s_byClientId
- = new Dictionary();
-
- public static PlayerTowerUpgrades GetForClient(ulong clientId)
- {
- s_byClientId.TryGetValue(clientId, out var upgrades);
- return upgrades;
- }
-
- public static PlayerTowerUpgrades Local
- {
- get
- {
- var nm = NetworkManager.Singleton;
- if (nm == null || !nm.IsClient) return null;
- return GetForClient(nm.LocalClientId);
- }
- }
-
- // ----- Networked state --------------------------------------------
-
- // TowerTypeIds (catalog indices) this player may upgrade into.
- private NetworkList unlockedNodes;
-
- /// Fired on every peer when the unlocked set changes. The HUD subscribes so a
- /// selected tower's upgrade buttons appear the moment a draft grants them.
- public event System.Action OnUpgradesChanged;
-
- private void Awake()
- {
- unlockedNodes = new NetworkList();
- }
-
- public override void OnNetworkSpawn()
- {
- s_byClientId[OwnerClientId] = this;
- unlockedNodes.OnListChanged += HandleListChanged;
- }
-
- public override void OnNetworkDespawn()
- {
- unlockedNodes.OnListChanged -= HandleListChanged;
- if (s_byClientId.TryGetValue(OwnerClientId, out var registered) && registered == this)
- s_byClientId.Remove(OwnerClientId);
- }
-
- private void HandleListChanged(NetworkListEvent _) => OnUpgradesChanged?.Invoke();
-
- // ----- Read API ---------------------------------------------------
-
- public int Count => unlockedNodes.Count;
-
- public int GetTypeIdAt(int index) => unlockedNodes[index];
-
- /// True if this player has unlocked the given upgrade node.
- public bool Contains(int towerTypeId)
- {
- for (int i = 0; i < unlockedNodes.Count; i++)
- if (unlockedNodes[i] == towerTypeId) return true;
- return false;
- }
-
- // ----- Server API -------------------------------------------------
-
- /// Server-only: clear the unlocked set. Called at match start.
- public void ServerInitialize()
- {
- if (!IsServer) return;
- unlockedNodes.Clear();
- }
-
- ///
- /// Server-only: unlock an upgrade node. Returns false if already unlocked (so a draft
- /// option can report a wasted pick rather than silently succeeding).
- ///
- public bool ServerUnlock(int towerTypeId)
- {
- if (!IsServer) return false;
- if (towerTypeId < 0 || Contains(towerTypeId)) return false;
-
- unlockedNodes.Add(towerTypeId);
- return true;
- }
- }
-}
diff --git a/Assets/_Project/Scripts/Gameplay/PlayerTowerUpgrades.cs.meta b/Assets/_Project/Scripts/Gameplay/PlayerTowerUpgrades.cs.meta
deleted file mode 100644
index c0821f7..0000000
--- a/Assets/_Project/Scripts/Gameplay/PlayerTowerUpgrades.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: f60f6928562351f4891142ad59ab6352
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Gameplay/TowerInstance.cs b/Assets/_Project/Scripts/Gameplay/TowerInstance.cs
index 6128ab4..519ff04 100644
--- a/Assets/_Project/Scripts/Gameplay/TowerInstance.cs
+++ b/Assets/_Project/Scripts/Gameplay/TowerInstance.cs
@@ -1,5 +1,4 @@
// Assets/_Project/Scripts/Gameplay/TowerInstance.cs
-using System.Collections.Generic;
using Unity.Netcode;
using UnityEngine;
using TD.Core;
@@ -106,24 +105,6 @@ namespace TD.Gameplay
readPerm: NetworkVariableReadPermission.Everyone,
writePerm: NetworkVariableWritePermission.Server);
- // Total gold sunk into this tower: its placement cost plus any gold later spent
- // upgrading it. Set on the server at spawn (= placement cost) and grown by the
- // (future) upgrade system via ServerAddUpgradeInvestment. Replicated so the HUD's
- // Sell button can preview the exact refund without a server round-trip.
- private readonly NetworkVariable goldInvested =
- new NetworkVariable(
- 0,
- readPerm: NetworkVariableReadPermission.Everyone,
- writePerm: NetworkVariableWritePermission.Server);
-
- // Number of upgrades applied to this tower. 0 = never upgraded. Drives the Wall's
- // "full refund only while un-upgraded" rule and will back tier display later.
- private readonly NetworkVariable upgradeCount =
- new NetworkVariable(
- 0,
- readPerm: NetworkVariableReadPermission.Everyone,
- writePerm: NetworkVariableWritePermission.Server);
-
// ----- Local resolved state -------------------------------------------
// Resolved on every client in OnNetworkSpawn from definitionTypeId via the catalog.
@@ -163,12 +144,6 @@ namespace TD.Gameplay
/// The footprint anchor tile (SW corner, world-tile coords).
public Vector2Int AnchorTile => anchorTile.Value;
- /// Total gold sunk into this tower so far (placement + upgrades).
- public int GoldInvested => goldInvested.Value;
-
- /// How many upgrades have been applied to this tower (0 = never upgraded).
- public int UpgradeCount => upgradeCount.Value;
-
/// World-unit height the post-construction drop animation falls from.
public float DropHeight => dropHeight;
@@ -279,11 +254,6 @@ namespace TD.Gameplay
anchorTile.Value = pendingAnchor;
ownerSlot.Value = pendingOwner;
- // Seed invested gold with the placement cost — the same amount
- // TowerPlacementManager deducted to build this tower. The upgrade system
- // grows this later via ServerAddUpgradeInvestment.
- goldInvested.Value = pendingDefinition != null ? pendingDefinition.GoldCost : 0;
-
// Clear the pending data — it's now committed to NetworkVariables.
hasPendingInit = false;
}
@@ -303,10 +273,6 @@ namespace TD.Gameplay
ApplyTint();
paintColor.OnValueChanged += HandlePaintColorChanged;
- // An upgrade swaps the replicated TypeId out from under every peer; re-resolve so
- // clients pick up the new stats and tint instead of holding the pre-upgrade asset.
- definitionTypeId.OnValueChanged += HandleDefinitionTypeChanged;
-
// Register for minimap rendering.
MinimapEntityRegistry.Register(this);
@@ -336,8 +302,7 @@ namespace TD.Gameplay
public override void OnNetworkDespawn()
{
- paintColor.OnValueChanged -= HandlePaintColorChanged;
- definitionTypeId.OnValueChanged -= HandleDefinitionTypeChanged;
+ paintColor.OnValueChanged -= HandlePaintColorChanged;
// Un-stamp the footprint when the tower is destroyed (sold, wave end, etc.)
// so the tiles become walkable and buildable again.
@@ -380,230 +345,6 @@ namespace TD.Gameplay
// Re-tint on every client (and the server) when the replicated paint color changes.
private void HandlePaintColorChanged(PaintColor previous, PaintColor current) => ApplyTint();
- // ----- Selling --------------------------------------------------------
-
- // Server-only guard: a fast double-click could deliver two sell RPCs before the
- // despawn propagates. Mirrors BuildSiteVisual.serverCancelled.
- private bool serverSold;
-
- ///
- /// Gold returned if this tower is sold right now. Single source of truth for both
- /// the server (which awards it) and the HUD (which labels the Sell button). By
- /// default this is of everything
- /// invested; a tower flagged
- /// (the Wall) returns the full amount while it has never been upgraded.
- ///
- public int ComputeSellRefund()
- {
- int invested = goldInvested.Value;
- if (resolvedDefinition == null)
- return Mathf.RoundToInt(invested * 0.75f);
-
- if (resolvedDefinition.FullRefundIfUnupgraded && upgradeCount.Value == 0)
- return invested;
-
- return Mathf.RoundToInt(invested * resolvedDefinition.SellRefundPercent);
- }
-
- ///
- /// Server-only: records gold spent upgrading this tower (so a later sell refunds a
- /// share of it) and marks the tower upgraded — which forfeits any
- /// full-refund-while-unupgraded rule. The upgrade system calls this when it lands.
- ///
- public void ServerAddUpgradeInvestment(int cost)
- {
- if (!IsServer) return;
- if (cost > 0) goldInvested.Value += cost;
- upgradeCount.Value += 1;
- }
-
- // ----- Upgrading ------------------------------------------------------
-
- ///
- /// Fired on every peer when this tower's definition changes (i.e. it was upgraded).
- /// The HUD subscribes to relabel a selected tower's action grid.
- ///
- public event System.Action OnDefinitionChanged;
-
- ///
- /// Gold cost to convert this tower into . The target's own
- /// is the price — upgrade nodes are never placed
- /// directly, so their cost field is free to mean "what this upgrade costs".
- ///
- public static int GetUpgradeCost(TowerDefinition target) => target != null ? target.GoldCost : 0;
-
- ///
- /// True if is a legal upgrade of this tower's current type: a
- /// direct child in the upgrade tree, with a matching footprint.
- ///
- ///
- /// The footprint check is not cosmetic. A tower's occupied/unwalkable tiles are
- /// stamped into the grid at its current size; converting to a differently-sized node would
- /// leave the stamp describing a shape the tower no longer has, corrupting both pathfinding
- /// and future placement checks. Growing a tower's footprint needs a stamp-swap (and a
- /// re-validation that the new tiles are even free), which the tree doesn't currently need —
- /// so it's rejected loudly rather than half-supported.
- ///
- public bool CanUpgradeTo(TowerDefinition target)
- {
- if (target == null || resolvedDefinition == null) return false;
- if (resolvedDefinition.UpgradePaths == null) return false;
-
- bool isChild = false;
- foreach (var path in resolvedDefinition.UpgradePaths)
- {
- if (path == target) { isChild = true; break; }
- }
- if (!isChild) return false;
-
- return target.FootprintSize == resolvedDefinition.FootprintSize;
- }
-
- ///
- /// Collects the upgrades can currently apply to this tower:
- /// direct children of its type that the player has unlocked. Does not filter on gold —
- /// the HUD shows unaffordable upgrades disabled rather than hiding them, so players can
- /// see what they're saving for.
- ///
- public void CollectAvailableUpgrades(ulong clientId, List<(TowerDefinition Def, int TypeId)> into)
- {
- into.Clear();
-
- var unlocked = PlayerTowerUpgrades.GetForClient(clientId);
- var pm = TowerPlacementManager.Instance;
- if (unlocked == null || pm == null || resolvedDefinition?.UpgradePaths == null) return;
-
- foreach (var path in resolvedDefinition.UpgradePaths)
- {
- if (path == null) continue;
- if (!pm.TryGetTypeId(path, out int typeId)) continue;
- if (!unlocked.Contains(typeId)) continue;
- if (!CanUpgradeTo(path)) continue;
-
- into.Add((path, typeId));
- }
- }
-
- ///
- /// Client → server request to convert this tower into .
- /// Accepted only from the owner, only for a node they've unlocked, only along a real tree
- /// edge, and only if they can pay.
- ///
- ///
- /// Every check is repeated here even though the HUD already filters — the HUD is a
- /// convenience, this is the authority. Same posture as placement, paint, and sell.
- ///
- [Rpc(SendTo.Server)]
- public void RequestUpgradeServerRpc(int targetTypeId, RpcParams rpcParams = default)
- {
- if (!IsServer) return;
- if (serverSold) return; // mid-sell; nothing to upgrade
-
- ulong senderClientId = rpcParams.Receive.SenderClientId;
- PlayerSlot senderSlot = PlayerMatchState.SlotForClient(senderClientId);
- if (senderSlot == PlayerSlot.None || senderSlot != ownerSlot.Value)
- {
- Debug.Log($"[TowerInstance] Upgrade rejected: client {senderClientId} " +
- $"({senderSlot}) does not own tower owned by {ownerSlot.Value}.");
- return;
- }
-
- var target = TowerPlacementManager.GetDefinition(targetTypeId);
- if (target == null)
- {
- Debug.Log($"[TowerInstance] Upgrade rejected: TypeId {targetTypeId} is not in the catalog.");
- return;
- }
-
- if (!CanUpgradeTo(target))
- {
- Debug.Log($"[TowerInstance] Upgrade rejected: '{target.name}' is not a " +
- $"same-footprint child of '{resolvedDefinition?.name}'.");
- return;
- }
-
- var unlocked = PlayerTowerUpgrades.GetForClient(senderClientId);
- if (unlocked == null || !unlocked.Contains(targetTypeId))
- {
- Debug.Log($"[TowerInstance] Upgrade rejected: client {senderClientId} has not " +
- $"unlocked '{target.name}'.");
- return;
- }
-
- int cost = GetUpgradeCost(target);
- var gold = PlayerGoldManager.GetForClient(senderClientId);
- if (gold == null || gold.CurrentGold < cost)
- {
- Debug.Log($"[TowerInstance] Upgrade rejected: client {senderClientId} cannot " +
- $"afford '{target.name}' ({cost}g).");
- return;
- }
-
- if (cost > 0) gold.DeductGold(cost);
-
- // Record the spend before switching type, so the refund reflects everything sunk in
- // and the tower loses any full-refund-while-unupgraded status.
- ServerAddUpgradeInvestment(cost);
-
- // Switching the replicated TypeId is the upgrade: TowerCombat re-reads Definition
- // every tick, so the new stats take effect on the next shot with nothing to notify.
- definitionTypeId.Value = targetTypeId;
- resolvedDefinition = target;
-
- OnDefinitionChanged?.Invoke();
- }
-
- // Re-resolve and re-tint on clients when the type changes under them (an upgrade landed).
- private void HandleDefinitionTypeChanged(int previous, int current)
- {
- if (previous == current) return;
-
- resolvedDefinition = TowerPlacementManager.GetDefinition(current);
- ApplyTint();
- OnDefinitionChanged?.Invoke();
- }
-
- ///
- /// Client → server request to sell this tower. Accepted only from the tower's owner
- /// (same ownership rule as placement and paint). The server refunds gold, broadcasts
- /// the sell VFX/SFX from a persistent object, then despawns the tower —
- /// restores the footprint's grid state and clears
- /// selection on every peer.
- ///
- [Rpc(SendTo.Server)]
- public void RequestSellServerRpc(RpcParams rpcParams = default)
- {
- if (!IsServer) return;
- if (serverSold) return; // idempotent guard against a double-click
-
- ulong senderClientId = rpcParams.Receive.SenderClientId;
- PlayerSlot senderSlot = PlayerMatchState.SlotForClient(senderClientId);
- if (senderSlot == PlayerSlot.None || senderSlot != ownerSlot.Value)
- {
- Debug.Log($"[TowerInstance] Sell rejected: client {senderClientId} " +
- $"({senderSlot}) does not own tower owned by {ownerSlot.Value}.");
- return;
- }
-
- serverSold = true;
-
- int refund = ComputeSellRefund();
- var goldManager = PlayerGoldManager.GetForClient(senderClientId);
- if (goldManager != null && refund > 0)
- // countAsEarned: false — a sell refund returns spent gold, it is not round
- // income, so it must not inflate the per-wave "earned" counter.
- goldManager.AwardGold(refund, countAsEarned: false);
-
- // Broadcast VFX/SFX from the persistent placement manager, capturing the world
- // position NOW — this NetworkObject despawns below, so it can't carry the RPC to
- // remote peers itself (same reason WaveManager routes kill/leak popups).
- var pm = TowerPlacementManager.Instance;
- if (pm != null) pm.BroadcastSellEffect(transform.position);
-
- if (NetworkObject != null && NetworkObject.IsSpawned)
- NetworkObject.Despawn(destroy: true);
- }
-
// ----- IMinimapEntity -------------------------------------------------
//
// Towers are static, so WorldPosition is cheap (no movement to track). Color reflects
@@ -663,20 +404,11 @@ namespace TD.Gameplay
? resolvedDefinition.FootprintSize
: new Vector2Int(2, 2);
- // Collect the footprint, then stamp walkability as a BATCH so a single
- // OnWalkabilityChanged fires for the whole footprint. Per-tile SetWalkable fires
- // that event once PER TILE — on despawn (sell) that meant up to 4 full enemy A*
- // re-paths for a 2×2 tower in one frame, which was the sell hitch. Placement
- // batches for the same reason (TowerPlacementManager.StampWalkable). Occupancy
- // doesn't fire walkability events, so it stays per-tile.
- var footprint = new List(footprintSize.x * footprintSize.y);
foreach (var tile in GridCoordinates.GetFootprintTiles(anchorTile.Value, footprintSize))
{
- footprint.Add(tile);
+ loader.SetWalkable(tile, walkable);
loader.SetOccupied(tile, occupied);
}
-
- loader.SetWalkableBatch(footprint, walkable);
}
// Reused per-instance across color updates to avoid per-call GC allocation.
diff --git a/Assets/_Project/Scripts/Gameplay/TowerPaintController.cs b/Assets/_Project/Scripts/Gameplay/TowerPaintController.cs
index 05a1613..e6c52e0 100644
--- a/Assets/_Project/Scripts/Gameplay/TowerPaintController.cs
+++ b/Assets/_Project/Scripts/Gameplay/TowerPaintController.cs
@@ -73,11 +73,10 @@ namespace TD.Gameplay
var keyboard = Keyboard.current;
// Right-click or Escape exits paint mode. (Escape is ignored while a HUD text
- // field has focus or the gear menu is open, so it means "cancel typing" /
- // "back out of the menu" there, matching other systems.)
+ // field has focus so it means "cancel typing" there, matching other systems.)
bool escape = keyboard != null
&& keyboard.escapeKey.wasPressedThisFrame
- && !HUDController.IsUiCapturingInput;
+ && !HUDController.IsTextInputActive;
if (mouse.rightButton.wasPressedThisFrame || escape)
{
CancelPaint();
diff --git a/Assets/_Project/Scripts/Gameplay/TowerPlacementManager.cs b/Assets/_Project/Scripts/Gameplay/TowerPlacementManager.cs
index 3706dfb..7c4662d 100644
--- a/Assets/_Project/Scripts/Gameplay/TowerPlacementManager.cs
+++ b/Assets/_Project/Scripts/Gameplay/TowerPlacementManager.cs
@@ -5,7 +5,6 @@ using UnityEngine;
using TD.Core;
using TD.Levels;
using TD.Towers;
-using TD.VFX;
namespace TD.Gameplay
{
@@ -351,113 +350,6 @@ namespace TD.Gameplay
$"client {req.SenderClientId} ({placingSlot}) at anchor {req.Anchor}.");
}
- // ----- Sell effects (persistent broadcaster) ----------------------
-
- ///
- /// Server-only: tells every peer to play the tower-sold VFX/SFX at
- /// . Called by as it sells,
- /// because the tower's own NetworkObject despawns the same frame — a persistent
- /// object must carry the broadcast so it isn't dropped.
- ///
- public void BroadcastSellEffect(Vector3 worldPos)
- {
- if (!IsServer) return;
- PlaySellEffectRpc(worldPos);
- }
-
- // One-time guard so a missing spawner warns once per peer instead of on every sale.
- private static bool s_warnedNoSellSpawner;
-
- [Rpc(SendTo.Everyone)]
- private void PlaySellEffectRpc(Vector3 worldPos)
- {
- var spawner = SellEffectSpawner.Instance;
- if (spawner == null)
- {
- if (!s_warnedNoSellSpawner)
- {
- Debug.LogWarning("[TowerPlacementManager] A tower was sold, but there is no " +
- "SellEffectSpawner in the scene — no coin VFX or sell sound " +
- "will play. Add a SellEffectSpawner GameObject to each Match " +
- "scene (assign its Sell Sound clip; the coin burst works even " +
- "with no VFX prefab).");
- s_warnedNoSellSpawner = true;
- }
- return;
- }
- spawner.Play(worldPos);
- }
-
- // ----- Phase teardown ---------------------------------------------
-
- ///
- /// Server-only: destroys every built tower on the map, refunding nothing. Called when a
- /// phase ends so the next phase starts from bare ground.
- ///
- /// How many towers were destroyed.
- ///
- /// Deliberately not the sell path. Selling refunds gold, plays the coin VFX and is
- /// owner-gated; none of that applies here. This is confiscation, not a transaction — the
- /// maze players spent a whole phase building is the price of the boss going down.
- ///
- /// Cleanup rides on despawn.
- /// already un-stamps the footprint, deregisters from the minimap and clears any local
- /// selection pointing at it, so this method only has to despawn. Shelved
- /// s self-clean the same way; queued and in-progress jobs are
- /// cancelled through their own Builder, which restores their footprint tiles.
- ///
- /// Queued jobs DO refund. Those towers were never delivered, so eating the
- /// gold would punish players for having a build queued when the boss happened to die. The
- /// no-refund rule applies to towers that got built.
- ///
- /// Timing matters for performance. Every despawn fires a walkability change,
- /// and a full maze is a lot of them. This runs with the field empty — the boss is dead and
- /// the next wave hasn't spawned — so no enemy is registered with the re-path scheduler and
- /// the recompute storm that would cause mid-wave never happens.
- ///
- public int ServerDestroyAllTowers()
- {
- if (!IsServer) return 0;
-
- // Cancel build queues first. This despawns their non-shelved visuals and frees the
- // footprint tiles they had reserved, so the sweep below only has to deal with what's
- // left standing.
- foreach (var pms in PlayerMatchState.AllPlayers)
- {
- var builder = Builder.GetForClient(pms.OwnerClientId);
- builder?.ServerCancelAllJobs();
- }
-
- var spawnManager = NetworkManager.Singleton?.SpawnManager;
- if (spawnManager == null) return 0;
-
- // Snapshot before iterating — despawning mutates the live spawned-objects collection.
- var snapshot = new System.Collections.Generic.List(
- spawnManager.SpawnedObjectsList);
-
- int destroyed = 0;
- foreach (var no in snapshot)
- {
- if (no == null || !no.IsSpawned) continue;
-
- if (no.GetComponent() != null)
- {
- no.Despawn(destroy: true);
- destroyed++;
- continue;
- }
-
- // Shelved build sites outlive their Builder's queue, so cancelling jobs above
- // doesn't reach them. Left alone they'd sit on the map as ghosts of towers that
- // no longer exist, still holding their footprint tiles occupied.
- var visual = no.GetComponent();
- if (visual != null && visual.IsShelved)
- no.Despawn(destroy: true);
- }
-
- return destroyed;
- }
-
// ----- Server-side commit hooks called by Builder ------------------
///
diff --git a/Assets/_Project/Scripts/Gameplay/WaveDefinition.cs b/Assets/_Project/Scripts/Gameplay/WaveDefinition.cs
index 5eeb6e5..7398dd0 100644
--- a/Assets/_Project/Scripts/Gameplay/WaveDefinition.cs
+++ b/Assets/_Project/Scripts/Gameplay/WaveDefinition.cs
@@ -41,70 +41,5 @@ namespace TD.Gameplay
[Tooltip("Enemy groups that make up this wave. Processed in order.")]
public WaveEntry[] Entries;
-
- ///
- /// The enemy type this wave is "about" — the first assigned entry's type. Null if the
- /// wave has no usable entries.
- ///
- ///
- /// The 2.0 design gives each wave a single enemy type, and enemy upgrades are keyed to a
- /// wave slot, so this is the identity the phase draw uses to keep slots distinct and the
- /// HUD uses to label an upcoming wave. still supports multiple
- /// groups (useful for staggering counts of the same type), but mixing types within
- /// one wave makes this ambiguous — see .
- ///
- public EnemyDefinition PrimaryEnemyType
- {
- get
- {
- if (Entries == null) return null;
- foreach (var e in Entries)
- {
- if (e.EnemyType != null && e.Count > 0) return e.EnemyType;
- }
- return null;
- }
- }
-
- ///
- /// True if every usable entry in this wave spawns the same enemy type. False means
- /// is only telling part of the story — the run validator
- /// warns on these rather than rejecting them, since a mixed wave still plays fine, it
- /// just labels and upgrades oddly.
- ///
- public bool HasSingleEnemyType
- {
- get
- {
- var first = PrimaryEnemyType;
- if (first == null) return false;
- foreach (var e in Entries)
- {
- if (e.EnemyType != null && e.Count > 0 && e.EnemyType != first) return false;
- }
- return true;
- }
- }
-
- /// Total enemies spawned per zone by this wave, across all entries.
- ///
- /// Also the divisor for the encounter's health budget — see
- /// . That makes count a texture
- /// knob rather than a difficulty one: a wave of 8 spawns eight tanks, a wave of 150 spawns
- /// a fragile swarm, and both present the same total health for their slot in the run.
- ///
- public int TotalEnemyCount
- {
- get
- {
- if (Entries == null) return 0;
- int total = 0;
- foreach (var e in Entries)
- {
- if (e.EnemyType != null && e.Count > 0) total += e.Count;
- }
- return total;
- }
- }
}
}
diff --git a/Assets/_Project/Scripts/Gameplay/WaveManager.cs b/Assets/_Project/Scripts/Gameplay/WaveManager.cs
index a40ed28..d1d889b 100644
--- a/Assets/_Project/Scripts/Gameplay/WaveManager.cs
+++ b/Assets/_Project/Scripts/Gameplay/WaveManager.cs
@@ -5,55 +5,44 @@ using UnityEngine;
using TD.Core;
using TD.Gameplay.BuilderEffects;
using TD.Gameplay.Draft;
-using TD.Gameplay.EnemyAbilities;
-using TD.Gameplay.EnemyUpgrades;
-using TD.Gameplay.Waves;
using TD.Levels;
using TD.UI;
namespace TD.Gameplay
{
///
- /// Server-authoritative encounter driver. Spawns enemies across all player zones, tracks
- /// completion, awards kill gold, and manages the shared lives pool. Which encounter to
- /// run comes from ; this class only runs it.
+ /// Server-authoritative wave controller. Spawns enemies across all player zones,
+ /// tracks wave completion, awards kill gold, and manages the shared lives pool.
///
///
- /// Encounter lifecycle:
+ /// Wave lifecycle:
///
- /// - When is entered, asks
- /// to draw phase 1 and then runs its first wave.
- /// - Each encounter is: inter-wave step (draft, then the enemy-buff vote, then the
- /// build countdown) → spawn → mop up. See .
- /// - Each spawns Count enemies per player zone. All
- /// spawn held, then release in 10% chunks on ReleaseInterval.
- /// - The encounter is complete only when every spawned enemy is dead or has leaked.
- /// then decides what comes next.
- /// - Final phase's boss dies → .
+ /// - When is entered,
+ /// advances
+ /// immediately (so the HUD shows the wave number during prep), then waits
+ /// before spawning.
+ /// - Each spawns Count enemies per player zone,
+ /// one zone per frame-group, with SpawnInterval seconds between
+ /// individual enemies in the group.
+ /// - After all entries are spawned, the wave is considered complete only when
+ /// every active enemy is either killed or has reached the goal.
+ /// - All waves exhausted → .
/// - Lives drop to 0 → .
///
///
- /// Why the wave list moved out. Waves used to be a hand-ordered array walked
- /// start-to-finish, so "which wave is next" was just an index++. Under the 2.0 cyclical
- /// design the same five waves repeat across three cycles while accumulating player-voted
- /// upgrades, and each phase draws a fresh five — progression that no longer fits in an index.
- /// owns it; this class asks it what to run.
- ///
/// Kill gold: When an enemy dies, names
/// the tower's player. resolves the
/// OwnerClientId, and awards
- /// the gold. GoldConfig is indexed by
- /// — a run-long counter, not a per-cycle one, so
- /// per-wave payouts keep climbing as the cycles repeat instead of resetting.
+ /// the gold.
///
/// Zone leak counts: is a NetworkList
- /// indexed by (int)PlayerSlot. It is incremented when an enemy
+ /// indexed by (int)PlayerSlot (indices 0–8). It is incremented when an enemy
/// crosses from one player zone into another, giving the HUD a per-player leak score.
/// Index 0 corresponds to and is unused.
///
/// Inspector setup:
///
- /// - Assign a RunDefinition to the scene's .
+ /// - Assign in order (Wave 1 at index 0).
/// - Set to match your level design intent.
///
///
@@ -65,29 +54,18 @@ namespace TD.Gameplay
// ----- Inspector --------------------------------------------------
+ [Tooltip("Wave definitions in order. Index 0 = Wave 1.")]
+ [SerializeField] private WaveDefinition[] waveDefinitions;
+
[Tooltip("Shared lives pool at the start of a match.")]
[SerializeField] private int startingLives = 20;
- [Tooltip("Seconds players get to make their personal draft pick. Ends early once every " +
- "player has picked. Be generous — this is reading time, not reaction time.")]
- [SerializeField] private float draftTimeSeconds = 30f;
-
- [Tooltip("Seconds players get to vote on the enemy buff. Ends early once every player " +
- "has voted. Generous enough that players can watch each other's votes land and " +
- "change their own in response.")]
- [SerializeField] private float voteTimeSeconds = 25f;
-
[Tooltip("Single source of truth for every gold tunable: starting gold, per-wave " +
"kill rewards, completion bonus, no-leak bonus. Required for a real match; " +
"if unset the game falls back to per-player startingGold defaults and grants " +
"no kill/wave rewards (designer-error indicator, not a supported runtime mode).")]
[SerializeField] private GoldConfig goldConfig;
- [Tooltip("How enemy health scales across the run. Required for a real match; if unset, " +
- "every enemy spawns at a flat fallback health and difficulty never progresses " +
- "(designer-error indicator, not a supported runtime mode).")]
- [SerializeField] private EnemyScalingConfig enemyScalingConfig;
-
// ----- Networked state --------------------------------------------
// Per-slot total leak counters across the whole match. Index = (int)PlayerSlot;
@@ -116,27 +94,14 @@ namespace TD.Gameplay
readPerm: NetworkVariableReadPermission.Everyone,
writePerm: NetworkVariableWritePermission.Server);
- // Which inter-wave stage the countdown above belongs to. One countdown serves all three
- // stages (they never overlap), so the HUD needs this to label it correctly.
- private readonly NetworkVariable interWaveStage =
- new NetworkVariable(
- value: InterWaveStage.None,
- readPerm: NetworkVariableReadPermission.Everyone,
- writePerm: NetworkVariableWritePermission.Server);
-
// ----- Server-local runtime state ---------------------------------
private int remainingLives;
private int activeEnemyCount;
private bool spawningComplete;
- private bool runStarted;
+ private int currentWaveIndex = -1; // -1 = not yet started
private Coroutine activeWaveCoroutine;
- // Wave slot the NEXT inter-wave vote will buff — captured when an encounter clears, before
- // the run advances past it. -1 means "no vote due": the run's first encounter (nothing has
- // been beaten yet) or straight after a boss (the phase's slots are being wiped).
- private int pendingVoteSlot = -1;
-
private readonly System.Collections.Generic.List heldEnemies
= new System.Collections.Generic.List();
@@ -212,11 +177,6 @@ namespace TD.Gameplay
{
var deck = PlayerTowerDeck.GetForClient(pms.OwnerClientId);
if (deck != null) deck.ServerInitialize(startingTypeIds);
-
- // Upgrade nodes start empty — every one is earned through the draft. Cleared
- // rather than left alone so Retry / return-to-lobby doesn't carry a previous
- // run's unlocks into a new one.
- PlayerTowerUpgrades.GetForClient(pms.OwnerClientId)?.ServerInitialize();
}
}
else
@@ -226,7 +186,7 @@ namespace TD.Gameplay
}
if (ms.Phase == MatchPhase.Playing)
- StartRun();
+ StartNextWave();
}
public override void OnNetworkDespawn()
@@ -239,6 +199,13 @@ namespace TD.Gameplay
// ----- Public accessors -------------------------------------------
+ ///
+ /// Total number of waves in this match. Same on every peer because
+ /// is a serialized prefab field, identical
+ /// on host and clients. Returns 0 if the array is unassigned.
+ ///
+ public int TotalWaves => waveDefinitions?.Length ?? 0;
+
///
/// Number of times enemies have leaked out of the given player's zone over the
/// entire match. Replicated — safe to call on any peer.
@@ -262,82 +229,34 @@ namespace TD.Gameplay
///
public float PrepCountdown => prepCountdown.Value;
- ///
- /// Which inter-wave step the current countdown belongs to, or
- /// while a wave is spawning or being fought. Replicated;
- /// safe on any peer. The HUD uses it to label the shared countdown.
- ///
- public InterWaveStage CurrentInterWaveStage => interWaveStage.Value;
-
-
- ///
- /// 1-based encounter number since the run began, or 0 before it starts. This is the key
- /// is indexed by. Replicated via , so it's
- /// safe on any peer.
- ///
- public int CurrentEncounterNumber
- => RunState.Instance != null && runStarted ? RunState.Instance.GlobalEncounterNumber : 0;
// ----- Phase handling ---------------------------------------------
private void HandlePhaseChanged(MatchPhase previous, MatchPhase next)
{
if (!IsServer) return;
- if (next == MatchPhase.Playing) StartRun();
+ if (next == MatchPhase.Playing && currentWaveIndex < 0)
+ StartNextWave();
}
- // ----- Run control ------------------------------------------------
+ // ----- Wave coroutine ---------------------------------------------
- ///
- /// Server-only: begin the run. Draws phase 1 and starts its first encounter. Idempotent —
- /// re-entering won't restart a run in progress.
- ///
- private void StartRun()
+ private void StartNextWave(bool skipPrep = false)
{
- if (!IsServer || runStarted) return;
+ currentWaveIndex++;
- var run = RunState.Instance;
- if (run == null)
+ if (waveDefinitions == null || currentWaveIndex >= waveDefinitions.Length)
{
- Debug.LogError("[WaveManager] No RunState in the scene — no waves can run. " +
- "Add a RunState object and assign it a RunDefinition.");
+ Debug.Log("[WaveManager] All waves complete. Victory.");
+ MatchState.Instance?.SetPhase(MatchPhase.Victory);
return;
}
- if (!run.ServerBeginRun())
- {
- // ServerBeginRun already logged the specific reason (missing asset, unplayable
- // pools). Bail rather than spawn an empty encounter that can never complete.
- Debug.LogError("[WaveManager] Run could not be started; see the RunState error above.");
- return;
- }
+ // Advance the replicated wave counter at the START of prep so the HUD
+ // shows the upcoming wave number during the countdown.
+ MatchState.Instance?.SetCurrentWave(currentWaveIndex + 1); // 1-based
- runStarted = true;
- StartEncounter(skipInterWave: false);
- }
-
- ///
- /// Server-only: run whatever encounter is currently pointing at.
- ///
- /// Skip the draft/vote/build step and spawn immediately.
- /// Used by the dev force-advance cheat.
- private void StartEncounter(bool skipInterWave)
- {
- var run = RunState.Instance;
- var def = run?.CurrentWave;
-
- if (def == null)
- {
- Debug.LogError($"[WaveManager] No wave drawn for {run?.ProgressLabel ?? "?"} — " +
- $"the run cannot continue.");
- return;
- }
-
- // Publish the run-long encounter number so the HUD and the gold lookup agree on
- // which encounter's payout table applies.
- MatchState.Instance?.SetCurrentWave(run.GlobalEncounterNumber);
-
- // Reset per-wave bookkeeping for the encounter that's about to begin:
+ // Reset per-wave bookkeeping for the wave that's about to begin:
// - waveLeakCounts: per-slot leaks this wave, used for the no-leak bonus.
// - PlayerGoldManager.goldEarnedThisWave: HUD top-bar resets so the
// "+N g/wave" counter starts at 0.
@@ -352,100 +271,23 @@ namespace TD.Gameplay
activeEnemyCount = 0;
spawningComplete = false;
- activeWaveCoroutine = StartCoroutine(RunEncounter(def, skipInterWave));
- }
-
- ///
- /// Server-only: the encounter is over — ask what's next and either
- /// start it or end the match.
- ///
- private void AdvanceRun()
- {
- var run = RunState.Instance;
- if (run == null) return;
-
- switch (run.ServerAdvance())
- {
- case RunAdvance.RunComplete:
- Debug.Log("[WaveManager] Final boss defeated. Victory.");
- MatchState.Instance?.SetPhase(MatchPhase.Victory);
- return;
-
- case RunAdvance.BossReady:
- Debug.Log($"[WaveManager] All cycles cleared — boss incoming " +
- $"({run.ProgressLabel}).");
- break;
-
- case RunAdvance.NextPhase:
- {
- // A phase boundary resets the board as well as the enemies: the maze players
- // spent the phase building is torn down, unrefunded, so the new phase starts
- // from bare ground. Runs here rather than inside RunState.ServerAdvance
- // because it touches player structures, which the run has no business owning.
- int destroyed = TowerPlacementManager.Instance?.ServerDestroyAllTowers() ?? 0;
- NotifyTowersWipedClientRpc(destroyed);
-
- Debug.Log($"[WaveManager] Boss down. New phase drawn: {run.ProgressLabel}. " +
- $"Enemy upgrades wiped; {destroyed} tower(s) destroyed.");
- break;
- }
-
- case RunAdvance.NextCycle:
- Debug.Log($"[WaveManager] Cycle complete — same waves return upgraded " +
- $"({run.ProgressLabel}).");
- break;
- }
-
- StartEncounter(skipInterWave: false);
+ activeWaveCoroutine = StartCoroutine(
+ RunWave(waveDefinitions[currentWaveIndex], skipPrep));
}
// ----- Dev / cheats -----------------------------------------------
///
- /// Dev cheat: skip the rest of the current encounter (despawn any remaining enemies, kill
- /// the timers) and start the next one immediately. Server-only — silently no-ops on
- /// clients. Safe to call during the inter-wave step, mid-spawn, or while enemies are alive.
+ /// Dev cheat: skip the rest of the current wave (despawn any remaining
+ /// enemies, kill the prep timer) and start the next wave immediately.
+ /// Server-only — silently no-ops on clients. Safe to call during prep,
+ /// mid-spawn, or while enemies are alive.
///
public void ForceAdvanceToNextWave()
{
- if (!IsServer || !runStarted) return;
- ClearFieldAndStopEncounter();
- AdvanceRun();
- }
+ if (!IsServer) return;
- ///
- /// Dev cheat: skip whole encounters until the run reaches this phase's boss. Useful for
- /// testing boss content without playing fifteen waves. Server-only.
- ///
- public void ForceAdvanceToBoss()
- {
- if (!IsServer || !runStarted) return;
-
- var run = RunState.Instance;
- if (run == null || run.IsBossStage) return;
-
- ClearFieldAndStopEncounter();
-
- // Advance until the boss is up (or the run ends, which shouldn't happen from a
- // non-boss encounter but is guarded anyway).
- int guard = run.EncountersPerPhase + 1;
- while (!run.IsBossStage && guard-- > 0)
- {
- if (run.ServerAdvance() == RunAdvance.RunComplete)
- {
- MatchState.Instance?.SetPhase(MatchPhase.Victory);
- return;
- }
- }
-
- StartEncounter(skipInterWave: false);
- }
-
- // Shared teardown for the dev cheats: stop the running encounter, wipe the field, and
- // resolve anything the players had open so nothing is left dangling mid-skip.
- private void ClearFieldAndStopEncounter()
- {
- // Stop the current encounter's coroutine (cancels timers + remaining spawns).
+ // Stop the current wave's coroutine (cancels prep timer + remaining spawns).
if (activeWaveCoroutine != null)
{
StopCoroutine(activeWaveCoroutine);
@@ -473,41 +315,59 @@ namespace TD.Gameplay
}
}
+ // Reset bookkeeping and start the next wave's RunWave coroutine,
+ // skipping the prep timer so spawning starts immediately.
activeEnemyCount = 0;
spawningComplete = false;
heldEnemies.Clear();
- // Skipping ahead interrupts the inter-wave step, so resolve anything the players had
- // open before moving on. The draft auto-resolves (a free reward shouldn't vanish
- // because a dev pressed a key); the vote is closed WITHOUT resolving, since applying a
- // buff nobody finished voting on would silently rewrite the run.
+ // Force-advancing interrupts the prep phase, so resolve any drafts the players
+ // had open before skipping ahead.
DraftService.Instance?.ServerAutoResolveAll();
- WaveVote.Instance?.ServerClose();
- interWaveStage.Value = InterWaveStage.None;
- prepCountdown.Value = 0f;
+ StartNextWave(skipPrep: true);
}
- // ----- Encounter coroutine ----------------------------------------
-
- private IEnumerator RunEncounter(WaveDefinition def, bool skipInterWave = false)
+ private IEnumerator RunWave(WaveDefinition def, bool skipPrep = false)
{
- // Draft, enemy-buff vote, then the build countdown. Skipped entirely when a dev
- // cheat forces the encounter to start immediately.
- if (!skipInterWave)
- yield return RunInterWave(def);
+ // Prep phase — players build while the countdown ticks. Skipped when
+ // a dev cheat forces the wave to start immediately. We tick the
+ // replicated prepCountdown each frame so every HUD can render the
+ // remaining time consistently. Server is the only writer; clients
+ // observe the value via NetworkVariable replication.
+ if (!skipPrep)
+ {
+ // Open a draft for every player at the start of the build phase. They pick
+ // during prep; any unpicked draft is auto-resolved when the timer expires.
+ DraftService.Instance?.ServerOfferToAll();
+ prepCountdown.Value = def.PrepTime;
+ float remaining = def.PrepTime;
+ // Throttle network sync to ~10 Hz. NetworkVariable replicates on every
+ // mutation; at 60 fps we'd send ~600 deltas per 10s prep purely to
+ // animate text that only changes once per second on the HUD. 0.1s
+ // gives a smooth-enough fall while keeping bandwidth minimal.
+ const float NetworkSyncInterval = 0.1f;
+ float nextSync = def.PrepTime - NetworkSyncInterval;
+ while (remaining > 0f)
+ {
+ yield return null;
+ remaining = Mathf.Max(0f, remaining - Time.deltaTime);
+ if (remaining <= nextSync || remaining <= 0f)
+ {
+ prepCountdown.Value = remaining;
+ nextSync = remaining - NetworkSyncInterval;
+ }
+ }
+
+ // Prep timer expired — auto-resolve any draft the player didn't pick so
+ // the free reward isn't wasted.
+ DraftService.Instance?.ServerAutoResolveAll();
+ }
// Ensure the countdown reads zero entering the spawn phase, regardless of
- // whether the build step was skipped or just expired.
+ // whether prep was skipped or just expired.
prepCountdown.Value = 0f;
- // Resolve this wave slot's voted buffs once, here — the set is fixed for the whole
- // encounter, so doing it per enemy would repeat the same lookup hundreds of times.
- // Must run AFTER the inter-wave step, since the vote that just closed may have added
- // to a slot this very encounter re-runs on a later cycle.
- ResolveCurrentWaveAbilities();
- ResolveCurrentEncounterHealth(def);
-
// Spawn all enemies at once in a held (untargetable, immobile) state.
if (def.Entries != null)
{
@@ -544,197 +404,6 @@ namespace TD.Gameplay
CheckWaveComplete();
}
- // ----- Inter-wave step ---------------------------------------------
-
- ///
- /// The step between encounters: players take their personal draft, then vote on the buff
- /// the wave they just cleared will carry into the next cycle, then build.
- ///
- ///
- /// The three stages run strictly in sequence and never overlap. The build countdown used
- /// to double as the draft window, which made "wait until everyone has chosen" impossible
- /// to express — players who wanted to build were paying for teammates who wanted to read
- /// their cards. Separating them costs a few seconds per wave and makes both steps mean
- /// what they say.
- ///
- /// Each stage ends early the moment every player has acted, so the generous timers
- /// are a ceiling for deliberation, not a floor everyone sits through.
- ///
- private IEnumerator RunInterWave(WaveDefinition def)
- {
- // ----- 1. Personal draft -----
- // Offered to everyone at once. Anyone who hasn't picked when the timer expires has
- // their draft auto-resolved, so an idle player never wastes a free reward.
- interWaveStage.Value = InterWaveStage.Draft;
- DraftService.Instance?.ServerOfferToAll();
- yield return TickCountdown(draftTimeSeconds, () => DraftService.AllPlayersPicked);
- DraftService.Instance?.ServerAutoResolveAll();
-
- // ----- 2. Enemy-buff vote -----
- // Votes on the wave that was just cleared, which is the slot captured before the run
- // advanced. Skipped on the run's first encounter (nothing has been defeated yet) and
- // after a boss (that phase's slots are being wiped anyway).
- if (pendingVoteSlot >= 0)
- {
- var vote = WaveVote.Instance;
- var run = RunState.Instance;
-
- // A vote-meta card may have armed this slot to skip its next vote and take
- // several buffs outright. That's the card's whole effect, so it pre-empts the
- // normal vote rather than running alongside it.
- int autoCount = run != null ? run.GetAutoApplyCount(pendingVoteSlot) : 0;
-
- if (autoCount > 0 && vote != null)
- {
- int applied = vote.ServerAutoApply(pendingVoteSlot, autoCount);
- run.ServerConsumeAutoApply(pendingVoteSlot);
- Debug.Log($"[WaveManager] Wave slot {pendingVoteSlot} auto-took {applied} " +
- $"buff(s); no vote held.");
- }
- else if (vote != null && vote.ServerOpenVote(pendingVoteSlot))
- {
- interWaveStage.Value = InterWaveStage.Vote;
- yield return TickCountdown(voteTimeSeconds, () => vote.AllPlayersVoted);
- vote.ServerResolve();
- }
-
- pendingVoteSlot = -1;
- }
-
- // ----- 3. Build -----
- interWaveStage.Value = InterWaveStage.Build;
- yield return TickCountdown(def.PrepTime);
-
- interWaveStage.Value = InterWaveStage.None;
- }
-
- ///
- /// Ticks down from to zero,
- /// exiting early once returns true.
- ///
- ///
- /// Network sync is throttled to ~10 Hz. NetworkVariable replicates on every
- /// mutation, so ticking at frame rate would push ~600 deltas across a 10-second countdown
- /// purely to animate text that changes once a second. A tenth of a second still falls
- /// smoothly while keeping the traffic negligible.
- ///
- private IEnumerator TickCountdown(float seconds, System.Func finishedEarly = null)
- {
- const float NetworkSyncInterval = 0.1f;
-
- prepCountdown.Value = seconds;
- float remaining = seconds;
- float nextSync = seconds - NetworkSyncInterval;
-
- while (remaining > 0f)
- {
- if (finishedEarly != null && finishedEarly()) break;
-
- yield return null;
- remaining = Mathf.Max(0f, remaining - Time.deltaTime);
-
- if (remaining <= nextSync || remaining <= 0f)
- {
- prepCountdown.Value = remaining;
- nextSync = remaining - NetworkSyncInterval;
- }
- }
-
- prepCountdown.Value = 0f;
- }
-
- // ----- Wave ability resolution -------------------------------------
-
- // Abilities carried by every enemy of the encounter currently spawning. Rebuilt once per
- // encounter (not per enemy) since a wave's buff set is fixed for its whole duration.
- private readonly System.Collections.Generic.List currentWaveAbilities
- = new System.Collections.Generic.List();
-
- private readonly System.Collections.Generic.List upgradeIdScratch
- = new System.Collections.Generic.List();
-
- ///
- /// Server-only: resolve the current wave slot's voted upgrades into the ability list every
- /// enemy of this encounter will carry.
- ///
- ///
- /// Called once at the top of an encounter's spawn phase. Cards that resolve to nothing
- /// (missing pool entry, non-ability card) are skipped silently here — the vote already
- /// validated them, and a warning per enemy would flood the log.
- ///
- private void ResolveCurrentWaveAbilities()
- {
- currentWaveAbilities.Clear();
-
- var run = RunState.Instance;
- var pool = EnemyUpgradePool.Instance;
- if (run == null || pool == null) return;
-
- run.GetUpgradesForSlot(run.CurrentUpgradeSlot, upgradeIdScratch);
-
- foreach (int id in upgradeIdScratch)
- {
- if (pool.Get(id) is AbilityEnemyUpgradeOption card && card.Ability != null)
- currentWaveAbilities.Add(card.Ability);
- }
-
- if (currentWaveAbilities.Count > 0)
- Debug.Log($"[WaveManager] {run.ProgressLabel} enemies carry " +
- $"{currentWaveAbilities.Count} wave buff(s).");
- }
-
- // ----- Encounter health resolution ---------------------------------
-
- // Health one enemy of this encounter gets BEFORE its type's HpMultiplier is applied:
- // the encounter's budget divided across the wave's bodies. Resolved once per encounter
- // for the same reason the ability list is — it's fixed for the whole wave, and the
- // divisor would otherwise be recomputed for every spawn.
- private float currentEncounterHpShare = FallbackEnemyHp;
-
- // Used only when no EnemyScalingConfig is assigned. Enemies stay killable so the match
- // is still playable, but difficulty never progresses — see the field's tooltip.
- private const float FallbackEnemyHp = 100f;
-
- ///
- /// Server-only: work out the per-enemy health share for the encounter about to spawn.
- ///
- ///
- /// Splits the encounter's total budget across the wave's enemy count, so a swarm and an
- /// elite wave in the same slot present the same threat. The type's
- /// is applied later, per enemy, so a wave
- /// mixing types skews each one independently off the shared share.
- ///
- private void ResolveCurrentEncounterHealth(WaveDefinition def)
- {
- if (enemyScalingConfig == null)
- {
- currentEncounterHpShare = FallbackEnemyHp;
- Debug.LogWarning("[WaveManager] No EnemyScalingConfig assigned — enemies spawn at " +
- $"a flat {FallbackEnemyHp} HP and difficulty will not progress.");
- return;
- }
-
- var run = RunState.Instance;
- int encounter = CurrentEncounterNumber;
- bool isBoss = run?.IsBossStage ?? false;
-
- int bodies = def != null ? def.TotalEnemyCount : 0;
- if (bodies <= 0)
- {
- // An empty wave can't be divided into. Nothing will spawn from it either, so this
- // only guards against a divide-by-zero on a misauthored asset.
- currentEncounterHpShare = FallbackEnemyHp;
- return;
- }
-
- currentEncounterHpShare =
- enemyScalingConfig.ResolvePerEnemyHp(encounter, isBoss, bodies, archetypeMultiplier: 1f);
-
- Debug.Log($"[WaveManager] Encounter {encounter}{(isBoss ? " (BOSS)" : "")}: " +
- $"{enemyScalingConfig.GetEncounterHpBudget(encounter, isBoss):0} HP budget " +
- $"across {bodies} enemies = {currentEncounterHpShare:0} each before type modifiers.");
- }
-
// ----- Spawn helpers ----------------------------------------------
private void SpawnEnemyInAllZones(EnemyDefinition def, bool held = false)
@@ -760,9 +429,7 @@ namespace TD.Gameplay
// PlayerZoneVolume (so OwnerGrid[spawnerTile] = None).
var spawner = zone.Spawners[0];
(float xHalf, float zHalf) = ComputeSpawnerHalfExtents(spawner.TileArea);
- SpawnEnemy(def, spawner.TilePosition, zone.Owner, currentWaveAbilities,
- xHalfExtent: xHalf, zHalfExtent: zHalf, held: held,
- facing: spawner.Facing);
+ SpawnEnemy(def, spawner.TilePosition, zone.Owner, xHalf, zHalf, held, spawner.Facing);
}
}
@@ -787,16 +454,7 @@ namespace TD.Gameplay
return (xHalf, zHalf);
}
- ///
- /// Server-only: build and spawn one enemy.
- ///
- /// Wave buffs this enemy carries. Null/empty for a plain enemy.
- /// Pre-computed stats to spawn with, bypassing both the
- /// definition's values and ' spawn modifications. Used by
- /// split-on-death, whose minions are derived from the parent rather than the asset.
private void SpawnEnemy(EnemyDefinition def, Vector2Int spawnerTile, PlayerSlot ownerSlot,
- System.Collections.Generic.IReadOnlyList abilities,
- EnemySpawnContext? contextOverride = null,
float xHalfExtent = 0f, float zHalfExtent = 0f, bool held = false,
Direction facing = Direction.South)
{
@@ -806,17 +464,6 @@ namespace TD.Gameplay
return;
}
- // Resolve the stats this enemy will actually spawn with. Abilities get first say —
- // flight, health and size all have to be settled before the position is computed and
- // before EnemyHealth/EnemyMovement read them, since each is captured once.
- var context = contextOverride
- ?? EnemySpawnContext.FromDefinition(def, currentEncounterHpShare * def.HpMultiplier);
- if (contextOverride == null && abilities != null)
- {
- for (int i = 0; i < abilities.Count; i++)
- abilities[i]?.ServerModifySpawn(ref context);
- }
-
Vector3 spawnPos = GridCoordinates.GridToWorld(spawnerTile);
if (xHalfExtent > 0f)
spawnPos.x += Random.Range(-xHalfExtent, xHalfExtent);
@@ -826,8 +473,8 @@ namespace TD.Gameplay
// Flying enemies spawn elevated so they visually soar over towers. The
// movement code preserves Y per-frame, so this height persists for the
// enemy's whole flight. NetworkTransform replicates the raised position.
- if (context.IsFlying)
- spawnPos.y += context.FlightHeight;
+ if (def.IsFlying)
+ spawnPos.y += def.FlightHeight;
float yaw = facing switch
{
@@ -843,12 +490,6 @@ namespace TD.Gameplay
spawnPos,
Quaternion.Euler(0f, yaw, 0f));
- // Scales the whole prefab hierarchy uniformly (split minions, size-changing buffs).
- // Relies on the enemy prefab's NetworkTransform syncing scale to clients — verify
- // that's enabled if a scaled spawn doesn't look smaller on remote peers.
- if (!Mathf.Approximately(context.VisualScale, 1f))
- go.transform.localScale *= context.VisualScale;
-
var health = go.GetComponent();
var movement = go.GetComponent();
@@ -860,25 +501,8 @@ namespace TD.Gameplay
return;
}
- // Boss-ness comes from the run's position (this is the phase's boss encounter), not
- // from the wave asset — the same WaveDefinition could legally sit in both a cycle pool
- // and a boss pool, and it's only a boss when drawn as one. Split minions inherit it
- // via the context override path, which never sets the flag.
- bool isBoss = contextOverride == null && (RunState.Instance?.IsBossStage ?? false);
-
- health.InitializeServer(context.MaxHp, context.LivesCost, context.IsFlying, held, isBoss);
- movement.InitializeServer(context.MoveSpeed, spawnerTile, ownerSlot, context.IsFlying);
-
- // Optional — only prefabs with an EnemyAbility component can carry wave buffs. See
- // EnemyAbility's remarks for why on-death abilities aren't event-subscription driven.
- var ability = go.GetComponent();
- if (ability != null)
- ability.InitializeServer(abilities, context);
- else if (abilities != null && abilities.Count > 0)
- Debug.LogWarning($"[WaveManager] '{def.EnemyPrefab.name}' has no EnemyAbility " +
- $"component, so this wave's {abilities.Count} buff(s) will not " +
- $"apply to it. Add the component to the prefab.");
-
+ health.InitializeServer(def.MaxHp, def.LivesCost, def.IsFlying, held);
+ movement.InitializeServer(def.MoveSpeed, spawnerTile, ownerSlot, def.IsFlying);
if (held) heldEnemies.Add(health);
health.OnDied += HandleEnemyKilled;
@@ -890,55 +514,17 @@ namespace TD.Gameplay
go.GetComponent().Spawn();
}
- ///
- /// Server-only: spawns copies of at
- /// , continuing that tile's A* path onward rather than
- /// restarting from a wave spawner. Used by on-death abilities (e.g.
- /// SplitOnDeathAbilityDefinition) to spawn smaller enemies from a corpse's
- /// position. Reuses so split children get the same
- /// activeEnemyCount/event-wiring bookkeeping as any other spawn.
- ///
- ///
- /// Minions inherit nothing. They are spawned with an empty ability list, so a split
- /// can never chain into further splits or pick up any other buff the parent's wave carries.
- /// That is the whole termination argument for the recursion — one extra generation, always,
- /// however many cards the wave has collected.
- ///
- /// Their stats come entirely from , which the calling
- /// ability derives from the parent's own spawned values. Passing it as an override also
- /// bypasses spawn modification, which is correct: those modifiers already applied to the
- /// parent and are baked into what's being scaled here.
- ///
- public void ServerSpawnSplitEnemies(EnemyDefinition def, int count, Vector2Int atTile,
- PlayerSlot ownerSlot, float scatterRadius,
- EnemySpawnContext context)
- {
- if (!IsServer) return;
- for (int i = 0; i < count; i++)
- SpawnEnemy(def, atTile, ownerSlot,
- abilities: null,
- contextOverride: context,
- xHalfExtent: scatterRadius, zHalfExtent: scatterRadius);
- }
-
// ----- Enemy event handlers (server-only) -------------------------
private void HandleEnemyKilled(EnemyHealth health)
{
- // Kill reward comes from GoldConfig for the current encounter — same value for
- // every enemy in it regardless of EnemyDefinition type. Missing config or an
- // unauthored encounter → 0 reward (gold flow disabled, designer-error mode).
+ // Kill reward comes from GoldConfig for the current wave — same value for
+ // every enemy in the wave regardless of EnemyDefinition type. Missing config
+ // or out-of-range wave → 0 reward (gold flow disabled, designer-error mode).
int killReward = 0;
- var goldEntry = goldConfig?.GetEncounterEntry(CurrentEncounterNumber);
+ var goldEntry = goldConfig?.GetWaveEntry(currentWaveIndex + 1);
if (goldEntry != null) killReward = goldEntry.GoldPerEnemy;
- // Wave buffs get to alter the bounty before anything else sees it — this is what
- // reward-suppressing cards hook. Applied before the builder's gold-per-kill bonus so
- // a card that zeroes the bounty doesn't also cancel a player's own upgrade.
- var enemyAbility = health.GetComponent();
- if (enemyAbility != null)
- killReward = enemyAbility.ServerModifyKillReward(killReward);
-
// Award kill gold to the tower owner that landed the killing blow. The builder's
// "gold per kill" effect (if granted) is queried live here rather than cached on
// the killing tower — see BuilderUpgradeManager's "query, don't snapshot" note.
@@ -965,12 +551,6 @@ namespace TD.Gameplay
if (totalReward > 0)
ShowGoldRewardClientRpc(health.transform.position, totalReward);
- // Resolve on-death abilities BEFORE unsubscribing/decrementing. A split-spawn's
- // activeEnemyCount++ (inside ServerSpawnSplitEnemies -> SpawnEnemy) must land before
- // this kill's activeEnemyCount-- below, or a split on a wave's last enemy could let
- // CheckWaveComplete see activeEnemyCount hit 0 and advance the wave prematurely.
- enemyAbility?.ServerInvokeOnDeath(health);
-
UnsubscribeEnemy(health);
DecrementAndCheckComplete();
}
@@ -997,11 +577,6 @@ namespace TD.Gameplay
if (livesCost > 0)
ShowLifeLossClientRpc(leakPos, livesCost);
- // Wave buffs that punish leaks beyond the life cost hook here, while the enemy is
- // still alive enough to be queried. Runs before the defeat check so a leak that ends
- // the match still applies its full penalty.
- movement.GetComponent()?.ServerInvokeReachedGoal(movement.OriginZone);
-
UnsubscribeEnemy(movement.GetComponent());
remainingLives = Mathf.Max(0, remainingLives - livesCost);
@@ -1034,41 +609,8 @@ namespace TD.Gameplay
OnLifeLost?.Invoke(amount);
}
- [ClientRpc]
- private void ShowGoldLossClientRpc(Vector3 worldPos, int amount)
- {
- FloatingTextSpawner.Instance?.SpawnGoldLoss(worldPos, amount);
- }
-
- ///
- /// Server-only: show a "-N gold" popup on every peer. Exposed because enemy abilities run
- /// inside ScriptableObjects, which have no NetworkBehaviour of their own to send a
- /// ClientRpc from.
- ///
- public void ServerBroadcastGoldLoss(Vector3 worldPos, int amount)
- {
- if (!IsServer || amount <= 0) return;
- ShowGoldLossClientRpc(worldPos, amount);
- }
-
- // Announces the end-of-phase tower wipe on every peer. Sent rather than derived locally
- // because the despawns arrive as individual NetworkObject removals with nothing tying them
- // together — without this a client just watches its maze evaporate for no stated reason.
- [ClientRpc]
- private void NotifyTowersWipedClientRpc(int destroyedCount)
- {
- OnTowersWiped?.Invoke(destroyedCount);
- }
-
// ----- Local-only notification events -----------------------------
- ///
- /// Fired on every peer when a phase ends and every built tower is destroyed. Argument is
- /// how many towers went down. HUD subscribes to explain what just happened; audio and
- /// camera-shake hooks can use it too.
- ///
- public static event System.Action OnTowersWiped;
-
///
/// Fired on every peer immediately after a life-loss popup spawns.
/// HUD subscribes to flash a centered banner; gameplay code can also
@@ -1115,24 +657,17 @@ namespace TD.Gameplay
// no-leak bonus only if the player's waveLeakCounts entry is exactly 0.
AwardWaveCompletionBonuses();
- // Capture which slot the upcoming vote will buff BEFORE advancing past it — the vote
- // is on "the wave you just beat", but by the time the inter-wave step runs the run has
- // already moved on. A boss clear is excluded: its phase's slots are about to be wiped,
- // so voting a buff onto them would be voting into a bin.
- var run = RunState.Instance;
- pendingVoteSlot = (run != null && !run.IsBossStage) ? run.CurrentUpgradeSlot : -1;
-
- Debug.Log($"[WaveManager] {run?.ProgressLabel ?? "Encounter"} complete.");
- AdvanceRun();
+ Debug.Log($"[WaveManager] Wave {currentWaveIndex + 1} complete. Starting next wave.");
+ StartNextWave();
}
// Server-only. Iterates active players, awards CompletionBonus to each, plus
// NoLeaksBonus to those whose per-wave leak counter is zero. Floating-text popups
// are spawned at each player's builder position so the reward is visible in-world.
- // Skipped silently if no goldConfig or no entry for this encounter.
+ // Skipped silently if no goldConfig or no entry for this wave.
private void AwardWaveCompletionBonuses()
{
- var entry = goldConfig?.GetEncounterEntry(CurrentEncounterNumber);
+ var entry = goldConfig?.GetWaveEntry(currentWaveIndex + 1);
if (entry == null) return;
int completionBonus = entry.CompletionBonus;
diff --git a/Assets/_Project/Scripts/Gameplay/Waves.meta b/Assets/_Project/Scripts/Gameplay/Waves.meta
deleted file mode 100644
index 67bd139..0000000
--- a/Assets/_Project/Scripts/Gameplay/Waves.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: f65ba25aee224c24dad3648d9e5e953e
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Project/Scripts/Gameplay/Waves/PhaseDefinition.cs b/Assets/_Project/Scripts/Gameplay/Waves/PhaseDefinition.cs
deleted file mode 100644
index 2700d59..0000000
--- a/Assets/_Project/Scripts/Gameplay/Waves/PhaseDefinition.cs
+++ /dev/null
@@ -1,102 +0,0 @@
-// Assets/_Project/Scripts/Gameplay/Waves/PhaseDefinition.cs
-using System.Collections.Generic;
-using UnityEngine;
-
-namespace TD.Gameplay.Waves
-{
- ///
- /// One phase of a run: the pool its cycle waves are drawn from, and the pool its boss is
- /// drawn from. A phase runs the same drawn wave set for every one of its cycles, then ends
- /// in a boss.
- ///
- ///
- /// Groups, not waves. Both pools are lists of assets rather
- /// than flat wave lists, so re-balancing a run means dragging a group between phases instead
- /// of re-authoring individual entries. Listing the same group in two phases is legal — the
- /// draws are independent.
- ///
- /// Draw contract. RunState draws WavesPerCycle waves from
- /// with distinct enemy types, because enemy upgrades are keyed
- /// to a wave slot and two slots sharing an enemy type would make "which wave did this buff
- /// apply to" ambiguous. That makes the real authoring requirement stricter than the entry
- /// count: a phase needs at least WavesPerCycle distinct enemy types across its groups,
- /// not just that many waves. exists so the draw can
- /// fail loudly at match start rather than mid-run.
- ///
- [CreateAssetMenu(fileName = "PhaseDefinition", menuName = "TD/Run/Phase Definition", order = 11)]
- public class PhaseDefinition : ScriptableObject
- {
- [Tooltip("Designer-facing name for this phase, shown in validation messages. " +
- "Falls back to the asset name when empty.")]
- public string DisplayName;
-
- [Tooltip("Groups this phase's cycle waves are drawn from. All groups are pooled " +
- "together for the draw; group membership is an authoring convenience, not a " +
- "draw boundary.")]
- public WaveGroup[] WaveGroups;
-
- [Tooltip("Groups this phase's boss is drawn from. A boss is an ordinary WaveDefinition " +
- "that happens to spawn a single powerful enemy.")]
- public WaveGroup[] BossGroups;
-
- [Tooltip("Enemy-buff cards votable during this phase. Gating is per PHASE, not per cycle " +
- "— every card listed here can be voted on from this phase's very first wave. " +
- "Cards must also appear in the scene's EnemyUpgradePool to be offerable.")]
- public EnemyUpgrades.EnemyUpgradeGroup[] EnemyUpgradeGroups;
-
- /// Name used in validation and log messages. Falls back to the asset name.
- public string Label => string.IsNullOrWhiteSpace(DisplayName) ? name : DisplayName;
-
- ///
- /// Appends every non-null, non-zero-weight entry across into
- /// . Zero-weight entries are filtered here (not at draw time) so
- /// "weight 0 means it can never appear" is enforced in exactly one place.
- ///
- public static void CollectCandidates(WaveGroup[] groups, List into)
- {
- into.Clear();
- if (groups == null) return;
-
- for (int g = 0; g < groups.Length; g++)
- {
- var group = groups[g];
- if (group?.Waves == null) continue;
-
- for (int i = 0; i < group.Waves.Length; i++)
- {
- var entry = group.Waves[i];
- if (entry?.Wave == null) continue;
- if (entry.Weight <= 0f) continue;
- into.Add(entry);
- }
- }
- }
-
- /// Collects this phase's cycle-wave candidates.
- public void CollectWaveCandidates(List into)
- => CollectCandidates(WaveGroups, into);
-
- /// Collects this phase's boss candidates.
- public void CollectBossCandidates(List into)
- => CollectCandidates(BossGroups, into);
-
- ///
- /// How many distinct enemy types are reachable across this phase's wave groups. The
- /// upper bound on how many wave slots this phase can fill, since the draw requires
- /// distinct types.
- ///
- public int CountDistinctEnemyTypes()
- {
- var candidates = new List();
- CollectWaveCandidates(candidates);
-
- var seen = new HashSet();
- foreach (var entry in candidates)
- {
- var type = entry.Wave.PrimaryEnemyType;
- if (type != null) seen.Add(type);
- }
- return seen.Count;
- }
- }
-}
diff --git a/Assets/_Project/Scripts/Gameplay/Waves/PhaseDefinition.cs.meta b/Assets/_Project/Scripts/Gameplay/Waves/PhaseDefinition.cs.meta
deleted file mode 100644
index c397855..0000000
--- a/Assets/_Project/Scripts/Gameplay/Waves/PhaseDefinition.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 8f73fcf0261654d44a6d0939bdc9b88f
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Gameplay/Waves/RunDefinition.cs b/Assets/_Project/Scripts/Gameplay/Waves/RunDefinition.cs
deleted file mode 100644
index 6b0feb3..0000000
--- a/Assets/_Project/Scripts/Gameplay/Waves/RunDefinition.cs
+++ /dev/null
@@ -1,209 +0,0 @@
-// Assets/_Project/Scripts/Gameplay/Waves/RunDefinition.cs
-using System.Collections.Generic;
-using System.Text;
-using UnityEngine;
-
-namespace TD.Gameplay.Waves
-{
- ///
- /// The whole run: how many waves make a cycle, how many cycles make a phase, and the
- /// per-phase pools everything is drawn from. One asset assigned to RunState in the
- /// match scene; replaces WaveManager's old flat, hand-ordered wave array.
- ///
- ///
- /// Stable wave ids. Waves are replicated by index into a flat list this asset builds
- /// by walking its phases, groups, and entries in declaration order (see
- /// ). Because every peer loads the identical asset, the same walk
- /// produces the same ids everywhere — the same trick the tower catalog and
- /// DraftPool already use, and the reason the server can say "wave slot 2 is wave #7"
- /// in a single int.
- ///
- /// The index is frozen for the match. RunState builds it once on spawn
- /// and never rebuilds, so editing this asset mid-play-session cannot renumber ids out from
- /// under in-flight replication. Edit-time changes invalidate the cache normally.
- ///
- [CreateAssetMenu(fileName = "RunDefinition", menuName = "TD/Run/Run Definition", order = 12)]
- public class RunDefinition : ScriptableObject
- {
- [Header("Structure")]
- [Tooltip("Waves in one cycle. The phase draws this many distinct-enemy-type waves and " +
- "replays them every cycle.")]
- [Min(1)]
- public int WavesPerCycle = 5;
-
- [Tooltip("Cycles before the phase's boss appears. The same drawn waves repeat each " +
- "cycle, carrying whatever upgrades players voted onto them.")]
- [Min(1)]
- public int CyclesPerPhase = 3;
-
- [Tooltip("Phases in order. Beating the last phase's boss wins the run. The MVP ships " +
- "one phase; the full design is three.")]
- public PhaseDefinition[] Phases;
-
- // ----- Flat wave index (network identity) --------------------------
-
- // Built lazily and cached. Null means "not built yet". Rebuilt only on explicit request
- // or on edit-time validation — never spontaneously during play, since ids in flight
- // would be invalidated.
- private List waveIndex;
- private Dictionary waveIds;
-
- /// Number of distinct waves reachable anywhere in this run.
- public int WaveCount
- {
- get { EnsureIndex(); return waveIndex.Count; }
- }
-
- /// Resolves a replicated wave id back to its asset, or null if out of range.
- public WaveDefinition GetWave(int waveId)
- {
- EnsureIndex();
- return (waveId >= 0 && waveId < waveIndex.Count) ? waveIndex[waveId] : null;
- }
-
- /// Resolves a wave asset to its replicated id.
- public bool TryGetWaveId(WaveDefinition wave, out int waveId)
- {
- EnsureIndex();
- if (wave != null && waveIds.TryGetValue(wave, out waveId)) return true;
- waveId = -1;
- return false;
- }
-
- ///
- /// Rebuilds the flat wave index from the current asset contents. Called once by
- /// RunState on every peer at match start so ids agree, and by the editor when
- /// the asset changes.
- ///
- public void RebuildIndex()
- {
- waveIndex ??= new List();
- waveIds ??= new Dictionary();
- waveIndex.Clear();
- waveIds.Clear();
-
- if (Phases == null) return;
-
- // Declaration-order walk. Every peer runs this over the same asset, so the
- // resulting ids match without any negotiation. Zero-weight entries are indexed
- // too: they can't be drawn, but indexing them keeps ids stable if a designer
- // re-weights between sessions.
- foreach (var phase in Phases)
- {
- if (phase == null) continue;
- IndexGroups(phase.WaveGroups);
- IndexGroups(phase.BossGroups);
- }
- }
-
- private void IndexGroups(WaveGroup[] groups)
- {
- if (groups == null) return;
- foreach (var group in groups)
- {
- if (group?.Waves == null) continue;
- foreach (var entry in group.Waves)
- {
- if (entry?.Wave == null) continue;
- if (waveIds.ContainsKey(entry.Wave)) continue; // first occurrence wins
- waveIds[entry.Wave] = waveIndex.Count;
- waveIndex.Add(entry.Wave);
- }
- }
- }
-
- private void EnsureIndex()
- {
- if (waveIndex == null || waveIds == null) RebuildIndex();
- }
-
- // ----- Validation --------------------------------------------------
-
- ///
- /// Checks that this run can actually be played: at least one phase, every phase able to
- /// fill slots with distinct enemy types, and every phase able
- /// to produce a boss. Returns true when the run is playable;
- /// carries the problems either way (it may hold warnings even
- /// on success).
- ///
- ///
- /// The distinct-enemy-type requirement is the one that bites in practice: a phase can
- /// hold twenty waves and still be unplayable if they all point at the same three enemies.
- /// Checking it up front turns that into a startup error instead of a draw that silently
- /// runs short a wave.
- ///
- public bool Validate(out string report)
- {
- var sb = new StringBuilder();
- bool ok = true;
-
- if (Phases == null || Phases.Length == 0)
- {
- sb.AppendLine("• No phases assigned — the run has nothing to play.");
- report = sb.ToString();
- return false;
- }
-
- var candidates = new List();
-
- for (int i = 0; i < Phases.Length; i++)
- {
- var phase = Phases[i];
- string label = phase != null ? phase.Label : $"Phase {i + 1}";
-
- if (phase == null)
- {
- sb.AppendLine($"• {label}: slot is empty.");
- ok = false;
- continue;
- }
-
- int distinctTypes = phase.CountDistinctEnemyTypes();
- if (distinctTypes < WavesPerCycle)
- {
- sb.AppendLine($"• {label}: needs {WavesPerCycle} distinct enemy types to fill " +
- $"a cycle but only {distinctTypes} are reachable. Add waves " +
- $"using other enemies, or lower Waves Per Cycle.");
- ok = false;
- }
-
- phase.CollectBossCandidates(candidates);
- if (candidates.Count == 0)
- {
- sb.AppendLine($"• {label}: no boss candidates (every boss group is empty, " +
- $"unassigned, or entirely zero-weight).");
- ok = false;
- }
-
- // Warnings — these don't block a run, they just play oddly.
- phase.CollectWaveCandidates(candidates);
- foreach (var entry in candidates)
- {
- if (entry.Wave.PrimaryEnemyType == null)
- sb.AppendLine($"• (warning) {label}: '{entry.Wave.name}' has no usable " +
- $"enemy entries and can never be drawn meaningfully.");
- else if (!entry.Wave.HasSingleEnemyType)
- sb.AppendLine($"• (warning) {label}: '{entry.Wave.name}' mixes enemy " +
- $"types. Upgrades and HUD labels will use " +
- $"'{entry.Wave.PrimaryEnemyType.name}' only.");
- }
- }
-
- report = sb.Length > 0 ? sb.ToString() : "Run structure OK.";
- return ok;
- }
-
-#if UNITY_EDITOR
- private void OnValidate()
- {
- // Only invalidate outside play mode. Clearing the cache mid-match would renumber
- // wave ids while replicated slot references are still pointing at the old numbering.
- if (!Application.isPlaying)
- {
- waveIndex = null;
- waveIds = null;
- }
- }
-#endif
- }
-}
diff --git a/Assets/_Project/Scripts/Gameplay/Waves/RunDefinition.cs.meta b/Assets/_Project/Scripts/Gameplay/Waves/RunDefinition.cs.meta
deleted file mode 100644
index 6c2d0d3..0000000
--- a/Assets/_Project/Scripts/Gameplay/Waves/RunDefinition.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 2b73bb9876b7fdb4480b13ff6bb8bd67
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Gameplay/Waves/RunState.cs b/Assets/_Project/Scripts/Gameplay/Waves/RunState.cs
deleted file mode 100644
index b31b585..0000000
--- a/Assets/_Project/Scripts/Gameplay/Waves/RunState.cs
+++ /dev/null
@@ -1,468 +0,0 @@
-// Assets/_Project/Scripts/Gameplay/Waves/RunState.cs
-using System.Collections.Generic;
-using Unity.Netcode;
-using UnityEngine;
-
-namespace TD.Gameplay.Waves
-{
- ///
- /// Outcome of advancing the run by one encounter. Returned by
- /// so WaveManager can react without
- /// re-deriving the progression maths.
- ///
- public enum RunAdvance : byte
- {
- /// Moved to the next wave inside the current cycle.
- NextWave = 0,
- /// Moved to the first wave of a new cycle — the same waves come round again,
- /// now carrying whatever upgrades players voted onto them.
- NextCycle = 1,
- /// Every cycle in the phase is done; the boss is up next.
- BossReady = 2,
- /// The boss died and a new phase was drawn. Upgrades were wiped.
- NextPhase = 3,
- /// The final phase's boss died — the run is won.
- RunComplete = 4,
- }
-
- ///
- /// Server-authoritative owner of run progression: which phase and cycle we're in, which waves
- /// this phase drew, and which upgrades the players have voted onto each wave slot.
- ///
- ///
- /// Split of duties with WaveManager. This type is the run's state —
- /// counters, draws, upgrade sets — and knows nothing about spawning. WaveManager is the
- /// driver: it runs an encounter, and when the field is clear it calls
- /// and acts on the returned . Keeping them
- /// apart is what stops WaveManager (already a large class) from also owning pool draws and
- /// upgrade bookkeeping.
- ///
- /// Linear encounter index. Rather than replicate cycle and wave separately and
- /// keep them in sync, one counter runs
- /// 0 .. CyclesPerPhase*WavesPerCycle inclusive, with the final value meaning "boss".
- /// Cycle and wave slot are derived from it, so they cannot disagree.
- ///
- /// Upgrades are keyed by wave slot, not enemy type. The phase draw guarantees the
- /// slots hold distinct enemy types, which makes slot and type interchangeable as a key — and
- /// slot is the one that survives a phase change cleanly, since the next phase reassigns every
- /// slot to a fresh enemy anyway.
- ///
- public class RunState : NetworkBehaviour
- {
- // ----- Singleton --------------------------------------------------
-
- public static RunState Instance { get; private set; }
-
- // ----- Inspector --------------------------------------------------
-
- [Tooltip("The run this match plays: phase pools, waves per cycle, cycles per phase. " +
- "Required — without it no waves can be drawn.")]
- [SerializeField] private RunDefinition runDefinition;
-
- // ----- Networked state --------------------------------------------
-
- private readonly NetworkVariable phaseIndex = new NetworkVariable(
- 0, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Server);
-
- // 0 .. (CyclesPerPhase * WavesPerCycle), where the top value means "boss".
- private readonly NetworkVariable encounterInPhase = new NetworkVariable(
- 0, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Server);
-
- // Wave ids (indices into RunDefinition's flat index) drawn for this phase's slots.
- // Exactly WavesPerCycle entries once a phase has been drawn.
- private NetworkList drawnWaveIds;
-
- private readonly NetworkVariable bossWaveId = new NetworkVariable(
- -1, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Server);
-
- // Upgrades voted onto wave slots, packed as (slot << SlotShift) | optionId. A flat
- // append-only list rather than a per-slot structure because NGO has no nested-collection
- // support and the counts here are tiny (a handful per slot, per phase).
- private NetworkList waveUpgrades;
-
- private const int SlotShift = 16;
- private const int OptionMask = (1 << SlotShift) - 1;
-
- // Per-slot count of buffs to apply automatically, skipping the next vote for that slot.
- // Indexed by upgrade slot (0..WavesPerCycle, the last being the boss). Zero means a normal
- // vote. Written by the vote-meta cards, consumed by the inter-wave step.
- private NetworkList autoApplyCounts;
-
- ///
- /// Fired on every peer whenever run progression or the drawn/upgraded wave set changes.
- /// The HUD subscribes to relabel without polling.
- ///
- public event System.Action OnRunChanged;
-
- // ----- Lifecycle ---------------------------------------------------
-
- private void Awake()
- {
- drawnWaveIds = new NetworkList();
- waveUpgrades = new NetworkList();
- autoApplyCounts = new NetworkList();
- }
-
- public override void OnNetworkSpawn()
- {
- if (Instance != null && Instance != this)
- {
- Debug.LogError("[RunState] Duplicate RunState detected. Only one may exist per scene.");
- return;
- }
- Instance = this;
-
- // Freeze the wave index on EVERY peer (not just the server) before any id crosses
- // the wire. Same asset, same declaration-order walk, same numbering.
- runDefinition?.RebuildIndex();
-
- // One auto-apply counter per upgrade slot, plus one for the boss slot.
- if (IsServer)
- {
- for (int i = 0; i <= WavesPerCycle; i++) autoApplyCounts.Add(0);
- }
-
- phaseIndex.OnValueChanged += HandleIntChanged;
- encounterInPhase.OnValueChanged += HandleIntChanged;
- bossWaveId.OnValueChanged += HandleIntChanged;
- drawnWaveIds.OnListChanged += HandleListChanged;
- waveUpgrades.OnListChanged += HandleListChanged;
- }
-
- public override void OnNetworkDespawn()
- {
- phaseIndex.OnValueChanged -= HandleIntChanged;
- encounterInPhase.OnValueChanged -= HandleIntChanged;
- bossWaveId.OnValueChanged -= HandleIntChanged;
- drawnWaveIds.OnListChanged -= HandleListChanged;
- waveUpgrades.OnListChanged -= HandleListChanged;
-
- if (Instance == this) Instance = null;
- }
-
- private void HandleIntChanged(int _, int __) => OnRunChanged?.Invoke();
- private void HandleListChanged(NetworkListEvent _) => OnRunChanged?.Invoke();
-
- // ----- Structure accessors -----------------------------------------
-
- /// The run asset driving this match, or null if unassigned (designer error).
- public RunDefinition Definition => runDefinition;
-
- public int WavesPerCycle => runDefinition != null ? Mathf.Max(1, runDefinition.WavesPerCycle) : 5;
- public int CyclesPerPhase => runDefinition != null ? Mathf.Max(1, runDefinition.CyclesPerPhase) : 3;
- public int PhaseCount => runDefinition?.Phases?.Length ?? 0;
-
- /// Encounters in one phase, counting its boss.
- public int EncountersPerPhase => CyclesPerPhase * WavesPerCycle + 1;
-
- // ----- Progression accessors ---------------------------------------
-
- /// Zero-based phase index.
- public int PhaseIndex => phaseIndex.Value;
-
- ///
- /// The phase currently being played, or null if the run isn't set up. Read by the vote
- /// generator to find which enemy-buff cards this phase allows.
- ///
- public PhaseDefinition CurrentPhase
- {
- get
- {
- var phases = runDefinition?.Phases;
- if (phases == null) return null;
- int i = phaseIndex.Value;
- return (i >= 0 && i < phases.Length) ? phases[i] : null;
- }
- }
-
- /// Zero-based cycle index within the current phase. Reads as the last cycle
- /// while the boss is up (the boss belongs to the phase, not to a fourth cycle).
- public int CycleIndex => Mathf.Min(encounterInPhase.Value / WavesPerCycle, CyclesPerPhase - 1);
-
- /// Zero-based wave slot within the current cycle. Meaningless while
- /// is true.
- public int WaveSlot => encounterInPhase.Value % WavesPerCycle;
-
- /// True when the next/current encounter is this phase's boss.
- public bool IsBossStage => encounterInPhase.Value >= CyclesPerPhase * WavesPerCycle;
-
- ///
- /// 1-based count of encounters since the run began, including bosses. This is the key
- /// GoldConfig.Encounters is indexed by, so payouts scale monotonically across the
- /// whole run rather than resetting every cycle.
- ///
- public int GlobalEncounterNumber
- => phaseIndex.Value * EncountersPerPhase + encounterInPhase.Value + 1;
-
- /// Human-readable progress line for the HUD, e.g. "Phase 1 · Cycle 2 · Wave 3/5".
- public string ProgressLabel
- => IsBossStage
- ? $"Phase {phaseIndex.Value + 1} · BOSS"
- : $"Phase {phaseIndex.Value + 1} · Cycle {CycleIndex + 1} · " +
- $"Wave {WaveSlot + 1}/{WavesPerCycle}";
-
- // ----- Wave accessors ----------------------------------------------
-
- /// The wave asset drawn for , or null if not drawn yet.
- public WaveDefinition GetSlotWave(int slot)
- {
- if (runDefinition == null) return null;
- if (slot < 0 || slot >= drawnWaveIds.Count) return null;
- return runDefinition.GetWave(drawnWaveIds[slot]);
- }
-
- /// This phase's boss wave, or null if not drawn yet.
- public WaveDefinition BossWave
- => runDefinition != null ? runDefinition.GetWave(bossWaveId.Value) : null;
-
- /// The wave the run is currently pointing at — boss or cycle wave.
- public WaveDefinition CurrentWave => IsBossStage ? BossWave : GetSlotWave(WaveSlot);
-
- ///
- /// The slot key upgrades attach to for the current encounter. The boss occupies the slot
- /// one past the last cycle wave, so boss upgrades (if ever voted) don't collide with it.
- ///
- public int CurrentUpgradeSlot => IsBossStage ? WavesPerCycle : WaveSlot;
-
- // ----- Upgrade accessors -------------------------------------------
-
- ///
- /// Collects the upgrade option ids voted onto , in the order they
- /// were applied. Safe on any peer.
- ///
- public void GetUpgradesForSlot(int slot, List into)
- {
- into.Clear();
- for (int i = 0; i < waveUpgrades.Count; i++)
- {
- int packed = waveUpgrades[i];
- if ((packed >> SlotShift) == slot) into.Add(packed & OptionMask);
- }
- }
-
- /// How many upgrades are stacked on .
- public int CountUpgradesForSlot(int slot)
- {
- int n = 0;
- for (int i = 0; i < waveUpgrades.Count; i++)
- if ((waveUpgrades[i] >> SlotShift) == slot) n++;
- return n;
- }
-
- /// True if already carries .
- /// Used to keep the vote from offering a buff a wave already has.
- public bool SlotHasUpgrade(int slot, int optionId)
- {
- int packed = (slot << SlotShift) | (optionId & OptionMask);
- for (int i = 0; i < waveUpgrades.Count; i++)
- if (waveUpgrades[i] == packed) return true;
- return false;
- }
-
- /// Server-only: stack an upgrade onto a wave slot. Ignores exact duplicates.
- public void ServerAddUpgrade(int slot, int optionId)
- {
- if (!IsServer) return;
- if (slot < 0 || optionId < 0) return;
- if (SlotHasUpgrade(slot, optionId)) return;
-
- waveUpgrades.Add((slot << SlotShift) | (optionId & OptionMask));
- }
-
- // ----- Auto-apply (vote-meta cards) --------------------------------
-
- ///
- /// How many buffs will take automatically instead of holding its
- /// next vote. Zero means a normal vote.
- ///
- public int GetAutoApplyCount(int slot)
- => (slot >= 0 && slot < autoApplyCounts.Count) ? autoApplyCounts[slot] : 0;
-
- ///
- /// Server-only: arm to auto-take buffs in
- /// place of its next vote. Takes the larger value if already armed, so two vote-meta cards
- /// on one slot don't cancel each other down.
- ///
- public void ServerSetAutoApply(int slot, int count)
- {
- if (!IsServer) return;
- if (slot < 0 || slot >= autoApplyCounts.Count || count <= 0) return;
- autoApplyCounts[slot] = Mathf.Max(autoApplyCounts[slot], count);
- }
-
- /// Server-only: disarm after its auto-apply has fired.
- public void ServerConsumeAutoApply(int slot)
- {
- if (!IsServer) return;
- if (slot < 0 || slot >= autoApplyCounts.Count) return;
- autoApplyCounts[slot] = 0;
- }
-
- private void ServerClearAutoApply()
- {
- for (int i = 0; i < autoApplyCounts.Count; i++) autoApplyCounts[i] = 0;
- }
-
- // ----- Server: run control ------------------------------------------
-
- ///
- /// Server-only: start the run at phase 0 and draw its waves. Returns false if the run
- /// definition is missing or unplayable — the caller should treat that as fatal rather
- /// than limping along with an empty schedule.
- ///
- public bool ServerBeginRun()
- {
- if (!IsServer) return false;
-
- if (runDefinition == null)
- {
- Debug.LogError("[RunState] No RunDefinition assigned — cannot start a run.");
- return false;
- }
-
- if (!runDefinition.Validate(out string report))
- {
- Debug.LogError($"[RunState] RunDefinition '{runDefinition.name}' is not playable:\n{report}");
- return false;
- }
-
- phaseIndex.Value = 0;
- encounterInPhase.Value = 0;
- waveUpgrades.Clear();
- ServerClearAutoApply();
-
- return ServerDrawPhase(0);
- }
-
- ///
- /// Server-only: advance one encounter, drawing a new phase (and wiping upgrades) when the
- /// boss falls. See for what the caller should do with each result.
- ///
- public RunAdvance ServerAdvance()
- {
- if (!IsServer) return RunAdvance.NextWave;
-
- // Boss just died: the phase is over. Either draw the next one or win the run.
- if (IsBossStage)
- {
- int next = phaseIndex.Value + 1;
- if (next >= PhaseCount) return RunAdvance.RunComplete;
-
- phaseIndex.Value = next;
- encounterInPhase.Value = 0;
-
- // A new phase means five brand-new waves, so every upgrade voted onto the old
- // slots is meaningless — wiped by design, not by omission. Armed auto-applies go
- // with them: they were promises made about waves that no longer exist.
- waveUpgrades.Clear();
- ServerClearAutoApply();
- ServerDrawPhase(next);
- return RunAdvance.NextPhase;
- }
-
- int previousCycle = CycleIndex;
- encounterInPhase.Value++;
-
- if (IsBossStage) return RunAdvance.BossReady;
- if (CycleIndex != previousCycle) return RunAdvance.NextCycle;
- return RunAdvance.NextWave;
- }
-
- // ----- Server: phase draw -------------------------------------------
-
- // Scratch list reused across draws; server is single-threaded so sharing is safe.
- private readonly List candidateScratch = new List();
-
- ///
- /// Server-only: draw this phase's cycle waves and boss. Cycle waves are drawn by relative
- /// weight without replacement and with distinct enemy types; the boss is a plain
- /// weighted draw.
- ///
- private bool ServerDrawPhase(int index)
- {
- if (!IsServer) return false;
-
- var phases = runDefinition?.Phases;
- if (phases == null || index < 0 || index >= phases.Length || phases[index] == null)
- {
- Debug.LogError($"[RunState] Phase {index + 1} is missing — cannot draw waves.");
- return false;
- }
-
- var phase = phases[index];
- int needed = WavesPerCycle;
-
- phase.CollectWaveCandidates(candidateScratch);
- drawnWaveIds.Clear();
-
- for (int drawn = 0; drawn < needed; drawn++)
- {
- var pick = DrawWeighted(candidateScratch);
- if (pick == null)
- {
- Debug.LogError(
- $"[RunState] {phase.Label}: ran out of candidates after {drawn} of " +
- $"{needed} wave(s). The phase needs {needed} waves with DISTINCT enemy " +
- $"types — check the RunDefinition inspector's phase-capacity readout.");
- return false;
- }
-
- if (!runDefinition.TryGetWaveId(pick.Wave, out int waveId))
- {
- Debug.LogError($"[RunState] Wave '{pick.Wave.name}' is not in the run's wave " +
- $"index. This should be impossible — the index is built from " +
- $"the same pools. Skipping.");
- candidateScratch.Remove(pick);
- drawn--;
- continue;
- }
-
- drawnWaveIds.Add(waveId);
-
- // Retire every remaining candidate sharing this enemy type, so distinctness is
- // enforced by shrinking the pool rather than by reject-and-retry.
- var type = pick.Wave.PrimaryEnemyType;
- candidateScratch.RemoveAll(
- e => e.Wave == pick.Wave || (type != null && e.Wave.PrimaryEnemyType == type));
- }
-
- // Boss draw — independent pool, no distinctness constraint.
- phase.CollectBossCandidates(candidateScratch);
- var boss = DrawWeighted(candidateScratch);
- if (boss == null || !runDefinition.TryGetWaveId(boss.Wave, out int bossId))
- {
- Debug.LogError($"[RunState] {phase.Label}: no boss could be drawn.");
- bossWaveId.Value = -1;
- return false;
- }
- bossWaveId.Value = bossId;
-
- Debug.Log($"[RunState] Drew {phase.Label}: {needed} wave(s) + boss " +
- $"'{boss.Wave.name}'.");
- return true;
- }
-
- // Relative-weight draw. Weights are normalized against the candidate set's total, so a
- // pool of three 1.0 entries is uniform and a 0.5 entry is half as likely as a 1.0 one.
- // Zero-weight entries never reach here — PhaseDefinition filters them during collection.
- private static WavePoolEntry DrawWeighted(List candidates)
- {
- if (candidates == null || candidates.Count == 0) return null;
-
- float total = 0f;
- for (int i = 0; i < candidates.Count; i++)
- total += Mathf.Max(0f, candidates[i].Weight);
-
- if (total <= 0f) return null;
-
- float roll = Random.value * total;
- for (int i = 0; i < candidates.Count; i++)
- {
- roll -= Mathf.Max(0f, candidates[i].Weight);
- if (roll <= 0f) return candidates[i];
- }
-
- return candidates[candidates.Count - 1]; // float drift fallback
- }
- }
-}
diff --git a/Assets/_Project/Scripts/Gameplay/Waves/RunState.cs.meta b/Assets/_Project/Scripts/Gameplay/Waves/RunState.cs.meta
deleted file mode 100644
index 4905373..0000000
--- a/Assets/_Project/Scripts/Gameplay/Waves/RunState.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: a293cf6dca91dd2478c9f4e799cf9e0d
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Gameplay/Waves/WaveGroup.cs b/Assets/_Project/Scripts/Gameplay/Waves/WaveGroup.cs
deleted file mode 100644
index 9d1c129..0000000
--- a/Assets/_Project/Scripts/Gameplay/Waves/WaveGroup.cs
+++ /dev/null
@@ -1,69 +0,0 @@
-// Assets/_Project/Scripts/Gameplay/Waves/WaveGroup.cs
-using System;
-using UnityEngine;
-using TD.Core;
-
-namespace TD.Gameplay.Waves
-{
- ///
- /// One weighted candidate inside a : a wave that may be drawn,
- /// and how likely it is to be drawn relative to the group's other entries.
- ///
- ///
- /// Weight is RELATIVE, not an absolute probability. The draw normalizes every
- /// candidate's weight against the summed weight of the whole candidate set, so three
- /// entries at 1.0 each are equally likely (33% apiece) and an entry at 0.5 is half as
- /// likely to be drawn as one at 1.0. This is what makes the default sane: new entries
- /// start at 1.0, so adding a wave to a group never silently re-weights the others.
- ///
- /// A class rather than a struct specifically so can carry a
- /// field initializer — Unity runs it when the inspector creates a fresh array element,
- /// which is what gives new entries their equal-by-default weighting. Mirrors
- /// .
- ///
- [Serializable]
- public class WavePoolEntry
- {
- [Tooltip("The wave that may be drawn from this group.")]
- public WaveDefinition Wave;
-
- [Tooltip("Relative draw weight within this group's pool. Entries at equal weight are " +
- "equally likely; an entry at 0.5 is half as likely as one at 1.0. NOT an " +
- "absolute percentage.")]
- [PoolWeight("wave")]
- public float Weight = 1f;
- }
-
- ///
- /// A named, reusable bag of candidate waves. Groups are the unit designers move between
- /// phases: dragging a group asset from Phase 1's pool to Phase 2's moves every wave in it
- /// (and their weights) in one action.
- ///
- ///
- /// Why weights live on the entry, not on . Weight is a
- /// property of "how common is this wave in this pool", not of the wave itself. Keeping
- /// it here means the same asset can be a staple of one group and
- /// a rarity in another, and a group carries its tuning with it when moved between phases.
- ///
- /// Boss groups use the same type. A boss wave is just a
- /// whose entries spawn a single powerful enemy, so boss pools
- /// are ordinary s referenced from
- /// .
- ///
- [CreateAssetMenu(fileName = "WaveGroup", menuName = "TD/Run/Wave Group", order = 10)]
- public class WaveGroup : ScriptableObject
- {
- [Tooltip("Designer-facing name for this group, shown in run-structure validation " +
- "messages. Falls back to the asset name when empty.")]
- public string DisplayName;
-
- [Tooltip("Candidate waves in this group, each with a relative draw weight.")]
- public WavePoolEntry[] Waves;
-
- /// Name used in validation and log messages. Falls back to the asset name.
- public string Label => string.IsNullOrWhiteSpace(DisplayName) ? name : DisplayName;
-
- /// Number of entries, including any that are null or zero-weight.
- public int Count => Waves?.Length ?? 0;
- }
-}
diff --git a/Assets/_Project/Scripts/Gameplay/Waves/WaveGroup.cs.meta b/Assets/_Project/Scripts/Gameplay/Waves/WaveGroup.cs.meta
deleted file mode 100644
index 817269e..0000000
--- a/Assets/_Project/Scripts/Gameplay/Waves/WaveGroup.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 5625bd25f378bda429666820a4940cee
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/Towers/TowerDefinition.cs b/Assets/_Project/Scripts/Towers/TowerDefinition.cs
index 8163490..3ce1632 100644
--- a/Assets/_Project/Scripts/Towers/TowerDefinition.cs
+++ b/Assets/_Project/Scripts/Towers/TowerDefinition.cs
@@ -47,22 +47,6 @@ namespace TD.Towers
"successful server-side placement validation.")]
public int GoldCost;
- // -------------------------------------------------------------------
- // Selling
- // -------------------------------------------------------------------
-
- [Header("Selling")]
- [Tooltip("Portion of the gold INVESTED in this tower (placement cost plus any gold " +
- "later spent upgrading it) that is refunded when it is sold, as a 0–1 factor. " +
- "Default 0.75 = 75%.")]
- [Range(0f, 1f)]
- public float SellRefundPercent = 0.75f;
-
- [Tooltip("When true, this tower refunds 100% of its invested gold while it has never " +
- "been upgraded (upgradeCount == 0); once upgraded it falls back to " +
- "SellRefundPercent. Set on the Wall so a fresh wall can be re-mazed for free.")]
- public bool FullRefundIfUnupgraded = false;
-
// -------------------------------------------------------------------
// Construction
// -------------------------------------------------------------------
diff --git a/Assets/_Project/Scripts/UI/FloatingTextSpawner.cs b/Assets/_Project/Scripts/UI/FloatingTextSpawner.cs
index 49b4e66..392b1a2 100644
--- a/Assets/_Project/Scripts/UI/FloatingTextSpawner.cs
+++ b/Assets/_Project/Scripts/UI/FloatingTextSpawner.cs
@@ -74,11 +74,6 @@ namespace TD.UI
public void SpawnGoldReward(Vector3 worldPos, int amount)
=> SpawnInternal(worldPos, $"+{amount}", goldColor);
- /// Spawns a gold-loss popup (e.g. "-15") at the given world position. Same colour
- /// as a reward so it reads as gold; the sign carries the meaning.
- public void SpawnGoldLoss(Vector3 worldPos, int amount)
- => SpawnInternal(worldPos, $"-{amount}", goldColor);
-
/// Spawns a life-loss popup (e.g. "-1") at the given world position.
public void SpawnLifeLoss(Vector3 worldPos, int amount)
=> SpawnInternal(worldPos, $"-{amount}", livesColor);
diff --git a/Assets/_Project/Scripts/UI/GameMenuView.cs b/Assets/_Project/Scripts/UI/GameMenuView.cs
deleted file mode 100644
index 7db20bd..0000000
--- a/Assets/_Project/Scripts/UI/GameMenuView.cs
+++ /dev/null
@@ -1,407 +0,0 @@
-// Assets/_Project/Scripts/UI/GameMenuView.cs
-using UnityEngine;
-using UnityEngine.UIElements;
-using TD.Audio;
-
-namespace TD.UI
-{
- ///
- /// The in-match menu: a gear button in the top-right of the HUD top bar plus a modal
- /// overlay with two options — Return to Title Screen and Audio Settings.
- ///
- ///
- /// Ownership. A plain class (not a MonoBehaviour), constructed by
- /// and given the HUD's root element — same pattern as
- /// MinimapView. Everything is built programmatically and appended last, so it
- /// z-orders above the rest of the HUD with no extra UIDocument or PanelSettings.
- ///
- /// Modality. The overlay is full-screen with
- /// , which both dims the match and makes
- /// true everywhere — so clicks
- /// can't reach towers or the ground underneath. HUDController also sets
- /// while it's open, which gates keyboard-driven
- /// gameplay (camera pan, hotkeys, Escape handlers). The match does not pause:
- /// this is a multiplayer game and waves keep running.
- ///
- /// Pages. One panel, three swapped bodies: the option list, the audio mixer,
- /// and a confirm step for leaving the match. Escape backs out one page at a time and
- /// closes from the root page.
- ///
- public class GameMenuView
- {
- private enum Page { Root, Audio, ConfirmLeave }
-
- // ----- Wiring -----------------------------------------------------
-
- private readonly System.Action onReturnToTitle;
-
- private readonly VisualElement overlay;
- private readonly Label header;
- private readonly VisualElement rootPage;
- private readonly VisualElement audioPage;
- private readonly VisualElement confirmPage;
-
- private readonly SliderInt musicSlider;
- private readonly Label musicValue;
- private readonly SliderInt sfxSlider;
- private readonly Label sfxValue;
-
- private Page page = Page.Root;
-
- // ----- Public API -------------------------------------------------
-
- public bool IsOpen { get; private set; }
-
- /// The HUD's rootVisualElement. The overlay is appended to it.
- /// The top-bar "menu-button" from HUD.uxml. May be null
- /// (Escape still works); the gear glyph is painted into it here.
- /// Optional sprite for the gear. When null, the icon is drawn
- /// procedurally so the button never depends on an art asset existing.
- /// Invoked once the player confirms leaving the match.
- public GameMenuView(VisualElement root, Button gearButton, Sprite gearIcon,
- System.Action onReturnToTitle)
- {
- this.onReturnToTitle = onReturnToTitle;
-
- if (gearButton != null)
- {
- gearButton.text = string.Empty;
- gearButton.Add(CreateGearIcon(gearIcon));
- gearButton.clicked += Toggle;
- gearButton.tooltip = "Menu (Esc)";
- }
-
- // ----- Overlay + panel shell ----------------------------------
-
- overlay = new VisualElement();
- overlay.style.position = Position.Absolute;
- overlay.style.left = 0;
- overlay.style.right = 0;
- overlay.style.top = 0;
- overlay.style.bottom = 0;
- overlay.style.alignItems = Align.Center;
- overlay.style.justifyContent = Justify.Center;
- overlay.style.backgroundColor = new Color(0f, 0f, 0f, 0.55f);
- overlay.style.display = DisplayStyle.None;
- overlay.pickingMode = PickingMode.Position;
-
- var panel = new VisualElement();
- panel.style.minWidth = 360;
- panel.style.paddingTop = panel.style.paddingBottom = 24;
- panel.style.paddingLeft = panel.style.paddingRight = 32;
- panel.style.backgroundColor = new Color(0.08f, 0.08f, 0.10f, 0.97f);
- panel.style.borderTopWidth = panel.style.borderBottomWidth =
- panel.style.borderLeftWidth = panel.style.borderRightWidth = 2;
- var border = new Color(0.4f, 0.4f, 0.45f);
- panel.style.borderTopColor = panel.style.borderBottomColor =
- panel.style.borderLeftColor = panel.style.borderRightColor = border;
- panel.style.alignItems = Align.Center;
- overlay.Add(panel);
-
- header = new Label("Menu");
- header.style.fontSize = 24;
- header.style.color = Color.white;
- header.style.marginBottom = 18;
- header.style.unityFontStyleAndWeight = FontStyle.Bold;
- panel.Add(header);
-
- // ----- Page 1: option list ------------------------------------
-
- rootPage = new VisualElement();
- rootPage.style.alignItems = Align.Center;
- rootPage.Add(MakeMenuButton("Return to Title Screen", () => GoTo(Page.ConfirmLeave)));
- rootPage.Add(MakeMenuButton("Audio Settings", () => GoTo(Page.Audio)));
- rootPage.Add(MakeMenuButton("Resume", Close));
- panel.Add(rootPage);
-
- // ----- Page 2: audio mixer ------------------------------------
-
- audioPage = new VisualElement();
- audioPage.style.alignItems = Align.Stretch;
- audioPage.style.display = DisplayStyle.None;
-
- audioPage.Add(BuildVolumeRow("Music", AudioVolumeSettings.MusicVolume,
- v => AudioVolumeSettings.MusicVolume = v,
- out musicSlider, out musicValue));
- audioPage.Add(BuildVolumeRow("Sound Effects", AudioVolumeSettings.SfxVolume,
- v => AudioVolumeSettings.SfxVolume = v,
- out sfxSlider, out sfxValue));
-
- var audioNote = new Label("Sound effects covers spells, towers, building, and enemies.");
- audioNote.style.fontSize = 11;
- audioNote.style.color = new Color(0.6f, 0.6f, 0.65f);
- audioNote.style.marginTop = 4;
- audioNote.style.marginBottom = 12;
- audioNote.style.whiteSpace = WhiteSpace.Normal;
- audioPage.Add(audioNote);
-
- var audioBack = MakeMenuButton("Back", () => GoTo(Page.Root));
- audioBack.style.alignSelf = Align.Center;
- audioPage.Add(audioBack);
- panel.Add(audioPage);
-
- // ----- Page 3: leave confirmation -----------------------------
-
- confirmPage = new VisualElement();
- confirmPage.style.alignItems = Align.Center;
- confirmPage.style.display = DisplayStyle.None;
-
- var confirmText = new Label("Leave the match and return to the title screen?");
- confirmText.style.color = new Color(0.88f, 0.88f, 0.9f);
- confirmText.style.whiteSpace = WhiteSpace.Normal;
- confirmText.style.marginBottom = 16;
- confirmText.style.unityTextAlign = TextAnchor.MiddleCenter;
- confirmPage.Add(confirmText);
-
- var confirmRow = new VisualElement();
- confirmRow.style.flexDirection = FlexDirection.Row;
- confirmPage.Add(confirmRow);
-
- var leaveBtn = MakeMenuButton("Leave Match", ConfirmLeave);
- leaveBtn.style.marginRight = 12;
- leaveBtn.style.minWidth = 150;
- confirmRow.Add(leaveBtn);
-
- var cancelBtn = MakeMenuButton("Cancel", () => GoTo(Page.Root));
- cancelBtn.style.minWidth = 150;
- confirmRow.Add(cancelBtn);
- panel.Add(confirmPage);
-
- root.Add(overlay);
- }
-
- public void Open()
- {
- if (IsOpen) return;
- IsOpen = true;
- GoTo(Page.Root);
- overlay.style.display = DisplayStyle.Flex;
- }
-
- public void Close()
- {
- if (!IsOpen) return;
- IsOpen = false;
- overlay.style.display = DisplayStyle.None;
- // Volume setters only touch the in-memory pref so a slider drag stays cheap;
- // write it out now that the player is done adjusting.
- AudioVolumeSettings.Flush();
- }
-
- public void Toggle()
- {
- if (IsOpen) Close();
- else Open();
- }
-
- ///
- /// Escape while the menu is open: back out one page, or close from the root page.
- ///
- public void Back()
- {
- if (page == Page.Root) Close();
- else GoTo(Page.Root);
- }
-
- // ----- Pages ------------------------------------------------------
-
- private void GoTo(Page next)
- {
- page = next;
-
- rootPage.style.display = next == Page.Root ? DisplayStyle.Flex : DisplayStyle.None;
- audioPage.style.display = next == Page.Audio ? DisplayStyle.Flex : DisplayStyle.None;
- confirmPage.style.display = next == Page.ConfirmLeave ? DisplayStyle.Flex : DisplayStyle.None;
-
- header.text = next switch
- {
- Page.Audio => "Audio Settings",
- Page.ConfirmLeave => "Leave Match",
- _ => "Menu",
- };
-
- // Another surface could have changed the volumes since this page was last
- // shown (a future title-screen mixer, a debug command). Re-sync on entry so
- // the sliders never display a stale value.
- if (next == Page.Audio) SyncSlidersFromSettings();
- }
-
- private void SyncSlidersFromSettings()
- {
- // SetValueWithoutNotify: writing the settings value back through the change
- // callback would be a no-op today (the setter early-outs on an unchanged value)
- // but it's the wrong direction of data flow, so don't.
- musicSlider.SetValueWithoutNotify(AudioVolumeSettings.MusicVolume);
- musicValue.text = AudioVolumeSettings.MusicVolume.ToString();
- sfxSlider.SetValueWithoutNotify(AudioVolumeSettings.SfxVolume);
- sfxValue.text = AudioVolumeSettings.SfxVolume.ToString();
- }
-
- private void ConfirmLeave()
- {
- AudioVolumeSettings.Flush();
- IsOpen = false;
- overlay.style.display = DisplayStyle.None;
- onReturnToTitle?.Invoke();
- }
-
- // ----- Element builders -------------------------------------------
-
- private static Button MakeMenuButton(string text, System.Action onClick)
- {
- var btn = new Button(() => onClick?.Invoke()) { text = text };
- btn.style.minWidth = 260;
- btn.style.height = 40;
- btn.style.fontSize = 16;
- btn.style.marginBottom = 8;
- return btn;
- }
-
- // One mixer row: name, 0..100 slider, live numeric readout.
- private static VisualElement BuildVolumeRow(string label, int initial,
- System.Action onChange,
- out SliderInt slider, out Label valueLabel)
- {
- var row = new VisualElement();
- row.style.flexDirection = FlexDirection.Row;
- // FlexStart, not Center: the labels are aligned to the slider's track by
- // AlignLabelsToTrack below, which needs every child's box to start at the same
- // top edge. See that method for why centering the boxes isn't enough.
- row.style.alignItems = Align.FlexStart;
- row.style.marginBottom = 10;
-
- var name = new Label(label);
- name.style.color = new Color(0.88f, 0.88f, 0.9f);
- name.style.fontSize = 14;
- name.style.width = 110;
- name.style.flexShrink = 0;
- name.style.unityTextAlign = TextAnchor.MiddleLeft;
- row.Add(name);
-
- slider = new SliderInt(AudioVolumeSettings.MinVolume, AudioVolumeSettings.MaxVolume);
- slider.style.width = 200;
- slider.style.flexShrink = 0;
- slider.SetValueWithoutNotify(initial);
- row.Add(slider);
-
- // Fixed width + right-align so the slider doesn't shift as the number goes
- // from 1 to 3 digits while dragging.
- var value = new Label(initial.ToString());
- value.style.color = Color.white;
- value.style.fontSize = 14;
- value.style.width = 40;
- value.style.flexShrink = 0;
- value.style.unityTextAlign = TextAnchor.MiddleRight;
- row.Add(value);
-
- AlignLabelsToTrack(row, slider, name, value);
-
- var captured = value;
- slider.RegisterValueChangedCallback(evt =>
- {
- captured.text = evt.newValue.ToString();
- onChange(evt.newValue);
- });
-
- valueLabel = value;
- return row;
- }
-
- // Vertically lines the row's text up with the groove the player actually sees.
- //
- // The default runtime theme gives a horizontal slider a box that's taller than its
- // track and does NOT center the track inside it, so `align-items: center` on the row
- // centers the *boxes* and leaves the text sitting well below the groove. The exact
- // offset is a theme detail, so it's measured rather than hardcoded: with the row
- // aligned FlexStart every child's box starts at the same top edge, so giving a label
- // a height of twice the track's offset from that edge puts its vertically-centered
- // text exactly on the track's center line.
- //
- // Runs on GeometryChangedEvent (not once at build time) because layout hasn't
- // happened yet during construction, and because it needs to re-solve when the panel
- // is rescaled or the menu is reopened at a different resolution.
- private static void AlignLabelsToTrack(VisualElement row, SliderInt slider,
- params Label[] labels)
- {
- // Align to the *tracker* — the thin groove. Not the drag-container: that's the
- // full hit area, tall enough to hold the dragger handle (which overhangs the
- // groove on both sides), so its center sits below the groove. Fall back through
- // the container to the slider itself if the theme's internal names ever change;
- // worst case is the old box-centered look, not a broken layout.
- VisualElement track = slider.Q(className: "unity-base-slider__tracker")
- ?? slider.Q(className: "unity-base-slider__drag-container")
- ?? slider;
-
- void Align(GeometryChangedEvent _)
- {
- float rowTop = row.worldBound.y;
- float trackCenter = track.worldBound.center.y;
- float half = trackCenter - rowTop;
- if (half <= 0f || float.IsNaN(half)) return; // pre-layout; a later event fixes it
-
- foreach (var label in labels)
- label.style.height = half * 2f;
- }
-
- row.RegisterCallback(Align);
- track.RegisterCallback(Align);
- }
-
- // Gear glyph. Drawn with Painter2D rather than a font glyph or a texture: Unity's
- // default runtime font has no U+2699 gear, and a procedural icon keeps the button
- // working in any scene without an imported sprite. Pass a sprite to override.
- private static VisualElement CreateGearIcon(Sprite icon)
- {
- var el = new VisualElement();
- el.pickingMode = PickingMode.Ignore;
- el.style.flexGrow = 1;
-
- if (icon != null)
- {
- el.style.backgroundImage = new StyleBackground(icon);
- return el;
- }
-
- el.generateVisualContent += PaintGear;
- // generateVisualContent runs against contentRect, which is 0×0 until the first
- // layout pass — repaint once the element actually has a size.
- el.RegisterCallback(_ => el.MarkDirtyRepaint());
- return el;
- }
-
- private static void PaintGear(MeshGenerationContext ctx)
- {
- var rect = ctx.visualElement.contentRect;
- if (rect.width < 4f || rect.height < 4f) return;
-
- var painter = ctx.painter2D;
- Vector2 center = rect.center;
- float outer = Mathf.Min(rect.width, rect.height) * 0.5f - 1f;
- float ring = outer * 0.62f;
- float hub = outer * 0.24f;
-
- painter.strokeColor = new Color(0.95f, 0.93f, 0.75f);
- painter.lineWidth = Mathf.Max(1.5f, outer * 0.20f);
-
- // Teeth: radial spokes from just inside the ring out to the edge.
- const int teeth = 8;
- painter.BeginPath();
- for (int i = 0; i < teeth; i++)
- {
- float angle = i * Mathf.PI * 2f / teeth;
- var dir = new Vector2(Mathf.Cos(angle), Mathf.Sin(angle));
- painter.MoveTo(center + dir * (ring * 0.85f));
- painter.LineTo(center + dir * outer);
- }
- painter.Stroke();
-
- painter.BeginPath();
- painter.Arc(center, ring, new Angle(0f, AngleUnit.Degree), new Angle(360f, AngleUnit.Degree));
- painter.Stroke();
-
- painter.BeginPath();
- painter.Arc(center, hub, new Angle(0f, AngleUnit.Degree), new Angle(360f, AngleUnit.Degree));
- painter.Stroke();
- }
- }
-}
diff --git a/Assets/_Project/Scripts/UI/GameMenuView.cs.meta b/Assets/_Project/Scripts/UI/GameMenuView.cs.meta
deleted file mode 100644
index 0d488ea..0000000
--- a/Assets/_Project/Scripts/UI/GameMenuView.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 0f5b5b3bcd6ea4765a92a784b9c8de13
\ No newline at end of file
diff --git a/Assets/_Project/Scripts/UI/HUDController.cs b/Assets/_Project/Scripts/UI/HUDController.cs
index 7b51380..504ac50 100644
--- a/Assets/_Project/Scripts/UI/HUDController.cs
+++ b/Assets/_Project/Scripts/UI/HUDController.cs
@@ -10,8 +10,6 @@ using TD.Core;
using TD.Gameplay;
using TD.Gameplay.BuilderSpells;
using TD.Gameplay.Draft;
-using TD.Gameplay.EnemyUpgrades;
-using TD.Gameplay.Waves;
using TD.Towers;
using TD.UI.Minimap;
@@ -45,10 +43,6 @@ namespace TD.UI
[Header("Settings")] [SerializeField] private float rejectionMessageDuration = 2.5f;
- [Tooltip("Optional icon for the top-right menu button. Leave empty to use the " +
- "procedurally-drawn gear (no art asset required).")]
- [SerializeField] private Sprite menuButtonIcon;
-
[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).")]
@@ -77,19 +71,6 @@ namespace TD.UI
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")
-
- // Top-bar row of buff badges for the wave currently spawning. Rebuilt only when the
- // underlying set changes — see waveBuffSignature.
- private VisualElement waveBuffRow;
-
- // Cheap change-detector for the buff row. RefreshTopBar runs every frame, and rebuilding
- // a handful of VisualElements at 60 Hz would churn the UI system for no reason. Encodes
- // the slot plus the option ids on it, so it changes when the wave advances OR when a vote
- // lands on the slot the players are about to face again.
- private int waveBuffSignature = int.MinValue;
-
- private readonly System.Collections.Generic.List waveBuffScratch
- = new System.Collections.Generic.List();
private VisualElement playerListContainer; // right-panel scoreboard rows
private Label portraitName;
private Label levelLabel;
@@ -120,6 +101,10 @@ namespace TD.UI
// 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;
// Draft overlay (roguelike between-waves draft). Non-modal: the card row floats at
@@ -127,45 +112,17 @@ namespace TD.UI
// shown when no draft is pending so a paid roll can be bought any time.
private VisualElement draftPanel;
private VisualElement draftCardRow;
+ private Button draftBuyButton;
private bool draftSubscribed;
private PlayerDraft subscribedDraft;
- // Enemy-buff vote overlay. Shares the draft overlay's position and card styling, but the
- // cards are shared rather than per-player and carry live voter badges.
- private VisualElement votePanel;
- private VisualElement voteCardRow;
- private Label voteTitle;
- private bool voteSubscribed;
- private WaveVote subscribedVote;
-
- // Boss health bar. Shown only while a boss is alive; polled per frame like lives/gold
- // rather than event-driven, since HP changes continuously anyway.
- private VisualElement bossBarPanel;
- private VisualElement bossHealthFill;
- private Label bossHealthText;
- private Label bossNameLabel;
-
- // Reused when painting voter badges so a rebuild-per-ballot doesn't allocate a list per
- // card. The HUD is single-threaded, so one shared buffer is safe.
- private readonly System.Collections.Generic.List voteVoterScratch
- = new System.Collections.Generic.List();
-
- // Reused when building a selected tower's upgrade buttons.
- private readonly System.Collections.Generic.List<(TowerDefinition Def, int TypeId)>
- upgradeOptionScratch
- = new System.Collections.Generic.List<(TowerDefinition, int)>();
-
- // Spell hotbar (bottom-ui Section 6). Frame is visibility:hidden (but still occupies
- // its reserved width, so the centered command bar never shifts) while the local player
+ // Spell hotbar (bottom-ui Section 6). Frame is display:none while the local player
// has no granted spells; rebuilt on grant (append-only, so this is rare). Cooldown
// state changes continuously and has no change event, so it's polled every Update.
private VisualElement spellHotbarFrame;
private VisualElement spellHotbar;
private bool spellLoadoutSubscribed;
private PlayerSpellLoadout subscribedSpellLoadout;
- // The loadout the hotbar is currently showing — the SELECTED builder's, or null when
- // no builder is selected (hotbar hidden). Cooldown polling reads this.
- private PlayerSpellLoadout displayedSpellLoadout;
private readonly List spellSlotUis = new List();
private readonly struct SpellSlotUi
@@ -196,34 +153,8 @@ namespace TD.UI
// Set true whenever the chat input or any other text field on the HUD has
// keyboard focus. Camera, builder input, and hotkey handlers all gate on
- // this to keep typing from driving gameplay. Setter is internal (not
- // private) so other standalone text-input surfaces — e.g. TD.Dev.DebugConsole,
- // which is not part of HUDController — can contribute to the same flag
- // instead of every gameplay script needing to know about every input widget.
- public static bool IsTextInputActive { get; internal set; }
-
- ///
- /// True while a modal UI surface (currently the gear menu) owns the screen. Gameplay
- /// systems should treat this exactly like — see
- /// , which is the flag they actually gate on.
- ///
- public static bool IsModalUiOpen { get; private set; }
-
- ///
- /// True when any UI surface is consuming keyboard/mouse input this frame: a focused
- /// text field OR an open modal menu. Gameplay input handlers (camera, selection,
- /// hotkeys, Escape-to-cancel) gate on this so typing or a menu never doubles as a
- /// gameplay command.
- ///
- public static bool IsUiCapturingInput => IsTextInputActive || IsModalUiOpen;
-
- // Frame until which chat's "Enter opens chat" behavior is suppressed. Other
- // standalone text-input surfaces (e.g. TD.Dev.DebugConsole) set this when their
- // own Enter-driven submit/close already consumed the keypress, so the same
- // Enter press doesn't also get interpreted as "open chat" this frame. Without
- // this, submitting a debug console command with Enter would immediately pop
- // chat open too, since both systems poll the raw key independently.
- internal static int SuppressChatOpenUntilFrame = -1;
+ // this to keep typing from driving gameplay.
+ public static bool IsTextInputActive { get; private set; }
// ----- State ------------------------------------------------------
@@ -240,10 +171,7 @@ namespace TD.UI
private bool matchStateSubscribed; // true once OnPhaseChanged is hooked
private bool deckSubscribed; // true once we've hooked the local PlayerTowerDeck.OnDeckChanged
private PlayerTowerDeck subscribedDeck; // the deck we hooked, so we can unsubscribe the same instance
- private bool upgradesSubscribed; // true once we've hooked the local PlayerTowerUpgrades
- private PlayerTowerUpgrades subscribedUpgrades;
private MinimapView minimapView;
- private GameMenuView gameMenu; // gear button + modal menu overlay
private IPanel myPanel; // tracked separately so OnDestroy only clears the static if it still points at us
// ----- Hotkeys ----------------------------------------------------
@@ -371,19 +299,6 @@ namespace TD.UI
PopulateGridForSelection(SelectionState.Instance?.SelectedObject);
}
- // Same idea for unlocked upgrade nodes: a draft pick can grant an upgrade while the
- // player already has the target tower selected, and the new branch should appear in the
- // action grid without needing a reselect.
- private void TrySubscribeUpgrades()
- {
- var upgrades = PlayerTowerUpgrades.Local;
- if (upgrades == null) return;
- upgrades.OnUpgradesChanged += HandleDeckChanged;
- subscribedUpgrades = upgrades;
- upgradesSubscribed = true;
- PopulateGridForSelection(SelectionState.Instance?.SelectedObject);
- }
-
// Hook the local player's draft so the overlay rebuilds when options are offered,
// picked, or rerolled. Retried each Update until the local PlayerDraft exists.
private void TrySubscribeDraft()
@@ -420,9 +335,9 @@ namespace TD.UI
// ----- Draft overlay ----------------------------------------------
- // Non-modal draft UI: a card row floats at top-center while the local player has an
- // unresolved offer. The host panel ignores picks so empty space clicks through to the
- // world — the player can still pan and inspect their maze while deciding.
+ // Non-modal draft UI: a card row floats at top-center while a draft is active, and
+ // a "Buy Roll" button shows when no draft is pending. The host panel ignores picks
+ // so empty space clicks through to the world — the player keeps building during prep.
private void BuildDraftOverlay(VisualElement root)
{
draftPanel = new VisualElement();
@@ -444,8 +359,12 @@ namespace TD.UI
draftCardRow.pickingMode = PickingMode.Ignore;
box.Add(draftCardRow);
- // The "Buy Roll" button lived here. Removed with the extra-draft shop for the 2.0 MVP
- // — see PlayerDraft's commented-out RequestBuyRerollRpc for why the backend was kept.
+ draftBuyButton = new Button(() => PlayerDraft.Local?.RequestBuyRerollRpc())
+ {
+ text = "Buy Roll"
+ };
+ draftBuyButton.style.marginTop = 6;
+ box.Add(draftBuyButton);
draftPanel.Add(box);
root.Add(draftPanel);
@@ -500,10 +419,9 @@ namespace TD.UI
title.style.marginBottom = 6;
card.Add(title);
- var iconSprite = option.ResolveIcon();
- if (iconSprite != null)
+ if (option.Icon != null)
{
- var img = new Image { sprite = iconSprite };
+ var img = new Image { sprite = option.Icon };
img.style.width = 48;
img.style.height = 48;
img.style.marginBottom = 6;
@@ -524,357 +442,56 @@ namespace TD.UI
return card;
}
- // Shows the draft row only while the local player has an unresolved offer. With the
- // extra-draft shop gone there is nothing else in this panel, so it hides entirely once
- // the player has picked — which also gives them a clear "I'm done, waiting on others"
- // signal during the barrier.
+ // Per-frame: toggles the card row vs. the buy button and keeps the buy button's
+ // label/enabled state in sync with gold. Cheap (mirrors the gold label's per-frame
+ // refresh); only allocates a string while the buy button is shown.
private void UpdateDraftVisibility()
{
if (draftPanel == null) return;
- var draft = PlayerDraft.Local;
- bool hasDraft = draft != null && draft.HasActiveDraft;
+ var draft = PlayerDraft.Local;
+ var service = DraftService.Instance;
+ var gold = PlayerGoldManager.Local;
+
+ bool hasDraft = draft != null && draft.HasActiveDraft;
+ bool canShowBuy = draft != null && service != null && !hasDraft;
if (draftCardRow != null)
draftCardRow.style.display = hasDraft ? DisplayStyle.Flex : DisplayStyle.None;
- draftPanel.style.display = hasDraft ? DisplayStyle.Flex : DisplayStyle.None;
- }
-
- // ----- Enemy-buff vote overlay --------------------------------------
-
- // Shared vote UI. Same anchor and card language as the draft row (they never show at the
- // same time — the inter-wave stages are strictly sequential), but every peer sees the same
- // three cards, and each card carries badges for the players currently voting for it.
- private void BuildVoteOverlay(VisualElement root)
- {
- votePanel = new VisualElement();
- votePanel.style.position = Position.Absolute;
- votePanel.style.left = 0;
- votePanel.style.right = 0;
- votePanel.style.top = 70; // below the top bar, same as the draft row
- votePanel.style.alignItems = Align.Center;
- votePanel.pickingMode = PickingMode.Ignore;
- votePanel.style.display = DisplayStyle.None;
-
- var box = new VisualElement();
- box.style.flexDirection = FlexDirection.Column;
- box.style.alignItems = Align.Center;
- box.pickingMode = PickingMode.Ignore;
-
- voteTitle = new Label("Choose how this wave comes back");
- voteTitle.style.fontSize = 15;
- voteTitle.style.color = new Color(0.95f, 0.72f, 0.55f);
- voteTitle.style.unityFontStyleAndWeight = FontStyle.Bold;
- voteTitle.style.marginBottom = 6;
- box.Add(voteTitle);
-
- voteCardRow = new VisualElement();
- voteCardRow.style.flexDirection = FlexDirection.Row;
- voteCardRow.pickingMode = PickingMode.Ignore;
- box.Add(voteCardRow);
-
- votePanel.Add(box);
- root.Add(votePanel);
-
- UpdateVoteVisibility();
- }
-
- // Hook the shared vote so the panel rebuilds whenever a ballot lands. Retried each Update
- // until the scene's WaveVote has network-spawned.
- private void TrySubscribeVote()
- {
- var vote = WaveVote.Instance;
- if (vote == null) return;
- vote.OnVoteChanged += HandleVoteChanged;
- subscribedVote = vote;
- voteSubscribed = true;
- RebuildVoteCards();
- }
-
- private void HandleVoteChanged()
- {
- RebuildVoteCards();
- }
-
- // Rebuilt on every ballot change, not just when the offered set changes — the voter
- // badges are the point of the panel, so they have to track votes as they land.
- private void RebuildVoteCards()
- {
- if (voteCardRow == null) return;
- voteCardRow.Clear();
-
- var vote = WaveVote.Instance;
- var pool = EnemyUpgradePool.Instance;
-
- if (vote != null && pool != null && vote.IsOpen)
+ if (draftBuyButton != null)
{
- for (int i = 0; i < vote.OptionCount; i++)
+ draftBuyButton.style.display = canShowBuy ? DisplayStyle.Flex : DisplayStyle.None;
+ if (canShowBuy)
{
- int id = vote.GetOptionId(i);
- var option = pool.Get(id);
- if (option == null) continue;
- voteCardRow.Add(CreateVoteCard(option, id, vote));
+ int cost = service.RerollCost;
+ draftBuyButton.text = $"Buy Roll ({cost}g)";
+ draftBuyButton.SetEnabled(gold != null && gold.CurrentGold >= cost);
}
}
- UpdateVoteVisibility();
- }
-
- private VisualElement CreateVoteCard(EnemyUpgradeOption option, int optionId, WaveVote vote)
- {
- var localSlot = PlayerMatchState.Local != null
- ? PlayerMatchState.Local.Slot
- : PlayerSlot.None;
- bool isLocalChoice = localSlot != PlayerSlot.None
- && vote.GetBallot(localSlot) == optionId;
-
- var card = new VisualElement();
- card.style.width = 160;
- card.style.marginLeft = card.style.marginRight = 4;
- card.style.paddingTop = card.style.paddingBottom = 10;
- card.style.paddingLeft = card.style.paddingRight = 10;
- card.style.backgroundColor = new Color(0.13f, 0.09f, 0.09f, 0.96f);
- card.style.borderTopWidth = card.style.borderBottomWidth =
- card.style.borderLeftWidth = card.style.borderRightWidth = 2;
-
- // The local player's current choice gets a bright border so they can tell at a glance
- // which one is theirs among everyone else's badges.
- var border = isLocalChoice
- ? new Color(0.95f, 0.72f, 0.35f)
- : new Color(0.45f, 0.30f, 0.30f);
- card.style.borderTopColor = card.style.borderBottomColor =
- card.style.borderLeftColor = card.style.borderRightColor = border;
- card.style.alignItems = Align.Center;
-
- var title = new Label(option.DisplayName);
- title.style.fontSize = 14;
- title.style.color = Color.white;
- title.style.unityFontStyleAndWeight = FontStyle.Bold;
- title.style.whiteSpace = WhiteSpace.Normal;
- title.style.unityTextAlign = TextAnchor.MiddleCenter;
- title.style.marginBottom = 6;
- card.Add(title);
-
- var iconSprite = option.ResolveIcon();
- if (iconSprite != null)
- {
- var img = new Image { sprite = iconSprite };
- img.style.width = 48;
- img.style.height = 48;
- img.style.marginBottom = 6;
- card.Add(img);
- }
-
- var desc = new Label(option.Description ?? "");
- desc.style.fontSize = 11;
- desc.style.color = new Color(0.82f, 0.82f, 0.82f);
- desc.style.whiteSpace = WhiteSpace.Normal;
- desc.style.unityTextAlign = TextAnchor.MiddleCenter;
- desc.style.marginBottom = 8;
- card.Add(desc);
-
- // Live voter badges — one per player currently voting for this card, in their own
- // player color. This row is why the panel rebuilds on every ballot change.
- var badgeRow = new VisualElement();
- badgeRow.style.flexDirection = FlexDirection.Row;
- badgeRow.style.justifyContent = Justify.Center;
- badgeRow.style.minHeight = 24;
- badgeRow.style.marginBottom = 6;
-
- voteVoterScratch.Clear();
- vote.GetVotersFor(optionId, voteVoterScratch);
- foreach (var voter in voteVoterScratch)
- badgeRow.Add(CreateVoterBadge(voter));
-
- card.Add(badgeRow);
-
- var pick = new Button(() => WaveVote.Instance?.RequestVoteRpc(optionId))
- {
- text = isLocalChoice ? "Voted" : "Vote"
- };
- card.Add(pick);
-
- return card;
- }
-
- // Small filled circle carrying the player's slot number, in their canonical player color.
- // Same visual vocabulary as the race-selection overlay's picker badges, so players read it
- // as "who" without a legend.
- private VisualElement CreateVoterBadge(PlayerSlot slot)
- {
- var badge = new Label(((int)slot).ToString());
- badge.style.width = 20;
- badge.style.height = 20;
- badge.style.marginLeft = badge.style.marginRight = 2;
- badge.style.backgroundColor = PlayerColors.Get(slot);
- badge.style.color = Color.black;
- badge.style.fontSize = 11;
- badge.style.unityFontStyleAndWeight = FontStyle.Bold;
- badge.style.unityTextAlign = TextAnchor.MiddleCenter;
- badge.style.borderTopLeftRadius = badge.style.borderTopRightRadius =
- badge.style.borderBottomLeftRadius = badge.style.borderBottomRightRadius = 10;
- return badge;
- }
-
- private void UpdateVoteVisibility()
- {
- if (votePanel == null) return;
-
- var vote = WaveVote.Instance;
- bool open = vote != null && vote.IsOpen && vote.OptionCount > 0;
- votePanel.style.display = open ? DisplayStyle.Flex : DisplayStyle.None;
- }
-
- // ----- Boss bar -----------------------------------------------------
-
- // Prominent health bar for the phase boss. Hidden whenever no boss is alive, so it costs
- // nothing outside a boss encounter.
- private void BuildBossBar(VisualElement root)
- {
- bossBarPanel = new VisualElement();
- bossBarPanel.style.position = Position.Absolute;
- bossBarPanel.style.left = 0;
- bossBarPanel.style.right = 0;
- bossBarPanel.style.top = 70;
- bossBarPanel.style.alignItems = Align.Center;
- bossBarPanel.pickingMode = PickingMode.Ignore;
- bossBarPanel.style.display = DisplayStyle.None;
-
- var box = new VisualElement();
- box.style.width = 440;
- box.style.alignItems = Align.Center;
- box.pickingMode = PickingMode.Ignore;
-
- bossNameLabel = new Label("BOSS");
- bossNameLabel.style.fontSize = 18;
- bossNameLabel.style.color = new Color(0.95f, 0.45f, 0.4f);
- bossNameLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
- bossNameLabel.style.marginBottom = 3;
- box.Add(bossNameLabel);
-
- var track = new VisualElement();
- track.style.width = 440;
- track.style.height = 18;
- track.style.backgroundColor = new Color(0.08f, 0.06f, 0.06f, 0.95f);
- track.style.borderTopWidth = track.style.borderBottomWidth =
- track.style.borderLeftWidth = track.style.borderRightWidth = 2;
- var trackBorder = new Color(0.5f, 0.25f, 0.22f);
- track.style.borderTopColor = track.style.borderBottomColor =
- track.style.borderLeftColor = track.style.borderRightColor = trackBorder;
- track.pickingMode = PickingMode.Ignore;
-
- bossHealthFill = new VisualElement();
- bossHealthFill.style.height = Length.Percent(100);
- bossHealthFill.style.width = Length.Percent(100);
- bossHealthFill.style.backgroundColor = new Color(0.75f, 0.18f, 0.16f);
- bossHealthFill.pickingMode = PickingMode.Ignore;
- track.Add(bossHealthFill);
-
- bossHealthText = new Label("");
- bossHealthText.style.position = Position.Absolute;
- bossHealthText.style.left = 0;
- bossHealthText.style.right = 0;
- bossHealthText.style.top = 0;
- bossHealthText.style.bottom = 0;
- bossHealthText.style.fontSize = 12;
- bossHealthText.style.color = Color.white;
- bossHealthText.style.unityTextAlign = TextAnchor.MiddleCenter;
- bossHealthText.pickingMode = PickingMode.Ignore;
- track.Add(bossHealthText);
-
- box.Add(track);
- bossBarPanel.Add(box);
- root.Add(bossBarPanel);
- }
-
- // Polled from the per-frame refresh, like lives and gold. Picks the boss in the local
- // player's own zone when there is one — the boss encounter spawns one per zone, and the
- // one actually walking through your maze is the one you need to watch.
- private void RefreshBossBar()
- {
- if (bossBarPanel == null) return;
-
- var boss = ResolveDisplayedBoss();
- if (boss == null)
- {
- bossBarPanel.style.display = DisplayStyle.None;
- return;
- }
-
- bossBarPanel.style.display = DisplayStyle.Flex;
-
- float max = Mathf.Max(1f, boss.MaxHp);
- float pct = Mathf.Clamp01(boss.CurrentHp / max);
- bossHealthFill.style.width = Length.Percent(pct * 100f);
- bossHealthText.text = $"{Mathf.CeilToInt(boss.CurrentHp)} / {Mathf.CeilToInt(max)}";
- bossNameLabel.text = boss.DisplayName;
- }
-
- private EnemyHealth ResolveDisplayedBoss()
- {
- var bosses = EnemyHealth.ActiveBosses;
- if (bosses.Count == 0) return null;
-
- var localSlot = PlayerMatchState.Local != null
- ? PlayerMatchState.Local.Slot
- : PlayerSlot.None;
-
- EnemyHealth fallback = null;
- for (int i = 0; i < bosses.Count; i++)
- {
- var boss = bosses[i];
- if (boss == null || boss.IsDead) continue;
-
- fallback ??= boss;
-
- if (localSlot != PlayerSlot.None)
- {
- var movement = boss.GetComponent();
- if (movement != null && movement.OriginZone == localSlot) return boss;
- }
- }
-
- return fallback;
+ draftPanel.style.display = (hasDraft || canShowBuy) ? DisplayStyle.Flex : DisplayStyle.None;
}
// ----- Spell hotbar -------------------------------------------------
- // Rebuilds the hotbar cells for the SELECTED builder's loadout. Called on selection
- // change and on OnLoadoutChanged (a new spell granted). Spells are append-only, so
- // this is rare, not a per-frame concern.
- //
- // The hotbar follows selection: it shows the selected builder's spells and is removed
- // entirely when no builder is selected (spells belong to the builder). While a builder
- // IS selected, the frame keeps its reserved width (visibility toggle, not display) so
- // granting a spell mid-selection doesn't reflow/shift the centered command bar.
+ // Rebuilds the hotbar cells from the local player's current loadout. Called on
+ // OnLoadoutChanged (a new spell granted) — spells are append-only, so this is rare,
+ // not a per-frame concern.
private void RebuildSpellHotbar()
{
if (spellHotbar == null) return;
spellHotbar.Clear();
spellSlotUis.Clear();
- var loadout = GetSelectedBuilderLoadout();
- displayedSpellLoadout = loadout;
+ var loadout = PlayerSpellLoadout.Local;
+ int slotCount = loadout?.SlotCount ?? 0;
if (spellHotbarFrame != null)
- {
- if (loadout == null)
- {
- // No builder selected → remove the slot (the command grid is hidden too).
- spellHotbarFrame.style.display = DisplayStyle.None;
- return;
- }
- // Builder selected → keep the reserved slot in layout; toggle only visibility so
- // a mid-selection spell grant doesn't jump the command bar.
- spellHotbarFrame.style.display = DisplayStyle.Flex;
- spellHotbarFrame.style.visibility =
- loadout.SlotCount > 0 ? Visibility.Visible : Visibility.Hidden;
- }
+ spellHotbarFrame.style.display = slotCount > 0 ? DisplayStyle.Flex : DisplayStyle.None;
if (loadout == null) return;
- int slotCount = loadout.SlotCount;
var layout = SpellHotkeys.Layout;
for (int i = 0; i < slotCount; i++)
{
@@ -914,17 +531,6 @@ namespace TD.UI
}
}
- // The loadout the spell hotbar should display: the currently-selected builder's
- // (resolved via its owner, so it shows whichever builder is selected — normally the
- // local player's), or null when the selection isn't a builder → hotbar hidden.
- private PlayerSpellLoadout GetSelectedBuilderLoadout()
- {
- var selected = SelectionState.Instance?.SelectedObject;
- if (selected is Builder builder)
- return PlayerSpellLoadout.GetForClient(builder.OwnerClientId);
- return null;
- }
-
// Per-frame: dims each slot while on cooldown and shows the remaining whole seconds.
// Cooldown has no change event (it advances continuously with server time), so this
// has to be polled — cheap at hotbar scale (at most MaxSpellSlots cells).
@@ -932,7 +538,7 @@ namespace TD.UI
{
if (spellSlotUis.Count == 0) return;
- var loadout = displayedSpellLoadout;
+ var loadout = PlayerSpellLoadout.Local;
if (loadout == null) return;
for (int i = 0; i < spellSlotUis.Count; i++)
@@ -990,7 +596,6 @@ namespace TD.UI
nextWaveLabel = Require