Map Updates
This commit is contained in:
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -0,0 +1,117 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 05d147603b5475a4db60d133ff96efbd
|
||||||
|
TextureImporter:
|
||||||
|
internalIDToNameTable: []
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 13
|
||||||
|
mipmaps:
|
||||||
|
mipMapMode: 0
|
||||||
|
enableMipMap: 1
|
||||||
|
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: 0
|
||||||
|
wrapV: 0
|
||||||
|
wrapW: 0
|
||||||
|
nPOTScale: 1
|
||||||
|
lightmap: 0
|
||||||
|
compressionQuality: 50
|
||||||
|
spriteMode: 0
|
||||||
|
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: 0
|
||||||
|
spriteTessellationDetail: -1
|
||||||
|
textureType: 0
|
||||||
|
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:
|
||||||
|
internalID: 0
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
secondaryTextures: []
|
||||||
|
spriteCustomMetadata:
|
||||||
|
entries: []
|
||||||
|
nameFileIdTable: {}
|
||||||
|
mipmapLimitGroupName:
|
||||||
|
pSDRemoveMatte: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
Binary file not shown.
@@ -0,0 +1,117 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c48fa3f0c43a1dd4c86973898ff25c99
|
||||||
|
TextureImporter:
|
||||||
|
internalIDToNameTable: []
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 13
|
||||||
|
mipmaps:
|
||||||
|
mipMapMode: 0
|
||||||
|
enableMipMap: 1
|
||||||
|
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: 0
|
||||||
|
wrapV: 0
|
||||||
|
wrapW: 0
|
||||||
|
nPOTScale: 1
|
||||||
|
lightmap: 0
|
||||||
|
compressionQuality: 50
|
||||||
|
spriteMode: 0
|
||||||
|
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: 0
|
||||||
|
spriteTessellationDetail: -1
|
||||||
|
textureType: 0
|
||||||
|
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:
|
||||||
|
internalID: 0
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
secondaryTextures: []
|
||||||
|
spriteCustomMetadata:
|
||||||
|
entries: []
|
||||||
|
nameFileIdTable: {}
|
||||||
|
mipmapLimitGroupName:
|
||||||
|
pSDRemoveMatte: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
Binary file not shown.
@@ -0,0 +1,117 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8d7fc182a6b916c49b59b3a54cf53d49
|
||||||
|
TextureImporter:
|
||||||
|
internalIDToNameTable: []
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 13
|
||||||
|
mipmaps:
|
||||||
|
mipMapMode: 0
|
||||||
|
enableMipMap: 1
|
||||||
|
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: 0
|
||||||
|
wrapV: 0
|
||||||
|
wrapW: 0
|
||||||
|
nPOTScale: 1
|
||||||
|
lightmap: 0
|
||||||
|
compressionQuality: 50
|
||||||
|
spriteMode: 0
|
||||||
|
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: 0
|
||||||
|
spriteTessellationDetail: -1
|
||||||
|
textureType: 0
|
||||||
|
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:
|
||||||
|
internalID: 0
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
secondaryTextures: []
|
||||||
|
spriteCustomMetadata:
|
||||||
|
entries: []
|
||||||
|
nameFileIdTable: {}
|
||||||
|
mipmapLimitGroupName:
|
||||||
|
pSDRemoveMatte: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!114 &-341605702001916123
|
||||||
|
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
|
||||||
|
--- !u!21 &2100000
|
||||||
|
Material:
|
||||||
|
serializedVersion: 8
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_Name: M_Aether_Ordered
|
||||||
|
m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
|
||||||
|
m_Parent: {fileID: 0}
|
||||||
|
m_ModifiedSerializedProperties: 0
|
||||||
|
m_ValidKeywords:
|
||||||
|
- _EMISSION
|
||||||
|
m_InvalidKeywords: []
|
||||||
|
m_LightmapFlags: 1
|
||||||
|
m_EnableInstancingVariants: 0
|
||||||
|
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.2
|
||||||
|
- _OcclusionStrength: 1
|
||||||
|
- _Parallax: 0.005
|
||||||
|
- _QueueOffset: 0
|
||||||
|
- _ReceiveShadows: 1
|
||||||
|
- _Smoothness: 0.55
|
||||||
|
- _SmoothnessTextureChannel: 0
|
||||||
|
- _SpecularHighlights: 1
|
||||||
|
- _SrcBlend: 1
|
||||||
|
- _SrcBlendAlpha: 1
|
||||||
|
- _Surface: 0
|
||||||
|
- _WorkflowMode: 1
|
||||||
|
- _XRMotionVectorsPass: 1
|
||||||
|
- _ZWrite: 1
|
||||||
|
m_Colors:
|
||||||
|
- _BaseColor: {r: 0.06, g: 0.2, b: 0.26, a: 1}
|
||||||
|
- _Color: {r: 0.059999965, g: 0.19999996, b: 0.25999996, a: 1}
|
||||||
|
- _EmissionColor: {r: 0.2, g: 1.8, b: 2.4, a: 1}
|
||||||
|
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
|
||||||
|
m_BuildTextureStacks: []
|
||||||
|
m_AllowLocking: 1
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9a2c9e498b0fb1d41aa9504f4f843185
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 2100000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
%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_Aether_Wild
|
||||||
|
m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
|
||||||
|
m_Parent: {fileID: 0}
|
||||||
|
m_ModifiedSerializedProperties: 0
|
||||||
|
m_ValidKeywords:
|
||||||
|
- _EMISSION
|
||||||
|
m_InvalidKeywords: []
|
||||||
|
m_LightmapFlags: 1
|
||||||
|
m_EnableInstancingVariants: 0
|
||||||
|
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.15
|
||||||
|
- _OcclusionStrength: 1
|
||||||
|
- _Parallax: 0.005
|
||||||
|
- _QueueOffset: 0
|
||||||
|
- _ReceiveShadows: 1
|
||||||
|
- _Smoothness: 0.35
|
||||||
|
- _SmoothnessTextureChannel: 0
|
||||||
|
- _SpecularHighlights: 1
|
||||||
|
- _SrcBlend: 1
|
||||||
|
- _SrcBlendAlpha: 1
|
||||||
|
- _Surface: 0
|
||||||
|
- _WorkflowMode: 1
|
||||||
|
- _XRMotionVectorsPass: 1
|
||||||
|
- _ZWrite: 1
|
||||||
|
m_Colors:
|
||||||
|
- _BaseColor: {r: 0.45, g: 0.16, b: 0.06, a: 1}
|
||||||
|
- _Color: {r: 0.45, g: 0.15999997, b: 0.059999965, a: 1}
|
||||||
|
- _EmissionColor: {r: 3, g: 1, b: 0.25, a: 1}
|
||||||
|
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
|
||||||
|
m_BuildTextureStacks: []
|
||||||
|
m_AllowLocking: 1
|
||||||
|
--- !u!114 &8700211274686457953
|
||||||
|
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
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e7f75fad709aa7b41bce85721cccd020
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 2100000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,149 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!1 &3885353946372160549
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 3572766465862231365}
|
||||||
|
- component: {fileID: 3909651526955663392}
|
||||||
|
- component: {fileID: 3320445911748035220}
|
||||||
|
- component: {fileID: 9053853372340598254}
|
||||||
|
- component: {fileID: 6834786618115927220}
|
||||||
|
- component: {fileID: 1664972900393443061}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: BlightClutter
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!4 &3572766465862231365
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 3885353946372160549}
|
||||||
|
serializedVersion: 2
|
||||||
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||||
|
m_LocalScale: {x: 2.5, y: 2.5, z: 2.5}
|
||||||
|
m_ConstrainProportionsScale: 0
|
||||||
|
m_Children: []
|
||||||
|
m_Father: {fileID: 0}
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
--- !u!33 &3909651526955663392
|
||||||
|
MeshFilter:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 3885353946372160549}
|
||||||
|
m_Mesh: {fileID: 4300000, guid: abc00000000010690097314383055197, type: 3}
|
||||||
|
--- !u!23 &3320445911748035220
|
||||||
|
MeshRenderer:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 3885353946372160549}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_CastShadows: 1
|
||||||
|
m_ReceiveShadows: 1
|
||||||
|
m_DynamicOccludee: 1
|
||||||
|
m_StaticShadowCaster: 0
|
||||||
|
m_MotionVectors: 1
|
||||||
|
m_LightProbeUsage: 1
|
||||||
|
m_ReflectionProbeUsage: 1
|
||||||
|
m_RayTracingMode: 2
|
||||||
|
m_RayTraceProcedural: 0
|
||||||
|
m_RayTracingAccelStructBuildFlagsOverride: 0
|
||||||
|
m_RayTracingAccelStructBuildFlags: 1
|
||||||
|
m_SmallMeshCulling: 1
|
||||||
|
m_ForceMeshLod: -1
|
||||||
|
m_MeshLodSelectionBias: 0
|
||||||
|
m_RenderingLayerMask: 1
|
||||||
|
m_RendererPriority: 0
|
||||||
|
m_Materials:
|
||||||
|
- {fileID: 2100000, guid: e7f75fad709aa7b41bce85721cccd020, 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: 1
|
||||||
|
m_IgnoreNormalsForChartDetection: 0
|
||||||
|
m_ImportantGI: 0
|
||||||
|
m_StitchLightmapSeams: 1
|
||||||
|
m_SelectedEditorRenderState: 3
|
||||||
|
m_MinimumChartSize: 4
|
||||||
|
m_AutoUVMaxDistance: 0.5
|
||||||
|
m_AutoUVMaxAngle: 89
|
||||||
|
m_LightmapParameters: {fileID: 0}
|
||||||
|
m_GlobalIlluminationMeshLod: 0
|
||||||
|
m_SortingLayerID: 0
|
||||||
|
m_SortingLayer: 0
|
||||||
|
m_SortingOrder: 0
|
||||||
|
m_MaskInteraction: 0
|
||||||
|
m_AdditionalVertexStreams: {fileID: 0}
|
||||||
|
--- !u!114 &9053853372340598254
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 3885353946372160549}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: c16549610bfe4458aa9389201d072bb6, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: Unity.Entities.Hybrid::Unity.Entities.Hybrid.Baking.LinkedEntityGroupAuthoring
|
||||||
|
--- !u!114 &6834786618115927220
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 3885353946372160549}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 7c79d771cedb4794bf100ce60df5f764, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: Unity.NetCode.Authoring.Hybrid::Unity.NetCode.GhostAuthoringComponent
|
||||||
|
HasOwner: 0
|
||||||
|
SupportAutoCommandTarget: 1
|
||||||
|
TrackInterpolationDelay: 0
|
||||||
|
GhostGroup: 0
|
||||||
|
UsePreSerialization: 0
|
||||||
|
UseSingleBaseline: 0
|
||||||
|
RollbackPredictedSpawnedGhostState: 0
|
||||||
|
RollbackPredictionOnStructuralChanges: 1
|
||||||
|
DefaultGhostMode: 0
|
||||||
|
SupportedGhostModes: 3
|
||||||
|
OptimizationMode: 0
|
||||||
|
Importance: 1
|
||||||
|
MaxSendRate: 0
|
||||||
|
prefabId: 8565e5eb00679fb45b8b7dac1e2ae9f3
|
||||||
|
--- !u!114 &1664972900393443061
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 3885353946372160549}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 6b1aa9b83194a2c41b940dd9532377f4, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: ProjectM.Authoring::ProjectM.Authoring.BlightClutterAuthoring
|
||||||
|
Remaining: 8
|
||||||
|
ScrapPerHit: 2
|
||||||
|
HitRadius: 1
|
||||||
|
Variant: 0
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6ffeddcc4482c0f44880dc9a555884dd
|
||||||
|
PrefabImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!1 &3885353946372160549
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 3572766465862231365}
|
||||||
|
- component: {fileID: 3909651526955663392}
|
||||||
|
- component: {fileID: 3320445911748035220}
|
||||||
|
- component: {fileID: 9053853372340598254}
|
||||||
|
- component: {fileID: 6834786618115927220}
|
||||||
|
- component: {fileID: 7685488391646220227}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: Pylon
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!4 &3572766465862231365
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 3885353946372160549}
|
||||||
|
serializedVersion: 2
|
||||||
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||||
|
m_LocalScale: {x: 2.5, y: 2.5, z: 2.5}
|
||||||
|
m_ConstrainProportionsScale: 0
|
||||||
|
m_Children: []
|
||||||
|
m_Father: {fileID: 0}
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
--- !u!33 &3909651526955663392
|
||||||
|
MeshFilter:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 3885353946372160549}
|
||||||
|
m_Mesh: {fileID: 4300000, guid: abc00000000010690097314383055197, type: 3}
|
||||||
|
--- !u!23 &3320445911748035220
|
||||||
|
MeshRenderer:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 3885353946372160549}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_CastShadows: 1
|
||||||
|
m_ReceiveShadows: 1
|
||||||
|
m_DynamicOccludee: 1
|
||||||
|
m_StaticShadowCaster: 0
|
||||||
|
m_MotionVectors: 1
|
||||||
|
m_LightProbeUsage: 1
|
||||||
|
m_ReflectionProbeUsage: 1
|
||||||
|
m_RayTracingMode: 2
|
||||||
|
m_RayTraceProcedural: 0
|
||||||
|
m_RayTracingAccelStructBuildFlagsOverride: 0
|
||||||
|
m_RayTracingAccelStructBuildFlags: 1
|
||||||
|
m_SmallMeshCulling: 1
|
||||||
|
m_ForceMeshLod: -1
|
||||||
|
m_MeshLodSelectionBias: 0
|
||||||
|
m_RenderingLayerMask: 1
|
||||||
|
m_RendererPriority: 0
|
||||||
|
m_Materials:
|
||||||
|
- {fileID: 2100000, guid: 9a2c9e498b0fb1d41aa9504f4f843185, 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: 1
|
||||||
|
m_IgnoreNormalsForChartDetection: 0
|
||||||
|
m_ImportantGI: 0
|
||||||
|
m_StitchLightmapSeams: 1
|
||||||
|
m_SelectedEditorRenderState: 3
|
||||||
|
m_MinimumChartSize: 4
|
||||||
|
m_AutoUVMaxDistance: 0.5
|
||||||
|
m_AutoUVMaxAngle: 89
|
||||||
|
m_LightmapParameters: {fileID: 0}
|
||||||
|
m_GlobalIlluminationMeshLod: 0
|
||||||
|
m_SortingLayerID: 0
|
||||||
|
m_SortingLayer: 0
|
||||||
|
m_SortingOrder: 0
|
||||||
|
m_MaskInteraction: 0
|
||||||
|
m_AdditionalVertexStreams: {fileID: 0}
|
||||||
|
--- !u!114 &9053853372340598254
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 3885353946372160549}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: c16549610bfe4458aa9389201d072bb6, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: Unity.Entities.Hybrid::Unity.Entities.Hybrid.Baking.LinkedEntityGroupAuthoring
|
||||||
|
--- !u!114 &6834786618115927220
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 3885353946372160549}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 7c79d771cedb4794bf100ce60df5f764, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: Unity.NetCode.Authoring.Hybrid::Unity.NetCode.GhostAuthoringComponent
|
||||||
|
HasOwner: 0
|
||||||
|
SupportAutoCommandTarget: 1
|
||||||
|
TrackInterpolationDelay: 0
|
||||||
|
GhostGroup: 0
|
||||||
|
UsePreSerialization: 0
|
||||||
|
UseSingleBaseline: 0
|
||||||
|
RollbackPredictedSpawnedGhostState: 0
|
||||||
|
RollbackPredictionOnStructuralChanges: 1
|
||||||
|
DefaultGhostMode: 0
|
||||||
|
SupportedGhostModes: 3
|
||||||
|
OptimizationMode: 0
|
||||||
|
Importance: 1
|
||||||
|
MaxSendRate: 0
|
||||||
|
prefabId:
|
||||||
|
--- !u!114 &7685488391646220227
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 3885353946372160549}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 3f03349205fb1fe43bf6aaff14fce0b7, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: ProjectM.Authoring::ProjectM.Authoring.StructureAuthoring
|
||||||
|
Kind: 6
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7d0637ef90f120a4c9e2ba637dfc00af
|
||||||
|
PrefabImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!1 &3885353946372160549
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 3572766465862231365}
|
||||||
|
- component: {fileID: 3909651526955663392}
|
||||||
|
- component: {fileID: 3320445911748035220}
|
||||||
|
- component: {fileID: 9053853372340598254}
|
||||||
|
- component: {fileID: 6834786618115927220}
|
||||||
|
- component: {fileID: 8793146551006314905}
|
||||||
|
- component: {fileID: 7779358222264100756}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: Wall
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!4 &3572766465862231365
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 3885353946372160549}
|
||||||
|
serializedVersion: 2
|
||||||
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||||
|
m_LocalScale: {x: 2.5, y: 2.5, z: 2.5}
|
||||||
|
m_ConstrainProportionsScale: 0
|
||||||
|
m_Children: []
|
||||||
|
m_Father: {fileID: 0}
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
--- !u!33 &3909651526955663392
|
||||||
|
MeshFilter:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 3885353946372160549}
|
||||||
|
m_Mesh: {fileID: 4300000, guid: abc00000000010690097314383055197, type: 3}
|
||||||
|
--- !u!23 &3320445911748035220
|
||||||
|
MeshRenderer:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 3885353946372160549}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_CastShadows: 1
|
||||||
|
m_ReceiveShadows: 1
|
||||||
|
m_DynamicOccludee: 1
|
||||||
|
m_StaticShadowCaster: 0
|
||||||
|
m_MotionVectors: 1
|
||||||
|
m_LightProbeUsage: 1
|
||||||
|
m_ReflectionProbeUsage: 1
|
||||||
|
m_RayTracingMode: 2
|
||||||
|
m_RayTraceProcedural: 0
|
||||||
|
m_RayTracingAccelStructBuildFlagsOverride: 0
|
||||||
|
m_RayTracingAccelStructBuildFlags: 1
|
||||||
|
m_SmallMeshCulling: 1
|
||||||
|
m_ForceMeshLod: -1
|
||||||
|
m_MeshLodSelectionBias: 0
|
||||||
|
m_RenderingLayerMask: 1
|
||||||
|
m_RendererPriority: 0
|
||||||
|
m_Materials:
|
||||||
|
- {fileID: 2100000, guid: 9a2c9e498b0fb1d41aa9504f4f843185, 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: 1
|
||||||
|
m_IgnoreNormalsForChartDetection: 0
|
||||||
|
m_ImportantGI: 0
|
||||||
|
m_StitchLightmapSeams: 1
|
||||||
|
m_SelectedEditorRenderState: 3
|
||||||
|
m_MinimumChartSize: 4
|
||||||
|
m_AutoUVMaxDistance: 0.5
|
||||||
|
m_AutoUVMaxAngle: 89
|
||||||
|
m_LightmapParameters: {fileID: 0}
|
||||||
|
m_GlobalIlluminationMeshLod: 0
|
||||||
|
m_SortingLayerID: 0
|
||||||
|
m_SortingLayer: 0
|
||||||
|
m_SortingOrder: 0
|
||||||
|
m_MaskInteraction: 0
|
||||||
|
m_AdditionalVertexStreams: {fileID: 0}
|
||||||
|
--- !u!114 &9053853372340598254
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 3885353946372160549}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: c16549610bfe4458aa9389201d072bb6, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: Unity.Entities.Hybrid::Unity.Entities.Hybrid.Baking.LinkedEntityGroupAuthoring
|
||||||
|
--- !u!114 &6834786618115927220
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 3885353946372160549}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 7c79d771cedb4794bf100ce60df5f764, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: Unity.NetCode.Authoring.Hybrid::Unity.NetCode.GhostAuthoringComponent
|
||||||
|
HasOwner: 0
|
||||||
|
SupportAutoCommandTarget: 1
|
||||||
|
TrackInterpolationDelay: 0
|
||||||
|
GhostGroup: 0
|
||||||
|
UsePreSerialization: 0
|
||||||
|
UseSingleBaseline: 0
|
||||||
|
RollbackPredictedSpawnedGhostState: 0
|
||||||
|
RollbackPredictionOnStructuralChanges: 1
|
||||||
|
DefaultGhostMode: 0
|
||||||
|
SupportedGhostModes: 3
|
||||||
|
OptimizationMode: 0
|
||||||
|
Importance: 1
|
||||||
|
MaxSendRate: 0
|
||||||
|
prefabId:
|
||||||
|
--- !u!114 &8793146551006314905
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 3885353946372160549}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 3f03349205fb1fe43bf6aaff14fce0b7, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: ProjectM.Authoring::ProjectM.Authoring.StructureAuthoring
|
||||||
|
Kind: 5
|
||||||
|
--- !u!65 &7779358222264100756
|
||||||
|
BoxCollider:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 3885353946372160549}
|
||||||
|
m_Material: {fileID: 0}
|
||||||
|
m_IncludeLayers:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 0
|
||||||
|
m_ExcludeLayers:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 0
|
||||||
|
m_LayerOverridePriority: 0
|
||||||
|
m_IsTrigger: 0
|
||||||
|
m_ProvidesContacts: 0
|
||||||
|
m_Enabled: 1
|
||||||
|
serializedVersion: 3
|
||||||
|
m_Size: {x: 0.58636487, y: 0.600076, z: 0.9781774}
|
||||||
|
m_Center: {x: 0, y: 0, z: 0.083845735}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1e321aea244cc484f99c1cdd68cb01c4
|
||||||
|
PrefabImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
using ProjectM.Simulation;
|
||||||
|
using Unity.Entities;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace ProjectM.Authoring
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Generic authoring for a non-functional build-structure ghost prefab (Wall / Pylon) — duplicate
|
||||||
|
/// Turret.prefab so the ownerless interpolated GhostAuthoringComponent on PlacedStructure.Type comes free,
|
||||||
|
/// then swap TurretAuthoring for this. Bakes ONLY <see cref="PlacedStructure"/>{Type=<see cref="Kind"/>}
|
||||||
|
/// (no <see cref="Turret"/> stats, so TurretFireSystem ignores it). BuildPlaceSystem overrides Cell +
|
||||||
|
/// LastProcessedTick and adds RegionTag{Base} at placement. <see cref="Kind"/> is a byte (StructureType.*) to
|
||||||
|
/// dodge the cross-assembly enum-in-Burst hazard and the MCP enum-drop gotcha.
|
||||||
|
/// </summary>
|
||||||
|
public class StructureAuthoring : MonoBehaviour
|
||||||
|
{
|
||||||
|
[Tooltip("StructureType byte: 5 = Wall, 6 = Pylon (do NOT use 1-4: Turret + reserved M7 automation).")]
|
||||||
|
public byte Kind = StructureType.Wall;
|
||||||
|
|
||||||
|
private class StructureBaker : Baker<StructureAuthoring>
|
||||||
|
{
|
||||||
|
public override void Bake(StructureAuthoring authoring)
|
||||||
|
{
|
||||||
|
var entity = GetEntity(authoring, TransformUsageFlags.Dynamic);
|
||||||
|
AddComponent(entity, new PlacedStructure
|
||||||
|
{
|
||||||
|
Type = authoring.Kind,
|
||||||
|
Cell = default,
|
||||||
|
NextTick = 0u,
|
||||||
|
LastProcessedTick = 0u,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3f03349205fb1fe43bf6aaff14fce0b7
|
||||||
@@ -20,6 +20,18 @@ namespace ProjectM.Authoring
|
|||||||
[Tooltip("Ore cost to build a turret.")]
|
[Tooltip("Ore cost to build a turret.")]
|
||||||
[Min(0)] public int TurretCostOre = 10;
|
[Min(0)] public int TurretCostOre = 10;
|
||||||
|
|
||||||
|
[Tooltip("Wall structure ghost prefab (StructureAuthoring{Wall} + GhostAuthoring).")]
|
||||||
|
public GameObject WallPrefab;
|
||||||
|
|
||||||
|
[Tooltip("Ore cost to build a wall.")]
|
||||||
|
[Min(0)] public int WallCostOre = 4;
|
||||||
|
|
||||||
|
[Tooltip("Pylon cosmetic-beacon ghost prefab (StructureAuthoring{Pylon} + GhostAuthoring).")]
|
||||||
|
public GameObject PylonPrefab;
|
||||||
|
|
||||||
|
[Tooltip("Ore cost to build a pylon.")]
|
||||||
|
[Min(0)] public int PylonCostOre = 2;
|
||||||
|
|
||||||
private class StructureCatalogBaker : Baker<StructureCatalogAuthoring>
|
private class StructureCatalogBaker : Baker<StructureCatalogAuthoring>
|
||||||
{
|
{
|
||||||
public override void Bake(StructureCatalogAuthoring authoring)
|
public override void Bake(StructureCatalogAuthoring authoring)
|
||||||
@@ -38,6 +50,28 @@ namespace ProjectM.Authoring
|
|||||||
CostAmount = authoring.TurretCostOre,
|
CostAmount = authoring.TurretCostOre,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (authoring.WallPrefab != null)
|
||||||
|
{
|
||||||
|
buf.Add(new StructureCatalogEntry
|
||||||
|
{
|
||||||
|
Type = StructureType.Wall,
|
||||||
|
Prefab = GetEntity(authoring.WallPrefab, TransformUsageFlags.Dynamic),
|
||||||
|
CostResourceId = ResourceId.Ore,
|
||||||
|
CostAmount = authoring.WallCostOre,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (authoring.PylonPrefab != null)
|
||||||
|
{
|
||||||
|
buf.Add(new StructureCatalogEntry
|
||||||
|
{
|
||||||
|
Type = StructureType.Pylon,
|
||||||
|
Prefab = GetEntity(authoring.PylonPrefab, TransformUsageFlags.Dynamic),
|
||||||
|
CostResourceId = ResourceId.Ore,
|
||||||
|
CostAmount = authoring.PylonCostOre,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
using ProjectM.Simulation;
|
||||||
|
using Unity.Entities;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace ProjectM.Authoring
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Authoring for a Blight-clutter ghost prefab (ownerless interpolated — duplicate from ResourceNode.prefab
|
||||||
|
/// so the GhostAuthoringComponent comes free). Bakes <see cref="BlightClutter"/> + <see cref="HitRadius"/>
|
||||||
|
/// (reused for the harvest/clear sweep) + <see cref="RegionTag"/>{Expedition} so GhostRelevancy scopes it to
|
||||||
|
/// expedition players. The field spawner overrides Variant (round-robin) + Position per instance. Defaults are
|
||||||
|
/// inline here (mirrors ResourceNodeAuthoring), not in Tuning.
|
||||||
|
/// </summary>
|
||||||
|
public class BlightClutterAuthoring : MonoBehaviour
|
||||||
|
{
|
||||||
|
[Tooltip("Hit-points before the clutter shatters.")]
|
||||||
|
[Min(1)] public int Remaining = 8;
|
||||||
|
|
||||||
|
[Tooltip("Scrap (Biomass) yielded per projectile hit — the 'minor scrap' trickle.")]
|
||||||
|
[Min(1f)] public float ScrapPerHit = 2f;
|
||||||
|
|
||||||
|
[Tooltip("Hit radius (world units) for the clear sweep.")]
|
||||||
|
[Min(0f)] public float HitRadius = 1.0f;
|
||||||
|
|
||||||
|
[Tooltip("Default visual variant (the spawner round-robins this per piece).")]
|
||||||
|
public byte Variant = 0;
|
||||||
|
|
||||||
|
private class BlightClutterBaker : Baker<BlightClutterAuthoring>
|
||||||
|
{
|
||||||
|
public override void Bake(BlightClutterAuthoring authoring)
|
||||||
|
{
|
||||||
|
var entity = GetEntity(authoring, TransformUsageFlags.Dynamic);
|
||||||
|
AddComponent(entity, new BlightClutter
|
||||||
|
{
|
||||||
|
Remaining = authoring.Remaining,
|
||||||
|
Variant = authoring.Variant,
|
||||||
|
ScrapResourceId = ResourceId.Biomass,
|
||||||
|
ScrapPerHit = authoring.ScrapPerHit,
|
||||||
|
});
|
||||||
|
AddComponent(entity, new HitRadius { Value = authoring.HitRadius });
|
||||||
|
AddComponent(entity, new RegionTag { Region = RegionId.Expedition });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6b1aa9b83194a2c41b940dd9532377f4
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
using ProjectM.Simulation;
|
||||||
|
using Unity.Entities;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace ProjectM.Authoring
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Authoring for the baked <see cref="ClutterFieldSpawner"/> singleton (mirrors ResourceFieldSpawnerAuthoring).
|
||||||
|
/// Place once in the gameplay subscene and assign the Blight-clutter ghost prefab; ExpeditionFieldSystem
|
||||||
|
/// scatters the clutter each expedition alongside the resource field. Carries no transform.
|
||||||
|
/// </summary>
|
||||||
|
public class ClutterFieldSpawnerAuthoring : MonoBehaviour
|
||||||
|
{
|
||||||
|
[Tooltip("Blight-clutter ghost prefab. Must carry BlightClutterAuthoring + a GhostAuthoringComponent (ownerless, interpolated).")]
|
||||||
|
public GameObject ClutterPrefab;
|
||||||
|
|
||||||
|
[Tooltip("Number of clutter pieces per expedition.")]
|
||||||
|
[Min(1)] public int Count = 14;
|
||||||
|
|
||||||
|
[Tooltip("Scatter radius (world units) around the expedition origin.")]
|
||||||
|
[Min(1f)] public float Radius = 14f;
|
||||||
|
|
||||||
|
private class ClutterFieldSpawnerBaker : Baker<ClutterFieldSpawnerAuthoring>
|
||||||
|
{
|
||||||
|
public override void Bake(ClutterFieldSpawnerAuthoring authoring)
|
||||||
|
{
|
||||||
|
var entity = GetEntity(authoring, TransformUsageFlags.None);
|
||||||
|
AddComponent(entity, new ClutterFieldSpawner
|
||||||
|
{
|
||||||
|
Prefab = authoring.ClutterPrefab != null
|
||||||
|
? GetEntity(authoring.ClutterPrefab, TransformUsageFlags.Dynamic)
|
||||||
|
: Entity.Null,
|
||||||
|
Count = authoring.Count,
|
||||||
|
Radius = authoring.Radius,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 02623cc30f984104b88a75782bf0dd07
|
||||||
@@ -30,6 +30,12 @@ namespace ProjectM.Client
|
|||||||
/// <summary>EDITOR / execute_code hook: queue a turret placement at a specific cell.</summary>
|
/// <summary>EDITOR / execute_code hook: queue a turret placement at a specific cell.</summary>
|
||||||
public static void PlaceTurret(int cellX, int cellZ) => PlaceStructure(StructureType.Turret, cellX, cellZ);
|
public static void PlaceTurret(int cellX, int cellZ) => PlaceStructure(StructureType.Turret, cellX, cellZ);
|
||||||
|
|
||||||
|
/// <summary>EDITOR / execute_code hook: queue a wall placement at a specific cell.</summary>
|
||||||
|
public static void PlaceWall(int cellX, int cellZ) => PlaceStructure(StructureType.Wall, cellX, cellZ);
|
||||||
|
|
||||||
|
/// <summary>EDITOR / execute_code hook: queue a pylon placement at a specific cell.</summary>
|
||||||
|
public static void PlacePylon(int cellX, int cellZ) => PlaceStructure(StructureType.Pylon, cellX, cellZ);
|
||||||
|
|
||||||
/// <summary>EDITOR / execute_code hook: queue an ability-damage upgrade.</summary>
|
/// <summary>EDITOR / execute_code hook: queue an ability-damage upgrade.</summary>
|
||||||
public static void UpgradeAbility() => s_PendingUpgrades++;
|
public static void UpgradeAbility() => s_PendingUpgrades++;
|
||||||
#endif
|
#endif
|
||||||
@@ -49,6 +55,10 @@ namespace ProjectM.Client
|
|||||||
{
|
{
|
||||||
if (keyboard.bKey.wasPressedThisFrame && TryGetLocalPlayerCell(out int2 cell))
|
if (keyboard.bKey.wasPressedThisFrame && TryGetLocalPlayerCell(out int2 cell))
|
||||||
SendBuild(connection, StructureType.Turret, cell.x, cell.y);
|
SendBuild(connection, StructureType.Turret, cell.x, cell.y);
|
||||||
|
if (keyboard.vKey.wasPressedThisFrame && TryGetLocalPlayerCell(out int2 wcell))
|
||||||
|
SendBuild(connection, StructureType.Wall, wcell.x, wcell.y);
|
||||||
|
if (keyboard.nKey.wasPressedThisFrame && TryGetLocalPlayerCell(out int2 pcell))
|
||||||
|
SendBuild(connection, StructureType.Pylon, pcell.x, pcell.y);
|
||||||
if (keyboard.uKey.wasPressedThisFrame)
|
if (keyboard.uKey.wasPressedThisFrame)
|
||||||
SendUpgrade(connection);
|
SendUpgrade(connection);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,226 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using ProjectM.Simulation;
|
||||||
|
using Unity.Entities;
|
||||||
|
using Unity.Mathematics;
|
||||||
|
using Unity.NetCode;
|
||||||
|
using Unity.Transforms;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace ProjectM.Client
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Client-only WORLD JUICE for harvesting resource nodes + smashing Blight clutter. A managed presentation
|
||||||
|
/// system (SystemBase, main thread, NO Burst) in <see cref="PresentationSystemGroup"/> that REACTS to
|
||||||
|
/// replicated state — it never runs simulation. Each frame it edge-detects every node/clutter ghost's
|
||||||
|
/// replicated <c>Remaining</c>: a decrease spawns a small tinted chip burst + soft SFX; a despawn (the server
|
||||||
|
/// destroyed it — depletion or shatter) spawns a clear burst + SFX, and clutter adds a camera punch (the
|
||||||
|
/// "carve through the frontier" smash). A PROXIMITY GATE suppresses the prune VFX unless the despawned
|
||||||
|
/// entity's last position was near the local player, so the region-transit despawn storm at +1000 X stays
|
||||||
|
/// silent off-camera (GhostRelevancy drops every expedition ghost at once when the player walks home).
|
||||||
|
/// Procedural particles + procedural SFX (mirrors CombatFeedbackSystem; self-contained); knobs live in
|
||||||
|
/// <see cref="WorldFeelConfig"/>. Never destroys a ghost — GhostDespawnSystem owns despawn; we only OBSERVE.
|
||||||
|
/// </summary>
|
||||||
|
[WorldSystemFilter(WorldSystemFilterFlags.ClientSimulation)]
|
||||||
|
[UpdateInGroup(typeof(PresentationSystemGroup))]
|
||||||
|
public partial class WorldFeedbackSystem : SystemBase
|
||||||
|
{
|
||||||
|
struct Cache { public int Remaining; public float3 Pos; public bool IsClutter; public Color Tint; }
|
||||||
|
|
||||||
|
readonly Dictionary<Entity, Cache> _cache = new();
|
||||||
|
readonly HashSet<Entity> _seen = new();
|
||||||
|
readonly List<Entity> _stale = new();
|
||||||
|
|
||||||
|
Transform _fxRoot;
|
||||||
|
ParticleSystem _chipFx;
|
||||||
|
ParticleSystem _clearFx;
|
||||||
|
AudioClip _chipClip;
|
||||||
|
AudioClip _clearClip;
|
||||||
|
|
||||||
|
protected override void OnCreate()
|
||||||
|
{
|
||||||
|
_chipClip = MakeClip("harvest_chip", 900f, 1400f, 0.06f, 0.30f);
|
||||||
|
_clearClip = MakeClip("clutter_clear", 420f, 90f, 0.22f, 0.50f);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnStartRunning()
|
||||||
|
{
|
||||||
|
if (_fxRoot != null) return;
|
||||||
|
_fxRoot = new GameObject("~WorldFeedbackFX").transform;
|
||||||
|
var mat = MakeParticleMaterial();
|
||||||
|
_chipFx = MakeBurst("HarvestChips", mat, new Color(2.6f, 1.9f, 0.7f), 0.10f, 5f, 0.30f, 256);
|
||||||
|
_clearFx = MakeBurst("ClutterClear", mat, new Color(3.0f, 1.1f, 0.25f), 0.16f, 7f, 0.45f, 512);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnDestroy()
|
||||||
|
{
|
||||||
|
if (_fxRoot != null) Object.Destroy(_fxRoot.gameObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnUpdate()
|
||||||
|
{
|
||||||
|
if (!WorldFeelConfig.Enabled) { _cache.Clear(); return; }
|
||||||
|
|
||||||
|
// Complete predicted/interpolation jobs writing these before the main-thread reads.
|
||||||
|
EntityManager.CompleteDependencyBeforeRO<ResourceNode>();
|
||||||
|
EntityManager.CompleteDependencyBeforeRO<BlightClutter>();
|
||||||
|
EntityManager.CompleteDependencyBeforeRO<LocalTransform>();
|
||||||
|
|
||||||
|
// Local player position (for the proximity gate).
|
||||||
|
bool haveLocal = false;
|
||||||
|
float3 localPos = default;
|
||||||
|
foreach (var xf in SystemAPI.Query<RefRO<LocalTransform>>().WithAll<GhostOwnerIsLocal, PlayerTag>())
|
||||||
|
{
|
||||||
|
localPos = xf.ValueRO.Position;
|
||||||
|
haveLocal = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_seen.Clear();
|
||||||
|
|
||||||
|
// Resource nodes — chip on depletion.
|
||||||
|
foreach (var (node, xf, e) in
|
||||||
|
SystemAPI.Query<RefRO<ResourceNode>, RefRO<LocalTransform>>().WithEntityAccess())
|
||||||
|
{
|
||||||
|
_seen.Add(e);
|
||||||
|
Observe(e, node.ValueRO.Remaining, xf.ValueRO.Position, false, TintForResource(node.ValueRO.ResourceId));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blight clutter — chip on damage.
|
||||||
|
foreach (var (clutter, xf, e) in
|
||||||
|
SystemAPI.Query<RefRO<BlightClutter>, RefRO<LocalTransform>>().WithEntityAccess())
|
||||||
|
{
|
||||||
|
_seen.Add(e);
|
||||||
|
Observe(e, clutter.ValueRO.Remaining, xf.ValueRO.Position, true, WorldFeelConfig.WildTint);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prune: a despawn = the server destroyed it (node depleted / clutter shattered). Gate on proximity so
|
||||||
|
// a region-transit despawn storm (every expedition ghost dropped at once, far at +1000 X) stays silent.
|
||||||
|
if (_cache.Count != _seen.Count)
|
||||||
|
{
|
||||||
|
_stale.Clear();
|
||||||
|
foreach (var kv in _cache)
|
||||||
|
if (!_seen.Contains(kv.Key)) _stale.Add(kv.Key);
|
||||||
|
|
||||||
|
float rangeSq = WorldFeelConfig.ProximityRange * WorldFeelConfig.ProximityRange;
|
||||||
|
for (int i = 0; i < _stale.Count; i++)
|
||||||
|
{
|
||||||
|
var c = _cache[_stale[i]];
|
||||||
|
if (haveLocal && math.distancesq(c.Pos, localPos) <= rangeSq)
|
||||||
|
{
|
||||||
|
EmitTinted(_clearFx, (Vector3)c.Pos + Vector3.up * 0.6f, WorldFeelConfig.ClearBurstCount, c.Tint);
|
||||||
|
PlayClip(_clearClip, (Vector3)c.Pos, WorldFeelConfig.ClearSfxVolume);
|
||||||
|
if (c.IsClutter)
|
||||||
|
{
|
||||||
|
PrototypeCameraRig.PunchFov(WorldFeelConfig.ClearFovKick, 90f);
|
||||||
|
PrototypeCameraRig.AddShake(WorldFeelConfig.ClearShake);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_cache.Remove(_stale[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Observe(Entity e, int remaining, float3 pos, bool isClutter, Color tint)
|
||||||
|
{
|
||||||
|
if (_cache.TryGetValue(e, out var prev) && remaining < prev.Remaining)
|
||||||
|
{
|
||||||
|
EmitTinted(_chipFx, (Vector3)pos + Vector3.up * 0.6f, WorldFeelConfig.ChipBurstCount, tint);
|
||||||
|
PlayClip(_chipClip, (Vector3)pos, WorldFeelConfig.ChipSfxVolume);
|
||||||
|
}
|
||||||
|
_cache[e] = new Cache { Remaining = remaining, Pos = pos, IsClutter = isClutter, Tint = tint };
|
||||||
|
}
|
||||||
|
|
||||||
|
static Color TintForResource(byte resourceId)
|
||||||
|
{
|
||||||
|
if (resourceId == ResourceId.Ore) return WorldFeelConfig.OreTint;
|
||||||
|
if (resourceId == ResourceId.Biomass) return WorldFeelConfig.BiomassTint;
|
||||||
|
return WorldFeelConfig.WildTint; // Aether + default
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- procedural particles + SFX (mirrors CombatFeedbackSystem; self-contained) ----
|
||||||
|
|
||||||
|
static void EmitTinted(ParticleSystem ps, Vector3 pos, int count, Color tint)
|
||||||
|
{
|
||||||
|
if (ps == null) return;
|
||||||
|
var main = ps.main;
|
||||||
|
main.startColor = tint;
|
||||||
|
ps.transform.position = pos;
|
||||||
|
ps.Emit(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Material MakeParticleMaterial()
|
||||||
|
{
|
||||||
|
Shader sh = Shader.Find("Sprites/Default");
|
||||||
|
if (sh == null) sh = Shader.Find("Universal Render Pipeline/Particles/Unlit");
|
||||||
|
if (sh == null) sh = Shader.Find("Unlit/Color");
|
||||||
|
return new Material(sh) { name = "WorldFeedbackParticle" };
|
||||||
|
}
|
||||||
|
|
||||||
|
ParticleSystem MakeBurst(string name, Material mat, Color color, float size, float speed, float life, int max)
|
||||||
|
{
|
||||||
|
var go = new GameObject(name);
|
||||||
|
go.transform.SetParent(_fxRoot, false);
|
||||||
|
var ps = go.AddComponent<ParticleSystem>();
|
||||||
|
|
||||||
|
var main = ps.main;
|
||||||
|
main.loop = false;
|
||||||
|
main.playOnAwake = false;
|
||||||
|
main.startLifetime = life;
|
||||||
|
main.startSpeed = speed;
|
||||||
|
main.startSize = size;
|
||||||
|
main.startColor = color;
|
||||||
|
main.maxParticles = max;
|
||||||
|
main.gravityModifier = 0.25f;
|
||||||
|
main.simulationSpace = ParticleSystemSimulationSpace.World;
|
||||||
|
|
||||||
|
var emission = ps.emission;
|
||||||
|
emission.enabled = false; // manual Emit(count)
|
||||||
|
|
||||||
|
var shape = ps.shape;
|
||||||
|
shape.enabled = true;
|
||||||
|
shape.shapeType = ParticleSystemShapeType.Sphere;
|
||||||
|
shape.radius = 0.10f;
|
||||||
|
|
||||||
|
var colOverLife = ps.colorOverLifetime;
|
||||||
|
colOverLife.enabled = true;
|
||||||
|
var grad = new Gradient();
|
||||||
|
grad.SetKeys(
|
||||||
|
new[] { new GradientColorKey(Color.white, 0f), new GradientColorKey(Color.white, 1f) },
|
||||||
|
new[] { new GradientAlphaKey(1f, 0f), new GradientAlphaKey(0f, 1f) });
|
||||||
|
colOverLife.color = new ParticleSystem.MinMaxGradient(grad);
|
||||||
|
|
||||||
|
var sizeOverLife = ps.sizeOverLifetime;
|
||||||
|
sizeOverLife.enabled = true;
|
||||||
|
sizeOverLife.size = new ParticleSystem.MinMaxCurve(1f, AnimationCurve.Linear(0f, 1f, 1f, 0.2f));
|
||||||
|
|
||||||
|
var renderer = ps.GetComponent<ParticleSystemRenderer>();
|
||||||
|
renderer.material = mat;
|
||||||
|
renderer.renderMode = ParticleSystemRenderMode.Billboard;
|
||||||
|
return ps;
|
||||||
|
}
|
||||||
|
|
||||||
|
static AudioClip MakeClip(string name, float f0, float f1, float dur, float vol)
|
||||||
|
{
|
||||||
|
const int rate = 44100;
|
||||||
|
int len = Mathf.Max(16, (int)(dur * rate));
|
||||||
|
var clip = AudioClip.Create(name, len, 1, rate, false);
|
||||||
|
var data = new float[len];
|
||||||
|
float phase = 0f;
|
||||||
|
for (int i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
float t = i / (float)len;
|
||||||
|
float env = Mathf.Exp(-5f * t);
|
||||||
|
float freq = Mathf.Lerp(f0, f1, t);
|
||||||
|
phase += 2f * Mathf.PI * freq / rate;
|
||||||
|
data[i] = Mathf.Sin(phase) * env * vol;
|
||||||
|
}
|
||||||
|
clip.SetData(data, 0);
|
||||||
|
return clip;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PlayClip(AudioClip clip, Vector3 pos, float vol)
|
||||||
|
{
|
||||||
|
if (clip == null) return;
|
||||||
|
AudioSource.PlayClipAtPoint(clip, pos, vol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 470fdf29ccc332d42bb93e0b1e249dfb
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace ProjectM.Client
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Live-tunable knobs for the client-only WORLD-FEEDBACK slice (harvesting nodes + smashing Blight clutter).
|
||||||
|
/// A static bridge (mirrors <see cref="FeelConfig"/>) so values can be poked at runtime via MCP execute_code
|
||||||
|
/// without a recompile (e.g. <c>ProjectM.Client.WorldFeelConfig.ClearFovKick = 1.2f;</c>). Read ONLY by
|
||||||
|
/// <see cref="WorldFeedbackSystem"/> (managed, main-thread). NEVER read from a [BurstCompile] system
|
||||||
|
/// (managed-static + Color-in-Burst hazards). <see cref="ResetDefaults"/> re-stamps on play-enter via
|
||||||
|
/// [RuntimeInitializeOnLoadMethod] because statics survive fast-enter-playmode reloads (else a poked value
|
||||||
|
/// leaks across play-enters).
|
||||||
|
/// </summary>
|
||||||
|
public static class WorldFeelConfig
|
||||||
|
{
|
||||||
|
/// <summary>Master gate for harvest/clear feedback.</summary>
|
||||||
|
public static bool Enabled;
|
||||||
|
|
||||||
|
/// <summary>Particle burst when a node/clutter loses hit-points (a chip).</summary>
|
||||||
|
public static int ChipBurstCount;
|
||||||
|
/// <summary>Particle burst when a node depletes / clutter shatters (the clear).</summary>
|
||||||
|
public static int ClearBurstCount;
|
||||||
|
|
||||||
|
/// <summary>Soft SFX volume on a chip.</summary>
|
||||||
|
public static float ChipSfxVolume;
|
||||||
|
/// <summary>SFX volume on a shatter / deplete.</summary>
|
||||||
|
public static float ClearSfxVolume;
|
||||||
|
|
||||||
|
/// <summary>Camera FOV kick (deg) when clutter shatters near the player — the satisfying smash. 0 = off.</summary>
|
||||||
|
public static float ClearFovKick;
|
||||||
|
/// <summary>Camera shake when clutter shatters near the player.</summary>
|
||||||
|
public static float ClearShake;
|
||||||
|
|
||||||
|
/// <summary>Only fire prune VFX when the despawned entity's last position is within this distance of the
|
||||||
|
/// local player, so a region-transit despawn storm at +1000 X stays silent off-camera.</summary>
|
||||||
|
public static float ProximityRange;
|
||||||
|
|
||||||
|
/// <summary>Tint for wild-Aether clutter shatter + Aether-node chips (HDR orange, pushes past bloom).</summary>
|
||||||
|
public static Color WildTint;
|
||||||
|
/// <summary>Tint for Ore-node chips (HDR amber).</summary>
|
||||||
|
public static Color OreTint;
|
||||||
|
/// <summary>Tint for Biomass-node chips (HDR sickly green).</summary>
|
||||||
|
public static Color BiomassTint;
|
||||||
|
|
||||||
|
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
||||||
|
public static void ResetDefaults()
|
||||||
|
{
|
||||||
|
Enabled = true;
|
||||||
|
ChipBurstCount = 6;
|
||||||
|
ClearBurstCount = 18;
|
||||||
|
ChipSfxVolume = 0.30f;
|
||||||
|
ClearSfxVolume = 0.55f;
|
||||||
|
ClearFovKick = 0.8f;
|
||||||
|
ClearShake = 0.12f;
|
||||||
|
ProximityRange = 40f;
|
||||||
|
WildTint = new Color(3.0f, 1.1f, 0.25f);
|
||||||
|
OreTint = new Color(2.6f, 1.9f, 0.7f);
|
||||||
|
BiomassTint = new Color(0.9f, 2.4f, 0.8f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1d6f72b25a2a1d14e988b5999e029c5c
|
||||||
@@ -12,10 +12,13 @@ namespace ProjectM.Server
|
|||||||
/// counts players whose server-only <see cref="RegionTag"/> is the Expedition region, and on the
|
/// counts players whose server-only <see cref="RegionTag"/> is the Expedition region, and on the
|
||||||
/// empty->occupied edge (a new sortie) bumps <see cref="CycleRuntime.ExpeditionEpoch"/> and scatters
|
/// empty->occupied edge (a new sortie) bumps <see cref="CycleRuntime.ExpeditionEpoch"/> and scatters
|
||||||
/// <see cref="ResourceFieldSpawner.Count"/> resource-node ghosts (seeded by the epoch) around the expedition
|
/// <see cref="ResourceFieldSpawner.Count"/> resource-node ghosts (seeded by the epoch) around the expedition
|
||||||
/// origin, each RegionTag{Expedition}; on the occupied->empty edge (the LAST player left) it destroys every
|
/// origin — PLUS, if a <see cref="ClutterFieldSpawner"/> singleton is present,
|
||||||
/// node. So the field lives as long as anyone is out there, not on a global timer. Plain server
|
/// <see cref="ClutterFieldSpawner.Count"/> Blight-clutter ghosts (seeded DISTINCTLY so clutter and nodes don't
|
||||||
/// SimulationSystemGroup. Server-authoritative; clients despawn nodes via GhostDespawnSystem. Per-epoch
|
/// co-locate, Variant round-robined), each RegionTag{Expedition}; on the occupied->empty edge (the LAST
|
||||||
/// reproducible (the seed is the monotonic int epoch, compared by equality — never tick math, never 0).
|
/// player left) it destroys every node AND every clutter piece. So the field lives as long as anyone is out
|
||||||
|
/// there, not on a global timer. Plain server SimulationSystemGroup. Server-authoritative; clients despawn
|
||||||
|
/// ghosts via GhostDespawnSystem. Per-epoch reproducible (the seed is the monotonic int epoch, compared by
|
||||||
|
/// equality — never tick math, never 0).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[BurstCompile]
|
[BurstCompile]
|
||||||
[WorldSystemFilter(WorldSystemFilterFlags.ServerSimulation)]
|
[WorldSystemFilter(WorldSystemFilterFlags.ServerSimulation)]
|
||||||
@@ -80,14 +83,42 @@ namespace ProjectM.Server
|
|||||||
rn.ResourceId = (byte)(ResourceId.Aether + (byte)(i % 3));
|
rn.ResourceId = (byte)(ResourceId.Aether + (byte)(i % 3));
|
||||||
ecb.SetComponent(node, rn);
|
ecb.SetComponent(node, rn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Blight clutter (OPTIONAL singleton): scatter alongside the nodes with a DISTINCT seed so the
|
||||||
|
// two fields don't co-locate. Round-robin Variant for client visual variety.
|
||||||
|
if (SystemAPI.TryGetSingleton<ClutterFieldSpawner>(out var clutterSpawner)
|
||||||
|
&& clutterSpawner.Prefab != Entity.Null)
|
||||||
|
{
|
||||||
|
var clutterXform = SystemAPI.GetComponent<LocalTransform>(clutterSpawner.Prefab);
|
||||||
|
var prefabClutter = SystemAPI.GetComponent<BlightClutter>(clutterSpawner.Prefab);
|
||||||
|
var crng = new Random((uint)math.max(1, runtime.ExpeditionEpoch * 2 + 1));
|
||||||
|
int ccount = math.max(1, clutterSpawner.Count);
|
||||||
|
for (int i = 0; i < ccount; i++)
|
||||||
|
{
|
||||||
|
var piece = ecb.Instantiate(clutterSpawner.Prefab);
|
||||||
|
|
||||||
|
float ang = crng.NextFloat(0f, math.PI * 2f);
|
||||||
|
float rad = clutterSpawner.Radius * math.sqrt(crng.NextFloat(0f, 1f));
|
||||||
|
var xform = clutterXform;
|
||||||
|
xform.Position = origin + new float3(math.cos(ang) * rad, 0f, math.sin(ang) * rad);
|
||||||
|
ecb.SetComponent(piece, xform);
|
||||||
|
|
||||||
|
var bc = prefabClutter;
|
||||||
|
bc.Variant = (byte)(i % 3);
|
||||||
|
ecb.SetComponent(piece, bc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
runtime.LastSpawnedEpoch = runtime.ExpeditionEpoch;
|
runtime.LastSpawnedEpoch = runtime.ExpeditionEpoch;
|
||||||
}
|
}
|
||||||
|
|
||||||
// DESTROY: the last player left the expedition — clear the whole field.
|
// DESTROY: the last player left the expedition — clear the whole field (nodes + clutter).
|
||||||
if (wasOccupied && !occupied)
|
if (wasOccupied && !occupied)
|
||||||
{
|
{
|
||||||
foreach (var (rn, e) in SystemAPI.Query<RefRO<ResourceNode>>().WithEntityAccess())
|
foreach (var (rn, e) in SystemAPI.Query<RefRO<ResourceNode>>().WithEntityAccess())
|
||||||
ecb.DestroyEntity(e);
|
ecb.DestroyEntity(e);
|
||||||
|
foreach (var (bc, e) in SystemAPI.Query<RefRO<BlightClutter>>().WithEntityAccess())
|
||||||
|
ecb.DestroyEntity(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
runtime.PrevExpeditionOccupied = (byte)(occupied ? 1 : 0);
|
runtime.PrevExpeditionOccupied = (byte)(occupied ? 1 : 0);
|
||||||
|
|||||||
@@ -9,18 +9,21 @@ using Unity.Transforms;
|
|||||||
namespace ProjectM.Server
|
namespace ProjectM.Server
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Server-only resource harvest: sweeps each surviving projectile's this-tick travel segment against
|
/// Server-only resource harvest + Blight-clutter clearing: sweeps each surviving projectile's this-tick travel
|
||||||
/// resource-node ghosts and deposits <see cref="ResourceNode.HarvestPerHit"/> of the node's
|
/// segment against a UNIFIED target set of resource-node ghosts AND Blight-clutter ghosts, deposits the hit
|
||||||
/// <see cref="ResourceNode.ResourceId"/> into the GLOBAL resource ledger (the CycleDirector's
|
/// target's yield into the GLOBAL resource ledger (the CycleDirector's <see cref="StorageEntry"/> buffer,
|
||||||
/// <see cref="StorageEntry"/> buffer, resolved via <see cref="ResourceLedger"/> — NEVER
|
/// resolved via <see cref="ResourceLedger"/> — NEVER GetSingleton<StorageEntry>, which would collide with
|
||||||
/// GetSingleton<StorageEntry>, which would collide with the base storage container). Runs in the plain
|
/// the base storage container) and decrements its Remaining; the target despawns at <= 0. Nodes deposit
|
||||||
/// server SimulationSystemGroup <c>[UpdateAfter(PredictedSimulationSystemGroup)]</c> — after
|
/// <see cref="ResourceNode.ResourceId"/> @ HarvestPerHit; clutter deposits <see cref="BlightClutter.ScrapResourceId"/>
|
||||||
/// ProjectileDamageSystem has already consumed Health-target hits and range-expired projectiles, so this
|
/// @ ScrapPerHit (a small "minor scrap" trickle — carving through the frontier). UNIFYING the two into one sweep
|
||||||
/// only sees true survivors. The swept segment is reconstructed from <see cref="Projectile.LastStep"/>
|
/// is a CORRECTNESS requirement: two separate sweeps would each DestroyEntity a projectile that overlaps a node
|
||||||
/// (written by ProjectileMoveSystem in the fixed-step group), so it is tunnelling-safe WITHOUT depending on
|
/// AND a clutter piece — a double DestroyEntity throws at ECB playback. Runs in the plain server
|
||||||
/// this plain group's variable-frame DeltaTime. A node hit by two projectiles in one tick deposits twice
|
/// SimulationSystemGroup <c>[UpdateAfter(PredictedSimulationSystemGroup)]</c> — after ProjectileDamageSystem has
|
||||||
/// but is destroyed exactly once. Relies on the asserted ~1000-unit base/expedition coordinate gap so a
|
/// consumed Health-target hits and range-expired projectiles, so this only sees true survivors. The swept
|
||||||
/// base projectile can never geometrically reach an expedition node.
|
/// segment is reconstructed from <see cref="Projectile.LastStep"/> (written by ProjectileMoveSystem in the
|
||||||
|
/// fixed-step group), so it is tunnelling-safe WITHOUT depending on this plain group's variable-frame DeltaTime.
|
||||||
|
/// A target hit by two projectiles in one tick deposits twice but is destroyed exactly once. Relies on the
|
||||||
|
/// asserted ~1000-unit base/expedition coordinate gap so a base projectile can never reach an expedition target.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[BurstCompile]
|
[BurstCompile]
|
||||||
[WorldSystemFilter(WorldSystemFilterFlags.ServerSimulation)]
|
[WorldSystemFilter(WorldSystemFilterFlags.ServerSimulation)]
|
||||||
@@ -34,7 +37,6 @@ namespace ProjectM.Server
|
|||||||
public void OnCreate(ref SystemState state)
|
public void OnCreate(ref SystemState state)
|
||||||
{
|
{
|
||||||
state.RequireForUpdate<Projectile>();
|
state.RequireForUpdate<Projectile>();
|
||||||
state.RequireForUpdate<ResourceNode>();
|
|
||||||
state.RequireForUpdate<ResourceLedger>();
|
state.RequireForUpdate<ResourceLedger>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,26 +46,43 @@ namespace ProjectM.Server
|
|||||||
var ledgerEntity = SystemAPI.GetSingletonEntity<ResourceLedger>();
|
var ledgerEntity = SystemAPI.GetSingletonEntity<ResourceLedger>();
|
||||||
var ledger = SystemAPI.GetBuffer<StorageEntry>(ledgerEntity);
|
var ledger = SystemAPI.GetBuffer<StorageEntry>(ledgerEntity);
|
||||||
|
|
||||||
// Snapshot all nodes once this tick.
|
// Snapshot all harvest/clear targets (nodes + clutter) once this tick into a UNIFIED set.
|
||||||
var nodeEntities = new NativeList<Entity>(Allocator.Temp);
|
var tgtEntity = new NativeList<Entity>(Allocator.Temp);
|
||||||
var nodePos = new NativeList<float2>(Allocator.Temp);
|
var tgtPos = new NativeList<float2>(Allocator.Temp);
|
||||||
var nodeRadius = new NativeList<float>(Allocator.Temp);
|
var tgtRadius = new NativeList<float>(Allocator.Temp);
|
||||||
var nodeRemaining = new NativeList<int>(Allocator.Temp);
|
var tgtRemaining = new NativeList<int>(Allocator.Temp);
|
||||||
var nodeResource = new NativeList<byte>(Allocator.Temp);
|
var tgtYieldId = new NativeList<byte>(Allocator.Temp);
|
||||||
var nodePerHit = new NativeList<float>(Allocator.Temp);
|
var tgtYieldPerHit = new NativeList<float>(Allocator.Temp);
|
||||||
|
var tgtVariant = new NativeList<byte>(Allocator.Temp);
|
||||||
|
var tgtIsClutter = new NativeList<bool>(Allocator.Temp);
|
||||||
|
|
||||||
foreach (var (xform, hr, node, e) in
|
foreach (var (xform, hr, node, e) in
|
||||||
SystemAPI.Query<RefRO<LocalTransform>, RefRO<HitRadius>, RefRO<ResourceNode>>().WithEntityAccess())
|
SystemAPI.Query<RefRO<LocalTransform>, RefRO<HitRadius>, RefRO<ResourceNode>>().WithEntityAccess())
|
||||||
{
|
{
|
||||||
nodeEntities.Add(e);
|
tgtEntity.Add(e);
|
||||||
nodePos.Add(xform.ValueRO.Position.xz);
|
tgtPos.Add(xform.ValueRO.Position.xz);
|
||||||
nodeRadius.Add(hr.ValueRO.Value);
|
tgtRadius.Add(hr.ValueRO.Value);
|
||||||
nodeRemaining.Add(node.ValueRO.Remaining);
|
tgtRemaining.Add(node.ValueRO.Remaining);
|
||||||
nodeResource.Add(node.ValueRO.ResourceId);
|
tgtYieldId.Add(node.ValueRO.ResourceId);
|
||||||
nodePerHit.Add(node.ValueRO.HarvestPerHit);
|
tgtYieldPerHit.Add(node.ValueRO.HarvestPerHit);
|
||||||
|
tgtVariant.Add(0);
|
||||||
|
tgtIsClutter.Add(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
var destroyed = new NativeArray<bool>(nodeEntities.Length, Allocator.Temp);
|
foreach (var (xform, hr, clutter, e) in
|
||||||
|
SystemAPI.Query<RefRO<LocalTransform>, RefRO<HitRadius>, RefRO<BlightClutter>>().WithEntityAccess())
|
||||||
|
{
|
||||||
|
tgtEntity.Add(e);
|
||||||
|
tgtPos.Add(xform.ValueRO.Position.xz);
|
||||||
|
tgtRadius.Add(hr.ValueRO.Value);
|
||||||
|
tgtRemaining.Add(clutter.ValueRO.Remaining);
|
||||||
|
tgtYieldId.Add(clutter.ValueRO.ScrapResourceId);
|
||||||
|
tgtYieldPerHit.Add(clutter.ValueRO.ScrapPerHit);
|
||||||
|
tgtVariant.Add(clutter.ValueRO.Variant);
|
||||||
|
tgtIsClutter.Add(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
var destroyed = new NativeArray<bool>(tgtEntity.Length, Allocator.Temp);
|
||||||
var ecb = new EntityCommandBuffer(Allocator.Temp);
|
var ecb = new EntityCommandBuffer(Allocator.Temp);
|
||||||
|
|
||||||
foreach (var (xform, proj, projEntity) in
|
foreach (var (xform, proj, projEntity) in
|
||||||
@@ -77,27 +96,34 @@ namespace ProjectM.Server
|
|||||||
|
|
||||||
int bestIdx = -1;
|
int bestIdx = -1;
|
||||||
float bestT = float.MaxValue;
|
float bestT = float.MaxValue;
|
||||||
for (int i = 0; i < nodeEntities.Length; i++)
|
bool overlappedCleared = false; // struck a target a sibling projectile already cleared THIS tick
|
||||||
|
for (int i = 0; i < tgtEntity.Length; i++)
|
||||||
{
|
{
|
||||||
if (destroyed[i]) continue;
|
float2 tp = tgtPos[i];
|
||||||
float2 tp = nodePos[i];
|
|
||||||
float t = segLenSq > 1e-8f ? math.saturate(math.dot(tp - segStart, seg) / segLenSq) : 0f;
|
float t = segLenSq > 1e-8f ? math.saturate(math.dot(tp - segStart, seg) / segLenSq) : 0f;
|
||||||
float2 closest = segStart + t * seg;
|
float2 closest = segStart + t * seg;
|
||||||
float hitDist = nodeRadius[i] + k_ProjectileRadius;
|
float hitDist = tgtRadius[i] + k_ProjectileRadius;
|
||||||
if (math.distancesq(tp, closest) <= hitDist * hitDist && t < bestT)
|
if (math.distancesq(tp, closest) > hitDist * hitDist)
|
||||||
{
|
continue;
|
||||||
bestT = t;
|
if (destroyed[i]) { overlappedCleared = true; continue; }
|
||||||
bestIdx = i;
|
if (t < bestT) { bestT = t; bestIdx = i; }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bestIdx < 0)
|
if (bestIdx < 0)
|
||||||
|
{
|
||||||
|
// No LIVE target on the segment. If the shot still overlapped a target a sibling projectile
|
||||||
|
// cleared this same tick, consume it anyway (a hit always spends the shot); else it's a miss.
|
||||||
|
if (overlappedCleared)
|
||||||
|
ecb.DestroyEntity(projEntity);
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
int amount = (int)nodePerHit[bestIdx];
|
// A positive baked yield must always make progress: a raw (int) truncation of a sub-1.0 per-hit
|
||||||
StorageMath.Deposit(ledger, nodeResource[bestIdx], amount);
|
// value would deposit 0 AND never decrement Remaining -> an immortal target that silently eats shots.
|
||||||
int rem = nodeRemaining[bestIdx] - amount;
|
int amount = math.max(1, (int)tgtYieldPerHit[bestIdx]);
|
||||||
nodeRemaining[bestIdx] = rem;
|
StorageMath.Deposit(ledger, tgtYieldId[bestIdx], amount);
|
||||||
|
int rem = tgtRemaining[bestIdx] - amount;
|
||||||
|
tgtRemaining[bestIdx] = rem;
|
||||||
ecb.DestroyEntity(projEntity);
|
ecb.DestroyEntity(projEntity);
|
||||||
|
|
||||||
if (rem <= 0)
|
if (rem <= 0)
|
||||||
@@ -105,17 +131,27 @@ namespace ProjectM.Server
|
|||||||
if (!destroyed[bestIdx])
|
if (!destroyed[bestIdx])
|
||||||
{
|
{
|
||||||
destroyed[bestIdx] = true;
|
destroyed[bestIdx] = true;
|
||||||
ecb.DestroyEntity(nodeEntities[bestIdx]);
|
ecb.DestroyEntity(tgtEntity[bestIdx]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (tgtIsClutter[bestIdx])
|
||||||
|
{
|
||||||
|
// Persist the decremented Remaining (replicated GhostField) so depletion carries across ticks.
|
||||||
|
SystemAPI.SetComponent(tgtEntity[bestIdx], new BlightClutter
|
||||||
|
{
|
||||||
|
Remaining = rem,
|
||||||
|
Variant = tgtVariant[bestIdx],
|
||||||
|
ScrapResourceId = tgtYieldId[bestIdx],
|
||||||
|
ScrapPerHit = tgtYieldPerHit[bestIdx],
|
||||||
|
});
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Persist the decremented Remaining (replicated GhostField) so depletion carries across ticks.
|
SystemAPI.SetComponent(tgtEntity[bestIdx], new ResourceNode
|
||||||
SystemAPI.SetComponent(nodeEntities[bestIdx], new ResourceNode
|
|
||||||
{
|
{
|
||||||
ResourceId = nodeResource[bestIdx],
|
ResourceId = tgtYieldId[bestIdx],
|
||||||
Remaining = rem,
|
Remaining = rem,
|
||||||
HarvestPerHit = nodePerHit[bestIdx],
|
HarvestPerHit = tgtYieldPerHit[bestIdx],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -123,12 +159,14 @@ namespace ProjectM.Server
|
|||||||
ecb.Playback(state.EntityManager);
|
ecb.Playback(state.EntityManager);
|
||||||
ecb.Dispose();
|
ecb.Dispose();
|
||||||
destroyed.Dispose();
|
destroyed.Dispose();
|
||||||
nodeEntities.Dispose();
|
tgtEntity.Dispose();
|
||||||
nodePos.Dispose();
|
tgtPos.Dispose();
|
||||||
nodeRadius.Dispose();
|
tgtRadius.Dispose();
|
||||||
nodeRemaining.Dispose();
|
tgtRemaining.Dispose();
|
||||||
nodeResource.Dispose();
|
tgtYieldId.Dispose();
|
||||||
nodePerHit.Dispose();
|
tgtYieldPerHit.Dispose();
|
||||||
|
tgtVariant.Dispose();
|
||||||
|
tgtIsClutter.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ namespace ProjectM.Simulation
|
|||||||
public const byte Harvester = 2;
|
public const byte Harvester = 2;
|
||||||
public const byte Fabricator = 3;
|
public const byte Fabricator = 3;
|
||||||
public const byte Conveyor = 4;
|
public const byte Conveyor = 4;
|
||||||
|
// World-pass structural/cosmetic build-out (additive; these byte values keep PlacedStructure.Type's
|
||||||
|
// [GhostField] serializer identical — no re-bake of existing ghosts). Do NOT reuse 2-4 (M7 automation).
|
||||||
|
public const byte Wall = 5;
|
||||||
|
public const byte Pylon = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
using Unity.Entities;
|
||||||
|
using Unity.NetCode;
|
||||||
|
|
||||||
|
namespace ProjectM.Simulation
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Destructible "Blight clutter" scattered across the expedition frontier — an ownerless INTERPOLATED ghost
|
||||||
|
/// (region-tagged Expedition; the <see cref="ResourceNode"/> sibling) that clients see and smash. The
|
||||||
|
/// server-only ResourceHarvestSystem sweeps projectiles against it UNIFIED with node harvesting (so an
|
||||||
|
/// overlapping projectile is consumed exactly once — two separate sweeps would double-DestroyEntity the
|
||||||
|
/// projectile and throw at ECB playback). Each hit deposits <see cref="ScrapPerHit"/> of
|
||||||
|
/// <see cref="ScrapResourceId"/> (a small trickle — "juice + minor scrap", no gating) into the GLOBAL
|
||||||
|
/// resource ledger and decrements <see cref="Remaining"/>; the clutter despawns at <= 0.
|
||||||
|
/// <see cref="Remaining"/>/<see cref="Variant"/> are [GhostField] so clients can show depletion + pick a smash
|
||||||
|
/// visual; <see cref="ScrapResourceId"/>/<see cref="ScrapPerHit"/> are baked, server-only. Distinct from
|
||||||
|
/// <see cref="ResourceNode"/> so it carries its own visuals, scatter density and client "clear" feedback while
|
||||||
|
/// reusing the exact, tunnel-safe harvest hit-test.
|
||||||
|
/// </summary>
|
||||||
|
public struct BlightClutter : IComponentData
|
||||||
|
{
|
||||||
|
/// <summary>Remaining hit-points; the clutter despawns when this reaches 0.</summary>
|
||||||
|
[GhostField] public int Remaining;
|
||||||
|
|
||||||
|
/// <summary>Visual variant id (the client picks a mesh/tint; round-robined by the field spawner).</summary>
|
||||||
|
[GhostField] public byte Variant;
|
||||||
|
|
||||||
|
/// <summary>Scrap resource yielded per hit (baked; server-only) — see <see cref="ResourceId"/> (Biomass).</summary>
|
||||||
|
public byte ScrapResourceId;
|
||||||
|
|
||||||
|
/// <summary>Scrap units yielded per projectile hit (baked; server-only).</summary>
|
||||||
|
public float ScrapPerHit;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: dd7823b664bb48e4e89d64439997d147
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
using Unity.Entities;
|
||||||
|
|
||||||
|
namespace ProjectM.Simulation
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Baked singleton holding the Blight-clutter ghost prefab + field shape. ExpeditionFieldSystem reads it
|
||||||
|
/// (alongside <see cref="ResourceFieldSpawner"/>) to scatter <see cref="Count"/> clutter ghosts within
|
||||||
|
/// <see cref="Radius"/> of the expedition origin on the SAME empty->occupied epoch edge as the resource
|
||||||
|
/// field (seeded distinctly so clutter and nodes don't co-locate), and clears them on occupied->empty.
|
||||||
|
/// OPTIONAL — if the singleton is absent, ExpeditionFieldSystem simply skips clutter. Mirrors
|
||||||
|
/// <see cref="ResourceFieldSpawner"/>; carries no transform.
|
||||||
|
/// </summary>
|
||||||
|
public struct ClutterFieldSpawner : IComponentData
|
||||||
|
{
|
||||||
|
/// <summary>Baked Blight-clutter ghost prefab to instantiate.</summary>
|
||||||
|
public Entity Prefab;
|
||||||
|
|
||||||
|
/// <summary>Number of clutter pieces to scatter per expedition.</summary>
|
||||||
|
public int Count;
|
||||||
|
|
||||||
|
/// <summary>Scatter radius (world units) around the expedition region origin.</summary>
|
||||||
|
public float Radius;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 22d21ff4c74c22443ba94c88db9ec099
|
||||||
@@ -129,6 +129,7 @@ GameObject:
|
|||||||
m_Component:
|
m_Component:
|
||||||
- component: {fileID: 17637047}
|
- component: {fileID: 17637047}
|
||||||
- component: {fileID: 17637046}
|
- component: {fileID: 17637046}
|
||||||
|
- component: {fileID: 17637048}
|
||||||
m_Layer: 0
|
m_Layer: 0
|
||||||
m_Name: ResourceFieldSpawner
|
m_Name: ResourceFieldSpawner
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
@@ -166,6 +167,21 @@ Transform:
|
|||||||
m_Children: []
|
m_Children: []
|
||||||
m_Father: {fileID: 0}
|
m_Father: {fileID: 0}
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
--- !u!114 &17637048
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 17637045}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 02623cc30f984104b88a75782bf0dd07, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: ProjectM.Authoring::ProjectM.Authoring.ClutterFieldSpawnerAuthoring
|
||||||
|
ClutterPrefab: {fileID: 3885353946372160549, guid: 6ffeddcc4482c0f44880dc9a555884dd, type: 3}
|
||||||
|
Count: 14
|
||||||
|
Radius: 14
|
||||||
--- !u!1 &236770150
|
--- !u!1 &236770150
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -304,6 +320,10 @@ MonoBehaviour:
|
|||||||
m_EditorClassIdentifier: ProjectM.Authoring::ProjectM.Authoring.StructureCatalogAuthoring
|
m_EditorClassIdentifier: ProjectM.Authoring::ProjectM.Authoring.StructureCatalogAuthoring
|
||||||
TurretPrefab: {fileID: 3885353946372160549, guid: 5459c9edea89bd94fa6f5043ae00eb40, type: 3}
|
TurretPrefab: {fileID: 3885353946372160549, guid: 5459c9edea89bd94fa6f5043ae00eb40, type: 3}
|
||||||
TurretCostOre: 10
|
TurretCostOre: 10
|
||||||
|
WallPrefab: {fileID: 3885353946372160549, guid: 1e321aea244cc484f99c1cdd68cb01c4, type: 3}
|
||||||
|
WallCostOre: 4
|
||||||
|
PylonPrefab: {fileID: 3885353946372160549, guid: 7d0637ef90f120a4c9e2ba637dfc00af, type: 3}
|
||||||
|
PylonCostOre: 2
|
||||||
--- !u!4 &380046995
|
--- !u!4 &380046995
|
||||||
Transform:
|
Transform:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
|
|||||||
@@ -113,5 +113,117 @@ namespace ProjectM.Tests
|
|||||||
Assert.IsTrue(em.Exists(proj), "A projectile that hits no node survives (no destroy-on-miss).");
|
Assert.IsTrue(em.Exists(proj), "A projectile that hits no node survives (no destroy-on-miss).");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Entity MakeClutter(EntityManager em, float3 pos, float hitRadius, int remaining, float scrapPerHit)
|
||||||
|
{
|
||||||
|
var e = em.CreateEntity();
|
||||||
|
em.AddComponentData(e, LocalTransform.FromPosition(pos));
|
||||||
|
em.AddComponentData(e, new HitRadius { Value = hitRadius });
|
||||||
|
em.AddComponentData(e, new BlightClutter { Remaining = remaining, Variant = 0, ScrapResourceId = ResourceId.Biomass, ScrapPerHit = scrapPerHit });
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Clutter_Hit_Deposits_Scrap_Decrements_And_Consumes_Projectile()
|
||||||
|
{
|
||||||
|
var (world, group, ledger) = MakeWorld("ClutterHit");
|
||||||
|
using (world)
|
||||||
|
{
|
||||||
|
var em = world.EntityManager;
|
||||||
|
var clutter = MakeClutter(em, new float3(10, 1, 10), hitRadius: 1f, remaining: 8, scrapPerHit: 2f);
|
||||||
|
var proj = MakeProjectile(em, new float3(10, 1, 10), new float2(1, 0), lastStep: 5f);
|
||||||
|
|
||||||
|
group.Update();
|
||||||
|
|
||||||
|
Assert.AreEqual(2, LedgerCount(em, ledger, ResourceId.Biomass), "Smashing clutter deposits ScrapPerHit Biomass.");
|
||||||
|
Assert.AreEqual(6, em.GetComponentData<BlightClutter>(clutter).Remaining, "Clutter Remaining decrements by the scrap amount.");
|
||||||
|
Assert.IsTrue(em.Exists(clutter), "Clutter with hit-points left survives.");
|
||||||
|
Assert.IsFalse(em.Exists(proj), "The smashing projectile is consumed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Clutter_Two_Projectiles_Shatter_It_At_Most_Once()
|
||||||
|
{
|
||||||
|
var (world, group, ledger) = MakeWorld("ClutterShatter");
|
||||||
|
using (world)
|
||||||
|
{
|
||||||
|
var em = world.EntityManager;
|
||||||
|
var clutter = MakeClutter(em, new float3(10, 1, 10), hitRadius: 1f, remaining: 3, scrapPerHit: 2f);
|
||||||
|
var p1 = MakeProjectile(em, new float3(10, 1, 10), new float2(1, 0), lastStep: 5f);
|
||||||
|
var p2 = MakeProjectile(em, new float3(10, 1, 10), new float2(0, 1), lastStep: 5f);
|
||||||
|
|
||||||
|
group.Update();
|
||||||
|
|
||||||
|
Assert.AreEqual(4, LedgerCount(em, ledger, ResourceId.Biomass), "Both hits deposit scrap, even though the second over-clears.");
|
||||||
|
Assert.IsFalse(em.Exists(clutter), "Shattered clutter is destroyed exactly once (a double destroy would throw at playback).");
|
||||||
|
Assert.IsFalse(em.Exists(p1));
|
||||||
|
Assert.IsFalse(em.Exists(p2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Overlapping_Node_And_Clutter_Consume_Projectile_Once_Hitting_Nearest()
|
||||||
|
{
|
||||||
|
var (world, group, ledger) = MakeWorld("OverlapNearest");
|
||||||
|
using (world)
|
||||||
|
{
|
||||||
|
var em = world.EntityManager;
|
||||||
|
// Segment runs +X from x=0 (segStart = pos - dir*lastStep) to x=10 (pos). Clutter at x=4 is nearer
|
||||||
|
// along the segment (t=0.4) than the node at x=9 (t=0.9), so the unified best-t sweep hits clutter.
|
||||||
|
var clutter = MakeClutter(em, new float3(4, 1, 0), hitRadius: 1f, remaining: 100, scrapPerHit: 2f);
|
||||||
|
var node = MakeNode(em, new float3(9, 1, 0), hitRadius: 1f, resourceId: ResourceId.Ore, remaining: 100, perHit: 25f);
|
||||||
|
var proj = MakeProjectile(em, new float3(10, 1, 0), new float2(1, 0), lastStep: 10f);
|
||||||
|
|
||||||
|
group.Update();
|
||||||
|
|
||||||
|
Assert.AreEqual(2, LedgerCount(em, ledger, ResourceId.Biomass), "The nearest target (clutter) is harvested.");
|
||||||
|
Assert.AreEqual(0, LedgerCount(em, ledger, ResourceId.Ore), "The farther target (node) is NOT hit — one projectile, one target.");
|
||||||
|
Assert.AreEqual(98, em.GetComponentData<BlightClutter>(clutter).Remaining);
|
||||||
|
Assert.AreEqual(100, em.GetComponentData<ResourceNode>(node).Remaining, "Node untouched.");
|
||||||
|
Assert.IsFalse(em.Exists(proj), "The projectile is consumed exactly once (no double-destroy across the two target types).");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Fractional_ScrapPerHit_Still_Deposits_And_Depletes_Never_Immortal()
|
||||||
|
{
|
||||||
|
var (world, group, ledger) = MakeWorld("FractionalScrap");
|
||||||
|
using (world)
|
||||||
|
{
|
||||||
|
var em = world.EntityManager;
|
||||||
|
// A sub-1.0 per-hit yield must NOT truncate to 0 (that would make the target immortal + eat shots).
|
||||||
|
var clutter = MakeClutter(em, new float3(10, 1, 10), hitRadius: 1f, remaining: 2, scrapPerHit: 0.5f);
|
||||||
|
var proj = MakeProjectile(em, new float3(10, 1, 10), new float2(1, 0), lastStep: 5f);
|
||||||
|
|
||||||
|
group.Update();
|
||||||
|
|
||||||
|
Assert.AreEqual(1, LedgerCount(em, ledger, ResourceId.Biomass), "A positive sub-1.0 yield deposits at least 1 (math.max(1, (int)...)).");
|
||||||
|
Assert.AreEqual(1, em.GetComponentData<BlightClutter>(clutter).Remaining, "Remaining decrements by at least 1, so depletion always progresses.");
|
||||||
|
Assert.IsTrue(em.Exists(clutter), "Still alive after one hit (2 -> 1), not immortal.");
|
||||||
|
Assert.IsFalse(em.Exists(proj), "The projectile is consumed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Second_Projectile_On_A_SameTick_Cleared_Target_Is_Consumed_Without_Double_Deposit()
|
||||||
|
{
|
||||||
|
var (world, group, ledger) = MakeWorld("SameTickCleared");
|
||||||
|
using (world)
|
||||||
|
{
|
||||||
|
var em = world.EntityManager;
|
||||||
|
// Both projectiles overlap one clutter that the FIRST hit shatters (remaining == scrapPerHit).
|
||||||
|
var clutter = MakeClutter(em, new float3(10, 1, 10), hitRadius: 1f, remaining: 2, scrapPerHit: 2f);
|
||||||
|
var p1 = MakeProjectile(em, new float3(10, 1, 10), new float2(1, 0), lastStep: 5f);
|
||||||
|
var p2 = MakeProjectile(em, new float3(10, 1, 10), new float2(0, 1), lastStep: 5f);
|
||||||
|
|
||||||
|
group.Update();
|
||||||
|
|
||||||
|
Assert.AreEqual(2, LedgerCount(em, ledger, ResourceId.Biomass), "Only the first hit deposits; the second strikes the already-cleared spot (no double-deposit).");
|
||||||
|
Assert.IsFalse(em.Exists(clutter), "The clutter is shattered exactly once.");
|
||||||
|
Assert.IsFalse(em.Exists(p1), "First projectile consumed (harvested).");
|
||||||
|
Assert.IsFalse(em.Exists(p2), "Second projectile is still consumed — a hit always spends the shot, even on a same-tick-cleared target.");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ Long-form originals + the milestone each came from: `Docs/Vault/_Meta/CLAUDE_Bui
|
|||||||
- **The player is a Unity Character Controller kinematic character** (NOT a dynamic Rigidbody — `PlayerMoveSystem`/`PlayerPlanarConstraintSystem` were deleted; the DR-006 predicted-physics infra is kept). `PlayerControlSystem` maps input → `CharacterControl`; `CharacterProcessor` collide-and-slides in the relocated `KinematicCharacterPhysicsUpdateGroup`. CC 1.4.2 API = `IKinematicCharacterProcessor<T>` + `KinematicCharacterDataAccess` + static `KinematicCharacterUtilities.Update_*` (verify shape with `unity_reflect`, don't assume the legacy aspect).
|
- **The player is a Unity Character Controller kinematic character** (NOT a dynamic Rigidbody — `PlayerMoveSystem`/`PlayerPlanarConstraintSystem` were deleted; the DR-006 predicted-physics infra is kept). `PlayerControlSystem` maps input → `CharacterControl`; `CharacterProcessor` collide-and-slides in the relocated `KinematicCharacterPhysicsUpdateGroup`. CC 1.4.2 API = `IKinematicCharacterProcessor<T>` + `KinematicCharacterDataAccess` + static `KinematicCharacterUtilities.Update_*` (verify shape with `unity_reflect`, don't assume the legacy aspect).
|
||||||
- **`KinematicCharacterUtilities.BakeCharacter` aborts if the GameObject has a `Rigidbody`** and needs uniform (1,1,1) scale. **`CharacterInterpolation` must be PredictedClient-only** (register a `DefaultVariantSystemBase` stripping it from server + interpolated prefabs) — else double-interp on remotes. **Do NOT copy the CC sample's global `LocalTransform → DontSerializeVariant`** (project-wide; breaks the non-character ghosts that rely on stock `LocalTransform` replication).
|
- **`KinematicCharacterUtilities.BakeCharacter` aborts if the GameObject has a `Rigidbody`** and needs uniform (1,1,1) scale. **`CharacterInterpolation` must be PredictedClient-only** (register a `DefaultVariantSystemBase` stripping it from server + interpolated prefabs) — else double-interp on remotes. **Do NOT copy the CC sample's global `LocalTransform → DontSerializeVariant`** (project-wide; breaks the non-character ghosts that rely on stock `LocalTransform` replication).
|
||||||
- **Top-down CC config:** `SnapToGround=false`, `InterpolateRotation=false` (rotation owned by `PlayerAimSystem`), `SimulateDynamicBody=false`; gravity handled by feeding `float3.zero` to `Update_GroundPushing`.
|
- **Top-down CC config:** `SnapToGround=false`, `InterpolateRotation=false` (rotation owned by `PlayerAimSystem`), `SimulateDynamicBody=false`; gravity handled by feeding `float3.zero` to `Update_GroundPushing`.
|
||||||
- **Hit/area tests must be SWEPT, not point checks** — a point distance check tunnels through a target when the per-tick step exceeds the target radius (high speed *or* tick-batching). Test the segment traversed this tick. **In a PLAIN `SimulationSystemGroup` system do NOT use `SystemAPI.Time.DeltaTime`** (it's the wall-frame delta, not the fixed step) — store the per-tick step on the projectile (`Projectile.LastStep`, written in the fixed-step group) and rebuild the segment as `cur - dir*LastStep`. A node hit by N projectiles in one tick: `ecb.DestroyEntity` **at-most-once** (destroyed-bitset; a double destroy throws at Playback).
|
- **Hit/area tests must be SWEPT, not point checks** — a point distance check tunnels through a target when the per-tick step exceeds the target radius (high speed *or* tick-batching). Test the segment traversed this tick. **In a PLAIN `SimulationSystemGroup` system do NOT use `SystemAPI.Time.DeltaTime`** (it's the wall-frame delta, not the fixed step) — store the per-tick step on the projectile (`Projectile.LastStep`, written in the fixed-step group) and rebuild the segment as `cur - dir*LastStep`. A node hit by N projectiles in one tick: `ecb.DestroyEntity` **at-most-once** (destroyed-bitset; a double destroy throws at Playback). **When TWO target types share one projectile pass (resource nodes + Blight clutter), UNIFY the sweep into one best-target loop + one shared destroyed-bitset** — separate sweep systems each `DestroyEntity` a projectile that overlaps both → double-destroy at Playback (DR-018). **A float per-hit yield cast `(int)` that ALSO gates despawn is an immortal-sink footgun:** a sub-1.0 value → `(int)`=0 → no deposit AND no `Remaining` decrement, yet the shot is still consumed → an unkillable target that silently eats projectiles. Guard with `math.max(1,(int)yield)` in the consumer **and** `[Min(1f)]` on the authoring (the node path had it; the clutter sibling didn't).
|
||||||
|
|
||||||
### Build / structures / grid
|
### Build / structures / grid
|
||||||
- **Build-grid math must be deterministic + integer-stable:** corner-origin, center-returning, **half-open** cell bounds, `math.floor` (not truncation — negatives). Lock `CellSize`/`PlotSize` as a coordinate space once (`BaseGridMath`, EditMode-tested) — changing them invalidates placed structures.
|
- **Build-grid math must be deterministic + integer-stable:** corner-origin, center-returning, **half-open** cell bounds, `math.floor` (not truncation — negatives). Lock `CellSize`/`PlotSize` as a coordinate space once (`BaseGridMath`, EditMode-tested) — changing them invalidates placed structures.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
tags:
|
tags:
|
||||||
- design
|
- design
|
||||||
- index
|
- index
|
||||||
updated: 2026-05-29
|
updated: 2026-06-04
|
||||||
permalink: gamevault/02-game-design/systems-index
|
permalink: gamevault/02-game-design/systems-index
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -42,6 +42,12 @@ One design doc per gameplay system, linked here. Each should state: purpose, com
|
|||||||
- **Systems:** `EnemyAISystem` (server-only, **plain `SimulationSystemGroup`**, `[UpdateAfter(PredictedSimulationSystemGroup)]` — the interpolated Husk ghost seeks the nearest living player, deals contact `DamageEvent`) + the `WaveSystem` threat director (escalating waves of Husk variants Grunt/Swarmer/Brute — replaced the flat sustain; see [[2026-06-02_GameFeel_Deepening]]); `HealthApplyDamageSystem` +`EnemyTag` death; `PlayerDeathStateSystem` (both worlds, predicted — derives `Dead` from `Health<=0`, gates movement/aim/fire via `.WithDisabled<Dead>()`); `PlayerRespawnSystem` (server-only — schedule + refill + reposition). Client presentation (managed, `PresentationSystemGroup`): `CombatFeedbackSystem` (damage numbers / VFX / procedural SFX / camera shake by edge-detecting replicated Health) + `HudSystem` (code-built uGUI health / cooldown / threat / DOWNED) + `PrototypeCameraRig.AddShake`.
|
- **Systems:** `EnemyAISystem` (server-only, **plain `SimulationSystemGroup`**, `[UpdateAfter(PredictedSimulationSystemGroup)]` — the interpolated Husk ghost seeks the nearest living player, deals contact `DamageEvent`) + the `WaveSystem` threat director (escalating waves of Husk variants Grunt/Swarmer/Brute — replaced the flat sustain; see [[2026-06-02_GameFeel_Deepening]]); `HealthApplyDamageSystem` +`EnemyTag` death; `PlayerDeathStateSystem` (both worlds, predicted — derives `Dead` from `Health<=0`, gates movement/aim/fire via `.WithDisabled<Dead>()`); `PlayerRespawnSystem` (server-only — schedule + refill + reposition). Client presentation (managed, `PresentationSystemGroup`): `CombatFeedbackSystem` (damage numbers / VFX / procedural SFX / camera shake by edge-detecting replicated Health) + `HudSystem` (code-built uGUI health / cooldown / threat / DOWNED) + `PrototypeCameraRig.AddShake`.
|
||||||
- **Netcode shape:** Husk = **ownerless interpolated** server-driven ghost (stock `LocalTransform` replication; `Health` `[GhostField]`); `Dead` = **local derived** enableable (NOT replicated — pure function of replicated Health); juice/HUD **observe** replicated state only (client world, never the sim). Identity: [[Identity]] (sci-fi frontier colony). Status: **built + runtime-validated** (Husks spawn(6)/replicate/chase/strike; death→respawn loop; HUD; emissive dark-sci-fi look); EditMode **74/74**. Decisions: [[DR-009_GameFeel_Identity_FirstBlood]].
|
- **Netcode shape:** Husk = **ownerless interpolated** server-driven ghost (stock `LocalTransform` replication; `Health` `[GhostField]`); `Dead` = **local derived** enableable (NOT replicated — pure function of replicated Health); juice/HUD **observe** replicated state only (client world, never the sim). Identity: [[Identity]] (sci-fi frontier colony). Status: **built + runtime-validated** (Husks spawn(6)/replicate/chase/strike; death→respawn loop; HUD; emissive dark-sci-fi look); EditMode **74/74**. Decisions: [[DR-009_GameFeel_Identity_FirstBlood]].
|
||||||
|
|
||||||
|
### Post-M8 — World-space cohesion pass (clearing + buildable Wall/Pylon + Blightfield dressing) · [[2026-06-04_World_Space_Cohesion_Pass]]
|
||||||
|
|
||||||
|
- **Components** (`ProjectM.Simulation`): `BlightClutter` (ownerless-interpolated ghost — `[GhostField]` Remaining+Variant, server-only ScrapResourceId/ScrapPerHit; RegionTag{Expedition} **sibling of `ResourceNode`**) + `ClutterFieldSpawner` (optional baked singleton); `StructureType.Wall=5`/`Pylon=6` (byte consts; 2–4 stay reserved for M7).
|
||||||
|
- **Systems:** `ResourceHarvestSystem` **unified** to sweep nodes AND clutter in one best-target loop (required — two separate sweeps would double-destroy an overlapping projectile at ECB playback; `math.max(1,(int)yield)` guards the immortal-sink); `ExpeditionFieldSystem` scatters/clears clutter beside the node field (distinct seed); `BuildPlaceSystem` **unchanged** (already type-generic) — new generic `StructureAuthoring{byte Kind}` + two additive `StructureCatalog` rows + `BuildSendSystem` V/N keys. Client: `WorldFeedbackSystem` (observe-only `PresentationSystemGroup` — chip/shatter juice, proximity-gated so region-transit stays silent) + live-tunable `WorldFeelConfig`.
|
||||||
|
- **Netcode/world shape:** clutter = ownerless interpolated, region-scoped via GhostRelevancy (like nodes); Wall/Pylon = ownerless interpolated structure ghosts (`PlacedStructure.Type` byte-additive → **no re-bake**); Wall carries a `PhysicsCollider` (CC-blocking), Pylon cosmetic. Visual: `M_Aether_Wild`/`M_Aether_Ordered` palette materials + a classic-URP Blightfield rock basin in `Game.unity` (relief via props, **no terrain**). Status: **built + validated** (EditMode **142/142**; in-editor Play introspected via execute_code; 4-lens adversarial review → 4 findings fixed). Decisions: [[DR-018_World_Space_Cohesion_Pass]] (builds on the M6 region split / build pipeline + M8 persistent base — those systems are documented in their DRs).
|
||||||
|
|
||||||
## Conventions
|
## Conventions
|
||||||
|
|
||||||
DOTS/ECS conventions live in repo `CLAUDE.md` and the `dots-dev` skill's `dots-conventions.md`. Don't duplicate volatile API details here — link to context7-derived notes instead.
|
DOTS/ECS conventions live in repo `CLAUDE.md` and the `dots-dev` skill's `dots-conventions.md`. Don't duplicate volatile API details here — link to context7-derived notes instead.
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
tags:
|
tags:
|
||||||
- roadmap
|
- roadmap
|
||||||
- backlog
|
- backlog
|
||||||
updated: 2026-06-03
|
updated: 2026-06-04
|
||||||
permalink: gamevault/06-roadmap/backlog
|
permalink: gamevault/06-roadmap/backlog
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -12,6 +12,8 @@ Unordered pool of candidate work. Promote to a [[Milestones|milestone]] when com
|
|||||||
|
|
||||||
> **2026-06-04 Polish & backlog-clear pass** ([[2026-06-04_Polish_Backlog_Pass]], [[DR-016_Stage_G_Combat_Gameplay]]) — delivered + validated (EditMode 86→127): hygiene/reconcile; **+system tests for every M6 server system**; centralized `Tuning.cs` consts; replicated **wave number** on the HUD; procedural **ambient + combat juice** (camera punch / FOV hit-stop, kill-shot fanfare, respawn shimmer, reticle lock-on tether); **ghost-prop reskin** (Storage/Turret/Node distinct materials) + **SSAO/ACES post verified**; **timed/removable modifiers**, **enemy knockback**, **Husk attack telegraph**; and the **aim-drift fix** (movement-based camera look-ahead). Remaining from the selected Stage-G slice: ranged **Spitter**, **multi-prefab abilities**, + small fold-ins (storage proximity-gate, pickup auto-grant, standalone-debug RPC). Then Stages H (controls/UI) + I (multi-client harness + operator live runs).
|
> **2026-06-04 Polish & backlog-clear pass** ([[2026-06-04_Polish_Backlog_Pass]], [[DR-016_Stage_G_Combat_Gameplay]]) — delivered + validated (EditMode 86→127): hygiene/reconcile; **+system tests for every M6 server system**; centralized `Tuning.cs` consts; replicated **wave number** on the HUD; procedural **ambient + combat juice** (camera punch / FOV hit-stop, kill-shot fanfare, respawn shimmer, reticle lock-on tether); **ghost-prop reskin** (Storage/Turret/Node distinct materials) + **SSAO/ACES post verified**; **timed/removable modifiers**, **enemy knockback**, **Husk attack telegraph**; and the **aim-drift fix** (movement-based camera look-ahead). Remaining from the selected Stage-G slice: ranged **Spitter**, **multi-prefab abilities**, + small fold-ins (storage proximity-gate, pickup auto-grant, standalone-debug RPC). Then Stages H (controls/UI) + I (multi-client harness + operator live runs).
|
||||||
|
|
||||||
|
> **2026-06-04 World-space cohesion pass** ([[2026-06-04_World_Space_Cohesion_Pass]], [[DR-018_World_Space_Cohesion_Pass]]) — delivered + validated (EditMode 127→**142**): **destructible "clearing"** (`BlightClutter` ghost smashed by the **unified** harvest sweep → Biomass scrap + smash juice, scattered/cleared with the expedition field); **build out the base** (additive `Wall`=5 / `Pylon`=6 buildables — Wall blocks the player, V/N keys); and a first **visual cohesion layer** (Aether cyan/orange material pair, the 3 new ghosts recoloured, the barren +1000 Blightfield turned into a 21-piece Synty rock/debris basin + orange wild-lights — relief via props, **no terrain**). In-editor Play introspected via execute_code; a 4-lens adversarial review (23→4) caught + fixed an immortal-shot-sink (fractional `ScrapPerHit` truncation). Base-side cohesion accents + clearing-as-gating deferred (see the 2026-06-04 follow-ups below).
|
||||||
|
|
||||||
- [x] Upgrade Unity 6.4 → 6.6 — done (now `6000.6.0a6`). Entities/Collections/Graphics → 6.5.0; **Netcode → 6.6.0 and Physics → 6.5.0 also renumbered into the editor line** (not independent 1.x as [[DR-001_Netcode_Test_Harness]] assumed). See [[2026-05-30_M1_Player_Slice]].
|
- [x] Upgrade Unity 6.4 → 6.6 — done (now `6000.6.0a6`). Entities/Collections/Graphics → 6.5.0; **Netcode → 6.6.0 and Physics → 6.5.0 also renumbered into the editor line** (not independent 1.x as [[DR-001_Netcode_Test_Harness]] assumed). See [[2026-05-30_M1_Player_Slice]].
|
||||||
- [x] Define the core gameplay loop and the first predicted player ghost — delivered as M1 ([[2026-05-30_M1_Player_Slice]]).
|
- [x] Define the core gameplay loop and the first predicted player ghost — delivered as M1 ([[2026-05-30_M1_Player_Slice]]).
|
||||||
- [x] **Re-validate the M1 play-tick on a stable Unity 6.x** — moot/subsumed 2026-06-04: M1–M6 are all runtime-validated on the stable **6.4.7** line (the 6.6 alpha netcode bug never affected 6.4.7). The original "blocked on 6.6 alpha" framing ([[DR-002_Unity66_Alpha_Netcode_Transport]]) no longer applies.
|
- [x] **Re-validate the M1 play-tick on a stable Unity 6.x** — moot/subsumed 2026-06-04: M1–M6 are all runtime-validated on the stable **6.4.7** line (the 6.6 alpha netcode bug never affected 6.4.7). The original "blocked on 6.6 alpha" framing ([[DR-002_Unity66_Alpha_Netcode_Transport]]) no longer applies.
|
||||||
@@ -35,7 +37,7 @@ Unordered pool of candidate work. Promote to a [[Milestones|milestone]] when com
|
|||||||
- [ ] **M5 follow-up — shared-storage disk persistence** (host-only): runtime structures don't exist until M6, so nothing to save yet; add a thin per-record serialization slice (replayed through M6's placement path) after M6. `BaseAnchor`/`StorageEntry` are already flat/serialization-friendly.
|
- [ ] **M5 follow-up — shared-storage disk persistence** (host-only): runtime structures don't exist until M6, so nothing to save yet; add a thin per-record serialization slice (replayed through M6's placement path) after M6. `BaseAnchor`/`StorageEntry` are already flat/serialization-friendly.
|
||||||
- [ ] **M5 follow-up — storage interaction polish**: proximity gate the deposit/withdraw (the container carries `HitRadius`); real item/UI model beyond the fixed test item; multi-writer ordering beyond first-come server apply.
|
- [ ] **M5 follow-up — storage interaction polish**: proximity gate the deposit/withdraw (the container carries `HitRadius`); real item/UI model beyond the fixed test item; multi-writer ordering beyond first-come server apply.
|
||||||
- [ ] **M5 follow-up — multi-client shared storage**: validate two clients see identical shared-storage buffer state (pairs with the deferred M5b multi-client interpolation + M4 two-build tests).
|
- [ ] **M5 follow-up — multi-client shared storage**: validate two clients see identical shared-storage buffer state (pairs with the deferred M5b multi-client interpolation + M4 two-build tests).
|
||||||
- [ ] **M5 follow-up — home-base visuals**: only an editor plot-bounds gizmo + a placeholder primitive container today; real ground/walls/structures arrive with M6.
|
- [ ] **M5 follow-up — home-base visuals**: only an editor plot-bounds gizmo + a placeholder primitive container today; real ground/walls/structures arrive with M6. **Partly addressed 2026-06-04** ([[DR-018_World_Space_Cohesion_Pass]]): the +1000 Blightfield got a real rock/debris basin + wild-lights and the buildable Wall/Pylon read in-palette; the **base-side** cohesion accents (cyan lights / perimeter relief / tiled ground) remain open.
|
||||||
- [ ] ~~**M5 follow-up — same-tick movement**~~ — moot after M5b (the CC character runs in the predicted fixed-step group; M5's `PlayerMoveSystem` is deleted). See [[DR-007_M5b_Character_Controller_Package]].
|
- [ ] ~~**M5 follow-up — same-tick movement**~~ — moot after M5b (the CC character runs in the predicted fixed-step group; M5's `PlayerMoveSystem` is deleted). See [[DR-007_M5b_Character_Controller_Package]].
|
||||||
- [ ] **M5b follow-up — multi-client CC interpolation validation**: live two-build / thin-client run to confirm remote-peer interpolation smoothness (predicted-only `CharacterInterpolation` variant is in; only single-client validated). Pairs with the deferred M4 real-LAN two-build test.
|
- [ ] **M5b follow-up — multi-client CC interpolation validation**: live two-build / thin-client run to confirm remote-peer interpolation smoothness (predicted-only `CharacterInterpolation` variant is in; only single-client validated). Pairs with the deferred M4 real-LAN two-build test.
|
||||||
- [ ] **M5b follow-up — player-vs-player collision**: currently non-physical (`SimulateDynamicBody=false`); enable + handle masses if mutual push is wanted.
|
- [ ] **M5b follow-up — player-vs-player collision**: currently non-physical (`SimulateDynamicBody=false`); enable + handle masses if mutual push is wanted.
|
||||||
@@ -68,7 +70,17 @@ Surfaced by the art/Synty/aim sessions ([[DR-010_Art_Import_URP_Conversion_Visua
|
|||||||
- [ ] **DR-011 follow-up — character models / DOTS skeletal animation**: player + Husks remain primitives; swap for real rigged models when the character pack lands.
|
- [ ] **DR-011 follow-up — character models / DOTS skeletal animation**: player + Husks remain primitives; swap for real rigged models when the character pack lands.
|
||||||
- [ ] **DR-011 follow-up — VFX-Graph hit/beam packs**: the GabrielAguiar VFX-Graph subset needs separate URP setup if wanted (Shuriken packs already wired).
|
- [ ] **DR-011 follow-up — VFX-Graph hit/beam packs**: the GabrielAguiar VFX-Graph subset needs separate URP setup if wanted (Shuriken packs already wired).
|
||||||
- [ ] **DR-011 follow-up — ghost-prop Synty reskin**: Storage/UpgradePickup ghosts still use BefourStudios meshes; reskin to Synty-style or simple emissive.
|
- [ ] **DR-011 follow-up — ghost-prop Synty reskin**: Storage/UpgradePickup ghosts still use BefourStudios meshes; reskin to Synty-style or simple emissive.
|
||||||
- [ ] **DR-011 follow-up — mixed-theme danger zones**: BioHorror/Synty corrupted-zone art for high-threat areas (deferred from the scope gate).
|
- [ ] **DR-011 follow-up — mixed-theme danger zones**: BioHorror/Synty corrupted-zone art for high-threat areas (deferred from the scope gate). **Partly addressed 2026-06-04**: the Blightfield now reads orange-wild via **all-Synty recolour** (rock/debris basin + wild-lights + orange clutter/nodes) per the [[DR-018_World_Space_Cohesion_Pass]] art fork; the **BioHorror** organic-corruption accent set is still deferred.
|
||||||
- [ ] **DR-012 follow-up — auto-target tuning**: priority/feel refinement; optional controller soft-target reticle snap (V Rising/Diablo-style).
|
- [ ] **DR-012 follow-up — auto-target tuning**: priority/feel refinement; optional controller soft-target reticle snap (V Rising/Diablo-style).
|
||||||
- [ ] **DR-012 follow-up — rebindable controls + multi-ability slots**.
|
- [ ] **DR-012 follow-up — rebindable controls + multi-ability slots**.
|
||||||
- [ ] **Pre-M6 cleanup follow-up — higher material fidelity**: ORM channels don't map uniformly to URP; a per-channel repack or small ORM ShaderGraph + baked reflection probe would deepen the look.
|
- [ ] **Pre-M6 cleanup follow-up — higher material fidelity**: ORM channels don't map uniformly to URP; a per-channel repack or small ORM ShaderGraph + baked reflection probe would deepen the look.
|
||||||
|
|
||||||
|
## 2026-06-04 World-space cohesion follow-ups
|
||||||
|
|
||||||
|
Surfaced by [[DR-018_World_Space_Cohesion_Pass]] (the balanced-slice scope deliberately left these for later).
|
||||||
|
|
||||||
|
- [ ] **Base-side cohesion accents**: cyan ordered-Aether lights + perimeter relief + re-enabled tiled base ground (deferred this pass to avoid clashing with the tuned SSAO/ACES look — the Blightfield got the dressing budget).
|
||||||
|
- [ ] **Clearing as a spatial mechanic**: shipped as juice + scrap only; make cleared cells buildable / reveal hidden nodes (the "functional gating" fork from the intake gate).
|
||||||
|
- [ ] **Build-palette HUD + ghost-preview**: dedicated V/N keys for now; a selectable palette + placement preview is the UX follow-up.
|
||||||
|
- [ ] **Per-`Variant` clutter meshes**: `BlightClutter.Variant` is replicated + round-robined but the prefab is single-mesh; swap per-variant meshes (additive, no schema change).
|
||||||
|
- [x] **Focused-editor validation** (2026-06-04): confirmed in Play — Wall **stops the player CC at the collider face** (X 2.50→3.257, server==client); Blightfield **reads orange-wild rock basin** vs cyan base (real player-view shots); **clutter-clear loop** deposits Biomass + shatters + replicates (BIO 0→10, clutter 14→13); node harvest works (AETHER 0→30). Console clean. **Still open:** deep basin density/lighting **feel-tuning**.
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
---
|
||||||
|
date: 2026-06-04
|
||||||
|
type: session
|
||||||
|
tags:
|
||||||
|
- session
|
||||||
|
- world-architecture
|
||||||
|
- economy
|
||||||
|
- building
|
||||||
|
- presentation
|
||||||
|
- art
|
||||||
|
- post-m8
|
||||||
|
permalink: gamevault/07-sessions/2026/2026-06-04-world-space-cohesion-pass
|
||||||
|
---
|
||||||
|
|
||||||
|
# Session 2026-06-04 — World-space cohesion pass
|
||||||
|
|
||||||
|
> Driven by `/dots-dev` (Feature track) under ultracode. Third log dated 2026-06-04 (after M8 + the Polish backlog pass).
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Make the flat-feeling world feel like **a more cohesive world** without editing the floor/terrain: non-flat feel
|
||||||
|
via props, **build out a base** (more placeable structures), and **destructible resource gathering + clearing out
|
||||||
|
areas**. Operator picked: balanced vertical slice · all-Synty recoloured Blight · clearing = juice + minor scrap ·
|
||||||
|
buildables = Walls + structural variety.
|
||||||
|
|
||||||
|
## Done
|
||||||
|
|
||||||
|
- **Clearing mechanic (net-new):** `BlightClutter` ownerless-interpolated ghost (RegionTag{Expedition}, [GhostField]
|
||||||
|
Remaining+Variant) + `ClutterFieldSpawner`. **Unified `ResourceHarvestSystem`** to sweep nodes + clutter in one
|
||||||
|
best-target loop (the double-destroy fix). `ExpeditionFieldSystem` scatters/clears clutter alongside the node field
|
||||||
|
(distinct seed). Client smash juice = new observe-only `WorldFeedbackSystem` + live-tunable `WorldFeelConfig`
|
||||||
|
(chip on `Remaining` decrease, shatter + camera punch on despawn, proximity-gated so region-transit stays silent).
|
||||||
|
- **Build out the base:** `StructureType.Wall=5`/`Pylon=6` (2–4 reserved for M7), generic `StructureAuthoring`, two
|
||||||
|
additive `StructureCatalog` rows, V/N build keys + editor hooks. `Wall.prefab` blocks the player (BoxCollider →
|
||||||
|
PhysicsCollider); `Pylon.prefab` cosmetic. `BuildPlaceSystem` untouched (already type-generic).
|
||||||
|
- **Visual cohesion:** `M_Aether_Wild`(orange)/`M_Aether_Ordered`(cyan) HDR-emissive materials; the 3 new ghosts
|
||||||
|
recoloured into the palette; the barren +1000 Blightfield became a 21-piece Synty rock/debris basin + 3 orange
|
||||||
|
wild-lights (classic-URP cosmetics in `Game.unity`, no subscene re-bake; relief via props, no terrain).
|
||||||
|
- **Validation:** EditMode **142/142** (5 harvest tests incl. overlap/fractional/same-tick-cleared). In-editor Play
|
||||||
|
(server+client) introspected via `execute_code`: baking, ghost registration, buildables (Ore 50→44, R1 collider),
|
||||||
|
clutter scatter (14+8 @ x≈1000), **0 errors**. A 4-lens adversarial-review Workflow (23 raised → 4 confirmed) caught
|
||||||
|
an immortal-shot-sink (sub-1.0 `ScrapPerHit` truncates to 0) + a same-tick-cleared passthrough — both fixed +
|
||||||
|
regression-tested before sign-off.
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
|
||||||
|
- [[DR-018_World_Space_Cohesion_Pass]] — clutter as a node-sibling on a unified sweep; byte-additive Wall/Pylon;
|
||||||
|
classic-URP Blightfield dressing; Aether-palette recolour. Builds on [[DR-013_M6_Aether_Cycle_Region_Split]] /
|
||||||
|
[[DR-014_M6_Build_Structures_Automation_Foundation]] / [[DR-015_The_Awakening_Engine_Fiction_Adoption]].
|
||||||
|
|
||||||
|
## Open / deferred
|
||||||
|
|
||||||
|
- Base-side cohesion accents (cyan lights / perimeter relief / re-enabled tiled ground) — deferred to avoid clashing
|
||||||
|
with the tuned SSAO/ACES look.
|
||||||
|
- Clearing as a spatial mechanic (gating / reveal nodes); build-palette HUD + ghost-preview; per-`Variant` clutter
|
||||||
|
meshes — all additive follow-ups.
|
||||||
|
- Eyeball the cohesion + Wall-blocks-player reads in a **focused** editor (mechanisms verified by value headless).
|
||||||
|
|
||||||
|
## Next
|
||||||
|
|
||||||
|
- A focused-editor play-through to tune the Blightfield basin density/lighting + the clutter/wall feel, then either
|
||||||
|
the base-side cohesion accents or fold clearing into a spatial mechanic — or proceed to M7 automation (the reserved
|
||||||
|
structure slots + tick fields are still the additive hook).
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
---
|
||||||
|
id: DR-018
|
||||||
|
title: World-space cohesion pass — destructible Blight clutter (clearing), buildable Wall/Pylon, Aether-palette Blightfield dressing
|
||||||
|
status: accepted
|
||||||
|
date: 2026-06-04
|
||||||
|
tags:
|
||||||
|
- decision
|
||||||
|
- netcode
|
||||||
|
- world-architecture
|
||||||
|
- economy
|
||||||
|
- building
|
||||||
|
- presentation
|
||||||
|
- art
|
||||||
|
- post-m8
|
||||||
|
permalink: gamevault/07-sessions/decisions/dr-018-world-space-cohesion-pass
|
||||||
|
---
|
||||||
|
|
||||||
|
# DR-018 — World-space cohesion pass
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
The operator asked for a **world-space pass** so the world "feels like a more cohesive world" instead of flat:
|
||||||
|
non-flat ground/foliage **without editing the terrain** (props on top only), **placing buildings / building out a
|
||||||
|
base**, and **destructible resource gathering + clearing out areas**. Recon (read-only swarm) found this is mostly a
|
||||||
|
**cohesion layer over the mature M8 foundation**, not greenfield: destructible `ResourceNode` harvesting already
|
||||||
|
existed ([[DR-013_M6_Aether_Cycle_Region_Split]]); the full grid-placement build pipeline existed but shipped only the
|
||||||
|
Turret ([[DR-014_M6_Build_Structures_Automation_Foundation]]); the world was a flat quad with ~60 clustered Synty
|
||||||
|
city props at the base and **only 4 lone pillars** at the +1000 Blightfield. The fiction ([[DR-015_The_Awakening_Engine_Fiction_Adoption]])
|
||||||
|
sets the duality: **cyan-ordered base sanctuary ↔ orange-red wild Blightfield**, Aether read by colour.
|
||||||
|
|
||||||
|
Four scope forks were resolved with the operator up front (intake `AskUserQuestion`): **balanced vertical slice**
|
||||||
|
(touch all three, foundation now / expand later) · **all-Synty recoloured** Blight (URP-native; not the HDRP
|
||||||
|
BefourStudios path) · **clearing = juice + minor scrap** (no hard gating) · **buildables = Walls + structural variety**
|
||||||
|
(cosmetic / player-blocking, NOT Husk-pathing-defensive). Hard constraints honoured: region offset (1000,0,0) +
|
||||||
|
GhostRelevancy, frozen 32×32 grid / save schema, byte ids (never enums) in Burst/RPC, static decor as collider-stripped
|
||||||
|
classic-URP cosmetics, Aether palette, predicted-loop server authority, **no terrain edit**.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
1. **Clearing = `BlightClutter`, a `ResourceNode` SIBLING harvested by a UNIFIED sweep.** New ownerless interpolated
|
||||||
|
ghost `BlightClutter { [GhostField] int Remaining; [GhostField] byte Variant; byte ScrapResourceId; float ScrapPerHit }`
|
||||||
|
(RegionTag{Expedition}, prefab duplicated from `ResourceNode.prefab` so the GhostAuthoring comes free). It deposits a
|
||||||
|
small Biomass "scrap" trickle and despawns at ≤0. **`ResourceHarvestSystem` was UNIFIED** to sweep nodes AND clutter
|
||||||
|
in ONE best-target loop — **a correctness requirement, not a convenience**: two separate sweep systems would each
|
||||||
|
`ecb.DestroyEntity` a projectile that overlaps a node AND a clutter piece → the documented "double DestroyEntity
|
||||||
|
throws at playback". `ExpeditionFieldSystem` scatters clutter from an OPTIONAL `ClutterFieldSpawner` singleton on the
|
||||||
|
same per-player-presence epoch edge as the node field (distinct seed `epoch*2+1`, round-robin Variant) and clears it
|
||||||
|
on the occupied→empty edge. Client smash juice = new **`WorldFeedbackSystem`** (observe-only managed `SystemBase` in
|
||||||
|
`PresentationSystemGroup`, mirrors `CombatFeedbackSystem`): edge-detects `Remaining` for a chip burst, despawn for a
|
||||||
|
shatter burst + camera punch, with a **proximity gate** (last-pos within ~40u of the local player) so the
|
||||||
|
region-transit relevancy-drop storm at +1000 stays silent off-camera. Knobs in live-tunable `WorldFeelConfig`.
|
||||||
|
|
||||||
|
2. **Buildables = byte-additive `Wall`(5)/`Pylon`(6).** `StructureType.Wall=5, Pylon=6` (2–4 stay RESERVED for M7
|
||||||
|
automation). A generic `StructureAuthoring{ byte Kind }` bakes only `PlacedStructure{Type=Kind}` (no `Turret`
|
||||||
|
component → `TurretFireSystem` ignores it). `StructureCatalogAuthoring` grew two additive rows; `BuildSendSystem`
|
||||||
|
grew dedicated **V=Wall / N=Pylon** keys + editor `PlaceWall`/`PlacePylon` hooks. **`BuildPlaceSystem` was NOT
|
||||||
|
touched** — it already resolves any catalog `Type` and stamps `PlacedStructure` + `RegionTag{Base}` generically.
|
||||||
|
`Wall.prefab` carries a static `BoxCollider` (baked → `PhysicsCollider` → the CC sweeps it → blocks the player);
|
||||||
|
`Pylon.prefab` has none (cosmetic beacon). Because `PlacedStructure.Type` is already a `[GhostField] byte`, adding
|
||||||
|
values 5/6 keeps the serializer identical → **no re-bake of existing ghosts**.
|
||||||
|
|
||||||
|
3. **Visual cohesion = Aether material pair + Blightfield basin (classic-URP cosmetics, no subscene re-bake).**
|
||||||
|
`M_Aether_Wild` (HDR orange emissive) + `M_Aether_Ordered` (HDR cyan emissive) URP/Lit assets; the three new ghosts
|
||||||
|
recoloured into the palette (Clutter→wild, Wall+Pylon→ordered). The barren +1000 Blightfield became a **21-piece
|
||||||
|
Synty rock/asteroid/debris basin + 3 orange wild-Aether point lights** under a `BlightfieldDecor` root in `Game.unity`
|
||||||
|
— deterministic ring/scatter at varied scale/height (the "non-flat" relief via props, **no terrain**), colliders
|
||||||
|
stripped (inert to the DOTS PhysicsWorld anyway, per the blessed `SyntyWorld` classic-URP pattern). Bulk authoring via
|
||||||
|
`execute_code` (PrefabUtility) for determinism + speed.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- **Validated.** EditMode **142/142** green (rewrote/added 5 `ResourceHarvestSystem` tests incl. node+clutter overlap,
|
||||||
|
fractional-yield, and same-tick-cleared consumption; existing 134 unaffected). **In-editor Play (server+client over
|
||||||
|
IPC), driven via `execute_code` world-introspection:** baking clean (catalog = Turret/Wall/Pylon with valid prefab
|
||||||
|
refs + correct costs; `ClutterFieldSpawner` baked); buildables end-to-end (built Wall@(16,16)+Pylon@(18,16), Ore 50→44,
|
||||||
|
Type GhostField replicated, Cell stays server-only=0 on client); **R1** confirmed (client Wall entity has
|
||||||
|
`PhysicsCollider`, Pylon doesn't); clutter scatter end-to-end (14 clutter + 8 nodes spawned + replicated at x≈1000,
|
||||||
|
round-robin variants); **R2** clear (the edited Bursted `ExpeditionFieldSystem` ran; **0 errors** all session, only the
|
||||||
|
pre-existing unfocused-editor Server-Tick-Batching warnings). Decor verified by VALUE (21 renderers, 0 broken shaders,
|
||||||
|
x∈[977,1020], correct HDR emission) — not just a screenshot.
|
||||||
|
- **Adversarial review** (4-lens Workflow → verify) raised 23, confirmed 4 (3 were one root issue). **Fixed before
|
||||||
|
sign-off:** an **immortal-shot-sink** — `int amount = (int)YieldPerHit` truncates a sub-1.0 per-hit value to 0, so the
|
||||||
|
projectile is consumed, nothing deposits, and the target never depletes; reachable only because
|
||||||
|
`BlightClutterAuthoring.ScrapPerHit` was `[Min(0f)]` (the node sibling is `[Min(1f)]`). Hardened with `[Min(1f)]` +
|
||||||
|
`math.max(1, (int)…)` in the system. Plus a low **same-tick-cleared passthrough** — a 2nd projectile whose only target
|
||||||
|
a sibling already cleared this tick now still consumes (an `overlappedCleared` branch) so "a hit always spends the
|
||||||
|
shot." Both regression-tested. The 19 refuted were correct-by-design (the `ghostId==0` relevancy skip, Temp-ECB
|
||||||
|
no-dispose matching sibling spawn systems, the proximity gate vs. atomic-teleport transit, observe-only audit).
|
||||||
|
- **No new asmdefs.** New code under `…/Economy/` (`BlightClutter`, `ClutterFieldSpawner` + authorings), `…/Building/`
|
||||||
|
(`StructureType.Wall/Pylon`, `StructureAuthoring`, catalog rows), `…/Client/Presentation/` (`WorldFeedbackSystem`,
|
||||||
|
`WorldFeelConfig`), `…/Client/Building/` (BuildSendSystem keys). New assets: `BlightClutter`/`Wall`/`Pylon` prefabs,
|
||||||
|
`M_Aether_Wild`/`M_Aether_Ordered` materials, `Gameplay.unity` (clutter spawner + catalog rows), `Game.unity`
|
||||||
|
(BlightfieldDecor). Reuses the runtime-ghost recipe / region split / GhostRelevancy / swept-hit / RPC-build /
|
||||||
|
StatModifier-free-economy patterns verbatim. Orthogonal to + additive over M8; does not touch the M7 automation slots.
|
||||||
|
|
||||||
|
## Open / deferred (defaulted, tunable)
|
||||||
|
|
||||||
|
- **Base-side cohesion**: this layer dressed the Blightfield (the barren gap); the cyan-ordered base still reads via the
|
||||||
|
existing city props + the new cyan-recoloured Wall/Pylon. A deliberate base accent (cyan lights, perimeter relief,
|
||||||
|
re-enabled tiled ground) was deferred to avoid clashing with the tuned SSAO/ACES look.
|
||||||
|
- **Clearing as a spatial mechanic**: shipped as juice + scrap only (no gating); making cleared cells buildable or
|
||||||
|
reveal hidden nodes (the "functional gating" fork) is a follow-up.
|
||||||
|
- **More buildables / build-palette HUD**: dedicated V/N keys for now (matches the B/U idiom); a selectable build palette
|
||||||
|
+ ghost-preview is polish.
|
||||||
|
- **Clutter visual variety**: `Variant` is replicated + round-robined but the prefab is single-mesh; per-variant meshes
|
||||||
|
are a follow-up (additive, no schema change).
|
||||||
|
- **Focused-editor Play confirmed (2026-06-04, same day):** the Wall **stops the player CC dead at the collider face**
|
||||||
|
(drove +X, X 2.50→**3.257**, server==client, no tunnel); the **Blightfield reads as an orange-wild rock basin** vs the
|
||||||
|
cyan base (real player-view game-camera screenshots); the **clutter-clear loop works end-to-end** (BIO 0→6→10, clutter
|
||||||
|
14→13 shatter replicated to the client); node harvest AETHER 0→30 (R2 clear on the re-edited system). Console clean.
|
||||||
|
Remaining is only **feel-tuning** (basin density/lighting) + the base-side accents — not correctness.
|
||||||
|
|
||||||
|
## Build gotchas recorded this session
|
||||||
|
|
||||||
|
- **A unified sweep is REQUIRED, not optional, when two target types share one projectile pass** — separate sweep
|
||||||
|
systems each `DestroyEntity` an overlapping projectile → double-destroy at ECB playback. One best-target loop over the
|
||||||
|
combined set + a shared destroyed-bitset is the correct shape.
|
||||||
|
- **Float per-hit yield truncated to an int that ALSO gates despawn is an immortal-sink footgun**: a sub-1.0 value →
|
||||||
|
`(int)`=0 → no deposit AND no decrement, yet the projectile is still consumed. Guard with `[Min(1f)]` on the authoring
|
||||||
|
AND `math.max(1, (int)…)` in the consumer (the node path was already `[Min(1f)]`; the new clutter path wasn't).
|
||||||
|
- **Unfocused-editor Play validation is feasible via `execute_code` world-introspection** even when the Play transition
|
||||||
|
stalls: identify worlds by `world.Name`, query/instantiate/mutate via the live `EntityManager`, drive client editor
|
||||||
|
hooks (`BuildSendSystem.PlaceWall`) + deposit into the ledger to exercise end-to-end paths, and read errors-only from
|
||||||
|
the console (the tick-batching warning spam is benign). Keep the session short (unfocused can dispose netcode worlds).
|
||||||
|
- **Editing a Bursted ISystem's BODY (not its query set) is low R2 risk** — the stale-binary hazard is tied to query-set
|
||||||
|
changes on an unfocused editor; a `math.max`/loop-reorder edit that leaves the SystemAPI query set unchanged is safe.
|
||||||
|
- **`manage_prefabs modify_contents component_properties` sets a `byte` field fine** (verified Pylon.Kind=6) — the
|
||||||
|
silent-drop gotcha is enum/Vector3, not byte. Still verify via `execute_code`.
|
||||||
|
|
||||||
|
Builds on + reuses [[DR-013_M6_Aether_Cycle_Region_Split]] (region split, GhostRelevancy, swept-hit, runtime-ghost),
|
||||||
|
[[DR-014_M6_Build_Structures_Automation_Foundation]] (build pipeline, structure ghost, frozen grid/schema),
|
||||||
|
[[DR-015_The_Awakening_Engine_Fiction_Adoption]] (Aether palette), and the client-presentation pattern from
|
||||||
|
[[DR-009_GameFeel_Identity_FirstBlood]]. Serves the cohesive-world + persistent-base pillars in [[Pillars]].
|
||||||
Reference in New Issue
Block a user