(Feat) Add Remove Building
This commit is contained in:
@@ -194,6 +194,15 @@
|
|||||||
"processors": "",
|
"processors": "",
|
||||||
"interactions": "",
|
"interactions": "",
|
||||||
"initialStateCheck": false
|
"initialStateCheck": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "BuildRotate",
|
||||||
|
"type": "Button",
|
||||||
|
"id": "c1d2e3f4-0004-4004-8004-000000000004",
|
||||||
|
"expectedControlType": "Button",
|
||||||
|
"processors": "",
|
||||||
|
"interactions": "",
|
||||||
|
"initialStateCheck": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"bindings": [
|
"bindings": [
|
||||||
@@ -450,6 +459,17 @@
|
|||||||
"isComposite": false,
|
"isComposite": false,
|
||||||
"isPartOfComposite": false
|
"isPartOfComposite": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"id": "c1d2e3f4-0005-4005-8005-000000000005",
|
||||||
|
"path": "<Keyboard>/r",
|
||||||
|
"interactions": "",
|
||||||
|
"processors": "",
|
||||||
|
"groups": ";Keyboard&Mouse",
|
||||||
|
"action": "BuildRotate",
|
||||||
|
"isComposite": false,
|
||||||
|
"isPartOfComposite": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "",
|
"name": "",
|
||||||
"id": "b1b01001-0001-0001-0001-000000000001",
|
"id": "b1b01001-0001-0001-0001-000000000001",
|
||||||
|
|||||||
@@ -1,204 +0,0 @@
|
|||||||
%YAML 1.1
|
|
||||||
%TAG !u! tag:unity3d.com,2011:
|
|
||||||
--- !u!21 &2100000
|
|
||||||
Material:
|
|
||||||
serializedVersion: 8
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_Name: New Material
|
|
||||||
m_Shader: {fileID: 4800000, guid: 69c1f799e772cb6438f56c23efccb782, type: 3}
|
|
||||||
m_Parent: {fileID: 0}
|
|
||||||
m_ModifiedSerializedProperties: 0
|
|
||||||
m_ValidKeywords: []
|
|
||||||
m_InvalidKeywords: []
|
|
||||||
m_LightmapFlags: 4
|
|
||||||
m_EnableInstancingVariants: 0
|
|
||||||
m_DoubleSidedGI: 0
|
|
||||||
m_CustomRenderQueue: -1
|
|
||||||
stringTagMap: {}
|
|
||||||
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}
|
|
||||||
- _Control:
|
|
||||||
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}
|
|
||||||
- _Mask0:
|
|
||||||
m_Texture: {fileID: 0}
|
|
||||||
m_Scale: {x: 1, y: 1}
|
|
||||||
m_Offset: {x: 0, y: 0}
|
|
||||||
- _Mask1:
|
|
||||||
m_Texture: {fileID: 0}
|
|
||||||
m_Scale: {x: 1, y: 1}
|
|
||||||
m_Offset: {x: 0, y: 0}
|
|
||||||
- _Mask2:
|
|
||||||
m_Texture: {fileID: 0}
|
|
||||||
m_Scale: {x: 1, y: 1}
|
|
||||||
m_Offset: {x: 0, y: 0}
|
|
||||||
- _Mask3:
|
|
||||||
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}
|
|
||||||
- _Normal0:
|
|
||||||
m_Texture: {fileID: 0}
|
|
||||||
m_Scale: {x: 1, y: 1}
|
|
||||||
m_Offset: {x: 0, y: 0}
|
|
||||||
- _Normal1:
|
|
||||||
m_Texture: {fileID: 0}
|
|
||||||
m_Scale: {x: 1, y: 1}
|
|
||||||
m_Offset: {x: 0, y: 0}
|
|
||||||
- _Normal2:
|
|
||||||
m_Texture: {fileID: 0}
|
|
||||||
m_Scale: {x: 1, y: 1}
|
|
||||||
m_Offset: {x: 0, y: 0}
|
|
||||||
- _Normal3:
|
|
||||||
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}
|
|
||||||
- _Splat0:
|
|
||||||
m_Texture: {fileID: 0}
|
|
||||||
m_Scale: {x: 1, y: 1}
|
|
||||||
m_Offset: {x: 0, y: 0}
|
|
||||||
- _Splat1:
|
|
||||||
m_Texture: {fileID: 0}
|
|
||||||
m_Scale: {x: 1, y: 1}
|
|
||||||
m_Offset: {x: 0, y: 0}
|
|
||||||
- _Splat2:
|
|
||||||
m_Texture: {fileID: 0}
|
|
||||||
m_Scale: {x: 1, y: 1}
|
|
||||||
m_Offset: {x: 0, y: 0}
|
|
||||||
- _Splat3:
|
|
||||||
m_Texture: {fileID: 0}
|
|
||||||
m_Scale: {x: 1, y: 1}
|
|
||||||
m_Offset: {x: 0, y: 0}
|
|
||||||
- _TerrainHolesTexture:
|
|
||||||
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
|
|
||||||
- _EnableHeightBlend: 0
|
|
||||||
- _EnableInstancedPerPixelNormal: 1
|
|
||||||
- _EnvironmentReflections: 1
|
|
||||||
- _GlossMapScale: 0
|
|
||||||
- _Glossiness: 0
|
|
||||||
- _GlossyReflections: 0
|
|
||||||
- _HeightTransition: 0
|
|
||||||
- _Metallic: 0
|
|
||||||
- _Metallic0: 0
|
|
||||||
- _Metallic1: 0
|
|
||||||
- _Metallic2: 0
|
|
||||||
- _Metallic3: 0
|
|
||||||
- _NumLayersCount: 1
|
|
||||||
- _OcclusionStrength: 1
|
|
||||||
- _Parallax: 0.005
|
|
||||||
- _QueueOffset: 0
|
|
||||||
- _ReceiveShadows: 1
|
|
||||||
- _Smoothness: 0.5
|
|
||||||
- _Smoothness0: 0.5
|
|
||||||
- _Smoothness1: 0.5
|
|
||||||
- _Smoothness2: 0.5
|
|
||||||
- _Smoothness3: 0.5
|
|
||||||
- _SmoothnessTextureChannel: 0
|
|
||||||
- _SpecularHighlights: 1
|
|
||||||
- _SrcBlend: 1
|
|
||||||
- _SrcBlendAlpha: 1
|
|
||||||
- _Surface: 0
|
|
||||||
- _WorkflowMode: 1
|
|
||||||
- _XRMotionVectorsPass: 1
|
|
||||||
- _ZWrite: 1
|
|
||||||
m_Colors:
|
|
||||||
- _BaseColor: {r: 1, g: 1, b: 1, a: 1}
|
|
||||||
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
|
||||||
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
|
|
||||||
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
|
|
||||||
m_BuildTextureStacks: []
|
|
||||||
m_AllowLocking: 1
|
|
||||||
--- !u!114 &200115796970648388
|
|
||||||
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
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: d804f7d018e008f4db0ea774eeaadda6
|
|
||||||
NativeFormatImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
mainObjectFileID: 2100000
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -2517,6 +2517,10 @@ MonoBehaviour:
|
|||||||
obstructionMask:
|
obstructionMask:
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
m_Bits: 512
|
m_Bits: 512
|
||||||
|
buildMask:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 512
|
||||||
|
demolitionMaterial: {fileID: 2100000, guid: 0a594aa41ef0d7e428398c2c32772c64, type: 2}
|
||||||
--- !u!1001 &298545449
|
--- !u!1001 &298545449
|
||||||
PrefabInstance:
|
PrefabInstance:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -5514,6 +5518,100 @@ Transform:
|
|||||||
m_CorrespondingSourceObject: {fileID: 8499178216258465, guid: e9894ffe485f7d5448260fd9338ebd0e, type: 3}
|
m_CorrespondingSourceObject: {fileID: 8499178216258465, guid: e9894ffe485f7d5448260fd9338ebd0e, type: 3}
|
||||||
m_PrefabInstance: {fileID: 663482573}
|
m_PrefabInstance: {fileID: 663482573}
|
||||||
m_PrefabAsset: {fileID: 0}
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
--- !u!1001 &668733573
|
||||||
|
PrefabInstance:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Modification:
|
||||||
|
serializedVersion: 3
|
||||||
|
m_TransformParent: {fileID: 0}
|
||||||
|
m_Modifications:
|
||||||
|
- target: {fileID: 781270504346949712, guid: 8b0c536290aeb0b4caafd67bc736e9d2, type: 3}
|
||||||
|
propertyPath: m_Name
|
||||||
|
value: Stations_5
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 781270504346949712, guid: 8b0c536290aeb0b4caafd67bc736e9d2, type: 3}
|
||||||
|
propertyPath: m_Layer
|
||||||
|
value: 6
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 985001107293900664, guid: 8b0c536290aeb0b4caafd67bc736e9d2, type: 3}
|
||||||
|
propertyPath: 'm_Materials.Array.data[0]'
|
||||||
|
value:
|
||||||
|
objectReference: {fileID: 1537286330038668216, guid: 73f5d288fb608fb4e9959cf5e9655783, type: 3}
|
||||||
|
- target: {fileID: 8601272150287418368, guid: 8b0c536290aeb0b4caafd67bc736e9d2, type: 3}
|
||||||
|
propertyPath: m_LocalPosition.x
|
||||||
|
value: 49.20204
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 8601272150287418368, guid: 8b0c536290aeb0b4caafd67bc736e9d2, type: 3}
|
||||||
|
propertyPath: m_LocalPosition.y
|
||||||
|
value: -0.005176544
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 8601272150287418368, guid: 8b0c536290aeb0b4caafd67bc736e9d2, type: 3}
|
||||||
|
propertyPath: m_LocalPosition.z
|
||||||
|
value: 55.359283
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 8601272150287418368, guid: 8b0c536290aeb0b4caafd67bc736e9d2, type: 3}
|
||||||
|
propertyPath: m_LocalRotation.w
|
||||||
|
value: 0.70710784
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 8601272150287418368, guid: 8b0c536290aeb0b4caafd67bc736e9d2, type: 3}
|
||||||
|
propertyPath: m_LocalRotation.x
|
||||||
|
value: -0
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 8601272150287418368, guid: 8b0c536290aeb0b4caafd67bc736e9d2, type: 3}
|
||||||
|
propertyPath: m_LocalRotation.y
|
||||||
|
value: -0.70710576
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 8601272150287418368, guid: 8b0c536290aeb0b4caafd67bc736e9d2, type: 3}
|
||||||
|
propertyPath: m_LocalRotation.z
|
||||||
|
value: -0
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 8601272150287418368, guid: 8b0c536290aeb0b4caafd67bc736e9d2, type: 3}
|
||||||
|
propertyPath: m_LocalEulerAnglesHint.x
|
||||||
|
value: 0
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 8601272150287418368, guid: 8b0c536290aeb0b4caafd67bc736e9d2, type: 3}
|
||||||
|
propertyPath: m_LocalEulerAnglesHint.y
|
||||||
|
value: -90
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 8601272150287418368, guid: 8b0c536290aeb0b4caafd67bc736e9d2, type: 3}
|
||||||
|
propertyPath: m_LocalEulerAnglesHint.z
|
||||||
|
value: 0
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
m_RemovedComponents: []
|
||||||
|
m_RemovedGameObjects: []
|
||||||
|
m_AddedGameObjects: []
|
||||||
|
m_AddedComponents:
|
||||||
|
- targetCorrespondingSourceObject: {fileID: 781270504346949712, guid: 8b0c536290aeb0b4caafd67bc736e9d2, type: 3}
|
||||||
|
insertIndex: -1
|
||||||
|
addedObject: {fileID: 668733575}
|
||||||
|
m_SourcePrefab: {fileID: 100100000, guid: 8b0c536290aeb0b4caafd67bc736e9d2, type: 3}
|
||||||
|
--- !u!1 &668733574 stripped
|
||||||
|
GameObject:
|
||||||
|
m_CorrespondingSourceObject: {fileID: 781270504346949712, guid: 8b0c536290aeb0b4caafd67bc736e9d2, type: 3}
|
||||||
|
m_PrefabInstance: {fileID: 668733573}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
--- !u!65 &668733575
|
||||||
|
BoxCollider:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 668733574}
|
||||||
|
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: 2.7156103, y: 1.8032616, z: 1.15291}
|
||||||
|
m_Center: {x: 0.3753096, y: 0.90680677, z: 0.050604135}
|
||||||
--- !u!1 &713085368
|
--- !u!1 &713085368
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -6309,6 +6407,8 @@ MonoBehaviour:
|
|||||||
_prefab: Assets/GAME/Prefabs/Structure/Cookingpot_Campfire.prefab
|
_prefab: Assets/GAME/Prefabs/Structure/Cookingpot_Campfire.prefab
|
||||||
- _gameObject: {fileID: 245990655}
|
- _gameObject: {fileID: 245990655}
|
||||||
_prefab: Assets/External/VFXPACK_FIRE_WALLCOEUR/Prefab/VFX_Fire.prefab
|
_prefab: Assets/External/VFXPACK_FIRE_WALLCOEUR/Prefab/VFX_Fire.prefab
|
||||||
|
- _gameObject: {fileID: 668733574}
|
||||||
|
_prefab: Assets/External/Static Soul Studio/Crafting Statinos/Built-in/Prefabs/Stations_5.prefab
|
||||||
--- !u!4 &740572252
|
--- !u!4 &740572252
|
||||||
Transform:
|
Transform:
|
||||||
m_ObjectHideFlags: 11
|
m_ObjectHideFlags: 11
|
||||||
@@ -29616,3 +29716,4 @@ SceneRoots:
|
|||||||
- {fileID: 1836939580}
|
- {fileID: 1836939580}
|
||||||
- {fileID: 1902801754}
|
- {fileID: 1902801754}
|
||||||
- {fileID: 1957565059}
|
- {fileID: 1957565059}
|
||||||
|
- {fileID: 668733573}
|
||||||
|
|||||||
@@ -62,6 +62,13 @@ namespace Ashwild.Building
|
|||||||
[Tooltip("Layers that block a build (structures, props, players). Must NOT include the ground.")]
|
[Tooltip("Layers that block a build (structures, props, players). Must NOT include the ground.")]
|
||||||
[SerializeField] private LayerMask obstructionMask;
|
[SerializeField] private LayerMask obstructionMask;
|
||||||
|
|
||||||
|
[Header("Demolition")]
|
||||||
|
[Tooltip("Layers the demolition aim can hit to target a committed structure. Narrow this to the structures' layer so walls, roofs, etc. are the only demolishable hits.")]
|
||||||
|
[SerializeField] private LayerMask buildMask = ~0;
|
||||||
|
|
||||||
|
[Tooltip("Overlay material added on top of the aimed build while it is the demolition target (e.g. a translucent red 'about to break' pass). Should be a transparent/additive material since it draws over the model.")]
|
||||||
|
[SerializeField] private Material demolitionMaterial;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region State
|
#region State
|
||||||
@@ -95,10 +102,16 @@ namespace Ashwild.Building
|
|||||||
private float yaw;
|
private float yaw;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The local player's aim transform (camera), resolved when the ghost is attached.
|
/// The local player's aim transform (camera), resolved lazily and shared by placement and
|
||||||
|
/// demolition.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private Transform aim;
|
private Transform aim;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The committed build currently under the demolition aim, or null when none is targeted.
|
||||||
|
/// </summary>
|
||||||
|
private BuiltStructure demoTarget;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Frame the ghost was attached, so a click landing that frame never confirms instantly.
|
/// Frame the ghost was attached, so a click landing that frame never confirms instantly.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -141,6 +154,8 @@ namespace Ashwild.Building
|
|||||||
PlayerEvents.AttackPressed += HandleConfirm;
|
PlayerEvents.AttackPressed += HandleConfirm;
|
||||||
PlayerEvents.SecondaryUsePressed += HandleCancel;
|
PlayerEvents.SecondaryUsePressed += HandleCancel;
|
||||||
PlayerEvents.HotbarScroll += HandleRotate;
|
PlayerEvents.HotbarScroll += HandleRotate;
|
||||||
|
PlayerEvents.BuildRotatePressed += HandleRotateKey;
|
||||||
|
PlayerEvents.DemolishModeChanged += HandleDemolishModeChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -152,16 +167,27 @@ namespace Ashwild.Building
|
|||||||
PlayerEvents.AttackPressed -= HandleConfirm;
|
PlayerEvents.AttackPressed -= HandleConfirm;
|
||||||
PlayerEvents.SecondaryUsePressed -= HandleCancel;
|
PlayerEvents.SecondaryUsePressed -= HandleCancel;
|
||||||
PlayerEvents.HotbarScroll -= HandleRotate;
|
PlayerEvents.HotbarScroll -= HandleRotate;
|
||||||
|
PlayerEvents.BuildRotatePressed -= HandleRotateKey;
|
||||||
|
PlayerEvents.DemolishModeChanged -= HandleDemolishModeChanged;
|
||||||
|
|
||||||
EndPlacement();
|
EndPlacement();
|
||||||
|
ClearDemoTarget();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Drives the owned ghost along the aim each frame; frozen while input is locked (e.g. the
|
/// Each frame, in exactly one mode: in demolition mode it tracks which build the player aims
|
||||||
/// menu was reopened over an active placement).
|
/// at; otherwise it drives the owned ghost along the aim. Both are frozen while input is locked
|
||||||
|
/// (e.g. the menu reopened over a placement), and leaving demolition mode clears the target.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void Update()
|
private void Update()
|
||||||
{
|
{
|
||||||
|
if (PlayerEvents.IsDemolishing && !PlayerEvents.InputLocked)
|
||||||
|
{
|
||||||
|
UpdateDemolitionTarget();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ClearDemoTarget();
|
||||||
|
|
||||||
if (ghost == null) return;
|
if (ghost == null) return;
|
||||||
if (PlayerEvents.InputLocked) return;
|
if (PlayerEvents.InputLocked) return;
|
||||||
|
|
||||||
@@ -268,9 +294,7 @@ namespace Ashwild.Building
|
|||||||
ghost = go;
|
ghost = go;
|
||||||
ghostValidity = go.GetComponentInChildren<BuildGhost>(true);
|
ghostValidity = go.GetComponentInChildren<BuildGhost>(true);
|
||||||
ghostSnaps = go.GetComponentsInChildren<BuildSnapPoint>(true);
|
ghostSnaps = go.GetComponentsInChildren<BuildSnapPoint>(true);
|
||||||
aim = aimCamera != null ? aimCamera.transform : (Camera.main != null ? Camera.main.transform : null);
|
ResolveAim();
|
||||||
if (aim == null)
|
|
||||||
Debug.LogError("[BuildManager] Pas de caméra d'aim (Camera.main introuvable — la caméra du joueur est-elle taguée MainCamera ?).", this);
|
|
||||||
|
|
||||||
placementBeganFrame = Time.frameCount;
|
placementBeganFrame = Time.frameCount;
|
||||||
UpdateGhostPose();
|
UpdateGhostPose();
|
||||||
@@ -278,11 +302,18 @@ namespace Ashwild.Building
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Left-click: commits the ghost's pose to the registry (spawned for everyone) and ends the
|
/// Left-click: demolishes the aimed build while in demolition mode, otherwise commits the
|
||||||
/// placement. Blocked on an invalid spot and on the very frame the ghost was attached.
|
/// ghost's pose to the registry (spawned for everyone) and ends the placement. Placement is
|
||||||
|
/// blocked on an invalid spot and on the very frame the ghost was attached.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void HandleConfirm()
|
private void HandleConfirm()
|
||||||
{
|
{
|
||||||
|
if (PlayerEvents.IsDemolishing)
|
||||||
|
{
|
||||||
|
TryDemolish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (ghost == null) return;
|
if (ghost == null) return;
|
||||||
if (PlayerEvents.InputLocked) return;
|
if (PlayerEvents.InputLocked) return;
|
||||||
if (Time.frameCount == placementBeganFrame) return;
|
if (Time.frameCount == placementBeganFrame) return;
|
||||||
@@ -316,6 +347,18 @@ namespace Ashwild.Building
|
|||||||
else if (scroll < 0f) yaw -= rotationStep;
|
else if (scroll < 0f) yaw -= rotationStep;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// R key: yaws the ghost by one rotation step in the same direction as a scroll-up, so the
|
||||||
|
/// build can be turned without the mouse wheel. Ignored when no ghost is being placed.
|
||||||
|
/// </summary>
|
||||||
|
private void HandleRotateKey()
|
||||||
|
{
|
||||||
|
if (ghost == null) return;
|
||||||
|
if (PlayerEvents.InputLocked) return;
|
||||||
|
|
||||||
|
yaw += rotationStep;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Poses the ghost each frame: quantise the aim hit to the grid with the current yaw, then
|
/// Poses the ghost each frame: quantise the aim hit to the grid with the current yaw, then
|
||||||
/// let a nearby matching socket pull it into an exact connection (sockets win over the grid).
|
/// let a nearby matching socket pull it into an exact connection (sockets win over the grid).
|
||||||
@@ -410,6 +453,76 @@ namespace Ashwild.Building
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves and caches the local player's aim transform — the assigned camera, else the
|
||||||
|
/// runtime Camera.main (the FPS camera). Logs once when neither is found.
|
||||||
|
/// </summary>
|
||||||
|
private Transform ResolveAim()
|
||||||
|
{
|
||||||
|
if (aim != null) return aim;
|
||||||
|
|
||||||
|
aim = aimCamera != null ? aimCamera.transform : (Camera.main != null ? Camera.main.transform : null);
|
||||||
|
if (aim == null)
|
||||||
|
Debug.LogError("[BuildManager] Pas de caméra d'aim (Camera.main introuvable — la caméra du joueur est-elle taguée MainCamera ?).", this);
|
||||||
|
return aim;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Demolition
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Leaving demolition mode drops any current target so the crosshair returns to normal; entering
|
||||||
|
/// it needs nothing here — Update starts tracking on the next frame.
|
||||||
|
/// </summary>
|
||||||
|
private void HandleDemolishModeChanged(bool on)
|
||||||
|
{
|
||||||
|
if (!on) ClearDemoTarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Casts the aim ray for a committed build and moves the demolition highlight when the target
|
||||||
|
/// changes: the previous build removes its overlay and the new one gets the demolition material
|
||||||
|
/// added on top, so exactly the aimed build reads as "about to break".
|
||||||
|
/// </summary>
|
||||||
|
private void UpdateDemolitionTarget()
|
||||||
|
{
|
||||||
|
Transform a = ResolveAim();
|
||||||
|
if (a == null) { ClearDemoTarget(); return; }
|
||||||
|
|
||||||
|
BuiltStructure hit = null;
|
||||||
|
if (Physics.Raycast(a.position, a.forward, out RaycastHit info, placeRange, buildMask, QueryTriggerInteraction.Ignore))
|
||||||
|
hit = info.collider.GetComponentInParent<BuiltStructure>();
|
||||||
|
|
||||||
|
if (hit == demoTarget) return;
|
||||||
|
|
||||||
|
if (demoTarget != null) demoTarget.ClearHighlight();
|
||||||
|
demoTarget = hit;
|
||||||
|
if (demoTarget != null) demoTarget.Highlight(demolitionMaterial);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Left-click while demolishing: asks the registry to remove the aimed build for everyone. The
|
||||||
|
/// target stays until the next frame re-evaluates, so a miss simply retargets.
|
||||||
|
/// </summary>
|
||||||
|
private void TryDemolish()
|
||||||
|
{
|
||||||
|
if (demoTarget == null) return;
|
||||||
|
if (BuildRegistry.Instance != null)
|
||||||
|
BuildRegistry.Instance.RequestDemolish(demoTarget.gameObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Drops the current demolition target, restoring its authored materials. Safe to call when
|
||||||
|
/// nothing is targeted.
|
||||||
|
/// </summary>
|
||||||
|
private void ClearDemoTarget()
|
||||||
|
{
|
||||||
|
if (demoTarget == null) return;
|
||||||
|
demoTarget.ClearHighlight();
|
||||||
|
demoTarget = null;
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,6 +99,43 @@ namespace Ashwild.Building
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Demolish Build
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called by BuildManager when the local player demolishes a build they aimed at: resolves the
|
||||||
|
/// hit instance to its index in the replicated list and asks the server to remove it. The index
|
||||||
|
/// is a valid shared key because <see cref="spawned"/> stays aligned with <see cref="builds"/>
|
||||||
|
/// on every machine. No-op when the object is not one of our committed builds.
|
||||||
|
/// </summary>
|
||||||
|
public void RequestDemolish(GameObject builtInstance)
|
||||||
|
{
|
||||||
|
if (builtInstance == null) return;
|
||||||
|
|
||||||
|
int index = spawned.IndexOf(builtInstance);
|
||||||
|
if (index < 0)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[BuildRegistry] '{builtInstance.name}' is not a committed build — nothing to demolish.", this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
RequestDemolishServerRpc(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Server-side: removes the record at this index, which replicates the removal (and the local
|
||||||
|
/// teardown + occupancy refresh) to every client. Range-guarded against a stale index from a
|
||||||
|
/// build that another player demolished during the round-trip.
|
||||||
|
/// </summary>
|
||||||
|
[ServerRpc(RequireOwnership = false)]
|
||||||
|
private void RequestDemolishServerRpc(int index, NetworkConnection conn = null)
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= builds.Count) return;
|
||||||
|
|
||||||
|
// TODO: refund the builder's resources (conn → PlayerInventory) once build cost lands.
|
||||||
|
builds.RemoveAt(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Networked Ghost Preview
|
#region Networked Ghost Preview
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -187,6 +224,7 @@ namespace Ashwild.Building
|
|||||||
case SyncListOperation.RemoveAt:
|
case SyncListOperation.RemoveAt:
|
||||||
DestroyAt(index);
|
DestroyAt(index);
|
||||||
spawned.RemoveAt(index);
|
spawned.RemoveAt(index);
|
||||||
|
RecomputeOccupancy();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SyncListOperation.Clear:
|
case SyncListOperation.Clear:
|
||||||
@@ -210,7 +248,10 @@ namespace Ashwild.Building
|
|||||||
Debug.LogWarning($"[BuildRegistry] Buildable id {record.buildableId} introuvable ou sans BuiltPrefab — rien de spawné.", this);
|
Debug.LogWarning($"[BuildRegistry] Buildable id {record.buildableId} introuvable ou sans BuiltPrefab — rien de spawné.", this);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return Instantiate(data.BuiltPrefab, record.position, record.rotation);
|
|
||||||
|
GameObject go = Instantiate(data.BuiltPrefab, record.position, record.rotation);
|
||||||
|
if (go.GetComponent<BuiltStructure>() == null) go.AddComponent<BuiltStructure>();
|
||||||
|
return go;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -246,6 +287,24 @@ namespace Ashwild.Building
|
|||||||
MarkOccupancy(go);
|
MarkOccupancy(go);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Recomputes occupancy from scratch after a removal: clears every socket, then re-marks the
|
||||||
|
/// connected pairs across what remains. Without this a demolished piece would leave its former
|
||||||
|
/// neighbours' sockets stuck "occupied", so nothing could ever be snapped back into the gap.
|
||||||
|
/// </summary>
|
||||||
|
private void RecomputeOccupancy()
|
||||||
|
{
|
||||||
|
foreach (GameObject go in spawned)
|
||||||
|
{
|
||||||
|
if (go == null) continue;
|
||||||
|
foreach (BuildSnapPoint sp in go.GetComponentsInChildren<BuildSnapPoint>(true))
|
||||||
|
sp.SetOccupied(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (GameObject go in spawned)
|
||||||
|
MarkOccupancy(go);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Marks the sockets a piece shares a position with — and the matching sockets on the pieces
|
/// Marks the sockets a piece shares a position with — and the matching sockets on the pieces
|
||||||
/// it touches — as occupied, so placement snapping skips them. Occupancy is derived purely
|
/// it touches — as occupied, so placement snapping skips them. Occupancy is derived purely
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Ashwild.Building
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Runtime marker added to every committed structure instance the BuildRegistry spawns, so the
|
||||||
|
/// demolition aim can recognise a build from a raycast hit (GetComponentInParent) and hand its
|
||||||
|
/// root back to the registry to remove. It also owns the demolition highlight: while it is the
|
||||||
|
/// hammer's target the demolition material is added on top of every renderer (an extra overlay
|
||||||
|
/// pass, like the ghost), and removed again when the aim leaves — so the player sees exactly which
|
||||||
|
/// build will break. Pure local visual + a pure tag: nothing about it ever crosses the wire.
|
||||||
|
/// </summary>
|
||||||
|
[DisallowMultipleComponent]
|
||||||
|
public class BuiltStructure : MonoBehaviour
|
||||||
|
{
|
||||||
|
#region State
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Every child renderer, cached so the highlight can swap and restore their materials.
|
||||||
|
/// </summary>
|
||||||
|
private Renderer[] renderers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Each renderer's original materials, kept so the swap is fully reversible.
|
||||||
|
/// </summary>
|
||||||
|
private Material[][] baseMaterials;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the demolition material is currently applied, so swaps stay idempotent.
|
||||||
|
/// </summary>
|
||||||
|
private bool highlighted;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Unity Lifecycle
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Caches every child renderer and its authored materials up front so highlighting is a cheap
|
||||||
|
/// array assignment with no per-frame allocation of the base look.
|
||||||
|
/// </summary>
|
||||||
|
private void Awake()
|
||||||
|
{
|
||||||
|
renderers = GetComponentsInChildren<Renderer>(true);
|
||||||
|
baseMaterials = new Material[renderers.Length][];
|
||||||
|
for (int i = 0; i < renderers.Length; i++)
|
||||||
|
baseMaterials[i] = renderers[i] != null ? renderers[i].sharedMaterials : new Material[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Public API
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the demolition material on top of each renderer's authored materials (an extra draw
|
||||||
|
/// pass), marking this build as the hammer's current target without hiding the model. Mirrors
|
||||||
|
/// the ghost overlay. No-op when already highlighted or no material was provided.
|
||||||
|
/// </summary>
|
||||||
|
public void Highlight(Material demolitionMaterial)
|
||||||
|
{
|
||||||
|
if (highlighted || demolitionMaterial == null) return;
|
||||||
|
highlighted = true;
|
||||||
|
|
||||||
|
for (int i = 0; i < renderers.Length; i++)
|
||||||
|
{
|
||||||
|
Renderer r = renderers[i];
|
||||||
|
if (r == null) continue;
|
||||||
|
|
||||||
|
Material[] baseMats = baseMaterials[i];
|
||||||
|
Material[] combined = new Material[baseMats.Length + 1];
|
||||||
|
for (int j = 0; j < baseMats.Length; j++) combined[j] = baseMats[j];
|
||||||
|
combined[baseMats.Length] = demolitionMaterial;
|
||||||
|
|
||||||
|
r.materials = combined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Restores each renderer's authored materials. No-op when not highlighted.
|
||||||
|
/// </summary>
|
||||||
|
public void ClearHighlight()
|
||||||
|
{
|
||||||
|
if (!highlighted) return;
|
||||||
|
highlighted = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < renderers.Length; i++)
|
||||||
|
if (renderers[i] != null) renderers[i].materials = baseMaterials[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: fe38331f524a7db4cb3d53381b9ea244
|
||||||
@@ -25,6 +25,8 @@ namespace Ashwild.Player
|
|||||||
public static bool IsBuildMenuOpen { get; private set; }
|
public static bool IsBuildMenuOpen { get; private set; }
|
||||||
// True while a build ghost is being positioned in the world (menu closed, no structure placed yet).
|
// True while a build ghost is being positioned in the world (menu closed, no structure placed yet).
|
||||||
public static bool IsPlacingBuild { get; private set; }
|
public static bool IsPlacingBuild { get; private set; }
|
||||||
|
// True while the build hammer is in demolition mode (right-click held, menu closed, not placing).
|
||||||
|
public static bool IsDemolishing { get; private set; }
|
||||||
public static bool IsPaused { get; private set; }
|
public static bool IsPaused { get; private set; }
|
||||||
|
|
||||||
// True when a networked session is live (host or client).
|
// True when a networked session is live (host or client).
|
||||||
@@ -52,11 +54,13 @@ namespace Ashwild.Player
|
|||||||
public static event Action CrouchPressed;
|
public static event Action CrouchPressed;
|
||||||
public static event Action AttackPressed;
|
public static event Action AttackPressed;
|
||||||
public static event Action SecondaryUsePressed; // right-click / secondary use (build menu, aim, block, ...)
|
public static event Action SecondaryUsePressed; // right-click / secondary use (build menu, aim, block, ...)
|
||||||
|
public static event Action<bool> SecondaryUseHeld; // right-click pressed (true) / released (false) — for tap-vs-hold gestures
|
||||||
public static event Action InteractPressed;
|
public static event Action InteractPressed;
|
||||||
public static event Action DropPressed;
|
public static event Action DropPressed;
|
||||||
public static event Action InventoryTogglePressed;
|
public static event Action InventoryTogglePressed;
|
||||||
public static event Action<int> HotbarSlotPressed;
|
public static event Action<int> HotbarSlotPressed;
|
||||||
public static event Action<float> HotbarScroll;
|
public static event Action<float> HotbarScroll;
|
||||||
|
public static event Action BuildRotatePressed; // rotate the build ghost one step (R key) while placing
|
||||||
public static event Action CancelPressed; // UI "Cancel" (Escape) — back / pause / close
|
public static event Action CancelPressed; // UI "Cancel" (Escape) — back / pause / close
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
@@ -128,6 +132,7 @@ namespace Ashwild.Player
|
|||||||
public static event Action BuildMenuToggleRequested; // the build hammer asks to open/close the construction menu
|
public static event Action BuildMenuToggleRequested; // the build hammer asks to open/close the construction menu
|
||||||
public static event Action<bool> BuildMenuOpenChanged; // the construction menu opened (true) or closed (false)
|
public static event Action<bool> BuildMenuOpenChanged; // the construction menu opened (true) or closed (false)
|
||||||
public static event Action<bool> BuildPlacingChanged; // a build ghost started (true) / stopped (false) being positioned
|
public static event Action<bool> BuildPlacingChanged; // a build ghost started (true) / stopped (false) being positioned
|
||||||
|
public static event Action<bool> DemolishModeChanged; // the hammer entered (true) / left (false) demolition mode (right-click held)
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Cooking events
|
// Cooking events
|
||||||
@@ -182,11 +187,13 @@ namespace Ashwild.Player
|
|||||||
public static void RaiseCrouchPressed() { Log(nameof(CrouchPressed)); CrouchPressed?.Invoke(); }
|
public static void RaiseCrouchPressed() { Log(nameof(CrouchPressed)); CrouchPressed?.Invoke(); }
|
||||||
public static void RaiseAttackPressed() { Log(nameof(AttackPressed)); AttackPressed?.Invoke(); }
|
public static void RaiseAttackPressed() { Log(nameof(AttackPressed)); AttackPressed?.Invoke(); }
|
||||||
public static void RaiseSecondaryUsePressed() { Log(nameof(SecondaryUsePressed)); SecondaryUsePressed?.Invoke(); }
|
public static void RaiseSecondaryUsePressed() { Log(nameof(SecondaryUsePressed)); SecondaryUsePressed?.Invoke(); }
|
||||||
|
public static void RaiseSecondaryUseHeld(bool held) { Log(nameof(SecondaryUseHeld)); SecondaryUseHeld?.Invoke(held); }
|
||||||
public static void RaiseInteractPressed() { Log(nameof(InteractPressed)); InteractPressed?.Invoke(); }
|
public static void RaiseInteractPressed() { Log(nameof(InteractPressed)); InteractPressed?.Invoke(); }
|
||||||
public static void RaiseDropPressed() { Log(nameof(DropPressed)); DropPressed?.Invoke(); }
|
public static void RaiseDropPressed() { Log(nameof(DropPressed)); DropPressed?.Invoke(); }
|
||||||
public static void RaiseInventoryTogglePressed() { Log(nameof(InventoryTogglePressed)); InventoryTogglePressed?.Invoke(); }
|
public static void RaiseInventoryTogglePressed() { Log(nameof(InventoryTogglePressed)); InventoryTogglePressed?.Invoke(); }
|
||||||
public static void RaiseHotbarSlotPressed(int i) { Log(nameof(HotbarSlotPressed)); HotbarSlotPressed?.Invoke(i); }
|
public static void RaiseHotbarSlotPressed(int i) { Log(nameof(HotbarSlotPressed)); HotbarSlotPressed?.Invoke(i); }
|
||||||
public static void RaiseHotbarScroll(float f) { HotbarScroll?.Invoke(f); }
|
public static void RaiseHotbarScroll(float f) { HotbarScroll?.Invoke(f); }
|
||||||
|
public static void RaiseBuildRotatePressed() { Log(nameof(BuildRotatePressed)); BuildRotatePressed?.Invoke(); }
|
||||||
public static void RaiseCancelPressed() { Log(nameof(CancelPressed)); CancelPressed?.Invoke(); }
|
public static void RaiseCancelPressed() { Log(nameof(CancelPressed)); CancelPressed?.Invoke(); }
|
||||||
|
|
||||||
public static void RaiseGroundedChanged(bool grounded, float impactSpeed)
|
public static void RaiseGroundedChanged(bool grounded, float impactSpeed)
|
||||||
@@ -294,6 +301,17 @@ namespace Ashwild.Player
|
|||||||
BuildPlacingChanged?.Invoke(placing);
|
BuildPlacingChanged?.Invoke(placing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Toggles demolition mode (right-click held with the hammer out). Owner-side only; the
|
||||||
|
/// hammer raises it and BuildManager drives the world-facing targeting/destroy from it.
|
||||||
|
/// </summary>
|
||||||
|
public static void RaiseDemolishModeChanged(bool on)
|
||||||
|
{
|
||||||
|
IsDemolishing = on;
|
||||||
|
Log(nameof(DemolishModeChanged));
|
||||||
|
DemolishModeChanged?.Invoke(on);
|
||||||
|
}
|
||||||
|
|
||||||
public static void RaiseFoodPlacedToCook(ItemData raw) { Log(nameof(FoodPlacedToCook)); FoodPlacedToCook?.Invoke(raw); }
|
public static void RaiseFoodPlacedToCook(ItemData raw) { Log(nameof(FoodPlacedToCook)); FoodPlacedToCook?.Invoke(raw); }
|
||||||
public static void RaiseFoodCookedReady(ItemData cooked) { Log(nameof(FoodCookedReady)); FoodCookedReady?.Invoke(cooked); }
|
public static void RaiseFoodCookedReady(ItemData cooked) { Log(nameof(FoodCookedReady)); FoodCookedReady?.Invoke(cooked); }
|
||||||
public static void RaiseFuelAdded(ItemData fuel) { Log(nameof(FuelAdded)); FuelAdded?.Invoke(fuel); }
|
public static void RaiseFuelAdded(ItemData fuel) { Log(nameof(FuelAdded)); FuelAdded?.Invoke(fuel); }
|
||||||
@@ -376,6 +394,7 @@ namespace Ashwild.Player
|
|||||||
IsChestOpen = false;
|
IsChestOpen = false;
|
||||||
IsBuildMenuOpen = false;
|
IsBuildMenuOpen = false;
|
||||||
IsPlacingBuild = false;
|
IsPlacingBuild = false;
|
||||||
|
IsDemolishing = false;
|
||||||
IsPaused = false;
|
IsPaused = false;
|
||||||
HoveredInteractable = null;
|
HoveredInteractable = null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,21 +4,28 @@ using Ashwild.Inventory;
|
|||||||
namespace Ashwild.Player
|
namespace Ashwild.Player
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Held-item logic for the build hammer, placed on its hand prefab. While equipped it
|
/// Held-item logic for the build hammer, placed on its hand prefab. It turns the single
|
||||||
/// listens to the secondary-use input (right-click) and asks the construction UI to
|
/// right-click into two gestures without any extra keybind: a quick <b>tap</b> opens/closes the
|
||||||
/// open/close through the bus — it never reaches for the menu itself, so the hammer
|
/// construction menu through the bus, while <b>holding</b> right-click past a short threshold puts
|
||||||
/// stays a pure input source and the UI owns its own state. Left-click placement will
|
/// the hammer into demolition mode for as long as it is held (releasing leaves it). The hammer
|
||||||
/// be added on top of this once the build menu exists.
|
/// stays a pure input source — it only raises bus requests; BuildManager owns the menu and the
|
||||||
|
/// world-facing targeting/destroy, so this never reaches for either.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DisallowMultipleComponent]
|
[DisallowMultipleComponent]
|
||||||
public class BuildHammerBehaviour : MonoBehaviour, IHeldItemBehaviour
|
public class BuildHammerBehaviour : MonoBehaviour, IHeldItemBehaviour
|
||||||
{
|
{
|
||||||
#region Serialized Fields
|
#region Serialized Fields
|
||||||
|
|
||||||
[Header("Cooldown")]
|
[Header("Gestures")]
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Minimum time, in seconds, between two menu toggles so a held right-click
|
/// How long, in seconds, right-click must stay held before it counts as a demolition hold
|
||||||
/// does not flicker the menu open and shut.
|
/// instead of a menu tap. A release before this opens/closes the menu.
|
||||||
|
/// </summary>
|
||||||
|
[SerializeField] private float holdThreshold = 0.2f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Minimum time, in seconds, between two menu toggles so a burst of taps does not flicker the
|
||||||
|
/// menu open and shut.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SerializeField] private float toggleCooldown = 0.25f;
|
[SerializeField] private float toggleCooldown = 0.25f;
|
||||||
|
|
||||||
@@ -31,24 +38,58 @@ namespace Ashwild.Player
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private float nextToggleTime;
|
private float nextToggleTime;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// True between an accepted press and its release. A press ignored at the gate (input locked
|
||||||
|
/// or already placing) never sets this, so its release is ignored too.
|
||||||
|
/// </summary>
|
||||||
|
private bool secondaryDown;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Time, in seconds, the current accepted press began — measured against <see cref="holdThreshold"/>.
|
||||||
|
/// </summary>
|
||||||
|
private float secondaryDownTime;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// True while the hold has crossed into demolition mode, so the release ends the mode rather
|
||||||
|
/// than toggling the menu.
|
||||||
|
/// </summary>
|
||||||
|
private bool demoActive;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Unity Lifecycle
|
#region Unity Lifecycle
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Subscribes to the secondary-use input for as long as the hammer is held.
|
/// Subscribes to the right-click hold stream for as long as the hammer is held.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnEnable()
|
private void OnEnable()
|
||||||
{
|
{
|
||||||
PlayerEvents.SecondaryUsePressed += HandleSecondaryUse;
|
PlayerEvents.SecondaryUseHeld += HandleSecondaryUse;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unsubscribes when the hammer is put away (the prefab is destroyed).
|
/// Unsubscribes and leaves demolition mode cleanly when the hammer is put away (its prefab is
|
||||||
|
/// destroyed mid-hold), so the mode never sticks on with no hammer to release it.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnDisable()
|
private void OnDisable()
|
||||||
{
|
{
|
||||||
PlayerEvents.SecondaryUsePressed -= HandleSecondaryUse;
|
PlayerEvents.SecondaryUseHeld -= HandleSecondaryUse;
|
||||||
|
if (demoActive) ExitDemolition();
|
||||||
|
secondaryDown = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Promotes a sustained press into demolition mode once it outlasts the tap threshold. Held
|
||||||
|
/// out of Update (not a timer) so the mode drops instantly if input locks or a placement
|
||||||
|
/// begins mid-hold.
|
||||||
|
/// </summary>
|
||||||
|
private void Update()
|
||||||
|
{
|
||||||
|
if (!secondaryDown || demoActive) return;
|
||||||
|
if (PlayerEvents.InputLocked || PlayerEvents.IsPlacingBuild) return;
|
||||||
|
if (Time.time - secondaryDownTime < holdThreshold) return;
|
||||||
|
|
||||||
|
EnterDemolition();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -56,8 +97,8 @@ namespace Ashwild.Player
|
|||||||
#region IHeldItemBehaviour
|
#region IHeldItemBehaviour
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Nothing to link yet: toggling the menu needs no player refs. Present to satisfy
|
/// Nothing to link: the hammer only raises bus requests. Present to satisfy the held-item
|
||||||
/// the held-item contract; placement logic added later will use the context.
|
/// contract; BuildManager holds the player/world refs the build UX needs.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Setup(HeldItemContext context, ItemData item)
|
public void Setup(HeldItemContext context, ItemData item)
|
||||||
{
|
{
|
||||||
@@ -68,20 +109,60 @@ namespace Ashwild.Player
|
|||||||
#region Event Handlers
|
#region Event Handlers
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Requests the construction menu to open/close on right-click, gated by lock and
|
/// The single right-click gesture, split by press and release: a press is tracked (unless
|
||||||
/// cooldown. While a ghost is being positioned, right-click cancels the placement instead
|
/// input is locked or a ghost is being placed, where right-click means "cancel" and is left to
|
||||||
/// (handled by the placement controller), so the menu is left alone here.
|
/// BuildManager); its release either ends demolition mode (if the hold engaged it) or, for a
|
||||||
|
/// quick tap, toggles the construction menu under the cooldown.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void HandleSecondaryUse()
|
private void HandleSecondaryUse(bool held)
|
||||||
{
|
{
|
||||||
if (PlayerEvents.InputLocked) return;
|
if (held)
|
||||||
if (PlayerEvents.IsPlacingBuild) return;
|
{
|
||||||
if (Time.time < nextToggleTime) return;
|
if (PlayerEvents.InputLocked || PlayerEvents.IsPlacingBuild) return;
|
||||||
|
secondaryDown = true;
|
||||||
|
secondaryDownTime = Time.time;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!secondaryDown) return;
|
||||||
|
secondaryDown = false;
|
||||||
|
|
||||||
|
if (demoActive) ExitDemolition();
|
||||||
|
else ToggleMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Internal Helpers
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Requests the construction menu to open/close, gated by the toggle cooldown.
|
||||||
|
/// </summary>
|
||||||
|
private void ToggleMenu()
|
||||||
|
{
|
||||||
|
if (Time.time < nextToggleTime) return;
|
||||||
nextToggleTime = Time.time + toggleCooldown;
|
nextToggleTime = Time.time + toggleCooldown;
|
||||||
PlayerEvents.RaiseBuildMenuToggleRequested();
|
PlayerEvents.RaiseBuildMenuToggleRequested();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enters demolition mode and announces it on the bus.
|
||||||
|
/// </summary>
|
||||||
|
private void EnterDemolition()
|
||||||
|
{
|
||||||
|
demoActive = true;
|
||||||
|
PlayerEvents.RaiseDemolishModeChanged(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Leaves demolition mode and announces it on the bus.
|
||||||
|
/// </summary>
|
||||||
|
private void ExitDemolition()
|
||||||
|
{
|
||||||
|
demoActive = false;
|
||||||
|
PlayerEvents.RaiseDemolishModeChanged(false);
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -200,10 +200,12 @@ namespace Ashwild.Player
|
|||||||
WireButton(playerMap, "Crouch", PlayerEvents.RaiseCrouchPressed);
|
WireButton(playerMap, "Crouch", PlayerEvents.RaiseCrouchPressed);
|
||||||
WireButton(playerMap, "Attack", PlayerEvents.RaiseAttackPressed);
|
WireButton(playerMap, "Attack", PlayerEvents.RaiseAttackPressed);
|
||||||
WireButton(playerMap, "SecondaryUse", PlayerEvents.RaiseSecondaryUsePressed);
|
WireButton(playerMap, "SecondaryUse", PlayerEvents.RaiseSecondaryUsePressed);
|
||||||
|
WireHold(playerMap, "SecondaryUse", PlayerEvents.RaiseSecondaryUseHeld);
|
||||||
WireButton(playerMap, "Interact", PlayerEvents.RaiseInteractPressed);
|
WireButton(playerMap, "Interact", PlayerEvents.RaiseInteractPressed);
|
||||||
WireButton(playerMap, "Drop", PlayerEvents.RaiseDropPressed);
|
WireButton(playerMap, "Drop", PlayerEvents.RaiseDropPressed);
|
||||||
WireButton(playerMap, "Inventory", PlayerEvents.RaiseInventoryTogglePressed);
|
WireButton(playerMap, "Inventory", PlayerEvents.RaiseInventoryTogglePressed);
|
||||||
WireScrollY(playerMap, "HotbarScroll", PlayerEvents.RaiseHotbarScroll);
|
WireScrollY(playerMap, "HotbarScroll", PlayerEvents.RaiseHotbarScroll);
|
||||||
|
WireButton(playerMap, "BuildRotate", PlayerEvents.RaiseBuildRotatePressed);
|
||||||
|
|
||||||
for (int i = 0; i < 10; i++)
|
for (int i = 0; i < 10; i++)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ Material:
|
|||||||
m_ModifiedSerializedProperties: 0
|
m_ModifiedSerializedProperties: 0
|
||||||
m_ValidKeywords:
|
m_ValidKeywords:
|
||||||
- _ALPHATEST_ON
|
- _ALPHATEST_ON
|
||||||
|
- _EMISSION
|
||||||
m_InvalidKeywords:
|
m_InvalidKeywords:
|
||||||
- _FLIPBOOKBLENDING_OFF
|
- _FLIPBOOKBLENDING_OFF
|
||||||
m_LightmapFlags: 6
|
m_LightmapFlags: 6
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ Material:
|
|||||||
m_ModifiedSerializedProperties: 0
|
m_ModifiedSerializedProperties: 0
|
||||||
m_ValidKeywords:
|
m_ValidKeywords:
|
||||||
- _ALPHATEST_ON
|
- _ALPHATEST_ON
|
||||||
|
- _EMISSION
|
||||||
m_InvalidKeywords:
|
m_InvalidKeywords:
|
||||||
- _FLIPBOOKBLENDING_OFF
|
- _FLIPBOOKBLENDING_OFF
|
||||||
m_LightmapFlags: 6
|
m_LightmapFlags: 6
|
||||||
|
|||||||
Reference in New Issue
Block a user