Merge remote-tracking branch 'origin/feat/inport-props' into feat/craft-discovery
# Conflicts: # Assets/External/Animated PBR Chest Demo/Materials/WoodChest.mat # Packages/com.distantlands.cozy.core/Content/Integration/Import for BiRP.unitypackage.meta # Packages/com.distantlands.cozy.core/Content/Integration/Import for HDRP.unitypackage.meta # Packages/com.distantlands.cozy.core/Content/Integration/Import for URP.unitypackage.meta
This commit is contained in:
Binary file not shown.
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c4627c2a24e5ef44290fe55663a464fb
|
||||
guid: e266f749230b8344e8ac3bde01875c57
|
||||
ModelImporter:
|
||||
serializedVersion: 24200
|
||||
internalIDToNameTable: []
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,110 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2abe49436f5479241b87304e791a0126
|
||||
ModelImporter:
|
||||
serializedVersion: 24200
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
materials:
|
||||
materialImportMode: 2
|
||||
materialName: 0
|
||||
materialSearch: 1
|
||||
materialLocation: 1
|
||||
animations:
|
||||
legacyGenerateAnimations: 4
|
||||
bakeSimulation: 0
|
||||
resampleCurves: 1
|
||||
optimizeGameObjects: 0
|
||||
removeConstantScaleCurves: 0
|
||||
motionNodeName:
|
||||
animationImportErrors:
|
||||
animationImportWarnings:
|
||||
animationRetargetingWarnings:
|
||||
animationDoRetargetingWarnings: 0
|
||||
importAnimatedCustomProperties: 0
|
||||
importConstraints: 0
|
||||
animationCompression: 1
|
||||
animationRotationError: 0.5
|
||||
animationPositionError: 0.5
|
||||
animationScaleError: 0.5
|
||||
animationWrapMode: 0
|
||||
extraExposedTransformPaths: []
|
||||
extraUserProperties: []
|
||||
clipAnimations: []
|
||||
isReadable: 0
|
||||
meshes:
|
||||
lODScreenPercentages: []
|
||||
globalScale: 1
|
||||
meshCompression: 0
|
||||
addColliders: 0
|
||||
useSRGBMaterialColor: 1
|
||||
sortHierarchyByName: 1
|
||||
importPhysicalCameras: 1
|
||||
importVisibility: 1
|
||||
importBlendShapes: 1
|
||||
importCameras: 1
|
||||
importLights: 1
|
||||
nodeNameCollisionStrategy: 1
|
||||
fileIdsGeneration: 2
|
||||
swapUVChannels: 0
|
||||
generateSecondaryUV: 0
|
||||
useFileUnits: 1
|
||||
keepQuads: 0
|
||||
weldVertices: 1
|
||||
bakeAxisConversion: 0
|
||||
preserveHierarchy: 0
|
||||
skinWeightsMode: 0
|
||||
maxBonesPerVertex: 4
|
||||
minBoneWeight: 0.001
|
||||
optimizeBones: 1
|
||||
generateMeshLods: 0
|
||||
meshLodGenerationFlags: 0
|
||||
maximumMeshLod: -1
|
||||
meshOptimizationFlags: -1
|
||||
indexFormat: 0
|
||||
secondaryUVAngleDistortion: 8
|
||||
secondaryUVAreaDistortion: 15.000001
|
||||
secondaryUVHardAngle: 88
|
||||
secondaryUVMarginMethod: 1
|
||||
secondaryUVMinLightmapResolution: 40
|
||||
secondaryUVMinObjectScale: 1
|
||||
secondaryUVPackMargin: 4
|
||||
useFileScale: 1
|
||||
strictVertexDataChecks: 0
|
||||
tangentSpace:
|
||||
normalSmoothAngle: 60
|
||||
normalImportMode: 0
|
||||
tangentImportMode: 3
|
||||
normalCalculationMode: 4
|
||||
legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0
|
||||
blendShapeNormalImportMode: 1
|
||||
normalSmoothingSource: 0
|
||||
referencedClips: []
|
||||
importAnimation: 1
|
||||
humanDescription:
|
||||
serializedVersion: 3
|
||||
human: []
|
||||
skeleton: []
|
||||
armTwist: 0.5
|
||||
foreArmTwist: 0.5
|
||||
upperLegTwist: 0.5
|
||||
legTwist: 0.5
|
||||
armStretch: 0.05
|
||||
legStretch: 0.05
|
||||
feetSpacing: 0
|
||||
globalScale: 1
|
||||
rootMotionBoneName:
|
||||
hasTranslationDoF: 0
|
||||
hasExtraRoot: 0
|
||||
skeletonHasParents: 1
|
||||
lastHumanDescriptionAvatarSource: {instanceID: 0}
|
||||
autoGenerateAvatarMappingIfUnspecified: 1
|
||||
animationType: 2
|
||||
humanoidOversampling: 1
|
||||
avatarSetup: 0
|
||||
addHumanoidExtraRootOnlyWhenUsingAvatar: 1
|
||||
importBlendShapeDeformPercent: 1
|
||||
remapMaterialsIfMaterialImportModeIsNone: 0
|
||||
additionalBone: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
Binary file not shown.
+1
-1
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0e8c003416261584dba3d25627309d45
|
||||
guid: 9bf264414994db540866bf91890392c5
|
||||
ModelImporter:
|
||||
serializedVersion: 24200
|
||||
internalIDToNameTable: []
|
||||
@@ -1,5 +1,6 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3c30eca10dcc29448a7002261aa4155a
|
||||
guid: 607358d940b4a2f4c8500567105eb947
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
@@ -0,0 +1,26 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: dc23e353ca2e8fb45ae7cbad52c55d9b, type: 3}
|
||||
m_Name: Arms_NoItem
|
||||
m_EditorClassIdentifier: Assembly-CSharp::Ashwild.Player.PlayerAnimationSet
|
||||
idle: {fileID: 7400000, guid: 4d1cfe2c715b99048bf9ed290a39804a, type: 2}
|
||||
walk: {fileID: 0}
|
||||
run: {fileID: 7400000, guid: 36a16c24398f4f6408705d2f08c04e9c, type: 2}
|
||||
crouchIdle: {fileID: 0}
|
||||
crouchWalk: {fileID: 0}
|
||||
jumpStart: {fileID: 7400000, guid: ed6b9364a1e545a44a5948671e37b470, type: 2}
|
||||
jumpLoop: {fileID: 7400000, guid: 759b71bd05a69c84684de5f6ff1136a8, type: 2}
|
||||
jumpLand: {fileID: 7400000, guid: 64a0ee3a08b870a469d084264a82c44d, type: 2}
|
||||
grab: {fileID: 7400000, guid: 186915c6ec7c6d843806bf0dc931e9d1, type: 2}
|
||||
equip: {fileID: 0}
|
||||
unequip: {fileID: 0}
|
||||
attackVariants: []
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0325f3104b37ee344bc81f011dc0183d
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,760 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1101 &-7768026170403279795
|
||||
AnimatorStateTransition:
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name:
|
||||
m_Conditions:
|
||||
- m_ConditionMode: 1
|
||||
m_ConditionEvent: Grab
|
||||
m_EventTreshold: 0
|
||||
m_DstStateMachine: {fileID: 0}
|
||||
m_DstState: {fileID: 2352030755958466567}
|
||||
m_Solo: 0
|
||||
m_Mute: 0
|
||||
m_IsExit: 0
|
||||
serializedVersion: 3
|
||||
m_TransitionDuration: 0.05
|
||||
m_TransitionOffset: 0
|
||||
m_ExitTime: 0.75
|
||||
m_HasExitTime: 0
|
||||
m_HasFixedDuration: 1
|
||||
m_InterruptionSource: 0
|
||||
m_OrderedInterruption: 1
|
||||
m_CanTransitionToSelf: 1
|
||||
--- !u!1101 &-7570865560154131062
|
||||
AnimatorStateTransition:
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name:
|
||||
m_Conditions:
|
||||
- m_ConditionMode: 1
|
||||
m_ConditionEvent: Grounded
|
||||
m_EventTreshold: 0
|
||||
m_DstStateMachine: {fileID: 0}
|
||||
m_DstState: {fileID: -2364944936454085822}
|
||||
m_Solo: 0
|
||||
m_Mute: 0
|
||||
m_IsExit: 0
|
||||
serializedVersion: 3
|
||||
m_TransitionDuration: 0.1
|
||||
m_TransitionOffset: 0
|
||||
m_ExitTime: 0.9
|
||||
m_HasExitTime: 0
|
||||
m_HasFixedDuration: 1
|
||||
m_InterruptionSource: 0
|
||||
m_OrderedInterruption: 1
|
||||
m_CanTransitionToSelf: 1
|
||||
--- !u!206 &-7222472619718724550
|
||||
BlendTree:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: Locomotion
|
||||
m_Childs:
|
||||
- serializedVersion: 2
|
||||
m_Motion: {fileID: 7400000, guid: 5b031096414174f4dbd97c5d39dc3f2a, type: 2}
|
||||
m_Threshold: 0.12217195
|
||||
m_Position: {x: 0, y: 0}
|
||||
m_TimeScale: 1
|
||||
m_CycleOffset: 0
|
||||
m_DirectBlendParameter: Blend
|
||||
m_Mirror: 0
|
||||
- serializedVersion: 2
|
||||
m_Motion: {fileID: 7400000, guid: 5b031096414174f4dbd97c5d39dc3f2a, type: 2}
|
||||
m_Threshold: 1
|
||||
m_Position: {x: 0, y: 0}
|
||||
m_TimeScale: 1
|
||||
m_CycleOffset: 0
|
||||
m_DirectBlendParameter: Blend
|
||||
m_Mirror: 0
|
||||
- serializedVersion: 2
|
||||
m_Motion: {fileID: 7400000, guid: 015f0ad2a5e37fe4d81a9e92ff924895, type: 2}
|
||||
m_Threshold: 1.8
|
||||
m_Position: {x: 0, y: 0}
|
||||
m_TimeScale: 1
|
||||
m_CycleOffset: 0
|
||||
m_DirectBlendParameter: Blend
|
||||
m_Mirror: 0
|
||||
m_BlendParameter: Speed
|
||||
m_BlendParameterY: Speed
|
||||
m_MinThreshold: 0.12217195
|
||||
m_MaxThreshold: 1.8
|
||||
m_UseAutomaticThresholds: 0
|
||||
m_NormalizedBlendValues: 0
|
||||
m_BlendType: 0
|
||||
--- !u!1101 &-6770753843745156850
|
||||
AnimatorStateTransition:
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name:
|
||||
m_Conditions:
|
||||
- m_ConditionMode: 1
|
||||
m_ConditionEvent: Equip
|
||||
m_EventTreshold: 0
|
||||
m_DstStateMachine: {fileID: 0}
|
||||
m_DstState: {fileID: -3651938701169484709}
|
||||
m_Solo: 0
|
||||
m_Mute: 0
|
||||
m_IsExit: 0
|
||||
serializedVersion: 3
|
||||
m_TransitionDuration: 0.05
|
||||
m_TransitionOffset: 0
|
||||
m_ExitTime: 0.75
|
||||
m_HasExitTime: 0
|
||||
m_HasFixedDuration: 1
|
||||
m_InterruptionSource: 0
|
||||
m_OrderedInterruption: 1
|
||||
m_CanTransitionToSelf: 1
|
||||
--- !u!1102 &-5880494228711490118
|
||||
AnimatorState:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: Unequip
|
||||
m_Speed: 1
|
||||
m_CycleOffset: 0
|
||||
m_Transitions:
|
||||
- {fileID: 8208370532479256070}
|
||||
m_StateMachineBehaviours: []
|
||||
m_Position: {x: 50, y: 50, z: 0}
|
||||
m_IKOnFeet: 0
|
||||
m_WriteDefaultValues: 1
|
||||
m_Mirror: 0
|
||||
m_SpeedParameterActive: 0
|
||||
m_MirrorParameterActive: 0
|
||||
m_CycleOffsetParameterActive: 0
|
||||
m_TimeParameterActive: 0
|
||||
m_Motion: {fileID: 7400000, guid: 3317fd1bafbd15c4ea7424510af1d201, type: 2}
|
||||
m_Tag:
|
||||
m_SpeedParameter:
|
||||
m_MirrorParameter:
|
||||
m_CycleOffsetParameter:
|
||||
m_TimeParameter:
|
||||
--- !u!1101 &-5828662857540650696
|
||||
AnimatorStateTransition:
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name:
|
||||
m_Conditions: []
|
||||
m_DstStateMachine: {fileID: 0}
|
||||
m_DstState: {fileID: 2928339155785256750}
|
||||
m_Solo: 0
|
||||
m_Mute: 0
|
||||
m_IsExit: 0
|
||||
serializedVersion: 3
|
||||
m_TransitionDuration: 0.1
|
||||
m_TransitionOffset: 0
|
||||
m_ExitTime: 0.9
|
||||
m_HasExitTime: 1
|
||||
m_HasFixedDuration: 1
|
||||
m_InterruptionSource: 0
|
||||
m_OrderedInterruption: 1
|
||||
m_CanTransitionToSelf: 1
|
||||
--- !u!1101 &-5727719339812418594
|
||||
AnimatorStateTransition:
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name:
|
||||
m_Conditions: []
|
||||
m_DstStateMachine: {fileID: 0}
|
||||
m_DstState: {fileID: -5423646191322609181}
|
||||
m_Solo: 0
|
||||
m_Mute: 0
|
||||
m_IsExit: 0
|
||||
serializedVersion: 3
|
||||
m_TransitionDuration: 0.15
|
||||
m_TransitionOffset: 0
|
||||
m_ExitTime: 0.7
|
||||
m_HasExitTime: 1
|
||||
m_HasFixedDuration: 1
|
||||
m_InterruptionSource: 0
|
||||
m_OrderedInterruption: 1
|
||||
m_CanTransitionToSelf: 1
|
||||
--- !u!1102 &-5423646191322609181
|
||||
AnimatorState:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: Locomotion
|
||||
m_Speed: 1
|
||||
m_CycleOffset: 0
|
||||
m_Transitions:
|
||||
- {fileID: 6155372356441573625}
|
||||
m_StateMachineBehaviours: []
|
||||
m_Position: {x: 50, y: 50, z: 0}
|
||||
m_IKOnFeet: 0
|
||||
m_WriteDefaultValues: 1
|
||||
m_Mirror: 0
|
||||
m_SpeedParameterActive: 0
|
||||
m_MirrorParameterActive: 0
|
||||
m_CycleOffsetParameterActive: 0
|
||||
m_TimeParameterActive: 0
|
||||
m_Motion: {fileID: -7222472619718724550}
|
||||
m_Tag:
|
||||
m_SpeedParameter:
|
||||
m_MirrorParameter:
|
||||
m_CycleOffsetParameter:
|
||||
m_TimeParameter:
|
||||
--- !u!1102 &-3804894209099826431
|
||||
AnimatorState:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: JumpLoop
|
||||
m_Speed: 1
|
||||
m_CycleOffset: 0
|
||||
m_Transitions:
|
||||
- {fileID: -7570865560154131062}
|
||||
m_StateMachineBehaviours: []
|
||||
m_Position: {x: 50, y: 50, z: 0}
|
||||
m_IKOnFeet: 0
|
||||
m_WriteDefaultValues: 1
|
||||
m_Mirror: 0
|
||||
m_SpeedParameterActive: 0
|
||||
m_MirrorParameterActive: 0
|
||||
m_CycleOffsetParameterActive: 0
|
||||
m_TimeParameterActive: 0
|
||||
m_Motion: {fileID: 7400000, guid: 8dad0aa5cd06836489f6df8ab42b72b8, type: 2}
|
||||
m_Tag:
|
||||
m_SpeedParameter:
|
||||
m_MirrorParameter:
|
||||
m_CycleOffsetParameter:
|
||||
m_TimeParameter:
|
||||
--- !u!1102 &-3651938701169484709
|
||||
AnimatorState:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: Equip
|
||||
m_Speed: 1
|
||||
m_CycleOffset: 0
|
||||
m_Transitions:
|
||||
- {fileID: -327675351798497380}
|
||||
m_StateMachineBehaviours: []
|
||||
m_Position: {x: 50, y: 50, z: 0}
|
||||
m_IKOnFeet: 0
|
||||
m_WriteDefaultValues: 1
|
||||
m_Mirror: 0
|
||||
m_SpeedParameterActive: 0
|
||||
m_MirrorParameterActive: 0
|
||||
m_CycleOffsetParameterActive: 0
|
||||
m_TimeParameterActive: 0
|
||||
m_Motion: {fileID: 7400000, guid: 22e1e804a172952409c0bb3d18c63ee9, type: 2}
|
||||
m_Tag:
|
||||
m_SpeedParameter:
|
||||
m_MirrorParameter:
|
||||
m_CycleOffsetParameter:
|
||||
m_TimeParameter:
|
||||
--- !u!1102 &-2975068770776088887
|
||||
AnimatorState:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: JumpStart
|
||||
m_Speed: 1
|
||||
m_CycleOffset: 0
|
||||
m_Transitions:
|
||||
- {fileID: 7652372711265941215}
|
||||
m_StateMachineBehaviours: []
|
||||
m_Position: {x: 50, y: 50, z: 0}
|
||||
m_IKOnFeet: 0
|
||||
m_WriteDefaultValues: 1
|
||||
m_Mirror: 0
|
||||
m_SpeedParameterActive: 0
|
||||
m_MirrorParameterActive: 0
|
||||
m_CycleOffsetParameterActive: 0
|
||||
m_TimeParameterActive: 0
|
||||
m_Motion: {fileID: 7400000, guid: e6d5ae3338322034aae86a9f047fcfaf, type: 2}
|
||||
m_Tag:
|
||||
m_SpeedParameter:
|
||||
m_MirrorParameter:
|
||||
m_CycleOffsetParameter:
|
||||
m_TimeParameter:
|
||||
--- !u!1102 &-2364944936454085822
|
||||
AnimatorState:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: JumpLand
|
||||
m_Speed: 1
|
||||
m_CycleOffset: 0
|
||||
m_Transitions:
|
||||
- {fileID: -5727719339812418594}
|
||||
m_StateMachineBehaviours: []
|
||||
m_Position: {x: 50, y: 50, z: 0}
|
||||
m_IKOnFeet: 0
|
||||
m_WriteDefaultValues: 1
|
||||
m_Mirror: 0
|
||||
m_SpeedParameterActive: 0
|
||||
m_MirrorParameterActive: 0
|
||||
m_CycleOffsetParameterActive: 0
|
||||
m_TimeParameterActive: 0
|
||||
m_Motion: {fileID: 7400000, guid: b0e882922a1b61e46986d81883a6224c, type: 2}
|
||||
m_Tag:
|
||||
m_SpeedParameter:
|
||||
m_MirrorParameter:
|
||||
m_CycleOffsetParameter:
|
||||
m_TimeParameter:
|
||||
--- !u!1101 &-327675351798497380
|
||||
AnimatorStateTransition:
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name:
|
||||
m_Conditions: []
|
||||
m_DstStateMachine: {fileID: 0}
|
||||
m_DstState: {fileID: 2928339155785256750}
|
||||
m_Solo: 0
|
||||
m_Mute: 0
|
||||
m_IsExit: 0
|
||||
serializedVersion: 3
|
||||
m_TransitionDuration: 0.1
|
||||
m_TransitionOffset: 0
|
||||
m_ExitTime: 0.9
|
||||
m_HasExitTime: 1
|
||||
m_HasFixedDuration: 1
|
||||
m_InterruptionSource: 0
|
||||
m_OrderedInterruption: 1
|
||||
m_CanTransitionToSelf: 1
|
||||
--- !u!91 &9100000
|
||||
AnimatorController:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: PlayerArms
|
||||
serializedVersion: 5
|
||||
m_AnimatorParameters:
|
||||
- m_Name: Speed
|
||||
m_Type: 1
|
||||
m_DefaultFloat: 0
|
||||
m_DefaultInt: 0
|
||||
m_DefaultBool: 0
|
||||
m_Controller: {fileID: 9100000}
|
||||
- m_Name: Grounded
|
||||
m_Type: 4
|
||||
m_DefaultFloat: 0
|
||||
m_DefaultInt: 0
|
||||
m_DefaultBool: 1
|
||||
m_Controller: {fileID: 9100000}
|
||||
- m_Name: Crouching
|
||||
m_Type: 4
|
||||
m_DefaultFloat: 0
|
||||
m_DefaultInt: 0
|
||||
m_DefaultBool: 0
|
||||
m_Controller: {fileID: 9100000}
|
||||
- m_Name: Sprinting
|
||||
m_Type: 4
|
||||
m_DefaultFloat: 0
|
||||
m_DefaultInt: 0
|
||||
m_DefaultBool: 0
|
||||
m_Controller: {fileID: 9100000}
|
||||
- m_Name: AttackIndex
|
||||
m_Type: 3
|
||||
m_DefaultFloat: 0
|
||||
m_DefaultInt: 0
|
||||
m_DefaultBool: 0
|
||||
m_Controller: {fileID: 9100000}
|
||||
- m_Name: Jump
|
||||
m_Type: 9
|
||||
m_DefaultFloat: 0
|
||||
m_DefaultInt: 0
|
||||
m_DefaultBool: 0
|
||||
m_Controller: {fileID: 9100000}
|
||||
- m_Name: Land
|
||||
m_Type: 9
|
||||
m_DefaultFloat: 0
|
||||
m_DefaultInt: 0
|
||||
m_DefaultBool: 0
|
||||
m_Controller: {fileID: 9100000}
|
||||
- m_Name: Attack
|
||||
m_Type: 9
|
||||
m_DefaultFloat: 0
|
||||
m_DefaultInt: 0
|
||||
m_DefaultBool: 0
|
||||
m_Controller: {fileID: 9100000}
|
||||
- m_Name: Grab
|
||||
m_Type: 9
|
||||
m_DefaultFloat: 0
|
||||
m_DefaultInt: 0
|
||||
m_DefaultBool: 0
|
||||
m_Controller: {fileID: 9100000}
|
||||
- m_Name: Equip
|
||||
m_Type: 9
|
||||
m_DefaultFloat: 0
|
||||
m_DefaultInt: 0
|
||||
m_DefaultBool: 0
|
||||
m_Controller: {fileID: 9100000}
|
||||
- m_Name: Unequip
|
||||
m_Type: 9
|
||||
m_DefaultFloat: 0
|
||||
m_DefaultInt: 0
|
||||
m_DefaultBool: 0
|
||||
m_Controller: {fileID: 9100000}
|
||||
m_AnimatorLayers:
|
||||
- serializedVersion: 5
|
||||
m_Name: Locomotion
|
||||
m_StateMachine: {fileID: 6400669144030604215}
|
||||
m_Mask: {fileID: 0}
|
||||
m_Motions: []
|
||||
m_Behaviours: []
|
||||
m_BlendingMode: 0
|
||||
m_SyncedLayerIndex: -1
|
||||
m_DefaultWeight: 0
|
||||
m_IKPass: 0
|
||||
m_SyncedLayerAffectsTiming: 0
|
||||
m_Controller: {fileID: 9100000}
|
||||
- serializedVersion: 5
|
||||
m_Name: Action
|
||||
m_StateMachine: {fileID: 6062655708864958475}
|
||||
m_Mask: {fileID: 0}
|
||||
m_Motions: []
|
||||
m_Behaviours: []
|
||||
m_BlendingMode: 0
|
||||
m_SyncedLayerIndex: -1
|
||||
m_DefaultWeight: 1
|
||||
m_IKPass: 0
|
||||
m_SyncedLayerAffectsTiming: 0
|
||||
m_Controller: {fileID: 9100000}
|
||||
--- !u!1102 &2352030755958466567
|
||||
AnimatorState:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: Grab
|
||||
m_Speed: 1
|
||||
m_CycleOffset: 0
|
||||
m_Transitions:
|
||||
- {fileID: -5828662857540650696}
|
||||
m_StateMachineBehaviours: []
|
||||
m_Position: {x: 50, y: 50, z: 0}
|
||||
m_IKOnFeet: 0
|
||||
m_WriteDefaultValues: 1
|
||||
m_Mirror: 0
|
||||
m_SpeedParameterActive: 0
|
||||
m_MirrorParameterActive: 0
|
||||
m_CycleOffsetParameterActive: 0
|
||||
m_TimeParameterActive: 0
|
||||
m_Motion: {fileID: 7400000, guid: 186915c6ec7c6d843806bf0dc931e9d1, type: 2}
|
||||
m_Tag:
|
||||
m_SpeedParameter:
|
||||
m_MirrorParameter:
|
||||
m_CycleOffsetParameter:
|
||||
m_TimeParameter:
|
||||
--- !u!1101 &2388065741145883338
|
||||
AnimatorStateTransition:
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name:
|
||||
m_Conditions: []
|
||||
m_DstStateMachine: {fileID: 0}
|
||||
m_DstState: {fileID: 2928339155785256750}
|
||||
m_Solo: 0
|
||||
m_Mute: 0
|
||||
m_IsExit: 0
|
||||
serializedVersion: 3
|
||||
m_TransitionDuration: 0.1
|
||||
m_TransitionOffset: 0
|
||||
m_ExitTime: 0.9
|
||||
m_HasExitTime: 1
|
||||
m_HasFixedDuration: 1
|
||||
m_InterruptionSource: 0
|
||||
m_OrderedInterruption: 1
|
||||
m_CanTransitionToSelf: 1
|
||||
--- !u!1102 &2928339155785256750
|
||||
AnimatorState:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: None
|
||||
m_Speed: 1
|
||||
m_CycleOffset: 0
|
||||
m_Transitions: []
|
||||
m_StateMachineBehaviours: []
|
||||
m_Position: {x: 50, y: 50, z: 0}
|
||||
m_IKOnFeet: 0
|
||||
m_WriteDefaultValues: 1
|
||||
m_Mirror: 0
|
||||
m_SpeedParameterActive: 0
|
||||
m_MirrorParameterActive: 0
|
||||
m_CycleOffsetParameterActive: 0
|
||||
m_TimeParameterActive: 0
|
||||
m_Motion: {fileID: 0}
|
||||
m_Tag:
|
||||
m_SpeedParameter:
|
||||
m_MirrorParameter:
|
||||
m_CycleOffsetParameter:
|
||||
m_TimeParameter:
|
||||
--- !u!1101 &2959764067242191139
|
||||
AnimatorStateTransition:
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name:
|
||||
m_Conditions:
|
||||
- m_ConditionMode: 1
|
||||
m_ConditionEvent: Attack
|
||||
m_EventTreshold: 0
|
||||
m_DstStateMachine: {fileID: 0}
|
||||
m_DstState: {fileID: 3391244461197431567}
|
||||
m_Solo: 0
|
||||
m_Mute: 0
|
||||
m_IsExit: 0
|
||||
serializedVersion: 3
|
||||
m_TransitionDuration: 0.05
|
||||
m_TransitionOffset: 0
|
||||
m_ExitTime: 0.75
|
||||
m_HasExitTime: 0
|
||||
m_HasFixedDuration: 1
|
||||
m_InterruptionSource: 0
|
||||
m_OrderedInterruption: 1
|
||||
m_CanTransitionToSelf: 1
|
||||
--- !u!1102 &3391244461197431567
|
||||
AnimatorState:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: Attack1
|
||||
m_Speed: 1
|
||||
m_CycleOffset: 0
|
||||
m_Transitions:
|
||||
- {fileID: 2388065741145883338}
|
||||
m_StateMachineBehaviours: []
|
||||
m_Position: {x: 50, y: 50, z: 0}
|
||||
m_IKOnFeet: 0
|
||||
m_WriteDefaultValues: 1
|
||||
m_Mirror: 0
|
||||
m_SpeedParameterActive: 0
|
||||
m_MirrorParameterActive: 0
|
||||
m_CycleOffsetParameterActive: 0
|
||||
m_TimeParameterActive: 0
|
||||
m_Motion: {fileID: 7400000, guid: 437e76c8dd69f2c449adbb8daacd4f2d, type: 2}
|
||||
m_Tag:
|
||||
m_SpeedParameter:
|
||||
m_MirrorParameter:
|
||||
m_CycleOffsetParameter:
|
||||
m_TimeParameter:
|
||||
--- !u!1101 &5330438093916323590
|
||||
AnimatorStateTransition:
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name:
|
||||
m_Conditions:
|
||||
- m_ConditionMode: 1
|
||||
m_ConditionEvent: Unequip
|
||||
m_EventTreshold: 0
|
||||
m_DstStateMachine: {fileID: 0}
|
||||
m_DstState: {fileID: -5880494228711490118}
|
||||
m_Solo: 0
|
||||
m_Mute: 0
|
||||
m_IsExit: 0
|
||||
serializedVersion: 3
|
||||
m_TransitionDuration: 0.05
|
||||
m_TransitionOffset: 0
|
||||
m_ExitTime: 0.75
|
||||
m_HasExitTime: 0
|
||||
m_HasFixedDuration: 1
|
||||
m_InterruptionSource: 0
|
||||
m_OrderedInterruption: 1
|
||||
m_CanTransitionToSelf: 1
|
||||
--- !u!1107 &6062655708864958475
|
||||
AnimatorStateMachine:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: Action
|
||||
m_ChildStates:
|
||||
- serializedVersion: 1
|
||||
m_State: {fileID: 2928339155785256750}
|
||||
m_Position: {x: 200, y: 0, z: 0}
|
||||
- serializedVersion: 1
|
||||
m_State: {fileID: 2352030755958466567}
|
||||
m_Position: {x: 235, y: 65, z: 0}
|
||||
- serializedVersion: 1
|
||||
m_State: {fileID: -3651938701169484709}
|
||||
m_Position: {x: 270, y: 130, z: 0}
|
||||
- serializedVersion: 1
|
||||
m_State: {fileID: -5880494228711490118}
|
||||
m_Position: {x: 305, y: 195, z: 0}
|
||||
- serializedVersion: 1
|
||||
m_State: {fileID: 3391244461197431567}
|
||||
m_Position: {x: 340, y: 260, z: 0}
|
||||
m_ChildStateMachines: []
|
||||
m_AnyStateTransitions:
|
||||
- {fileID: -7768026170403279795}
|
||||
- {fileID: -6770753843745156850}
|
||||
- {fileID: 5330438093916323590}
|
||||
- {fileID: 2959764067242191139}
|
||||
m_EntryTransitions: []
|
||||
m_StateMachineTransitions: {}
|
||||
m_StateMachineBehaviours: []
|
||||
m_AnyStatePosition: {x: 50, y: 20, z: 0}
|
||||
m_EntryPosition: {x: 50, y: 120, z: 0}
|
||||
m_ExitPosition: {x: 800, y: 120, z: 0}
|
||||
m_ParentStateMachinePosition: {x: 800, y: 20, z: 0}
|
||||
m_DefaultState: {fileID: 2928339155785256750}
|
||||
--- !u!1101 &6155372356441573625
|
||||
AnimatorStateTransition:
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name:
|
||||
m_Conditions:
|
||||
- m_ConditionMode: 1
|
||||
m_ConditionEvent: Jump
|
||||
m_EventTreshold: 0
|
||||
m_DstStateMachine: {fileID: 0}
|
||||
m_DstState: {fileID: -2975068770776088887}
|
||||
m_Solo: 0
|
||||
m_Mute: 0
|
||||
m_IsExit: 0
|
||||
serializedVersion: 3
|
||||
m_TransitionDuration: 0.05
|
||||
m_TransitionOffset: 0
|
||||
m_ExitTime: 0.9
|
||||
m_HasExitTime: 0
|
||||
m_HasFixedDuration: 1
|
||||
m_InterruptionSource: 0
|
||||
m_OrderedInterruption: 1
|
||||
m_CanTransitionToSelf: 1
|
||||
--- !u!1107 &6400669144030604215
|
||||
AnimatorStateMachine:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: Base Layer
|
||||
m_ChildStates:
|
||||
- serializedVersion: 1
|
||||
m_State: {fileID: -5423646191322609181}
|
||||
m_Position: {x: -330, y: 230, z: 0}
|
||||
- serializedVersion: 1
|
||||
m_State: {fileID: -2975068770776088887}
|
||||
m_Position: {x: -360, y: 380, z: 0}
|
||||
- serializedVersion: 1
|
||||
m_State: {fileID: -3804894209099826431}
|
||||
m_Position: {x: -60, y: 380, z: 0}
|
||||
- serializedVersion: 1
|
||||
m_State: {fileID: -2364944936454085822}
|
||||
m_Position: {x: 240, y: 370, z: 0}
|
||||
m_ChildStateMachines: []
|
||||
m_AnyStateTransitions:
|
||||
- {fileID: 9065006211006322926}
|
||||
m_EntryTransitions: []
|
||||
m_StateMachineTransitions: {}
|
||||
m_StateMachineBehaviours: []
|
||||
m_AnyStatePosition: {x: 50, y: 20, z: 0}
|
||||
m_EntryPosition: {x: 50, y: 120, z: 0}
|
||||
m_ExitPosition: {x: 800, y: 120, z: 0}
|
||||
m_ParentStateMachinePosition: {x: 800, y: 20, z: 0}
|
||||
m_DefaultState: {fileID: -5423646191322609181}
|
||||
--- !u!1101 &7652372711265941215
|
||||
AnimatorStateTransition:
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name:
|
||||
m_Conditions: []
|
||||
m_DstStateMachine: {fileID: 0}
|
||||
m_DstState: {fileID: -3804894209099826431}
|
||||
m_Solo: 0
|
||||
m_Mute: 0
|
||||
m_IsExit: 0
|
||||
serializedVersion: 3
|
||||
m_TransitionDuration: 0.1
|
||||
m_TransitionOffset: 0
|
||||
m_ExitTime: 0.8
|
||||
m_HasExitTime: 1
|
||||
m_HasFixedDuration: 1
|
||||
m_InterruptionSource: 0
|
||||
m_OrderedInterruption: 1
|
||||
m_CanTransitionToSelf: 1
|
||||
--- !u!1101 &8208370532479256070
|
||||
AnimatorStateTransition:
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name:
|
||||
m_Conditions: []
|
||||
m_DstStateMachine: {fileID: 0}
|
||||
m_DstState: {fileID: 2928339155785256750}
|
||||
m_Solo: 0
|
||||
m_Mute: 0
|
||||
m_IsExit: 0
|
||||
serializedVersion: 3
|
||||
m_TransitionDuration: 0.1
|
||||
m_TransitionOffset: 0
|
||||
m_ExitTime: 0.9
|
||||
m_HasExitTime: 1
|
||||
m_HasFixedDuration: 1
|
||||
m_InterruptionSource: 0
|
||||
m_OrderedInterruption: 1
|
||||
m_CanTransitionToSelf: 1
|
||||
--- !u!1101 &9065006211006322926
|
||||
AnimatorStateTransition:
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name:
|
||||
m_Conditions:
|
||||
- m_ConditionMode: 2
|
||||
m_ConditionEvent: Grounded
|
||||
m_EventTreshold: 0
|
||||
m_DstStateMachine: {fileID: 0}
|
||||
m_DstState: {fileID: -3804894209099826431}
|
||||
m_Solo: 0
|
||||
m_Mute: 0
|
||||
m_IsExit: 0
|
||||
serializedVersion: 3
|
||||
m_TransitionDuration: 0.15
|
||||
m_TransitionOffset: 0
|
||||
m_ExitTime: 0.75
|
||||
m_HasExitTime: 0
|
||||
m_HasFixedDuration: 1
|
||||
m_InterruptionSource: 0
|
||||
m_OrderedInterruption: 1
|
||||
m_CanTransitionToSelf: 0
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3088f779b365aad4fab7962c5c538a0c
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 9100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d0339948e55135040a64121e910b6cda
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,53 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!74 &7400000
|
||||
AnimationClip:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: Attack1
|
||||
serializedVersion: 7
|
||||
m_Legacy: 0
|
||||
m_Compressed: 0
|
||||
m_UseHighQualityCurve: 1
|
||||
m_RotationCurves: []
|
||||
m_CompressedRotationCurves: []
|
||||
m_EulerCurves: []
|
||||
m_PositionCurves: []
|
||||
m_ScaleCurves: []
|
||||
m_FloatCurves: []
|
||||
m_PPtrCurves: []
|
||||
m_SampleRate: 60
|
||||
m_WrapMode: 0
|
||||
m_Bounds:
|
||||
m_Center: {x: 0, y: 0, z: 0}
|
||||
m_Extent: {x: 0, y: 0, z: 0}
|
||||
m_ClipBindingConstant:
|
||||
genericBindings: []
|
||||
pptrCurveMapping: []
|
||||
m_AnimationClipSettings:
|
||||
serializedVersion: 2
|
||||
m_AdditiveReferencePoseClip: {fileID: 0}
|
||||
m_AdditiveReferencePoseTime: 0
|
||||
m_StartTime: 0
|
||||
m_StopTime: 1
|
||||
m_OrientationOffsetY: 0
|
||||
m_Level: 0
|
||||
m_CycleOffset: 0
|
||||
m_HasAdditiveReferencePose: 0
|
||||
m_LoopTime: 0
|
||||
m_LoopBlend: 0
|
||||
m_LoopBlendOrientation: 0
|
||||
m_LoopBlendPositionY: 0
|
||||
m_LoopBlendPositionXZ: 0
|
||||
m_KeepOriginalOrientation: 0
|
||||
m_KeepOriginalPositionY: 1
|
||||
m_KeepOriginalPositionXZ: 0
|
||||
m_HeightFromFeet: 0
|
||||
m_Mirror: 0
|
||||
m_EditorCurves: []
|
||||
m_EulerEditorCurves: []
|
||||
m_HasGenericRootTransform: 0
|
||||
m_HasMotionFloatCurves: 0
|
||||
m_Events: []
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c82bc18d1efc9e44da5875479eb1f8a0
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 7400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,53 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!74 &7400000
|
||||
AnimationClip:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: Equip
|
||||
serializedVersion: 7
|
||||
m_Legacy: 0
|
||||
m_Compressed: 0
|
||||
m_UseHighQualityCurve: 1
|
||||
m_RotationCurves: []
|
||||
m_CompressedRotationCurves: []
|
||||
m_EulerCurves: []
|
||||
m_PositionCurves: []
|
||||
m_ScaleCurves: []
|
||||
m_FloatCurves: []
|
||||
m_PPtrCurves: []
|
||||
m_SampleRate: 60
|
||||
m_WrapMode: 0
|
||||
m_Bounds:
|
||||
m_Center: {x: 0, y: 0, z: 0}
|
||||
m_Extent: {x: 0, y: 0, z: 0}
|
||||
m_ClipBindingConstant:
|
||||
genericBindings: []
|
||||
pptrCurveMapping: []
|
||||
m_AnimationClipSettings:
|
||||
serializedVersion: 2
|
||||
m_AdditiveReferencePoseClip: {fileID: 0}
|
||||
m_AdditiveReferencePoseTime: 0
|
||||
m_StartTime: 0
|
||||
m_StopTime: 1
|
||||
m_OrientationOffsetY: 0
|
||||
m_Level: 0
|
||||
m_CycleOffset: 0
|
||||
m_HasAdditiveReferencePose: 0
|
||||
m_LoopTime: 0
|
||||
m_LoopBlend: 0
|
||||
m_LoopBlendOrientation: 0
|
||||
m_LoopBlendPositionY: 0
|
||||
m_LoopBlendPositionXZ: 0
|
||||
m_KeepOriginalOrientation: 0
|
||||
m_KeepOriginalPositionY: 1
|
||||
m_KeepOriginalPositionXZ: 0
|
||||
m_HeightFromFeet: 0
|
||||
m_Mirror: 0
|
||||
m_EditorCurves: []
|
||||
m_EulerEditorCurves: []
|
||||
m_HasGenericRootTransform: 0
|
||||
m_HasMotionFloatCurves: 0
|
||||
m_Events: []
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0e51461fd6a778348a4da71e46fc3073
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 7400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9d97d3fe8ddb61e4db050777017826f6
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 7400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,53 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!74 &7400000
|
||||
AnimationClip:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: Idle
|
||||
serializedVersion: 7
|
||||
m_Legacy: 0
|
||||
m_Compressed: 0
|
||||
m_UseHighQualityCurve: 1
|
||||
m_RotationCurves: []
|
||||
m_CompressedRotationCurves: []
|
||||
m_EulerCurves: []
|
||||
m_PositionCurves: []
|
||||
m_ScaleCurves: []
|
||||
m_FloatCurves: []
|
||||
m_PPtrCurves: []
|
||||
m_SampleRate: 60
|
||||
m_WrapMode: 0
|
||||
m_Bounds:
|
||||
m_Center: {x: 0, y: 0, z: 0}
|
||||
m_Extent: {x: 0, y: 0, z: 0}
|
||||
m_ClipBindingConstant:
|
||||
genericBindings: []
|
||||
pptrCurveMapping: []
|
||||
m_AnimationClipSettings:
|
||||
serializedVersion: 2
|
||||
m_AdditiveReferencePoseClip: {fileID: 0}
|
||||
m_AdditiveReferencePoseTime: 0
|
||||
m_StartTime: 0
|
||||
m_StopTime: 1
|
||||
m_OrientationOffsetY: 0
|
||||
m_Level: 0
|
||||
m_CycleOffset: 0
|
||||
m_HasAdditiveReferencePose: 0
|
||||
m_LoopTime: 1
|
||||
m_LoopBlend: 0
|
||||
m_LoopBlendOrientation: 0
|
||||
m_LoopBlendPositionY: 0
|
||||
m_LoopBlendPositionXZ: 0
|
||||
m_KeepOriginalOrientation: 0
|
||||
m_KeepOriginalPositionY: 1
|
||||
m_KeepOriginalPositionXZ: 0
|
||||
m_HeightFromFeet: 0
|
||||
m_Mirror: 0
|
||||
m_EditorCurves: []
|
||||
m_EulerEditorCurves: []
|
||||
m_HasGenericRootTransform: 0
|
||||
m_HasMotionFloatCurves: 0
|
||||
m_Events: []
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5b031096414174f4dbd97c5d39dc3f2a
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 7400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b0e882922a1b61e46986d81883a6224c
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 7400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8dad0aa5cd06836489f6df8ab42b72b8
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 7400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e6d5ae3338322034aae86a9f047fcfaf
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 7400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 015f0ad2a5e37fe4d81a9e92ff924895
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 7400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,53 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!74 &7400000
|
||||
AnimationClip:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: Unequip
|
||||
serializedVersion: 7
|
||||
m_Legacy: 0
|
||||
m_Compressed: 0
|
||||
m_UseHighQualityCurve: 1
|
||||
m_RotationCurves: []
|
||||
m_CompressedRotationCurves: []
|
||||
m_EulerCurves: []
|
||||
m_PositionCurves: []
|
||||
m_ScaleCurves: []
|
||||
m_FloatCurves: []
|
||||
m_PPtrCurves: []
|
||||
m_SampleRate: 60
|
||||
m_WrapMode: 0
|
||||
m_Bounds:
|
||||
m_Center: {x: 0, y: 0, z: 0}
|
||||
m_Extent: {x: 0, y: 0, z: 0}
|
||||
m_ClipBindingConstant:
|
||||
genericBindings: []
|
||||
pptrCurveMapping: []
|
||||
m_AnimationClipSettings:
|
||||
serializedVersion: 2
|
||||
m_AdditiveReferencePoseClip: {fileID: 0}
|
||||
m_AdditiveReferencePoseTime: 0
|
||||
m_StartTime: 0
|
||||
m_StopTime: 1
|
||||
m_OrientationOffsetY: 0
|
||||
m_Level: 0
|
||||
m_CycleOffset: 0
|
||||
m_HasAdditiveReferencePose: 0
|
||||
m_LoopTime: 0
|
||||
m_LoopBlend: 0
|
||||
m_LoopBlendOrientation: 0
|
||||
m_LoopBlendPositionY: 0
|
||||
m_LoopBlendPositionXZ: 0
|
||||
m_KeepOriginalOrientation: 0
|
||||
m_KeepOriginalPositionY: 1
|
||||
m_KeepOriginalPositionXZ: 0
|
||||
m_HeightFromFeet: 0
|
||||
m_Mirror: 0
|
||||
m_EditorCurves: []
|
||||
m_EulerEditorCurves: []
|
||||
m_HasGenericRootTransform: 0
|
||||
m_HasMotionFloatCurves: 0
|
||||
m_Events: []
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 937416bdbf03ec44eb996b063842cb7c
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 7400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,53 +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: GAME_StylizedSky
|
||||
m_Shader: {fileID: 4800000, guid: b2c3d4e5f607182930a4b5c6d7e8f9a0, 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: []
|
||||
m_LockedProperties:
|
||||
m_SavedProperties:
|
||||
serializedVersion: 3
|
||||
m_TexEnvs: []
|
||||
m_Ints: []
|
||||
m_Floats:
|
||||
- _CloudCoverage: 0.5
|
||||
- _CloudScale: 1.2
|
||||
- _CloudSoftness: 0.2
|
||||
- _CloudSpeed: 0.01
|
||||
- _Exposure: 1
|
||||
- _HorizonSharpness: 2.2
|
||||
- _ShootingStarRate: 0.06
|
||||
- _StarDensity: 0.04
|
||||
- _StarTwinkleSpeed: 2
|
||||
- _SunGlowSize: 64
|
||||
- _SunGlowStrength: 1
|
||||
- _SunSize: 0.03
|
||||
m_Colors:
|
||||
- _CloudColor: {r: 1, g: 0.98, b: 0.95, a: 1}
|
||||
- _CloudShadowColor: {r: 0.55, g: 0.6, b: 0.72, a: 1}
|
||||
- _CloudWindDir: {r: 1, g: 0.3, b: 0, a: 0}
|
||||
- _DayHorizon: {r: 0.62, g: 0.78, b: 0.92, a: 1}
|
||||
- _DayZenith: {r: 0.18, g: 0.42, b: 0.78, a: 1}
|
||||
- _NightHorizon: {r: 0.04, g: 0.06, b: 0.12, a: 1}
|
||||
- _NightZenith: {r: 0.01, g: 0.02, b: 0.06, a: 1}
|
||||
- _ShootingStarColor: {r: 1, g: 1, b: 0.9, a: 1}
|
||||
- _SunColor: {r: 1, g: 0.95, b: 0.85, a: 1}
|
||||
- _SunGlowColor: {r: 1, g: 0.7, b: 0.4, a: 1}
|
||||
- _TwilightColor: {r: 0.95, g: 0.45, b: 0.25, a: 1}
|
||||
m_BuildTextureStacks: []
|
||||
m_AllowLocking: 1
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c3d4e5f607182930a4b5c6d7e8f9a0b1
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 2100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,140 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &-5129662474584914909
|
||||
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: Wood_Pickaxe_mat
|
||||
m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
|
||||
m_Parent: {fileID: 0}
|
||||
m_ModifiedSerializedProperties: 0
|
||||
m_ValidKeywords:
|
||||
- _METALLICSPECGLOSSMAP
|
||||
- _NORMALMAP
|
||||
- _OCCLUSIONMAP
|
||||
m_InvalidKeywords: []
|
||||
m_LightmapFlags: 4
|
||||
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: 2800000, guid: ed3a180c3723ad04887dab81ae8ef4af, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _BumpMap:
|
||||
m_Texture: {fileID: 2800000, guid: 3253f09a2195f3a46bf1f1ce72663871, type: 3}
|
||||
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: 2800000, guid: ed3a180c3723ad04887dab81ae8ef4af, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MetallicGlossMap:
|
||||
m_Texture: {fileID: 2800000, guid: a386090fb7578c0479fca4dfe0406e32, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _OcclusionMap:
|
||||
m_Texture: {fileID: 2800000, guid: e64752a1e81f4fd459423cafa20d8e40, type: 3}
|
||||
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
|
||||
- _OcclusionStrength: 1
|
||||
- _Parallax: 0.005
|
||||
- _QueueOffset: 0
|
||||
- _ReceiveShadows: 1
|
||||
- _Smoothness: 1
|
||||
- _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
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bdc23ba104673b14fad16ffd6c5c73fe
|
||||
guid: f066455c9b1b5644d9684ada3f210715
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 2100000
|
||||
@@ -63,7 +63,7 @@ MonoBehaviour:
|
||||
_spectatorInterpolation: 2
|
||||
_enableTeleport: 0
|
||||
_teleportThreshold: 1
|
||||
<PrefabId>k__BackingField: 10
|
||||
<PrefabId>k__BackingField: 11
|
||||
<SpawnableCollectionId>k__BackingField: 0
|
||||
<AssetPathHash>k__BackingField: 9975403408747794214
|
||||
SceneId: 0
|
||||
@@ -90,7 +90,7 @@ MonoBehaviour:
|
||||
_initializeOrder: 0
|
||||
_preventDespawnOnDisconnect: 0
|
||||
_defaultDespawnType: 0
|
||||
_initializedTimestamp: -8584169414543529327
|
||||
_initializedTimestamp: -8584168773125460856
|
||||
--- !u!114 &289947267238384498
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -90,7 +90,7 @@ MonoBehaviour:
|
||||
_initializeOrder: 0
|
||||
_preventDespawnOnDisconnect: 0
|
||||
_defaultDespawnType: 0
|
||||
_initializedTimestamp: -8584169414543529327
|
||||
_initializedTimestamp: -8584168773125460856
|
||||
--- !u!114 &3185372856389979425
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -63,7 +63,7 @@ MonoBehaviour:
|
||||
_spectatorInterpolation: 2
|
||||
_enableTeleport: 0
|
||||
_teleportThreshold: 1
|
||||
<PrefabId>k__BackingField: 17
|
||||
<PrefabId>k__BackingField: 20
|
||||
<SpawnableCollectionId>k__BackingField: 0
|
||||
<AssetPathHash>k__BackingField: 16949488085050316302
|
||||
SceneId: 0
|
||||
@@ -90,7 +90,7 @@ MonoBehaviour:
|
||||
_initializeOrder: 0
|
||||
_preventDespawnOnDisconnect: 0
|
||||
_defaultDespawnType: 0
|
||||
_initializedTimestamp: -8584169414543529327
|
||||
_initializedTimestamp: -8584168773125460856
|
||||
--- !u!114 &3704013715362460501
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1001 &1101463419124301057
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_Modification:
|
||||
serializedVersion: 3
|
||||
m_TransformParent: {fileID: 0}
|
||||
m_Modifications:
|
||||
- target: {fileID: 8219425546800018065, guid: c00bd91003bf1774f8fedd4f0d12e8c9, type: 3}
|
||||
propertyPath: m_Name
|
||||
value: Pioche_en_Bois
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8219425546800018065, guid: c00bd91003bf1774f8fedd4f0d12e8c9, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 11
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8744463598777624619, guid: c00bd91003bf1774f8fedd4f0d12e8c9, type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8744463598777624619, guid: c00bd91003bf1774f8fedd4f0d12e8c9, type: 3}
|
||||
propertyPath: m_LocalPosition.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8744463598777624619, guid: c00bd91003bf1774f8fedd4f0d12e8c9, type: 3}
|
||||
propertyPath: m_LocalPosition.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8744463598777624619, guid: c00bd91003bf1774f8fedd4f0d12e8c9, type: 3}
|
||||
propertyPath: m_LocalRotation.w
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8744463598777624619, guid: c00bd91003bf1774f8fedd4f0d12e8c9, type: 3}
|
||||
propertyPath: m_LocalRotation.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8744463598777624619, guid: c00bd91003bf1774f8fedd4f0d12e8c9, type: 3}
|
||||
propertyPath: m_LocalRotation.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8744463598777624619, guid: c00bd91003bf1774f8fedd4f0d12e8c9, type: 3}
|
||||
propertyPath: m_LocalRotation.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8744463598777624619, guid: c00bd91003bf1774f8fedd4f0d12e8c9, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8744463598777624619, guid: c00bd91003bf1774f8fedd4f0d12e8c9, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8744463598777624619, guid: c00bd91003bf1774f8fedd4f0d12e8c9, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents: []
|
||||
m_RemovedGameObjects: []
|
||||
m_AddedGameObjects: []
|
||||
m_AddedComponents:
|
||||
- targetCorrespondingSourceObject: {fileID: 8219425546800018065, guid: c00bd91003bf1774f8fedd4f0d12e8c9, type: 3}
|
||||
insertIndex: -1
|
||||
addedObject: {fileID: 4534358425995801905}
|
||||
- targetCorrespondingSourceObject: {fileID: 8219425546800018065, guid: c00bd91003bf1774f8fedd4f0d12e8c9, type: 3}
|
||||
insertIndex: -1
|
||||
addedObject: {fileID: 4200035120000206940}
|
||||
m_SourcePrefab: {fileID: 100100000, guid: c00bd91003bf1774f8fedd4f0d12e8c9, type: 3}
|
||||
--- !u!1 &9032088729056219024 stripped
|
||||
GameObject:
|
||||
m_CorrespondingSourceObject: {fileID: 8219425546800018065, guid: c00bd91003bf1774f8fedd4f0d12e8c9, type: 3}
|
||||
m_PrefabInstance: {fileID: 1101463419124301057}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!65 &4534358425995801905
|
||||
BoxCollider:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 9032088729056219024}
|
||||
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.60922796, y: 0.65663785, z: 0.10033387}
|
||||
m_Center: {x: -0.055951215, y: 0.329326, z: 0}
|
||||
--- !u!114 &4200035120000206940
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 9032088729056219024}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: eed740dea8aa59b4cb516f25ce139314, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::Ashwild.Network.Pickable
|
||||
id: -1
|
||||
itemData: {fileID: 11400000, guid: 87cbf77d115c72d4e9cbcc71feceab59, type: 2}
|
||||
quantity: 1
|
||||
interactionVerb: Pick up
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cdeb9c121d922ed459bb03f302f541cf
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -33,7 +33,7 @@ Transform:
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 1385163106223489329}
|
||||
- {fileID: 7041199371496847532}
|
||||
- {fileID: 4516709421929778689}
|
||||
m_Father: {fileID: 2946508416080010728}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!20 &8458408591187477977
|
||||
@@ -69,13 +69,13 @@ Camera:
|
||||
height: 1
|
||||
near clip plane: 0.1
|
||||
far clip plane: 50000
|
||||
field of view: 70
|
||||
field of view: 60
|
||||
orthographic: 0
|
||||
orthographic size: 5
|
||||
m_Depth: -1
|
||||
m_CullingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 27391
|
||||
m_Bits: 60159
|
||||
m_RenderingPath: -1
|
||||
m_TargetTexture: {fileID: 0}
|
||||
m_TargetDisplay: 0
|
||||
@@ -206,7 +206,7 @@ Camera:
|
||||
height: 1
|
||||
near clip plane: 0.3
|
||||
far clip plane: 50000
|
||||
field of view: 70
|
||||
field of view: 60
|
||||
orthographic: 0
|
||||
orthographic size: 5
|
||||
m_Depth: -1
|
||||
@@ -418,7 +418,6 @@ GameObject:
|
||||
- component: {fileID: 7491008438145463287}
|
||||
- component: {fileID: 9009865201504802574}
|
||||
- component: {fileID: 6967422961657961542}
|
||||
- component: {fileID: 8358435118901160354}
|
||||
- component: {fileID: 5082660776968564343}
|
||||
- component: {fileID: 2842852335340042134}
|
||||
- component: {fileID: 232791202490841691}
|
||||
@@ -432,7 +431,7 @@ GameObject:
|
||||
- component: {fileID: 1890885114087150018}
|
||||
- component: {fileID: -4423719270533760289}
|
||||
- component: {fileID: 656527942023048284}
|
||||
- component: {fileID: 4969709554801488359}
|
||||
- component: {fileID: 8608524212292653}
|
||||
m_Layer: 10
|
||||
m_Name: Player
|
||||
m_TagString: Untagged
|
||||
@@ -608,39 +607,6 @@ MonoBehaviour:
|
||||
deathPitch: 20
|
||||
deathRoll: 35
|
||||
deathFallSpeed: 4
|
||||
--- !u!114 &8358435118901160354
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5167716622245296089}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: b8c9d0e1f20314255567890123456abc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
toolHolder: {fileID: 7041199371496847532}
|
||||
enableWeaponBob: 1
|
||||
bobFrequency: 8
|
||||
bobAmplitudeY: 0.04
|
||||
bobAmplitudeX: 0.03
|
||||
bobSmooth: 8
|
||||
sprintBobMultiplier: 3
|
||||
crouchBobMultiplier: 0.5
|
||||
enableWeaponSway: 1
|
||||
swayAmount: 0.02
|
||||
swaySmooth: 6
|
||||
swayMaxAmount: 0.06
|
||||
enableIdleTilt: 1
|
||||
idleTiltFrequency: 0.4
|
||||
idleTiltAmplitudeZ: 1.5
|
||||
idleTiltAmplitudeX: 0.8
|
||||
idleTiltSmooth: 4
|
||||
equipDropOffset: -0.5
|
||||
equipTiltAngle: -15
|
||||
equipDuration: 0.25
|
||||
unequipDuration: 0.12
|
||||
--- !u!114 &5082660776968564343
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -728,9 +694,9 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: ea0a96d50c9b5de46b7cbae92f5f29ec, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::HotbarController
|
||||
toolHolder: {fileID: 7041199371496847532}
|
||||
toolHolderAnim: {fileID: 8358435118901160354}
|
||||
handHolder: {fileID: 4880923047972336482}
|
||||
raycastOrigin: {fileID: 8833472930861132790}
|
||||
stowTimeout: 0.05
|
||||
--- !u!114 &4232943699575871128
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -746,7 +712,7 @@ MonoBehaviour:
|
||||
interactRange: 5
|
||||
interactMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 2112
|
||||
m_Bits: 34880
|
||||
raycastOrigin: {fileID: 8833472930861132790}
|
||||
hoverStabilityTime: 0.08
|
||||
idleGizmoColor: {r: 0, g: 1, b: 0, a: 1}
|
||||
@@ -815,7 +781,7 @@ MonoBehaviour:
|
||||
_spectatorInterpolation: 2
|
||||
_enableTeleport: 0
|
||||
_teleportThreshold: 1
|
||||
<PrefabId>k__BackingField: 15
|
||||
<PrefabId>k__BackingField: 18
|
||||
<SpawnableCollectionId>k__BackingField: 0
|
||||
<AssetPathHash>k__BackingField: 16376119982057009716
|
||||
SceneId: 0
|
||||
@@ -838,7 +804,7 @@ MonoBehaviour:
|
||||
- {fileID: 1890885114087150018}
|
||||
- {fileID: -4423719270533760289}
|
||||
- {fileID: 656527942023048284}
|
||||
- {fileID: 4969709554801488359}
|
||||
- {fileID: 0}
|
||||
InitializedParentNetworkBehaviour: {fileID: 0}
|
||||
InitializedNestedNetworkObjects: []
|
||||
RuntimeParentNetworkBehaviour: {fileID: 0}
|
||||
@@ -849,7 +815,7 @@ MonoBehaviour:
|
||||
_initializeOrder: 0
|
||||
_preventDespawnOnDisconnect: 0
|
||||
_defaultDespawnType: 0
|
||||
_initializedTimestamp: -8584169414543529327
|
||||
_initializedTimestamp: -8584168773125460856
|
||||
--- !u!114 &-2455020581440275693
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -914,7 +880,6 @@ MonoBehaviour:
|
||||
ownerOnlyBehaviours:
|
||||
- {fileID: 9009865201504802574}
|
||||
- {fileID: 6967422961657961542}
|
||||
- {fileID: 8358435118901160354}
|
||||
- {fileID: 5082660776968564343}
|
||||
- {fileID: 2842852335340042134}
|
||||
- {fileID: 232791202490841691}
|
||||
@@ -924,7 +889,7 @@ MonoBehaviour:
|
||||
- {fileID: -4423719270533760289}
|
||||
ownerOnlyObjects:
|
||||
- {fileID: 3016760143010852029}
|
||||
- {fileID: 5457574158965716017}
|
||||
- {fileID: 0}
|
||||
- {fileID: 3278963701000438075}
|
||||
verboseLogging: 1
|
||||
--- !u!82 &1509526206074017255
|
||||
@@ -1073,7 +1038,7 @@ MonoBehaviour:
|
||||
_componentIndexCache: 6
|
||||
_addedNetworkObject: {fileID: -5889822588553870904}
|
||||
_networkObjectCache: {fileID: -5889822588553870904}
|
||||
--- !u!114 &4969709554801488359
|
||||
--- !u!114 &8608524212292653
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
@@ -1082,45 +1047,26 @@ MonoBehaviour:
|
||||
m_GameObject: {fileID: 5167716622245296089}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 23848f2e7f7ecba4791aa89d5344b3d0, type: 3}
|
||||
m_Script: {fileID: 11500000, guid: 8c1abd96334ddec4f8abf1ccdde057f9, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::Ashwild.Network.PlayerBodyVisibility
|
||||
_componentIndexCache: 7
|
||||
_addedNetworkObject: {fileID: -5889822588553870904}
|
||||
_networkObjectCache: {fileID: -5889822588553870904}
|
||||
bodyRoot: {fileID: 0}
|
||||
localBodyLayer: LocalBody
|
||||
--- !u!1 &5457574158965716017
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 7041199371496847532}
|
||||
m_Layer: 10
|
||||
m_Name: Tools
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &7041199371496847532
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5457574158965716017}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0.997, y: -0.498, z: 0.8660002}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 1
|
||||
m_Children: []
|
||||
m_Father: {fileID: 8833472930861132790}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_EditorClassIdentifier: Assembly-CSharp::Ashwild.Player.PlayerAnimatorDriver
|
||||
animator: {fileID: 4959095178830542625}
|
||||
orientation: {fileID: 4516709421929778689}
|
||||
speedParam: Speed
|
||||
moveXParam: MoveX
|
||||
moveYParam: MoveY
|
||||
normalizeSpeed: 6
|
||||
floatDampTime: 0.1
|
||||
groundedParam: Grounded
|
||||
crouchingParam: Crouching
|
||||
sprintingParam: Sprinting
|
||||
attackIndexParam: AttackIndex
|
||||
jumpParam: Jump
|
||||
attackParam: Attack
|
||||
landParam: Land
|
||||
grabParam: Grab
|
||||
equipParam: Equip
|
||||
unequipParam: Unequip
|
||||
--- !u!1 &5566668569623061449
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -1223,3 +1169,487 @@ MonoBehaviour:
|
||||
m_BlockingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
--- !u!1001 &4117462364724767210
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_Modification:
|
||||
serializedVersion: 3
|
||||
m_TransformParent: {fileID: 8833472930861132790}
|
||||
m_Modifications:
|
||||
- target: {fileID: -9114728121406467333, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -9091960592056978994, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -9005932998126021395, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8912366441439371061, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_LocalPosition.y
|
||||
value: -1.7
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_LocalPosition.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_LocalRotation.w
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_LocalRotation.x
|
||||
value: -0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_LocalRotation.y
|
||||
value: -0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_LocalRotation.z
|
||||
value: -0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8339925065626534255, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -7908252628669667388, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -7390994396509830658, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -7364163322242580340, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -7026674463283310403, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -6957328845325165403, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -6932409164393742537, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -6836594550065954337, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -6638746771798630907, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -6093025019509219202, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -5952996068900135978, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -5841592934306472080, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -5723284251943254774, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_CastShadows
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -5723284251943254774, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: 'm_Materials.Array.data[0]'
|
||||
value:
|
||||
objectReference: {fileID: 2100000, guid: 31fb94b2d48381d4ba5aab59da4b55c6, type: 2}
|
||||
- target: {fileID: -5683104882415255012, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -5571614014550383139, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -5264148493031375657, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -5245216604706196616, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -4929363766397250142, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -4882516702600463819, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -4876098737634327325, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -4703455372617856419, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -4673936467165797694, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -4422683196719403145, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -4109202245699340355, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -3539817387139676878, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -3510496321243579410, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -3321234979978117840, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -2985295434639030125, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -2612923978381773798, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -2574962596187546846, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -2501239100867950142, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -2478737075563321435, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -2274508901948732838, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -1865324859566571703, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -1759871094696943916, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -1513047751338434092, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -1242403873309359080, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -952861315693316402, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -692291630260226444, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_IsActive
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -447087479574337256, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -342157409561538205, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -147738579016540067, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -43976543173020249, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 162632890210374811, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 495104665796651920, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 517141129348201922, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 590226951443985453, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 776757099945431579, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 816216322533564102, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 818932178456988484, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 919132149155446097, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Name
|
||||
value: BasePose
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 919132149155446097, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1126206503167985268, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1568840458994535760, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1585756772874795506, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1658199428886355469, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2319399366478941826, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2410996474783720867, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2705212354660149930, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2986432728573076944, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3197789887595205092, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4024074265046454402, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4268245502459120835, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4333340788299748075, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4410306326381788422, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4740887147367331461, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5143341373155657318, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5515368194204627430, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5646708832558167896, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5718925870888544817, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6058156678288545041, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6122941722233347071, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6232925062385134541, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6413882497215545230, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6899282620180350615, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7040351110097015869, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7165621064625342025, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_IsActive
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7382610344456276889, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7944114425124236386, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8362050438629551374, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8362050438629551374, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_IsActive
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8368779862852866610, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8384586351209892030, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8820328435933464060, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents: []
|
||||
m_RemovedGameObjects:
|
||||
- {fileID: -692291630260226444, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
- {fileID: 7165621064625342025, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
m_AddedGameObjects: []
|
||||
m_AddedComponents:
|
||||
- targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
insertIndex: -1
|
||||
addedObject: {fileID: 4959095178830542625}
|
||||
- targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
insertIndex: -1
|
||||
addedObject: {fileID: 377270758029266651}
|
||||
- targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
insertIndex: -1
|
||||
addedObject: {fileID: 2318304105580012496}
|
||||
m_SourcePrefab: {fileID: 100100000, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
--- !u!1 &3883584906608547003 stripped
|
||||
GameObject:
|
||||
m_CorrespondingSourceObject: {fileID: 919132149155446097, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
m_PrefabInstance: {fileID: 4117462364724767210}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!95 &4959095178830542625
|
||||
Animator:
|
||||
serializedVersion: 7
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3883584906608547003}
|
||||
m_Enabled: 1
|
||||
m_Avatar: {fileID: 0}
|
||||
m_Controller: {fileID: 9100000, guid: 3088f779b365aad4fab7962c5c538a0c, type: 2}
|
||||
m_CullingMode: 0
|
||||
m_UpdateMode: 0
|
||||
m_ApplyRootMotion: 0
|
||||
m_LinearVelocityBlending: 0
|
||||
m_StabilizeFeet: 0
|
||||
m_AnimatePhysics: 0
|
||||
m_WarningMessage:
|
||||
m_HasTransformHierarchy: 1
|
||||
m_AllowConstantClipSamplingOptimization: 1
|
||||
m_KeepAnimatorStateOnDisable: 0
|
||||
m_WriteDefaultValuesOnDisable: 0
|
||||
--- !u!114 &377270758029266651
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3883584906608547003}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 1efb301f6b6f0674c94758684e992e0b, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::Ashwild.Player.PlayerAnimationSetBinder
|
||||
animator: {fileID: 4959095178830542625}
|
||||
rig: 0
|
||||
defaultSet: {fileID: 11400000, guid: 0325f3104b37ee344bc81f011dc0183d, type: 2}
|
||||
--- !u!114 &2318304105580012496
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3883584906608547003}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 4f541ac054ea4fc40976871e6e445941, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::Ashwild.Player.ArmsAnimationEvents
|
||||
--- !u!4 &4516709421929778689 stripped
|
||||
Transform:
|
||||
m_CorrespondingSourceObject: {fileID: -8679921383154817045, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
m_PrefabInstance: {fileID: 4117462364724767210}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!4 &4880923047972336482 stripped
|
||||
Transform:
|
||||
m_CorrespondingSourceObject: {fileID: 8834003712752867976, guid: e266f749230b8344e8ac3bde01875c57, type: 3}
|
||||
m_PrefabInstance: {fileID: 4117462364724767210}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
|
||||
@@ -70,6 +70,9 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: fe38331f524a7db4cb3d53381b9ea244, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::Ashwild.Building.BuiltStructure
|
||||
_componentIndexCache: 0
|
||||
_addedNetworkObject: {fileID: 8751794752429108}
|
||||
_networkObjectCache: {fileID: 8751794752429108}
|
||||
bumpDuration: 0.22
|
||||
startScaleFactor: 0.7
|
||||
bumpEase: 27
|
||||
@@ -131,6 +134,7 @@ MonoBehaviour:
|
||||
<PredictedSpawn>k__BackingField: {fileID: 0}
|
||||
<PredictedOwner>k__BackingField: {fileID: 0}
|
||||
NetworkBehaviours:
|
||||
- {fileID: 7507030739449404127}
|
||||
- {fileID: 675340278771037740}
|
||||
InitializedParentNetworkBehaviour: {fileID: 0}
|
||||
InitializedNestedNetworkObjects: []
|
||||
@@ -142,7 +146,7 @@ MonoBehaviour:
|
||||
_initializeOrder: 0
|
||||
_preventDespawnOnDisconnect: 0
|
||||
_defaultDespawnType: 0
|
||||
_initializedTimestamp: -8584169414543529327
|
||||
_initializedTimestamp: -8584168773125460856
|
||||
--- !u!114 &675340278771037740
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -155,7 +159,7 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: ee73c1f9305ee50408bbbee938a15194, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::Ashwild.Storage.Chest
|
||||
_componentIndexCache: 0
|
||||
_componentIndexCache: 1
|
||||
_addedNetworkObject: {fileID: 8751794752429108}
|
||||
_networkObjectCache: {fileID: 8751794752429108}
|
||||
data: {fileID: 11400000, guid: 38eb565e82ffe934ab6d60830d1d453a, type: 2}
|
||||
|
||||
@@ -239,9 +239,9 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: fe38331f524a7db4cb3d53381b9ea244, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::Ashwild.Building.BuiltStructure
|
||||
_componentIndexCache: 255
|
||||
_componentIndexCache: 0
|
||||
_addedNetworkObject: {fileID: 7022851135897287337}
|
||||
_networkObjectCache: {fileID: 0}
|
||||
_networkObjectCache: {fileID: 7022851135897287337}
|
||||
bumpDuration: 0.22
|
||||
startScaleFactor: 0.7
|
||||
bumpEase: 27
|
||||
@@ -287,22 +287,23 @@ MonoBehaviour:
|
||||
_spectatorInterpolation: 2
|
||||
_enableTeleport: 0
|
||||
_teleportThreshold: 1
|
||||
<PrefabId>k__BackingField: 65535
|
||||
<PrefabId>k__BackingField: 7
|
||||
<SpawnableCollectionId>k__BackingField: 0
|
||||
<AssetPathHash>k__BackingField: 7547832251807066330
|
||||
SceneId: 0
|
||||
SerializedTransformProperties:
|
||||
Position: {x: 0, y: 0, z: 0}
|
||||
Rotation: {x: 0, y: 0, z: 0, w: 0}
|
||||
Scale: {x: 0, y: 0, z: 0}
|
||||
IsValid: 0
|
||||
Rotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
Scale: {x: 1, y: 1, z: 1}
|
||||
IsValid: 1
|
||||
<IsNested>k__BackingField: 0
|
||||
WasActiveDuringEdit: 0
|
||||
WasActiveDuringEdit_Set1: 1
|
||||
<ComponentIndex>k__BackingField: 0
|
||||
<PredictedSpawn>k__BackingField: {fileID: 0}
|
||||
<PredictedOwner>k__BackingField: {fileID: 0}
|
||||
NetworkBehaviours: []
|
||||
NetworkBehaviours:
|
||||
- {fileID: 1224852433561986364}
|
||||
InitializedParentNetworkBehaviour: {fileID: 0}
|
||||
InitializedNestedNetworkObjects: []
|
||||
RuntimeParentNetworkBehaviour: {fileID: 0}
|
||||
@@ -313,7 +314,7 @@ MonoBehaviour:
|
||||
_initializeOrder: 0
|
||||
_preventDespawnOnDisconnect: 0
|
||||
_defaultDespawnType: 0
|
||||
_initializedTimestamp: 0
|
||||
_initializedTimestamp: -8584168773125460856
|
||||
--- !u!1 &7687585878458156323
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -271,9 +271,9 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: fe38331f524a7db4cb3d53381b9ea244, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::Ashwild.Building.BuiltStructure
|
||||
_componentIndexCache: 255
|
||||
_componentIndexCache: 0
|
||||
_addedNetworkObject: {fileID: 4444565792432705460}
|
||||
_networkObjectCache: {fileID: 0}
|
||||
_networkObjectCache: {fileID: 4444565792432705460}
|
||||
bumpDuration: 0.22
|
||||
startScaleFactor: 0.7
|
||||
bumpEase: 27
|
||||
@@ -319,22 +319,23 @@ MonoBehaviour:
|
||||
_spectatorInterpolation: 2
|
||||
_enableTeleport: 0
|
||||
_teleportThreshold: 1
|
||||
<PrefabId>k__BackingField: 65535
|
||||
<PrefabId>k__BackingField: 14
|
||||
<SpawnableCollectionId>k__BackingField: 0
|
||||
<AssetPathHash>k__BackingField: 13832512290486259770
|
||||
SceneId: 0
|
||||
SerializedTransformProperties:
|
||||
Position: {x: 0, y: 0, z: 0}
|
||||
Rotation: {x: 0, y: 0, z: 0, w: 0}
|
||||
Scale: {x: 0, y: 0, z: 0}
|
||||
IsValid: 0
|
||||
Rotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
Scale: {x: 1, y: 1, z: 1}
|
||||
IsValid: 1
|
||||
<IsNested>k__BackingField: 0
|
||||
WasActiveDuringEdit: 0
|
||||
WasActiveDuringEdit_Set1: 1
|
||||
<ComponentIndex>k__BackingField: 0
|
||||
<PredictedSpawn>k__BackingField: {fileID: 0}
|
||||
<PredictedOwner>k__BackingField: {fileID: 0}
|
||||
NetworkBehaviours: []
|
||||
NetworkBehaviours:
|
||||
- {fileID: 2163295442053852858}
|
||||
InitializedParentNetworkBehaviour: {fileID: 0}
|
||||
InitializedNestedNetworkObjects: []
|
||||
RuntimeParentNetworkBehaviour: {fileID: 0}
|
||||
@@ -345,7 +346,7 @@ MonoBehaviour:
|
||||
_initializeOrder: 0
|
||||
_preventDespawnOnDisconnect: 0
|
||||
_defaultDespawnType: 0
|
||||
_initializedTimestamp: 0
|
||||
_initializedTimestamp: -8584168773125460856
|
||||
--- !u!1 &6937546176162827135
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -137,9 +137,9 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: fe38331f524a7db4cb3d53381b9ea244, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::Ashwild.Building.BuiltStructure
|
||||
_componentIndexCache: 255
|
||||
_componentIndexCache: 0
|
||||
_addedNetworkObject: {fileID: 7405293751935118856}
|
||||
_networkObjectCache: {fileID: 0}
|
||||
_networkObjectCache: {fileID: 7405293751935118856}
|
||||
bumpDuration: 0.22
|
||||
startScaleFactor: 0.7
|
||||
bumpEase: 27
|
||||
@@ -185,22 +185,23 @@ MonoBehaviour:
|
||||
_spectatorInterpolation: 2
|
||||
_enableTeleport: 0
|
||||
_teleportThreshold: 1
|
||||
<PrefabId>k__BackingField: 65535
|
||||
<PrefabId>k__BackingField: 15
|
||||
<SpawnableCollectionId>k__BackingField: 0
|
||||
<AssetPathHash>k__BackingField: 13925520005596096250
|
||||
SceneId: 0
|
||||
SerializedTransformProperties:
|
||||
Position: {x: 0, y: 0, z: 0}
|
||||
Rotation: {x: 0, y: 0, z: 0, w: 0}
|
||||
Scale: {x: 0, y: 0, z: 0}
|
||||
IsValid: 0
|
||||
Rotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
Scale: {x: 1, y: 1, z: 1}
|
||||
IsValid: 1
|
||||
<IsNested>k__BackingField: 0
|
||||
WasActiveDuringEdit: 0
|
||||
WasActiveDuringEdit_Set1: 1
|
||||
<ComponentIndex>k__BackingField: 0
|
||||
<PredictedSpawn>k__BackingField: {fileID: 0}
|
||||
<PredictedOwner>k__BackingField: {fileID: 0}
|
||||
NetworkBehaviours: []
|
||||
NetworkBehaviours:
|
||||
- {fileID: 6858850823605817203}
|
||||
InitializedParentNetworkBehaviour: {fileID: 0}
|
||||
InitializedNestedNetworkObjects: []
|
||||
RuntimeParentNetworkBehaviour: {fileID: 0}
|
||||
@@ -211,7 +212,7 @@ MonoBehaviour:
|
||||
_initializeOrder: 0
|
||||
_preventDespawnOnDisconnect: 0
|
||||
_defaultDespawnType: 0
|
||||
_initializedTimestamp: 0
|
||||
_initializedTimestamp: -8584168773125460856
|
||||
--- !u!1 &5016747873711697717
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
Binary file not shown.
+1
-1
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 308c4c6aa7df5ef45ad84b0c139e7f17
|
||||
guid: b7e9f89bddf5e004d9f51e129188d1fe
|
||||
ModelImporter:
|
||||
serializedVersion: 24200
|
||||
internalIDToNameTable: []
|
||||
@@ -222,7 +222,7 @@ MonoBehaviour:
|
||||
_spectatorInterpolation: 2
|
||||
_enableTeleport: 0
|
||||
_teleportThreshold: 1
|
||||
<PrefabId>k__BackingField: 16
|
||||
<PrefabId>k__BackingField: 19
|
||||
<SpawnableCollectionId>k__BackingField: 0
|
||||
<AssetPathHash>k__BackingField: 16508065741492498975
|
||||
SceneId: 0
|
||||
@@ -249,7 +249,7 @@ MonoBehaviour:
|
||||
_initializeOrder: 0
|
||||
_preventDespawnOnDisconnect: 0
|
||||
_defaultDespawnType: 0
|
||||
_initializedTimestamp: -8584169414543529327
|
||||
_initializedTimestamp: -8584168773125460856
|
||||
--- !u!114 &4182754159189231312
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -87,7 +87,7 @@ MonoBehaviour:
|
||||
_spectatorInterpolation: 2
|
||||
_enableTeleport: 0
|
||||
_teleportThreshold: 1
|
||||
<PrefabId>k__BackingField: 14
|
||||
<PrefabId>k__BackingField: 17
|
||||
<SpawnableCollectionId>k__BackingField: 0
|
||||
<AssetPathHash>k__BackingField: 15960435111263854789
|
||||
SceneId: 0
|
||||
@@ -114,7 +114,7 @@ MonoBehaviour:
|
||||
_initializeOrder: 0
|
||||
_preventDespawnOnDisconnect: 0
|
||||
_defaultDespawnType: 0
|
||||
_initializedTimestamp: -8584169414543529327
|
||||
_initializedTimestamp: -8584168773125460856
|
||||
--- !u!114 &4872016000618054028
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -88,7 +88,7 @@ MonoBehaviour:
|
||||
_spectatorInterpolation: 2
|
||||
_enableTeleport: 0
|
||||
_teleportThreshold: 1
|
||||
<PrefabId>k__BackingField: 9
|
||||
<PrefabId>k__BackingField: 10
|
||||
<SpawnableCollectionId>k__BackingField: 0
|
||||
<AssetPathHash>k__BackingField: 9001145751081742879
|
||||
SceneId: 0
|
||||
@@ -115,7 +115,7 @@ MonoBehaviour:
|
||||
_initializeOrder: 0
|
||||
_preventDespawnOnDisconnect: 0
|
||||
_defaultDespawnType: 0
|
||||
_initializedTimestamp: -8584169414543529327
|
||||
_initializedTimestamp: -8584168773125460856
|
||||
--- !u!114 &5061449885792543640
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -596,7 +596,7 @@ MonoBehaviour:
|
||||
_spectatorInterpolation: 2
|
||||
_enableTeleport: 0
|
||||
_teleportThreshold: 1
|
||||
<PrefabId>k__BackingField: 20
|
||||
<PrefabId>k__BackingField: 23
|
||||
<SpawnableCollectionId>k__BackingField: 0
|
||||
<AssetPathHash>k__BackingField: 17746024873254907359
|
||||
SceneId: 0
|
||||
@@ -623,7 +623,7 @@ MonoBehaviour:
|
||||
_initializeOrder: 0
|
||||
_preventDespawnOnDisconnect: 0
|
||||
_defaultDespawnType: 0
|
||||
_initializedTimestamp: -8584169414543529327
|
||||
_initializedTimestamp: -8584168773125460856
|
||||
--- !u!114 &5834036106823394854
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -297,7 +297,7 @@ MonoBehaviour:
|
||||
_spectatorInterpolation: 2
|
||||
_enableTeleport: 0
|
||||
_teleportThreshold: 1
|
||||
<PrefabId>k__BackingField: 11
|
||||
<PrefabId>k__BackingField: 12
|
||||
<SpawnableCollectionId>k__BackingField: 0
|
||||
<AssetPathHash>k__BackingField: 10703378808306651026
|
||||
SceneId: 0
|
||||
@@ -324,7 +324,7 @@ MonoBehaviour:
|
||||
_initializeOrder: 0
|
||||
_preventDespawnOnDisconnect: 0
|
||||
_defaultDespawnType: 0
|
||||
_initializedTimestamp: -8584169414543529327
|
||||
_initializedTimestamp: -8584168773125460856
|
||||
--- !u!1 &5855379289088646484
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1001 &4835160831230511101
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_Modification:
|
||||
serializedVersion: 3
|
||||
m_TransformParent: {fileID: 0}
|
||||
m_Modifications:
|
||||
- target: {fileID: 8219425546800018065, guid: c00bd91003bf1774f8fedd4f0d12e8c9, type: 3}
|
||||
propertyPath: m_Name
|
||||
value: Pioche_en_Bois
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8219425546800018065, guid: c00bd91003bf1774f8fedd4f0d12e8c9, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 8
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8744463598777624619, guid: c00bd91003bf1774f8fedd4f0d12e8c9, type: 3}
|
||||
propertyPath: m_LocalScale.x
|
||||
value: 1.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8744463598777624619, guid: c00bd91003bf1774f8fedd4f0d12e8c9, type: 3}
|
||||
propertyPath: m_LocalScale.y
|
||||
value: 1.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8744463598777624619, guid: c00bd91003bf1774f8fedd4f0d12e8c9, type: 3}
|
||||
propertyPath: m_LocalScale.z
|
||||
value: 1.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8744463598777624619, guid: c00bd91003bf1774f8fedd4f0d12e8c9, type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8744463598777624619, guid: c00bd91003bf1774f8fedd4f0d12e8c9, type: 3}
|
||||
propertyPath: m_LocalPosition.y
|
||||
value: -0.29
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8744463598777624619, guid: c00bd91003bf1774f8fedd4f0d12e8c9, type: 3}
|
||||
propertyPath: m_LocalPosition.z
|
||||
value: 0.15
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8744463598777624619, guid: c00bd91003bf1774f8fedd4f0d12e8c9, type: 3}
|
||||
propertyPath: m_LocalRotation.w
|
||||
value: 0.7071068
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8744463598777624619, guid: c00bd91003bf1774f8fedd4f0d12e8c9, type: 3}
|
||||
propertyPath: m_LocalRotation.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8744463598777624619, guid: c00bd91003bf1774f8fedd4f0d12e8c9, type: 3}
|
||||
propertyPath: m_LocalRotation.y
|
||||
value: 0.7071068
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8744463598777624619, guid: c00bd91003bf1774f8fedd4f0d12e8c9, type: 3}
|
||||
propertyPath: m_LocalRotation.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8744463598777624619, guid: c00bd91003bf1774f8fedd4f0d12e8c9, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8744463598777624619, guid: c00bd91003bf1774f8fedd4f0d12e8c9, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8744463598777624619, guid: c00bd91003bf1774f8fedd4f0d12e8c9, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents: []
|
||||
m_RemovedGameObjects: []
|
||||
m_AddedGameObjects: []
|
||||
m_AddedComponents:
|
||||
- targetCorrespondingSourceObject: {fileID: 8219425546800018065, guid: c00bd91003bf1774f8fedd4f0d12e8c9, type: 3}
|
||||
insertIndex: -1
|
||||
addedObject: {fileID: 6886932543381458403}
|
||||
- targetCorrespondingSourceObject: {fileID: 8219425546800018065, guid: c00bd91003bf1774f8fedd4f0d12e8c9, type: 3}
|
||||
insertIndex: -1
|
||||
addedObject: {fileID: 3662451106709642073}
|
||||
m_SourcePrefab: {fileID: 100100000, guid: c00bd91003bf1774f8fedd4f0d12e8c9, type: 3}
|
||||
--- !u!1 &3533270531403527532 stripped
|
||||
GameObject:
|
||||
m_CorrespondingSourceObject: {fileID: 8219425546800018065, guid: c00bd91003bf1774f8fedd4f0d12e8c9, type: 3}
|
||||
m_PrefabInstance: {fileID: 4835160831230511101}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!114 &6886932543381458403
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3533270531403527532}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: ccc8774893954fc40a153ddb5fdb1ffb, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::Ashwild.Player.ToolBehaviour
|
||||
harvestRange: 3
|
||||
harvestMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 128
|
||||
baseDamage: 10
|
||||
wrongToolMultiplier: 0.25
|
||||
swingCooldown: 0.5
|
||||
--- !u!114 &3662451106709642073
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3533270531403527532}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 7940b6e982f984f49856a6ea916a1918, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::Ashwild.Player.HeldItemOffset
|
||||
positionOffset: {x: 0, y: -0.29, z: 0.15}
|
||||
rotationOffset: {x: 0, y: 90, z: 0}
|
||||
scale: {x: 1.5, y: 1.5, z: 1.5}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cf74ac15809b5e74787b243d40877ba0
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,63 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1001 &9137850018353647552
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_Modification:
|
||||
serializedVersion: 3
|
||||
m_TransformParent: {fileID: 0}
|
||||
m_Modifications:
|
||||
- target: {fileID: -8679921383154817045, guid: 9bf264414994db540866bf91890392c5, type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: 9bf264414994db540866bf91890392c5, type: 3}
|
||||
propertyPath: m_LocalPosition.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: 9bf264414994db540866bf91890392c5, type: 3}
|
||||
propertyPath: m_LocalPosition.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: 9bf264414994db540866bf91890392c5, type: 3}
|
||||
propertyPath: m_LocalRotation.w
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: 9bf264414994db540866bf91890392c5, type: 3}
|
||||
propertyPath: m_LocalRotation.x
|
||||
value: -0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: 9bf264414994db540866bf91890392c5, type: 3}
|
||||
propertyPath: m_LocalRotation.y
|
||||
value: -0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: 9bf264414994db540866bf91890392c5, type: 3}
|
||||
propertyPath: m_LocalRotation.z
|
||||
value: -0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: 9bf264414994db540866bf91890392c5, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: 9bf264414994db540866bf91890392c5, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: 9bf264414994db540866bf91890392c5, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -7511558181221131132, guid: 9bf264414994db540866bf91890392c5, type: 3}
|
||||
propertyPath: 'm_Materials.Array.data[0]'
|
||||
value:
|
||||
objectReference: {fileID: 2100000, guid: f066455c9b1b5644d9684ada3f210715, type: 2}
|
||||
- target: {fileID: 919132149155446097, guid: 9bf264414994db540866bf91890392c5, type: 3}
|
||||
propertyPath: m_Name
|
||||
value: Wood_Pickaxe
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents: []
|
||||
m_RemovedGameObjects: []
|
||||
m_AddedGameObjects: []
|
||||
m_AddedComponents: []
|
||||
m_SourcePrefab: {fileID: 100100000, guid: 9bf264414994db540866bf91890392c5, type: 3}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c00bd91003bf1774f8fedd4f0d12e8c9
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -7187,13 +7187,14 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: 892ceea9ba1c06b4fb516e6f9eb55ff0, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::InventoryCategoryManager
|
||||
categoryButtons:
|
||||
- {fileID: 407549691945498409}
|
||||
- {fileID: 7857620881853131658}
|
||||
panels:
|
||||
- {fileID: 4092515852661730968}
|
||||
- {fileID: 3551148638030693533}
|
||||
defaultCategoryIndex: 0
|
||||
categories:
|
||||
- category: 0
|
||||
button: {fileID: 407549691945498409}
|
||||
panel: {fileID: 4092515852661730968}
|
||||
- category: 1
|
||||
button: {fileID: 7857620881853131658}
|
||||
panel: {fileID: 3551148638030693533}
|
||||
defaultCategory: 0
|
||||
--- !u!1001 &1985671894588496012
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -24,6 +24,7 @@ MonoBehaviour:
|
||||
- {fileID: 11400000, guid: a827e12e2b364d73bd1038d60d5258a1, type: 2}
|
||||
- {fileID: 11400000, guid: 0c27b42a96974b60b484bf475036ef58, type: 2}
|
||||
- {fileID: 11400000, guid: db082cae7b71f3643ad33fcc5d4c474b, type: 2}
|
||||
- {fileID: 11400000, guid: 87cbf77d115c72d4e9cbcc71feceab59, type: 2}
|
||||
- {fileID: 11400000, guid: 60132cba2202496eb74f2afc1f89c81a, type: 2}
|
||||
- {fileID: 11400000, guid: f9cafd470cfc4aa8aceea5b263c4c25c, type: 2}
|
||||
- {fileID: 11400000, guid: 6b543ed8734f4dcf984c1c43d63a9207, type: 2}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e84df5fb440a92e40a048fc13e628d67
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+1625
-18781
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using Ashwild.Inventory;
|
||||
using Ashwild.Network;
|
||||
using DG.Tweening;
|
||||
using FishNet.Connection;
|
||||
using FishNet.Object;
|
||||
@@ -250,13 +251,17 @@ namespace Ashwild.Building
|
||||
/// health: a full-health build refunds its whole cost, a half-health one refunds half, and so on
|
||||
/// (per line, floored — a partial unit is not returned). No-op for a free build, when the player's
|
||||
/// inventory cannot be resolved, or when the fraction rounds every line down to nothing.
|
||||
///
|
||||
/// The inventory comes from the shared lookup, which scans the connection's objects rather than
|
||||
/// trusting FirstObject — the demolisher may also own a build ghost, which FirstObject can resolve
|
||||
/// to instead of the player.
|
||||
/// </summary>
|
||||
private void RefundResources(NetworkConnection conn)
|
||||
{
|
||||
BuildableData data = Data;
|
||||
if (data == null || data.Cost == null || data.Cost.Length == 0) return;
|
||||
|
||||
PlayerInventory inventory = ResolveInventory(conn);
|
||||
PlayerInventory inventory = NetworkPlayerLookup.ResolveInventory(conn);
|
||||
if (inventory == null) return;
|
||||
|
||||
float max = data.MaxHealth;
|
||||
@@ -270,16 +275,6 @@ namespace Ashwild.Building
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the PlayerInventory on the player object owned by the given connection, mirroring
|
||||
/// CookingStation so refunds land in the requester's own inventory.
|
||||
/// </summary>
|
||||
private static PlayerInventory ResolveInventory(NetworkConnection conn)
|
||||
{
|
||||
NetworkObject playerObject = conn != null ? conn.FirstObject : null;
|
||||
return playerObject != null ? playerObject.GetComponent<PlayerInventory>() : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes this structure for every player. Server-side only.
|
||||
/// </summary>
|
||||
|
||||
@@ -315,6 +315,7 @@ namespace Ashwild.Cooking
|
||||
if (cooked != null && !inv.CanFit(cooked, 1))
|
||||
{
|
||||
Debug.LogWarning($"[CookingStation] '{name}' cannot give '{cooked.ItemName}' — inventory full.", this);
|
||||
PlayerEvents.RaiseInteractionRefused("Inventory full");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -463,13 +464,10 @@ namespace Ashwild.Cooking
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the PlayerInventory on the player object owned by the given connection.
|
||||
/// Returns the PlayerInventory owned by the given connection. Delegates to the shared lookup,
|
||||
/// which scans the connection's objects instead of trusting FirstObject (see NetworkPlayerLookup).
|
||||
/// </summary>
|
||||
private PlayerInventory ResolveInventory(NetworkConnection conn)
|
||||
{
|
||||
NetworkObject playerObject = conn != null ? conn.FirstObject : null;
|
||||
return playerObject != null ? playerObject.GetComponent<PlayerInventory>() : null;
|
||||
}
|
||||
private PlayerInventory ResolveInventory(NetworkConnection conn) => NetworkPlayerLookup.ResolveInventory(conn);
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -11,9 +11,23 @@ namespace Ashwild.Crafting
|
||||
/// from recipe data, hands it the player-dependent queries, and refreshes it whenever the
|
||||
/// local player's inventory changes. The UI never reaches back into the player or this class.
|
||||
/// Keep this and CraftingUI as stable scene objects so the reference can't break.
|
||||
///
|
||||
/// It also owns the crafting *context* — bare hands or a station. Opening the panel from a
|
||||
/// <see cref="CraftingStation"/> widens the visible catalog to that station's recipes for as long as
|
||||
/// the window stays open; closing it drops back to hand crafting.
|
||||
/// </summary>
|
||||
public class CraftingManager : MonoBehaviour
|
||||
{
|
||||
#region Singleton
|
||||
|
||||
/// <summary>
|
||||
/// The scene's crafting manager, so a world station can hand itself over without a
|
||||
/// FindObjectOfType. Mirrors BuildManager.
|
||||
/// </summary>
|
||||
public static CraftingManager Instance { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Serialized Fields
|
||||
|
||||
[Header("Data")]
|
||||
@@ -34,6 +48,12 @@ namespace Ashwild.Crafting
|
||||
private bool bound;
|
||||
private readonly HashSet<CraftingRecipe> unlockedRecipes = new HashSet<CraftingRecipe>();
|
||||
|
||||
/// <summary>
|
||||
/// The station the panel is currently open on, or None while the player crafts bare-handed
|
||||
/// (the inventory opened with the normal key). Reset whenever the window closes.
|
||||
/// </summary>
|
||||
private CraftingStationType activeStation = CraftingStationType.None;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public API
|
||||
@@ -43,37 +63,64 @@ namespace Ashwild.Crafting
|
||||
/// </summary>
|
||||
public CraftingRecipe[] Recipes => recipes;
|
||||
|
||||
/// <summary>
|
||||
/// Opens the crafting panel on the given station: its recipes join the catalog and the inventory
|
||||
/// window opens straight on the craft tab. The context lasts until the window closes.
|
||||
/// Called by <see cref="CraftingStation.Interact"/> on the interacting client only.
|
||||
/// </summary>
|
||||
public void OpenStation(CraftingStation station)
|
||||
{
|
||||
if (station == null) return;
|
||||
|
||||
if (InventoryUI.Instance == null)
|
||||
{
|
||||
Debug.LogError("[CraftingManager] No InventoryUI in the scene — cannot open the crafting panel.", this);
|
||||
return;
|
||||
}
|
||||
|
||||
activeStation = station.StationType;
|
||||
InventoryUI.Instance.OpenAtCategory(InventoryCategory.Crafting);
|
||||
|
||||
if (craftingUI != null)
|
||||
craftingUI.Refresh();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the local player can currently craft the recipe the given number of times.
|
||||
/// A recipe is only craftable once every ingredient has been discovered, on top of the usual
|
||||
/// inventory check — an ingredient still shown as a mystery ("?") blocks the craft.
|
||||
/// A recipe is only craftable once every ingredient has been discovered and the right station is
|
||||
/// open, on top of the usual inventory check — an ingredient still shown as a mystery ("?")
|
||||
/// blocks the craft.
|
||||
/// </summary>
|
||||
public bool CanCraft(CraftingRecipe recipe, int count = 1)
|
||||
{
|
||||
return PlayerInventory.Instance != null
|
||||
&& IsStationAvailable(recipe)
|
||||
&& IsFullyDiscovered(recipe)
|
||||
&& recipe.CanCraft(PlayerInventory.Instance, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Consumes the ingredients and produces the result if the local player can afford it and has
|
||||
/// discovered every ingredient. The result item is added through the inventory, which in turn
|
||||
/// discovers it — so crafting an intermediate result reveals the recipes further down the chain.
|
||||
/// Consumes the ingredients and produces the result if the local player can afford it, has
|
||||
/// discovered every ingredient, and is at the station the recipe requires. The result item is
|
||||
/// added through the inventory, which in turn discovers it — so crafting an intermediate result
|
||||
/// reveals the recipes further down the chain.
|
||||
///
|
||||
/// The station is re-checked here rather than trusted from the visible list: the panel could
|
||||
/// have been left open through a context change, and a bench recipe must never be craftable
|
||||
/// bare-handed.
|
||||
/// </summary>
|
||||
public bool TryCraft(CraftingRecipe recipe, int count = 1)
|
||||
{
|
||||
PlayerInventory inv = PlayerInventory.Instance;
|
||||
if (inv == null || !IsFullyDiscovered(recipe) || !recipe.CanCraft(inv, count))
|
||||
if (inv == null || !IsStationAvailable(recipe) || !IsFullyDiscovered(recipe) || !recipe.CanCraft(inv, count))
|
||||
return false;
|
||||
|
||||
// Consume ingredients
|
||||
for (int i = 0; i < recipe.Ingredients.Length; i++)
|
||||
{
|
||||
CraftingIngredient ingredient = recipe.Ingredients[i];
|
||||
inv.RemoveItemByData(ingredient.item, ingredient.quantity * count);
|
||||
}
|
||||
|
||||
// Produce result
|
||||
inv.AddItem(recipe.ResultItem, recipe.ResultQuantity * count);
|
||||
|
||||
onCraftSuccess?.Invoke(recipe);
|
||||
@@ -85,12 +132,22 @@ namespace Ashwild.Crafting
|
||||
#region Unity Lifecycle
|
||||
|
||||
/// <summary>
|
||||
/// Subscribes to the local player spawn so we can bind to its inventory.
|
||||
/// Registers the singleton so world stations can reach the manager.
|
||||
/// </summary>
|
||||
private void Awake()
|
||||
{
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribes to the local player spawn so we can bind to its inventory, and to the window's
|
||||
/// open state so the station context is dropped when it closes.
|
||||
/// </summary>
|
||||
private void OnEnable()
|
||||
{
|
||||
PlayerEvents.LocalPlayerSpawned += HandleLocalPlayerSpawned;
|
||||
PlayerEvents.ItemDiscovered += HandleItemDiscovered;
|
||||
PlayerEvents.InventoryOpenChanged += HandleInventoryOpenChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -100,6 +157,7 @@ namespace Ashwild.Crafting
|
||||
{
|
||||
PlayerEvents.LocalPlayerSpawned -= HandleLocalPlayerSpawned;
|
||||
PlayerEvents.ItemDiscovered -= HandleItemDiscovered;
|
||||
PlayerEvents.InventoryOpenChanged -= HandleInventoryOpenChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -118,12 +176,14 @@ namespace Ashwild.Crafting
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops listening to the inventory when destroyed.
|
||||
/// Stops listening to the inventory and clears the singleton when destroyed.
|
||||
/// </summary>
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (inventory != null)
|
||||
inventory.onSlotChanged.RemoveListener(HandleInventoryChanged);
|
||||
|
||||
if (Instance == this) Instance = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -157,6 +217,21 @@ namespace Ashwild.Crafting
|
||||
craftingUI.Refresh();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Drops the station context when the inventory window closes, so the next hand-opened panel is
|
||||
/// back to hand-only recipes. Tying this to the window rather than to a distance check keeps a
|
||||
/// single, unambiguous rule: the station lasts exactly as long as the panel it opened.
|
||||
/// </summary>
|
||||
private void HandleInventoryOpenChanged(bool open)
|
||||
{
|
||||
if (open || activeStation == CraftingStationType.None) return;
|
||||
|
||||
activeStation = CraftingStationType.None;
|
||||
|
||||
if (craftingUI != null)
|
||||
craftingUI.Refresh();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Helpers
|
||||
@@ -187,13 +262,17 @@ namespace Ashwild.Crafting
|
||||
private void SeedUnlockedRecipes()
|
||||
{
|
||||
for (int i = 0; i < recipes.Length; i++)
|
||||
if (IsRecipeVisible(recipes[i]))
|
||||
if (IsDiscoveryRevealed(recipes[i]))
|
||||
unlockedRecipes.Add(recipes[i]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises a "NEW" notification for every recipe that has just become visible (its first
|
||||
/// Raises a "NEW" notification for every recipe that has just been revealed (its first
|
||||
/// discovered ingredient), each announced once. Called after an item is discovered.
|
||||
///
|
||||
/// Deliberately keyed on discovery alone, not on the station filter: a bench recipe must still
|
||||
/// announce itself the moment its ingredient is found — that is the cue telling the player the
|
||||
/// bench is worth building — instead of silently waiting for the bench to be opened.
|
||||
/// </summary>
|
||||
private void NotifyNewlyUnlockedRecipes()
|
||||
{
|
||||
@@ -201,7 +280,7 @@ namespace Ashwild.Crafting
|
||||
{
|
||||
CraftingRecipe recipe = recipes[i];
|
||||
if (unlockedRecipes.Contains(recipe)) continue;
|
||||
if (!IsRecipeVisible(recipe)) continue;
|
||||
if (!IsDiscoveryRevealed(recipe)) continue;
|
||||
|
||||
unlockedRecipes.Add(recipe);
|
||||
PlayerEvents.RaiseRecipeDiscovered(recipe.Icon, recipe.RecipeName);
|
||||
@@ -225,10 +304,21 @@ namespace Ashwild.Crafting
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the recipe should appear at all: revealed as soon as at least one of its ingredients
|
||||
/// has been discovered. Undiscovered ingredients then show as a mystery until found.
|
||||
/// Whether the recipe should appear in the list right now: revealed by discovery AND allowed by
|
||||
/// the station the panel is open on. The two criteria stay separate helpers on purpose —
|
||||
/// discovery is permanent player progress, the station is a transient context — and only their
|
||||
/// conjunction drives what the player sees.
|
||||
/// </summary>
|
||||
private bool IsRecipeVisible(CraftingRecipe recipe)
|
||||
{
|
||||
return IsStationAvailable(recipe) && IsDiscoveryRevealed(recipe);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the recipe has been revealed by discovery: true as soon as at least one of its
|
||||
/// ingredients has been found. Undiscovered ingredients then show as a mystery until found.
|
||||
/// </summary>
|
||||
private bool IsDiscoveryRevealed(CraftingRecipe recipe)
|
||||
{
|
||||
CraftingIngredient[] ingredients = recipe.Ingredients;
|
||||
for (int i = 0; i < ingredients.Length; i++)
|
||||
@@ -236,6 +326,17 @@ namespace Ashwild.Crafting
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the recipe's required station matches the current context: a recipe requiring none is
|
||||
/// craftable everywhere (bare hands and every station alike), any other one only on a station of
|
||||
/// its exact type.
|
||||
/// </summary>
|
||||
private bool IsStationAvailable(CraftingRecipe recipe)
|
||||
{
|
||||
return recipe.RequiredStation == CraftingStationType.None
|
||||
|| recipe.RequiredStation == activeStation;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether every ingredient of the recipe has been discovered — the gate for actually crafting it.
|
||||
/// </summary>
|
||||
|
||||
@@ -3,6 +3,11 @@ using Ashwild.Inventory;
|
||||
|
||||
namespace Ashwild.Crafting
|
||||
{
|
||||
/// <summary>
|
||||
/// Authoring data for one craftable result: what it produces, what it costs, and where it can be
|
||||
/// crafted. Pure data — the craft itself is performed by CraftingManager, which owns the player
|
||||
/// and discovery rules on top of what is declared here.
|
||||
/// </summary>
|
||||
[CreateAssetMenu(fileName = "NewRecipe", menuName = "Items/Crafting Recipe")]
|
||||
public class CraftingRecipe : ScriptableObject
|
||||
{
|
||||
@@ -11,6 +16,10 @@ namespace Ashwild.Crafting
|
||||
[SerializeField] private ItemData resultItem;
|
||||
[SerializeField] private int resultQuantity = 1;
|
||||
|
||||
[Header("Station")]
|
||||
[Tooltip("None = craftable bare-handed AND on every station. Any other value restricts this recipe to that station.")]
|
||||
[SerializeField] private CraftingStationType requiredStation = CraftingStationType.None;
|
||||
|
||||
[Header("Ingredients")]
|
||||
[SerializeField] private CraftingIngredient[] ingredients;
|
||||
|
||||
@@ -20,6 +29,17 @@ namespace Ashwild.Crafting
|
||||
public int ResultQuantity => resultQuantity;
|
||||
public CraftingIngredient[] Ingredients => ingredients;
|
||||
|
||||
/// <summary>
|
||||
/// The station this recipe must be crafted on, or <see cref="CraftingStationType.None"/> when it
|
||||
/// needs none.
|
||||
/// </summary>
|
||||
public CraftingStationType RequiredStation => requiredStation;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the given inventory holds enough of every ingredient to craft this recipe the given
|
||||
/// number of times. Purely a resource check — discovery and station rules are enforced by
|
||||
/// CraftingManager on top of it.
|
||||
/// </summary>
|
||||
public bool CanCraft(PlayerInventory inventory, int count = 1)
|
||||
{
|
||||
for (int i = 0; i < ingredients.Length; i++)
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
using UnityEngine;
|
||||
using Ashwild.Interaction;
|
||||
|
||||
namespace Ashwild.Crafting
|
||||
{
|
||||
/// <summary>
|
||||
/// A world workstation the player interacts with to open the crafting panel with this station's
|
||||
/// recipes unlocked. It declares which station it is and nothing more: the recipe catalog, the
|
||||
/// filtering and the craft itself all stay in <see cref="CraftingManager"/>, which this simply hands
|
||||
/// the station type to.
|
||||
///
|
||||
/// Multiplayer: deliberately a plain MonoBehaviour, NOT a NetworkBehaviour. Unlike a chest or a
|
||||
/// cooking station it owns no shared state — a craft only consumes and produces in the crafting
|
||||
/// player's own (client-authoritative) inventory — so two players using the same bench at once can
|
||||
/// never clobber each other and there is strictly nothing to replicate. The object it sits on is
|
||||
/// still a networked build (it carries BuiltStructure like any placed structure); this component
|
||||
/// just does not participate in that.
|
||||
/// </summary>
|
||||
[DisallowMultipleComponent]
|
||||
public class CraftingStation : MonoBehaviour, IInteractable
|
||||
{
|
||||
#region Serialized Fields
|
||||
|
||||
[Header("Station")]
|
||||
[Tooltip("Recipes requiring this exact station become craftable while its panel is open.")]
|
||||
[SerializeField] private CraftingStationType stationType = CraftingStationType.Workbench;
|
||||
|
||||
[Tooltip("Name shown in the interaction prompt.")]
|
||||
[SerializeField] private string displayName = "Établi";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public API
|
||||
|
||||
/// <summary>
|
||||
/// Which station this is — the key CraftingManager filters recipes against.
|
||||
/// </summary>
|
||||
public CraftingStationType StationType => stationType;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity Lifecycle
|
||||
|
||||
/// <summary>
|
||||
/// Warns early when the station is left on None, which would unlock nothing beyond hand crafting
|
||||
/// and read as a broken bench in game.
|
||||
/// </summary>
|
||||
private void Awake()
|
||||
{
|
||||
if (stationType == CraftingStationType.None)
|
||||
Debug.LogError($"[CraftingStation] '{name}' has its station type left on None — it will unlock no recipe.", this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IInteractable
|
||||
|
||||
/// <summary>
|
||||
/// Prompt shown while aiming at the station.
|
||||
/// </summary>
|
||||
public string InteractionPrompt => $"Utiliser {displayName}";
|
||||
|
||||
/// <summary>
|
||||
/// Opens the crafting panel bound to this station. Runs on the interacting client only — nothing
|
||||
/// here is replicated, every player browses their own panel.
|
||||
/// </summary>
|
||||
public void Interact()
|
||||
{
|
||||
if (CraftingManager.Instance == null)
|
||||
{
|
||||
Debug.LogError("[CraftingStation] No CraftingManager in the scene — cannot open the crafting panel.", this);
|
||||
return;
|
||||
}
|
||||
|
||||
CraftingManager.Instance.OpenStation(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3323e66aad475444191d473bbba8673a
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace Ashwild.Crafting
|
||||
{
|
||||
/// <summary>
|
||||
/// Which workstation a recipe needs to be crafted on. Deliberately an enum rather than a bool so a
|
||||
/// second station (forge, alchemy bench, ...) is a new value instead of a data migration on every
|
||||
/// existing recipe.
|
||||
///
|
||||
/// <see cref="None"/> is the default (value 0) so every recipe authored before this existed stays
|
||||
/// hand-craftable, and it means "no station required" — such a recipe is craftable bare-handed *and*
|
||||
/// on every station. Any other value restricts the recipe to a station of that exact type.
|
||||
/// </summary>
|
||||
public enum CraftingStationType
|
||||
{
|
||||
None = 0,
|
||||
Workbench = 1,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0587c6bc97def02449cfde6af1d03815
|
||||
@@ -1,121 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Profiling;
|
||||
using Ashwild.Building;
|
||||
using Debug = UnityEngine.Debug;
|
||||
|
||||
namespace Ashwild.EditorTools
|
||||
{
|
||||
/// <summary>
|
||||
/// A load test for the decision to spawn every build as a NetworkObject: places a grid of structures
|
||||
/// and reports how long it took and what it cost in memory, so the "can FishNet carry a whole base"
|
||||
/// question can be answered with numbers instead of estimates.
|
||||
///
|
||||
/// Deliberately single-instance. Measuring a second client's join bandwidth would mean two peers, and
|
||||
/// the session is wired to FishySteamworks — that costs far more setup than the risk it covers. Join
|
||||
/// traffic is a one-shot cost of roughly a spawn packet per object anyway; what actually matters day
|
||||
/// to day is the steady state, and that is exactly what this measures. Watch the profiler's frame time
|
||||
/// after the spawn, not just the numbers logged here.
|
||||
///
|
||||
/// Caveat when reading the elapsed time: this places everything in a single frame, which no player
|
||||
/// ever does. Treat it as a worst case for the spawn burst, not as a gameplay measurement.
|
||||
/// </summary>
|
||||
public static class BuildStressTest
|
||||
{
|
||||
#region Constants
|
||||
|
||||
private const float Spacing = 4f;
|
||||
private const float GroundHeight = 0f;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Menu
|
||||
|
||||
/// <summary>
|
||||
/// Places 250 structures — a realistic co-op base.
|
||||
/// </summary>
|
||||
[MenuItem("Tools/Ashwild/Stress Test/Spawn 250 Builds")]
|
||||
public static void Spawn250() => SpawnGrid(250);
|
||||
|
||||
/// <summary>
|
||||
/// Places 1000 structures — an ambitious long-save base, the level the NetworkObject decision was
|
||||
/// judged against.
|
||||
/// </summary>
|
||||
[MenuItem("Tools/Ashwild/Stress Test/Spawn 1000 Builds")]
|
||||
public static void Spawn1000() => SpawnGrid(1000);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Commits <paramref name="count"/> builds of the first usable buildable on a grid around the
|
||||
/// origin, then logs the elapsed time and the memory delta. Requires play mode with a live session,
|
||||
/// since committing goes through the registry's server RPC exactly as a real placement would.
|
||||
/// </summary>
|
||||
private static void SpawnGrid(int count)
|
||||
{
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
Debug.LogError("[BuildStressTest] Enter play mode and host a session first — builds are spawned through the server.");
|
||||
return;
|
||||
}
|
||||
if (BuildRegistry.Instance == null)
|
||||
{
|
||||
Debug.LogError("[BuildStressTest] No BuildRegistry in the session — is the scene's BuildRegistry present and the session started?");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TryResolveBuildable(out ushort id, out string label)) return;
|
||||
|
||||
int side = Mathf.CeilToInt(Mathf.Sqrt(count));
|
||||
long memoryBefore = Profiler.GetTotalAllocatedMemoryLong();
|
||||
Stopwatch watch = Stopwatch.StartNew();
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
Vector3 position = new Vector3((i % side) * Spacing, GroundHeight, (i / side) * Spacing);
|
||||
BuildRegistry.Instance.RequestBuild(id, position, Quaternion.identity);
|
||||
}
|
||||
|
||||
watch.Stop();
|
||||
long memoryDelta = Profiler.GetTotalAllocatedMemoryLong() - memoryBefore;
|
||||
|
||||
Debug.Log($"[BuildStressTest] Requested {count}× '{label}' in {watch.ElapsedMilliseconds} ms " +
|
||||
$"({memoryDelta / 1024f / 1024f:F1} MB allocated this frame). " +
|
||||
"Spawns complete over the next frames — check the profiler's steady-state frame time now.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Picks the first buildable that can actually be spawned, so the test never fails halfway on a
|
||||
/// half-authored asset. Reports clearly when the database is empty or unbuilt.
|
||||
/// </summary>
|
||||
private static bool TryResolveBuildable(out ushort id, out string label)
|
||||
{
|
||||
id = 0;
|
||||
label = string.Empty;
|
||||
|
||||
BuildableDatabase database = BuildableDatabase.Instance;
|
||||
if (database == null || database.Buildables == null || database.Buildables.Length == 0)
|
||||
{
|
||||
Debug.LogError("[BuildStressTest] BuildableDatabase is empty — run Tools ▸ Ashwild ▸ Rebuild Buildable Database.");
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (BuildableData buildable in database.Buildables)
|
||||
{
|
||||
if (buildable == null || buildable.BuiltPrefab == null) continue;
|
||||
|
||||
id = database.GetId(buildable);
|
||||
label = buildable.name;
|
||||
return true;
|
||||
}
|
||||
|
||||
Debug.LogError("[BuildStressTest] No buildable with a BuiltPrefab found.");
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dfaebf3f0f14ca94e803a73cc00965f7
|
||||
@@ -1,109 +0,0 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using Ashwild.Building;
|
||||
using FishNet.Object;
|
||||
|
||||
namespace Ashwild.EditorTools
|
||||
{
|
||||
/// <summary>
|
||||
/// One-shot migration for the move to server-spawned builds: every committed structure is now a real
|
||||
/// NetworkObject, so each buildable's BuiltPrefab needs a NetworkObject and a BuiltStructure on its
|
||||
/// root. Prefabs authored before that change have neither (or only BuiltStructure, which is now a
|
||||
/// NetworkBehaviour and cannot function alone), and a missing NetworkObject makes the prefab silently
|
||||
/// unspawnable — the exact failure that used to leave a placed chest deactivated.
|
||||
///
|
||||
/// Safe to re-run: it only touches prefabs that are actually missing a component, and reports what it
|
||||
/// changed. Kept as an explicit menu action rather than an automatic postprocessor because it rewrites
|
||||
/// authored assets, which should never happen behind the designer's back.
|
||||
/// </summary>
|
||||
public static class BuiltPrefabMigrator
|
||||
{
|
||||
#region Menu
|
||||
|
||||
/// <summary>
|
||||
/// Scans every BuildableData, ensures its BuiltPrefab carries NetworkObject + BuiltStructure, and
|
||||
/// asks FishNet to rescan its spawnable prefab collection afterwards.
|
||||
/// </summary>
|
||||
[MenuItem("Tools/Ashwild/Migrate Built Prefabs To Network Objects")]
|
||||
public static void Migrate()
|
||||
{
|
||||
string[] guids = AssetDatabase.FindAssets("t:BuildableData");
|
||||
int migrated = 0;
|
||||
int alreadyFine = 0;
|
||||
int missingPrefab = 0;
|
||||
|
||||
foreach (string guid in guids)
|
||||
{
|
||||
BuildableData buildable = AssetDatabase.LoadAssetAtPath<BuildableData>(AssetDatabase.GUIDToAssetPath(guid));
|
||||
if (buildable == null) continue;
|
||||
|
||||
if (buildable.BuiltPrefab == null)
|
||||
{
|
||||
Debug.LogWarning($"[BuiltPrefabMigrator] '{buildable.name}' has no BuiltPrefab — skipped.", buildable);
|
||||
missingPrefab++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (EnsureNetworked(buildable.BuiltPrefab)) migrated++;
|
||||
else alreadyFine++;
|
||||
}
|
||||
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
RefreshNetworkPrefabRegistry();
|
||||
|
||||
Debug.Log($"[BuiltPrefabMigrator] Done — {migrated} prefab(s) migrated, {alreadyFine} already correct, {missingPrefab} buildable(s) without a BuiltPrefab.");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Adds whatever the prefab root is missing and saves it. The NetworkObject goes on first so that
|
||||
/// BuiltStructure's RequireComponent is already satisfied when it is added. Returns true when the
|
||||
/// prefab was actually changed.
|
||||
/// </summary>
|
||||
private static bool EnsureNetworked(GameObject prefab)
|
||||
{
|
||||
string path = AssetDatabase.GetAssetPath(prefab);
|
||||
if (string.IsNullOrEmpty(path)) return false;
|
||||
|
||||
GameObject root = PrefabUtility.LoadPrefabContents(path);
|
||||
bool changed = false;
|
||||
|
||||
if (root.GetComponent<NetworkObject>() == null)
|
||||
{
|
||||
root.AddComponent<NetworkObject>();
|
||||
changed = true;
|
||||
}
|
||||
if (root.GetComponent<BuiltStructure>() == null)
|
||||
{
|
||||
root.AddComponent<BuiltStructure>();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed)
|
||||
{
|
||||
PrefabUtility.SaveAsPrefabAsset(root, path);
|
||||
Debug.Log($"[BuiltPrefabMigrator] Migrated '{path}'.", AssetDatabase.LoadAssetAtPath<GameObject>(path));
|
||||
}
|
||||
|
||||
PrefabUtility.UnloadPrefabContents(root);
|
||||
return changed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Forces FishNet to rescan DefaultPrefabObjects so the migrated prefabs are spawnable. Invoked
|
||||
/// through the menu item because the generator API is internal to the FishNet assembly.
|
||||
/// </summary>
|
||||
private static void RefreshNetworkPrefabRegistry()
|
||||
{
|
||||
const string menu = "Tools/Fish-Networking/Utility/Refresh Default Prefabs";
|
||||
if (!EditorApplication.ExecuteMenuItem(menu))
|
||||
Debug.LogWarning($"[BuiltPrefabMigrator] Could not run '{menu}' — run it by hand so the built prefabs are registered as spawnable.");
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c1d18a05426439d47937c24568677913
|
||||
@@ -59,11 +59,6 @@
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.ash-search {
|
||||
width: 180px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
/* ── Body split ──────────────────────────────────────────── */
|
||||
.ash-body {
|
||||
flex-direction: row;
|
||||
@@ -81,6 +76,57 @@
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
/* ── Left-pane filter header (search + type) ─────────────── */
|
||||
.ash-listheader {
|
||||
padding-top: 6px;
|
||||
padding-bottom: 6px;
|
||||
padding-left: 6px;
|
||||
padding-right: 6px;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-color: rgb(18, 19, 22);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ash-listsearch {
|
||||
margin: 0;
|
||||
width: auto;
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
height: 22px;
|
||||
border-radius: 4px;
|
||||
background-color: rgb(46, 47, 52);
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
.ash-listsearch .unity-toolbar-search-field__search-button {
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
.ash-listsearch .unity-base-text-field__input {
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
.ash-typefilter {
|
||||
margin-top: 6px;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.ash-typefilter .unity-base-field__label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ash-typefilter .unity-base-popup-field__input {
|
||||
border-radius: 4px;
|
||||
background-color: rgb(46, 47, 52);
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
.ash-right {
|
||||
flex-grow: 1;
|
||||
padding: 12px;
|
||||
@@ -277,6 +323,15 @@
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
/* Small italic note clarifying a field inside a card */
|
||||
.ash-card__hint {
|
||||
margin-top: 6px;
|
||||
font-size: 10px;
|
||||
color: rgb(130, 132, 140);
|
||||
-unity-font-style: italic;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.ash-description .unity-text-field__input {
|
||||
min-height: 48px;
|
||||
white-space: normal;
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace Ashwild.EditorTools
|
||||
#region Constants
|
||||
|
||||
private const string UssPath = "Assets/GAME/Script/Editor/Database/AshwildDatabase.uss";
|
||||
private const string AllTypesLabel = "All Types";
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -38,6 +39,7 @@ namespace Ashwild.EditorTools
|
||||
|
||||
private Tab activeTab = Tab.Items;
|
||||
private string searchFilter = string.Empty;
|
||||
private string itemTypeFilter = AllTypesLabel;
|
||||
|
||||
private readonly List<Object> sourceItems = new List<Object>();
|
||||
private Object selectedAsset;
|
||||
@@ -51,6 +53,7 @@ namespace Ashwild.EditorTools
|
||||
private Button itemsTab;
|
||||
private Button recipesTab;
|
||||
private Button buildablesTab;
|
||||
private PopupField<string> typeFilterField;
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -106,7 +109,8 @@ namespace Ashwild.EditorTools
|
||||
#region Toolbar
|
||||
|
||||
/// <summary>
|
||||
/// Builds the top toolbar: the two family tabs, a search field, and the database rebuild action.
|
||||
/// Builds the top toolbar: the three family tabs and the database rebuild action. The search
|
||||
/// field and type filter live in the left-pane header, directly above the list they filter.
|
||||
/// </summary>
|
||||
private VisualElement BuildToolbar()
|
||||
{
|
||||
@@ -124,15 +128,6 @@ namespace Ashwild.EditorTools
|
||||
spacer.AddToClassList("ash-toolbar-spacer");
|
||||
toolbar.Add(spacer);
|
||||
|
||||
ToolbarSearchField search = new ToolbarSearchField();
|
||||
search.AddToClassList("ash-search");
|
||||
search.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
searchFilter = evt.newValue ?? string.Empty;
|
||||
RefreshList();
|
||||
});
|
||||
toolbar.Add(search);
|
||||
|
||||
Button rebuild = new Button(ItemDatabaseBuilder.Rebuild) { text = "Rebuild DB" };
|
||||
rebuild.AddToClassList("ash-btn");
|
||||
rebuild.tooltip = "Re-scan every ItemData and rewrite the network ItemDatabase.";
|
||||
@@ -175,6 +170,8 @@ namespace Ashwild.EditorTools
|
||||
recipesTab.EnableInClassList("ash-tab--active", tab == Tab.Recipes);
|
||||
buildablesTab.EnableInClassList("ash-tab--active", tab == Tab.Buildables);
|
||||
|
||||
UpdateTypeFilterVisibility();
|
||||
|
||||
selectedAsset = null;
|
||||
RefreshList();
|
||||
ShowSelection();
|
||||
@@ -185,13 +182,16 @@ namespace Ashwild.EditorTools
|
||||
#region Left Pane
|
||||
|
||||
/// <summary>
|
||||
/// Builds the left pane: a styled list of the active family plus a footer "New" button.
|
||||
/// Builds the left pane: a filter header (search + item-type filter), a styled list of the
|
||||
/// active family, and a footer "New" button.
|
||||
/// </summary>
|
||||
private VisualElement BuildLeftPane()
|
||||
{
|
||||
VisualElement left = new VisualElement();
|
||||
left.AddToClassList("ash-left");
|
||||
|
||||
left.Add(BuildListHeader());
|
||||
|
||||
listView = new ListView(sourceItems)
|
||||
{
|
||||
fixedItemHeight = 48,
|
||||
@@ -214,6 +214,58 @@ namespace Ashwild.EditorTools
|
||||
return left;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the header sitting above the list: a search field that filters by name across every
|
||||
/// family, and an item-type dropdown that narrows the item list to a single type. The type
|
||||
/// filter is only meaningful for items, so it is hidden on the Recipes and Buildables tabs.
|
||||
/// </summary>
|
||||
private VisualElement BuildListHeader()
|
||||
{
|
||||
VisualElement header = new VisualElement();
|
||||
header.AddToClassList("ash-listheader");
|
||||
|
||||
ToolbarSearchField search = new ToolbarSearchField();
|
||||
search.AddToClassList("ash-listsearch");
|
||||
search.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
searchFilter = evt.newValue ?? string.Empty;
|
||||
RefreshList();
|
||||
});
|
||||
header.Add(search);
|
||||
|
||||
List<string> typeChoices = new List<string> { AllTypesLabel };
|
||||
typeChoices.AddRange(System.Enum.GetNames(typeof(ItemType)));
|
||||
|
||||
typeFilterField = new PopupField<string>(typeChoices, 0);
|
||||
typeFilterField.AddToClassList("ash-typefilter");
|
||||
typeFilterField.tooltip = "Show only items of the selected type.";
|
||||
typeFilterField.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
itemTypeFilter = evt.newValue ?? AllTypesLabel;
|
||||
RefreshList();
|
||||
});
|
||||
header.Add(typeFilterField);
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows the item-type dropdown only on the Items tab and resets it to "all" when leaving, so a
|
||||
/// stale type filter never silently narrows a list it cannot apply to.
|
||||
/// </summary>
|
||||
private void UpdateTypeFilterVisibility()
|
||||
{
|
||||
if (typeFilterField == null) return;
|
||||
|
||||
bool showForItems = activeTab == Tab.Items;
|
||||
typeFilterField.style.display = showForItems ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
if (!showForItems)
|
||||
{
|
||||
itemTypeFilter = AllTypesLabel;
|
||||
typeFilterField.SetValueWithoutNotify(AllTypesLabel);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the reusable visual for a single list row (icon, name, type tag).
|
||||
/// </summary>
|
||||
@@ -251,6 +303,7 @@ namespace Ashwild.EditorTools
|
||||
{
|
||||
ItemData item => AshwildUI.TypeColor(item.ItemType),
|
||||
BuildableData => AshwildUI.BuildableColor,
|
||||
CraftingRecipe recipe when recipe.RequiredStation != CraftingStationType.None => AshwildUI.StationColor,
|
||||
_ => new Color(0.5f, 0.52f, 0.58f)
|
||||
};
|
||||
|
||||
@@ -421,6 +474,9 @@ namespace Ashwild.EditorTools
|
||||
if (!string.IsNullOrEmpty(searchFilter)
|
||||
&& AssetDisplayName(asset).IndexOf(searchFilter, System.StringComparison.OrdinalIgnoreCase) < 0)
|
||||
continue;
|
||||
if (activeTab == Tab.Items && itemTypeFilter != AllTypesLabel
|
||||
&& asset is ItemData item && item.ItemType.ToString() != itemTypeFilter)
|
||||
continue;
|
||||
sourceItems.Add(asset);
|
||||
}
|
||||
|
||||
@@ -465,12 +521,15 @@ namespace Ashwild.EditorTools
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The short type tag shown on the right of a list row (item type, or "Recipe").
|
||||
/// The short type tag shown on the right of a list row. A recipe reports the station it is
|
||||
/// crafted on rather than a flat "Recipe", so scanning the list tells which recipes are bench-
|
||||
/// locked; hand recipes (station None) keep reading "Recipe".
|
||||
/// </summary>
|
||||
private static string AssetTag(Object asset)
|
||||
{
|
||||
if (asset is ItemData item) return item.ItemType.ToString();
|
||||
if (asset is CraftingRecipe) return "Recipe";
|
||||
if (asset is CraftingRecipe recipe)
|
||||
return recipe.RequiredStation == CraftingStationType.None ? "Recipe" : recipe.RequiredStation.ToString();
|
||||
if (asset is BuildableData) return "Buildable";
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,12 @@ namespace Ashwild.EditorTools
|
||||
/// </summary>
|
||||
public static readonly Color BuildableColor = new Color(0.38f, 0.64f, 0.86f);
|
||||
|
||||
/// <summary>
|
||||
/// The accent colour marking a recipe that is locked to a workstation, so the bench-only ones
|
||||
/// stand out from the plain (hand-craftable) rows at a glance.
|
||||
/// </summary>
|
||||
public static readonly Color StationColor = new Color(0.76f, 0.60f, 0.90f);
|
||||
|
||||
/// <summary>
|
||||
/// The accent colour that identifies an item type across the list badges, hero and cards.
|
||||
/// </summary>
|
||||
|
||||
@@ -10,11 +10,13 @@ namespace Ashwild.EditorTools
|
||||
/// <summary>
|
||||
/// Editor-only factory that creates ItemData assets and, on demand, builds the world pickup
|
||||
/// prefab (Pickable + collider on the Pickable layer) and the in-hand prefab (ToolBehaviour +
|
||||
/// HeldItemOffset on the Tools layer) by wrapping a chosen source object — a model (FBX) or an
|
||||
/// existing prefab. The source is nested as a child so the designer's authored model stays intact
|
||||
/// and editable, while the generated root carries the gameplay components and a collider auto-fit
|
||||
/// to the source's renderers. Every reference is wired back onto the ItemData. Follows §3 of the
|
||||
/// project rules: world pickups are plain WorldObjects (registry-synced), never NetworkObjects.
|
||||
/// HeldItemOffset on the Tools layer) as a Prefab Variant of a chosen source prefab. The designer
|
||||
/// authors one base prefab with its meshes and materials set up (the raw FBX comes in untextured),
|
||||
/// hands it to the generator, and each generated prefab becomes a variant that carries the gameplay
|
||||
/// components as overrides on its own root — so re-texturing the base propagates to both variants.
|
||||
/// A non-prefab source falls back to a plain duplicate. Every reference is wired back onto the
|
||||
/// ItemData. Follows §3 of the project rules: world pickups are plain WorldObjects (registry-synced),
|
||||
/// never NetworkObjects.
|
||||
/// </summary>
|
||||
public static class ItemAssetFactory
|
||||
{
|
||||
@@ -24,6 +26,9 @@ namespace Ashwild.EditorTools
|
||||
private const string WorldPrefabFolder = "Assets/GAME/Prefabs/Pickable";
|
||||
private const string HandPrefabFolder = "Assets/GAME/Prefabs/Tools";
|
||||
|
||||
private const string WorldPrefabSuffix = "_Pickable";
|
||||
private const string HandPrefabSuffix = "_Tools";
|
||||
|
||||
private const string PickableLayerName = "Pickable";
|
||||
private const string ToolsLayerName = "Tools";
|
||||
private const string HarvestableLayerName = "Harvestable";
|
||||
@@ -65,19 +70,20 @@ namespace Ashwild.EditorTools
|
||||
#region World Prefab
|
||||
|
||||
/// <summary>
|
||||
/// Builds the world pickup prefab for an item by wrapping the source object: a root on the
|
||||
/// Pickable layer carries the source as a child, an auto-fitted non-trigger BoxCollider (so the
|
||||
/// interactor ray — which reads the Pickable off the hit collider's own GameObject — has a
|
||||
/// target on the root), and the Pickable component wired to this item. The id stays -1; scene
|
||||
/// instances get a baked id later via Tools ▸ Ashwild ▸ Assign World Object IDs. The finished
|
||||
/// prefab is assigned back onto ItemData.worldPrefab. Returns the saved prefab, or null on error.
|
||||
/// Builds the world pickup prefab for an item as a variant of the source prefab: the variant root
|
||||
/// sits on the Pickable layer, gains an auto-fitted non-trigger BoxCollider (so the interactor ray
|
||||
/// — which reads the Pickable off the hit collider's own GameObject — has a target on the root),
|
||||
/// and a Pickable wired to this item. The id stays -1; scene instances get a baked id later via
|
||||
/// Tools ▸ Ashwild ▸ Setup World Object IDs. The finished prefab is assigned back onto
|
||||
/// ItemData.worldPrefab. Returns the saved prefab, or null on error.
|
||||
/// </summary>
|
||||
public static GameObject GenerateWorldPrefab(ItemData item, GameObject source)
|
||||
{
|
||||
if (!ValidateInputs(item, source, "world")) return null;
|
||||
if (!EnsureFolder(WorldPrefabFolder)) return null;
|
||||
|
||||
GameObject root = BuildRoot(item, source, ResolveLayer(PickableLayerName));
|
||||
string prefabName = item.name + WorldPrefabSuffix;
|
||||
GameObject root = InstantiateSourceRoot(source, ResolveLayer(PickableLayerName), prefabName);
|
||||
FitBoxCollider(root);
|
||||
|
||||
Pickable pickable = root.AddComponent<Pickable>();
|
||||
@@ -85,7 +91,7 @@ namespace Ashwild.EditorTools
|
||||
so.FindProperty("itemData").objectReferenceValue = item;
|
||||
so.ApplyModifiedPropertiesWithoutUndo();
|
||||
|
||||
return SaveAndAssign(root, WorldPrefabFolder, item, "worldPrefab");
|
||||
return SaveAndAssign(root, WorldPrefabFolder, item, "worldPrefab", prefabName);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -93,18 +99,19 @@ namespace Ashwild.EditorTools
|
||||
#region Hand Prefab
|
||||
|
||||
/// <summary>
|
||||
/// Builds the in-hand prefab for a tool/weapon by wrapping the source object: a root on the
|
||||
/// Tools layer carries the source as a child, a ToolBehaviour (harvest ray masked to the
|
||||
/// Harvestable layer) and a HeldItemOffset so it can be posed in the holder. No collider —
|
||||
/// held items carry no physics. The prefab is assigned back onto ItemData.handPrefab.
|
||||
/// Returns the prefab, or null on error.
|
||||
/// Builds the in-hand prefab for a tool/weapon as a variant of the source prefab: the variant
|
||||
/// root sits on the Tools layer and gains a ToolBehaviour (harvest ray masked to the Harvestable
|
||||
/// layer) and a HeldItemOffset so it can be posed in the holder. No collider — held items carry
|
||||
/// no physics. The prefab is assigned back onto ItemData.handPrefab. Returns the prefab, or null
|
||||
/// on error.
|
||||
/// </summary>
|
||||
public static GameObject GenerateHandPrefab(ItemData item, GameObject source)
|
||||
{
|
||||
if (!ValidateInputs(item, source, "hand")) return null;
|
||||
if (!EnsureFolder(HandPrefabFolder)) return null;
|
||||
|
||||
GameObject root = BuildRoot(item, source, ResolveLayer(ToolsLayerName));
|
||||
string prefabName = item.name + HandPrefabSuffix;
|
||||
GameObject root = InstantiateSourceRoot(source, ResolveLayer(ToolsLayerName), prefabName);
|
||||
|
||||
ToolBehaviour tool = root.AddComponent<ToolBehaviour>();
|
||||
SerializedObject so = new SerializedObject(tool);
|
||||
@@ -113,7 +120,7 @@ namespace Ashwild.EditorTools
|
||||
|
||||
root.AddComponent<HeldItemOffset>();
|
||||
|
||||
return SaveAndAssign(root, HandPrefabFolder, item, "handPrefab");
|
||||
return SaveAndAssign(root, HandPrefabFolder, item, "handPrefab", prefabName);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -139,18 +146,20 @@ namespace Ashwild.EditorTools
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the generated root named after the item, on the given layer, with the source object
|
||||
/// (model or prefab) nested as a child at the origin so its authored transform is preserved as
|
||||
/// a prefab link rather than flattened.
|
||||
/// Instantiates the source prefab itself as the generated root — keeping the prefab connection so
|
||||
/// SaveAndAssign turns it into a variant — then applies the target name and layer as variant
|
||||
/// overrides. A non-prefab source falls back to a plain (unlinked) copy, which SaveAndAssign then
|
||||
/// saves as a regular prefab rather than a variant.
|
||||
/// </summary>
|
||||
private static GameObject BuildRoot(ItemData item, GameObject source, int layer)
|
||||
private static GameObject InstantiateSourceRoot(GameObject source, int layer, string rootName)
|
||||
{
|
||||
GameObject root = new GameObject(item.name) { layer = layer };
|
||||
GameObject root = PrefabUtility.InstantiatePrefab(source) as GameObject;
|
||||
if (root == null) root = Object.Instantiate(source);
|
||||
|
||||
GameObject visual = (GameObject)PrefabUtility.InstantiatePrefab(source);
|
||||
if (visual == null) visual = Object.Instantiate(source);
|
||||
visual.transform.SetParent(root.transform, false);
|
||||
visual.transform.localPosition = Vector3.zero;
|
||||
root.name = rootName;
|
||||
root.layer = layer;
|
||||
root.transform.position = Vector3.zero;
|
||||
root.transform.rotation = Quaternion.identity;
|
||||
|
||||
return root;
|
||||
}
|
||||
@@ -179,12 +188,13 @@ namespace Ashwild.EditorTools
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves a built GameObject as a prefab under a unique path, destroys the scene instance,
|
||||
/// wires the saved prefab onto the given ItemData field, and refreshes the asset database.
|
||||
/// Saves a built GameObject as a prefab under a unique path (named with the type-specific suffix
|
||||
/// so pickables and hand tools stay distinguishable), destroys the scene instance, wires the
|
||||
/// saved prefab onto the given ItemData field, and refreshes the asset database.
|
||||
/// </summary>
|
||||
private static GameObject SaveAndAssign(GameObject root, string folder, ItemData item, string itemField)
|
||||
private static GameObject SaveAndAssign(GameObject root, string folder, ItemData item, string itemField, string prefabName)
|
||||
{
|
||||
string prefabPath = AssetDatabase.GenerateUniqueAssetPath($"{folder}/{item.name}.prefab");
|
||||
string prefabPath = AssetDatabase.GenerateUniqueAssetPath($"{folder}/{prefabName}.prefab");
|
||||
GameObject prefab = PrefabUtility.SaveAsPrefabAsset(root, prefabPath);
|
||||
Object.DestroyImmediate(root);
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEditor;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using Ashwild.Inventory;
|
||||
using Ashwild.Player;
|
||||
|
||||
namespace Ashwild.EditorTools
|
||||
{
|
||||
@@ -37,8 +40,6 @@ namespace Ashwild.EditorTools
|
||||
private VisualElement maxStackRow;
|
||||
private VisualElement fuelSecondsRow;
|
||||
private VisualElement generationCard;
|
||||
private Button worldGenButton;
|
||||
private Button handGenButton;
|
||||
private ObjectField worldField;
|
||||
private ObjectField handField;
|
||||
private IMGUIContainer iconPickerProxy;
|
||||
@@ -70,6 +71,7 @@ namespace Ashwild.EditorTools
|
||||
Root.Add(BuildCookingCard());
|
||||
Root.Add(BuildFuelCard());
|
||||
Root.Add(BuildPrefabsCard());
|
||||
Root.Add(BuildAnimationCard());
|
||||
Root.Add(BuildGenerationCard());
|
||||
|
||||
currentType = item.ItemType;
|
||||
@@ -114,6 +116,7 @@ namespace Ashwild.EditorTools
|
||||
nameField.AddToClassList("ash-hero__name");
|
||||
nameField.BindProperty(so.FindProperty("itemName"));
|
||||
nameField.RegisterValueChangedCallback(_ => onMetaChanged?.Invoke());
|
||||
nameField.RegisterCallback<FocusOutEvent>(_ => RenameAssetToItemName());
|
||||
info.Add(AshwildUI.EditableNameRow(nameField));
|
||||
|
||||
VisualElement typeRow = new VisualElement();
|
||||
@@ -201,6 +204,50 @@ namespace Ashwild.EditorTools
|
||||
|
||||
#endregion
|
||||
|
||||
#region Asset Naming
|
||||
|
||||
/// <summary>
|
||||
/// Renames the underlying asset file to match the authored item name (spaces → underscores) so
|
||||
/// the ScriptableObject on disk reads like the item it holds instead of the generic "NewItem".
|
||||
/// Runs on focus-out, not per keystroke, so the file is renamed once the name is committed; a
|
||||
/// blank name, an unchanged name, or a rename collision is skipped (the latter logged).
|
||||
/// </summary>
|
||||
private void RenameAssetToItemName()
|
||||
{
|
||||
string path = AssetDatabase.GetAssetPath(item);
|
||||
if (string.IsNullOrEmpty(path)) return;
|
||||
|
||||
string desired = ToAssetFileName(item.ItemName);
|
||||
if (string.IsNullOrEmpty(desired)) return;
|
||||
|
||||
if (string.Equals(Path.GetFileNameWithoutExtension(path), desired, StringComparison.Ordinal)) return;
|
||||
|
||||
string error = AssetDatabase.RenameAsset(path, desired);
|
||||
if (!string.IsNullOrEmpty(error))
|
||||
{
|
||||
Debug.LogError($"[ItemEditorView] Could not rename asset to '{desired}': {error}", item);
|
||||
return;
|
||||
}
|
||||
|
||||
onMetaChanged?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Turns an authored item name into a valid asset file name: trims, collapses every whitespace
|
||||
/// run to a single underscore, and drops characters the filesystem forbids in a file name.
|
||||
/// </summary>
|
||||
private static string ToAssetFileName(string itemName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(itemName)) return string.Empty;
|
||||
|
||||
string underscored = Regex.Replace(itemName.Trim(), @"\s+", "_");
|
||||
foreach (char invalid in Path.GetInvalidFileNameChars())
|
||||
underscored = underscored.Replace(invalid.ToString(), string.Empty);
|
||||
return underscored;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Cards
|
||||
|
||||
/// <summary>
|
||||
@@ -330,29 +377,47 @@ namespace Ashwild.EditorTools
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generation card: pick a source model/prefab, then build the world pickup (always) and the
|
||||
/// in-hand prefab (tools/weapons only) wrapped around it.
|
||||
/// Animation card: the clip sets this item imposes on each rig while it is held. Both are
|
||||
/// optional — an item that leaves them empty simply keeps the bare-hand animations, which is the
|
||||
/// right answer for every material and most consumables.
|
||||
/// </summary>
|
||||
private VisualElement BuildAnimationCard()
|
||||
{
|
||||
VisualElement card = AshwildUI.Card("Animation");
|
||||
|
||||
ObjectField armsField = new ObjectField("Arms Set") { objectType = typeof(PlayerAnimationSet), allowSceneObjects = false };
|
||||
armsField.BindProperty(so.FindProperty("armsAnimationSet"));
|
||||
card.Add(armsField);
|
||||
|
||||
ObjectField bodyField = new ObjectField("Body Set") { objectType = typeof(PlayerAnimationSet), allowSceneObjects = false };
|
||||
bodyField.BindProperty(so.FindProperty("bodyAnimationSet"));
|
||||
card.Add(bodyField);
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generation card: pick a source prefab, then build both the world pickup (always) and the
|
||||
/// in-hand prefab (tools/weapons only) from it with one button, creating only the ones still
|
||||
/// missing.
|
||||
/// </summary>
|
||||
private VisualElement BuildGenerationCard()
|
||||
{
|
||||
generationCard = AshwildUI.Card("Prefab Generation");
|
||||
generationCard.AddToClassList("ash-card--accent");
|
||||
|
||||
ObjectField source = new ObjectField("Source (Prefab / Model)") { objectType = typeof(GameObject), allowSceneObjects = false };
|
||||
ObjectField source = new ObjectField("Source Prefab") { objectType = typeof(GameObject), allowSceneObjects = false };
|
||||
source.tooltip = "The set-up prefab (meshes + materials). Generation saves a Prefab Variant of it with the gameplay components added.";
|
||||
source.RegisterValueChangedCallback(evt => pendingSource = evt.newValue as GameObject);
|
||||
generationCard.Add(source);
|
||||
|
||||
VisualElement buttons = new VisualElement();
|
||||
buttons.AddToClassList("ash-buttons");
|
||||
|
||||
worldGenButton = new Button(GenerateWorld) { text = "Generate World Prefab" };
|
||||
worldGenButton.AddToClassList("ash-btn");
|
||||
worldGenButton.AddToClassList("ash-btn--primary");
|
||||
buttons.Add(worldGenButton);
|
||||
|
||||
handGenButton = new Button(GenerateHand) { text = "Generate Hand Prefab" };
|
||||
handGenButton.AddToClassList("ash-btn");
|
||||
buttons.Add(handGenButton);
|
||||
Button generate = new Button(GeneratePrefabs) { text = "Generate Prefab" };
|
||||
generate.AddToClassList("ash-btn");
|
||||
generate.AddToClassList("ash-btn--primary");
|
||||
buttons.Add(generate);
|
||||
|
||||
generationCard.Add(buttons);
|
||||
return generationCard;
|
||||
@@ -363,21 +428,23 @@ namespace Ashwild.EditorTools
|
||||
#region Actions
|
||||
|
||||
/// <summary>
|
||||
/// Builds the world pickup prefab around the chosen source and re-renders on success.
|
||||
/// Builds every prefab still missing for the item from the chosen source: the world pickup when
|
||||
/// none is set, and the in-hand prefab when the item is a tool/weapon and none is set. Re-renders
|
||||
/// once if anything was generated so the view reflects the new references and hides the card when
|
||||
/// nothing is left to build.
|
||||
/// </summary>
|
||||
private void GenerateWorld()
|
||||
private void GeneratePrefabs()
|
||||
{
|
||||
if (ItemAssetFactory.GenerateWorldPrefab(item, pendingSource) != null)
|
||||
onPrefabGenerated?.Invoke();
|
||||
}
|
||||
bool generated = false;
|
||||
|
||||
/// <summary>
|
||||
/// Builds the in-hand prefab around the chosen source and re-renders on success.
|
||||
/// </summary>
|
||||
private void GenerateHand()
|
||||
{
|
||||
if (ItemAssetFactory.GenerateHandPrefab(item, pendingSource) != null)
|
||||
onPrefabGenerated?.Invoke();
|
||||
if (worldField.value == null)
|
||||
generated |= ItemAssetFactory.GenerateWorldPrefab(item, pendingSource) != null;
|
||||
|
||||
bool isToolLike = currentType == ItemType.Tool || currentType == ItemType.Weapon;
|
||||
if (isToolLike && handField.value == null)
|
||||
generated |= ItemAssetFactory.GenerateHandPrefab(item, pendingSource) != null;
|
||||
|
||||
if (generated) onPrefabGenerated?.Invoke();
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -398,21 +465,19 @@ namespace Ashwild.EditorTools
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hides a generation button once its prefab already exists (and the hand button entirely for
|
||||
/// non-tools), and collapses the whole generation card when nothing is left to generate — so a
|
||||
/// fully wired item shows no redundant tooling. Re-evaluated when the type or a prefab field changes.
|
||||
/// Collapses the whole generation card once every prefab the item needs already exists — the
|
||||
/// world pickup for any item, plus the hand prefab for tools/weapons — so a fully wired item
|
||||
/// shows no redundant tooling. Re-evaluated when the type or a prefab field changes.
|
||||
/// </summary>
|
||||
private void UpdateGenerationVisibility()
|
||||
{
|
||||
if (worldField == null || handField == null || generationCard == null) return;
|
||||
|
||||
bool isToolLike = currentType == ItemType.Tool || currentType == ItemType.Weapon;
|
||||
bool showWorld = worldField.value == null;
|
||||
bool showHand = isToolLike && handField.value == null;
|
||||
bool needWorld = worldField.value == null;
|
||||
bool needHand = isToolLike && handField.value == null;
|
||||
|
||||
SetRowVisible(worldGenButton, showWorld);
|
||||
SetRowVisible(handGenButton, showHand);
|
||||
SetRowVisible(generationCard, showWorld || showHand);
|
||||
SetRowVisible(generationCard, needWorld || needHand);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -13,8 +13,8 @@ namespace Ashwild.EditorTools
|
||||
/// editor IS the recipe equation: a row of interactive ingredient cards ("+"-joined) leading to
|
||||
/// "=" and the result card. Each card's icon opens an item picker to set/change the item, a
|
||||
/// −/+ stepper adjusts its quantity, and ingredient cards carry a remove button; a dashed "+" tile
|
||||
/// appends a new ingredient. A compact name field sits above it. All edits write straight to the
|
||||
/// asset and repaint the strip.
|
||||
/// appends a new ingredient. A compact name field sits above it, and a station card below states
|
||||
/// where the recipe can be crafted. All edits write straight to the asset and repaint the strip.
|
||||
/// </summary>
|
||||
public class RecipeEditorView
|
||||
{
|
||||
@@ -51,6 +51,7 @@ namespace Ashwild.EditorTools
|
||||
Root = new VisualElement();
|
||||
Root.Add(BuildNameField());
|
||||
Root.Add(BuildEquation());
|
||||
Root.Add(BuildStationCard());
|
||||
Root.Add(BuildPickerProxy());
|
||||
|
||||
RefreshEquation();
|
||||
@@ -76,6 +77,28 @@ namespace Ashwild.EditorTools
|
||||
return row;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the card stating where the recipe can be crafted. The hint spells out the meaning of
|
||||
/// None, which reads as "no station" but actually means "craftable everywhere — bare hands and
|
||||
/// every station alike"; any other value restricts the recipe to that station only.
|
||||
/// Changing it refreshes the list row, whose tag shows the station.
|
||||
/// </summary>
|
||||
private VisualElement BuildStationCard()
|
||||
{
|
||||
VisualElement card = AshwildUI.Card("Station");
|
||||
|
||||
EnumField stationField = new EnumField("Crafted On", recipe.RequiredStation);
|
||||
stationField.BindProperty(so.FindProperty("requiredStation"));
|
||||
stationField.RegisterValueChangedCallback(_ => onMetaChanged?.Invoke());
|
||||
card.Add(stationField);
|
||||
|
||||
Label hint = new Label("None = craftable by hand and on every station.");
|
||||
hint.AddToClassList("ash-card__hint");
|
||||
card.Add(hint);
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Equation
|
||||
|
||||
@@ -0,0 +1,344 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Animations;
|
||||
using UnityEngine;
|
||||
using Ashwild.Player;
|
||||
|
||||
namespace Ashwild.EditorTools
|
||||
{
|
||||
/// <summary>
|
||||
/// Editor tool that builds the master Animator Controller the player rigs run on. The controller is
|
||||
/// authored once and never edited per weapon: every state points at an empty placeholder clip whose
|
||||
/// *name* is the slot key, and PlayerAnimationSetBinder swaps those clips at runtime for whatever the
|
||||
/// held item authors.
|
||||
///
|
||||
/// Generating it rather than clicking it together is not a convenience — the contract between the
|
||||
/// controller and PlayerAnimationSet is a set of exact strings (parameter names, clip names), and a
|
||||
/// single typo there fails silently: the parameter simply never moves and the rig stands still with
|
||||
/// no error to explain why. Encoding the contract in code makes it impossible to get wrong, and lets
|
||||
/// the controller be rebuilt from scratch after any manual experiment.
|
||||
///
|
||||
/// Placeholder clips are deliberately empty and stored as their own assets rather than reusing a real
|
||||
/// animation, so the master controller depends on no FBX and the slot list stays explicit. A slot no
|
||||
/// set ever fills therefore plays nothing, which reads as an obvious gap instead of a wrong pose.
|
||||
///
|
||||
/// Safe to re-run: it rewrites the controller in place, keeping the asset's GUID so every Animator
|
||||
/// already pointing at it stays wired.
|
||||
/// </summary>
|
||||
public static class PlayerAnimatorControllerBuilder
|
||||
{
|
||||
#region Constants
|
||||
|
||||
private const string AnimationsFolder = "Assets/GAME/Animations/Arms";
|
||||
private const string SlotsFolder = AnimationsFolder + "/Slots";
|
||||
private const string ArmsControllerPath = AnimationsFolder + "/PlayerArms.controller";
|
||||
|
||||
/// <summary>
|
||||
/// Blend thresholds for the ground tree. They are the gait levels PlayerAnimatorDriver reports —
|
||||
/// 0 idle, 1 walk, 2 run — not speeds in metres per second, so retuning how fast the player moves
|
||||
/// never desynchronises the animation.
|
||||
///
|
||||
/// RunClipSpeed exists only while walk and run share one authored clip: with the same motion in
|
||||
/// both slots, playing the run entry faster is the only thing that distinguishes sprinting from
|
||||
/// walking. Drop it back to 1 as soon as a real run animation fills the Run slot.
|
||||
/// </summary>
|
||||
private const float WalkThreshold = 1f;
|
||||
private const float RunThreshold = 2f;
|
||||
private const float RunClipSpeed = 1.5f;
|
||||
|
||||
private const string SpeedParam = "Speed";
|
||||
private const string GroundedParam = "Grounded";
|
||||
private const string CrouchingParam = "Crouching";
|
||||
private const string SprintingParam = "Sprinting";
|
||||
private const string AttackIndexParam = "AttackIndex";
|
||||
private const string JumpParam = "Jump";
|
||||
private const string LandParam = "Land";
|
||||
private const string AttackParam = "Attack";
|
||||
private const string GrabParam = "Grab";
|
||||
private const string EquipParam = "Equip";
|
||||
private const string UnequipParam = "Unequip";
|
||||
|
||||
/// <summary>
|
||||
/// Every clip slot the controller declares, in the order a reader should meet them. These strings
|
||||
/// are the keys PlayerAnimationSet is queried with — they must match its slot constants exactly.
|
||||
/// </summary>
|
||||
private static readonly string[] Slots =
|
||||
{
|
||||
PlayerAnimationSet.SlotIdle,
|
||||
PlayerAnimationSet.SlotWalk,
|
||||
PlayerAnimationSet.SlotRun,
|
||||
PlayerAnimationSet.SlotJumpStart,
|
||||
PlayerAnimationSet.SlotJumpLoop,
|
||||
PlayerAnimationSet.SlotJumpLand,
|
||||
PlayerAnimationSet.SlotGrab,
|
||||
PlayerAnimationSet.SlotEquip,
|
||||
PlayerAnimationSet.SlotUnequip,
|
||||
PlayerAnimationSet.SlotAttackPrefix + "1",
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
#region Menu
|
||||
|
||||
/// <summary>
|
||||
/// Builds (or rebuilds) the first-person arms controller and the placeholder clips it references.
|
||||
/// </summary>
|
||||
[MenuItem("Tools/Ashwild/Build Player Arms Controller")]
|
||||
public static void BuildArmsController()
|
||||
{
|
||||
EnsureFolders();
|
||||
|
||||
Dictionary<string, AnimationClip> placeholders = new Dictionary<string, AnimationClip>();
|
||||
foreach (string slot in Slots)
|
||||
placeholders[slot] = GetOrCreatePlaceholder(slot);
|
||||
|
||||
AnimatorController controller = GetOrCreateController(ArmsControllerPath);
|
||||
ClearController(controller);
|
||||
AddParameters(controller);
|
||||
BuildLocomotionLayer(controller, placeholders);
|
||||
BuildActionLayer(controller, placeholders);
|
||||
|
||||
EditorUtility.SetDirty(controller);
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
|
||||
Debug.Log($"[PlayerAnimatorControllerBuilder] Built {ArmsControllerPath} with {Slots.Length} clip slots. " +
|
||||
"Assign it to the arms Animator, then point PlayerAnimationSetBinder at your Arms_NoItem set.",
|
||||
controller);
|
||||
|
||||
Selection.activeObject = controller;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Layers
|
||||
|
||||
/// <summary>
|
||||
/// Base layer: the movement the player is always doing. A 1D blend tree covers ground movement so
|
||||
/// idle and run ease into each other instead of snapping, and the jump chain is a straight line
|
||||
/// (start → loop → land) driven by the Grounded flag rather than by timers, so a fall the player
|
||||
/// never jumped into still enters the loop from Any State.
|
||||
/// </summary>
|
||||
private static void BuildLocomotionLayer(AnimatorController controller, Dictionary<string, AnimationClip> clips)
|
||||
{
|
||||
AnimatorControllerLayer[] layers = controller.layers;
|
||||
layers[0].name = "Locomotion";
|
||||
controller.layers = layers;
|
||||
|
||||
AnimatorStateMachine machine = controller.layers[0].stateMachine;
|
||||
|
||||
BlendTree tree;
|
||||
AnimatorState locomotion = controller.CreateBlendTreeInController("Locomotion", out tree, 0);
|
||||
tree.blendType = BlendTreeType.Simple1D;
|
||||
tree.blendParameter = SpeedParam;
|
||||
tree.useAutomaticThresholds = false;
|
||||
tree.AddChild(clips[PlayerAnimationSet.SlotIdle], 0f);
|
||||
tree.AddChild(clips[PlayerAnimationSet.SlotWalk], WalkThreshold);
|
||||
tree.AddChild(clips[PlayerAnimationSet.SlotRun], RunThreshold);
|
||||
SetChildSpeed(tree, 2, RunClipSpeed);
|
||||
|
||||
AnimatorState jumpStart = machine.AddState(PlayerAnimationSet.SlotJumpStart);
|
||||
jumpStart.motion = clips[PlayerAnimationSet.SlotJumpStart];
|
||||
|
||||
AnimatorState jumpLoop = machine.AddState(PlayerAnimationSet.SlotJumpLoop);
|
||||
jumpLoop.motion = clips[PlayerAnimationSet.SlotJumpLoop];
|
||||
|
||||
AnimatorState jumpLand = machine.AddState(PlayerAnimationSet.SlotJumpLand);
|
||||
jumpLand.motion = clips[PlayerAnimationSet.SlotJumpLand];
|
||||
|
||||
machine.defaultState = locomotion;
|
||||
|
||||
AnimatorStateTransition toJump = locomotion.AddTransition(jumpStart);
|
||||
toJump.hasExitTime = false;
|
||||
toJump.duration = 0.05f;
|
||||
toJump.AddCondition(AnimatorConditionMode.If, 0f, JumpParam);
|
||||
|
||||
AnimatorStateTransition startToLoop = jumpStart.AddTransition(jumpLoop);
|
||||
startToLoop.hasExitTime = true;
|
||||
startToLoop.exitTime = 0.8f;
|
||||
startToLoop.duration = 0.1f;
|
||||
|
||||
AnimatorStateTransition anyToLoop = machine.AddAnyStateTransition(jumpLoop);
|
||||
anyToLoop.hasExitTime = false;
|
||||
anyToLoop.duration = 0.15f;
|
||||
anyToLoop.canTransitionToSelf = false;
|
||||
anyToLoop.AddCondition(AnimatorConditionMode.IfNot, 0f, GroundedParam);
|
||||
|
||||
AnimatorStateTransition loopToLand = jumpLoop.AddTransition(jumpLand);
|
||||
loopToLand.hasExitTime = false;
|
||||
loopToLand.duration = 0.1f;
|
||||
loopToLand.AddCondition(AnimatorConditionMode.If, 0f, GroundedParam);
|
||||
|
||||
AnimatorStateTransition landToLocomotion = jumpLand.AddTransition(locomotion);
|
||||
landToLocomotion.hasExitTime = true;
|
||||
landToLocomotion.exitTime = 0.7f;
|
||||
landToLocomotion.duration = 0.15f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Action layer: the one-shots that play *over* whatever the legs are doing. It sits on an empty
|
||||
/// default state at full weight, so it contributes nothing until an action fires and the player
|
||||
/// keeps running normally underneath. Every action returns to that empty state on exit time,
|
||||
/// which is what lets a swing interrupt itself cleanly on the next click.
|
||||
/// </summary>
|
||||
private static void BuildActionLayer(AnimatorController controller, Dictionary<string, AnimationClip> clips)
|
||||
{
|
||||
controller.AddLayer("Action");
|
||||
AnimatorControllerLayer[] layers = controller.layers;
|
||||
AnimatorControllerLayer action = layers[1];
|
||||
action.defaultWeight = 1f;
|
||||
action.blendingMode = AnimatorLayerBlendingMode.Override;
|
||||
controller.layers = layers;
|
||||
|
||||
AnimatorStateMachine machine = action.stateMachine;
|
||||
AnimatorState none = machine.AddState("None");
|
||||
machine.defaultState = none;
|
||||
|
||||
AddOneShot(machine, none, PlayerAnimationSet.SlotGrab, clips, GrabParam);
|
||||
AddOneShot(machine, none, PlayerAnimationSet.SlotEquip, clips, EquipParam);
|
||||
AddOneShot(machine, none, PlayerAnimationSet.SlotUnequip, clips, UnequipParam);
|
||||
AddOneShot(machine, none, PlayerAnimationSet.SlotAttackPrefix + "1", clips, AttackParam);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wires one action: entered from Any State on its trigger so it can fire at any moment (and
|
||||
/// re-fire while already playing, which a combo needs), and released back to the empty state on
|
||||
/// exit time so the layer stops contributing as soon as the action is over.
|
||||
/// </summary>
|
||||
private static void AddOneShot(AnimatorStateMachine machine, AnimatorState none, string slot,
|
||||
Dictionary<string, AnimationClip> clips, string trigger)
|
||||
{
|
||||
AnimatorState state = machine.AddState(slot);
|
||||
state.motion = clips[slot];
|
||||
|
||||
AnimatorStateTransition enter = machine.AddAnyStateTransition(state);
|
||||
enter.hasExitTime = false;
|
||||
enter.duration = 0.05f;
|
||||
enter.canTransitionToSelf = true;
|
||||
enter.AddCondition(AnimatorConditionMode.If, 0f, trigger);
|
||||
|
||||
AnimatorStateTransition exit = state.AddTransition(none);
|
||||
exit.hasExitTime = true;
|
||||
exit.exitTime = 0.9f;
|
||||
exit.duration = 0.1f;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Sets one blend-tree child's playback rate. The children array must be reassigned wholesale
|
||||
/// because BlendTree.children hands back a copy — mutating the returned struct in place silently
|
||||
/// does nothing.
|
||||
/// </summary>
|
||||
private static void SetChildSpeed(BlendTree tree, int index, float speed)
|
||||
{
|
||||
ChildMotion[] children = tree.children;
|
||||
if (index < 0 || index >= children.Length) return;
|
||||
children[index].timeScale = speed;
|
||||
tree.children = children;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Declares every parameter PlayerAnimatorDriver writes. Names are the ones the driver defaults
|
||||
/// to, so a freshly added driver works with no inspector edits.
|
||||
/// </summary>
|
||||
private static void AddParameters(AnimatorController controller)
|
||||
{
|
||||
controller.AddParameter(SpeedParam, AnimatorControllerParameterType.Float);
|
||||
controller.AddParameter(GroundedParam, AnimatorControllerParameterType.Bool);
|
||||
controller.AddParameter(CrouchingParam, AnimatorControllerParameterType.Bool);
|
||||
controller.AddParameter(SprintingParam, AnimatorControllerParameterType.Bool);
|
||||
controller.AddParameter(AttackIndexParam, AnimatorControllerParameterType.Int);
|
||||
controller.AddParameter(JumpParam, AnimatorControllerParameterType.Trigger);
|
||||
controller.AddParameter(LandParam, AnimatorControllerParameterType.Trigger);
|
||||
controller.AddParameter(AttackParam, AnimatorControllerParameterType.Trigger);
|
||||
controller.AddParameter(GrabParam, AnimatorControllerParameterType.Trigger);
|
||||
controller.AddParameter(EquipParam, AnimatorControllerParameterType.Trigger);
|
||||
controller.AddParameter(UnequipParam, AnimatorControllerParameterType.Trigger);
|
||||
|
||||
SetDefaultBool(controller, GroundedParam, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Seeds a bool's authored default so the rig starts in a sane pose on the very first frame,
|
||||
/// before the driver has pushed anything — a player spawning "not grounded" would otherwise flash
|
||||
/// the fall loop.
|
||||
/// </summary>
|
||||
private static void SetDefaultBool(AnimatorController controller, string paramName, bool value)
|
||||
{
|
||||
AnimatorControllerParameter[] parameters = controller.parameters;
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
if (parameters[i].name != paramName) continue;
|
||||
parameters[i].defaultBool = value;
|
||||
break;
|
||||
}
|
||||
controller.parameters = parameters;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Empties an existing controller so a rebuild never stacks duplicate states or parameters on top
|
||||
/// of the previous run. The asset itself is kept so its GUID — and every Animator reference to
|
||||
/// it — survives.
|
||||
/// </summary>
|
||||
private static void ClearController(AnimatorController controller)
|
||||
{
|
||||
for (int i = controller.layers.Length - 1; i > 0; i--)
|
||||
controller.RemoveLayer(i);
|
||||
|
||||
while (controller.parameters.Length > 0)
|
||||
controller.RemoveParameter(0);
|
||||
|
||||
AnimatorStateMachine machine = controller.layers[0].stateMachine;
|
||||
|
||||
for (int i = machine.states.Length - 1; i >= 0; i--)
|
||||
machine.RemoveState(machine.states[i].state);
|
||||
|
||||
for (int i = machine.anyStateTransitions.Length - 1; i >= 0; i--)
|
||||
machine.RemoveAnyStateTransition(machine.anyStateTransitions[i]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the controller at a path, creating it on first run.
|
||||
/// </summary>
|
||||
private static AnimatorController GetOrCreateController(string path)
|
||||
{
|
||||
AnimatorController existing = AssetDatabase.LoadAssetAtPath<AnimatorController>(path);
|
||||
return existing != null ? existing : AnimatorController.CreateAnimatorControllerAtPath(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads (or creates) the empty clip that stands in for a slot. Its name is the slot key the
|
||||
/// binder matches sets against, which is the whole reason these exist as named assets.
|
||||
/// </summary>
|
||||
private static AnimationClip GetOrCreatePlaceholder(string slot)
|
||||
{
|
||||
string path = $"{SlotsFolder}/{slot}.anim";
|
||||
AnimationClip existing = AssetDatabase.LoadAssetAtPath<AnimationClip>(path);
|
||||
if (existing != null) return existing;
|
||||
|
||||
AnimationClip clip = new AnimationClip { name = slot };
|
||||
AssetDatabase.CreateAsset(clip, path);
|
||||
return clip;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes sure the target folders exist before anything is written into them.
|
||||
/// </summary>
|
||||
private static void EnsureFolders()
|
||||
{
|
||||
if (!AssetDatabase.IsValidFolder(AnimationsFolder))
|
||||
{
|
||||
Debug.LogError($"[PlayerAnimatorControllerBuilder] '{AnimationsFolder}' does not exist — create it first.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!AssetDatabase.IsValidFolder(SlotsFolder))
|
||||
AssetDatabase.CreateFolder(AnimationsFolder, "Slots");
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 17862d3b35172c644928bc3acaebe6e5
|
||||
@@ -45,6 +45,16 @@ namespace Ashwild.EditorTools
|
||||
|
||||
#endregion
|
||||
|
||||
#region Subject Config
|
||||
|
||||
/// <summary>
|
||||
/// The subject's own local rotation (Euler degrees), independent of the camera orbit. Persisted
|
||||
/// on the stage so it survives target swaps, and re-applied whenever a new subject loads.
|
||||
/// </summary>
|
||||
public Vector3 SubjectEuler;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Lighting Config
|
||||
|
||||
public Color KeyColor = Color.white;
|
||||
@@ -187,7 +197,7 @@ namespace Ashwild.EditorTools
|
||||
|
||||
targetInstance.hideFlags = HideFlags.HideAndDontSave;
|
||||
targetInstance.transform.position = Vector3.zero;
|
||||
targetInstance.transform.rotation = Quaternion.identity;
|
||||
targetInstance.transform.rotation = Quaternion.Euler(SubjectEuler);
|
||||
SceneManager.MoveGameObjectToScene(targetInstance, scene);
|
||||
|
||||
ComputeBounds();
|
||||
@@ -253,6 +263,20 @@ namespace Ashwild.EditorTools
|
||||
/// </summary>
|
||||
public void FrameTarget() => Zoom = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// Re-poses the subject to the given local Euler rotation and recomputes its framing bounds, so
|
||||
/// the camera keeps pivoting on the rotated silhouette's centre. No-op (but the value is still
|
||||
/// remembered) when no subject is loaded.
|
||||
/// </summary>
|
||||
public void SetSubjectRotation(Vector3 euler)
|
||||
{
|
||||
SubjectEuler = euler;
|
||||
if (targetInstance == null) return;
|
||||
|
||||
targetInstance.transform.rotation = Quaternion.Euler(euler);
|
||||
ComputeBounds();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The world-space radius of the subject's bounding sphere, clamped to a small minimum so a
|
||||
/// zero-size target still frames sanely.
|
||||
|
||||
@@ -181,8 +181,8 @@ namespace Ashwild.EditorTools
|
||||
|
||||
/// <summary>
|
||||
/// Target card: a Prefab/Mesh kind selector that filters the object picker to only those assets
|
||||
/// (so materials, scripts and the like never clutter it), the object field itself, plus quick
|
||||
/// Frame and auto-rotate controls that act on the loaded subject.
|
||||
/// (so materials, scripts and the like never clutter it), the object field itself, per-axis
|
||||
/// rotation sliders that pose the subject, plus quick Frame and auto-rotate controls.
|
||||
/// </summary>
|
||||
private VisualElement BuildTargetCard()
|
||||
{
|
||||
@@ -206,6 +206,13 @@ namespace Ashwild.EditorTools
|
||||
card.Add(kind);
|
||||
card.Add(field);
|
||||
|
||||
card.Add(SliderRow("Rotate X", 0f, 360f, stage.SubjectEuler.x,
|
||||
v => stage.SetSubjectRotation(new Vector3(v, stage.SubjectEuler.y, stage.SubjectEuler.z))));
|
||||
card.Add(SliderRow("Rotate Y", 0f, 360f, stage.SubjectEuler.y,
|
||||
v => stage.SetSubjectRotation(new Vector3(stage.SubjectEuler.x, v, stage.SubjectEuler.z))));
|
||||
card.Add(SliderRow("Rotate Z", 0f, 360f, stage.SubjectEuler.z,
|
||||
v => stage.SetSubjectRotation(new Vector3(stage.SubjectEuler.x, stage.SubjectEuler.y, v))));
|
||||
|
||||
VisualElement buttons = new VisualElement();
|
||||
buttons.AddToClassList("ss-buttons");
|
||||
|
||||
@@ -213,6 +220,10 @@ namespace Ashwild.EditorTools
|
||||
frame.AddToClassList("ss-btn");
|
||||
buttons.Add(frame);
|
||||
|
||||
Button resetRotation = new Button(() => { stage.SetSubjectRotation(Vector3.zero); RebuildControls(); RenderPreview(); }) { text = "Reset Rotation" };
|
||||
resetRotation.AddToClassList("ss-btn");
|
||||
buttons.Add(resetRotation);
|
||||
|
||||
Toggle rotate = new Toggle("Auto-rotate") { value = autoRotate };
|
||||
rotate.RegisterValueChangedCallback(evt => SetAutoRotate(evt.newValue));
|
||||
buttons.Add(rotate);
|
||||
@@ -355,7 +366,10 @@ namespace Ashwild.EditorTools
|
||||
|
||||
/// <summary>
|
||||
/// Builds the output-folder row: a read-only path label and a Browse button that opens a folder
|
||||
/// picker and remembers the choice in EditorPrefs.
|
||||
/// picker and remembers the choice in EditorPrefs. The picker starts from the resolved export
|
||||
/// folder (normalised to native separators) so it opens where the label points — the native
|
||||
/// Windows dialog ignores forward-slash paths like Application.dataPath and would otherwise fall
|
||||
/// back to the shell's last-used location (often another project entirely).
|
||||
/// </summary>
|
||||
private VisualElement BuildFolderRow()
|
||||
{
|
||||
@@ -364,11 +378,12 @@ namespace Ashwild.EditorTools
|
||||
|
||||
Label path = new Label(ShortFolder());
|
||||
path.AddToClassList("ss-path");
|
||||
path.tooltip = exportFolder;
|
||||
path.tooltip = ResolveFolder();
|
||||
|
||||
Button browse = new Button(() =>
|
||||
{
|
||||
string chosen = EditorUtility.OpenFolderPanel("Export folder", exportFolder, string.Empty);
|
||||
string start = ResolveFolder().Replace('/', Path.DirectorySeparatorChar);
|
||||
string chosen = EditorUtility.OpenFolderPanel("Export folder", start, string.Empty);
|
||||
if (string.IsNullOrEmpty(chosen)) return;
|
||||
exportFolder = chosen;
|
||||
EditorPrefs.SetString(FolderPrefKey, exportFolder);
|
||||
@@ -627,8 +642,8 @@ namespace Ashwild.EditorTools
|
||||
|
||||
/// <summary>
|
||||
/// Captures the current view at the export resolution and writes it as a PNG to the chosen folder,
|
||||
/// avoiding overwrites by auto-numbering, refreshing the AssetDatabase when the path is in-project,
|
||||
/// and revealing the file. Guards a missing target and failed writes with clear logs.
|
||||
/// avoiding overwrites by auto-numbering, importing it as a single-mode Sprite when the path is
|
||||
/// in-project, and revealing the file. Guards a missing target and failed writes with clear logs.
|
||||
/// </summary>
|
||||
private void ExportPng()
|
||||
{
|
||||
@@ -656,7 +671,7 @@ namespace Ashwild.EditorTools
|
||||
}
|
||||
|
||||
EditorPrefs.SetString(FilePrefKey, exportFileName);
|
||||
RefreshIfInProject(path);
|
||||
ImportAsSprite(path);
|
||||
Debug.Log($"[ScreenshotStudio] Exported {exportWidth}×{exportHeight} PNG → {path}");
|
||||
ShowNotification(new GUIContent($"Exported {Path.GetFileName(path)}"));
|
||||
}
|
||||
@@ -676,6 +691,7 @@ namespace Ashwild.EditorTools
|
||||
string folder = ResolveFolder();
|
||||
string baseName = Path.GetFileNameWithoutExtension(ResolveFileName());
|
||||
float startYaw = stage.Yaw;
|
||||
System.Collections.Generic.List<string> written = new System.Collections.Generic.List<string>();
|
||||
|
||||
try
|
||||
{
|
||||
@@ -691,7 +707,9 @@ namespace Ashwild.EditorTools
|
||||
|
||||
byte[] png = frame.EncodeToPNG();
|
||||
DestroyImmediate(frame);
|
||||
File.WriteAllBytes(Path.Combine(folder, $"{baseName}_{i:000}.png"), png);
|
||||
string framePath = Path.Combine(folder, $"{baseName}_{i:000}.png");
|
||||
File.WriteAllBytes(framePath, png);
|
||||
written.Add(framePath);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -702,10 +720,10 @@ namespace Ashwild.EditorTools
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
stage.Yaw = startYaw;
|
||||
RefreshIfInProject(folder);
|
||||
foreach (string framePath in written) ImportAsSprite(framePath);
|
||||
RenderPreview();
|
||||
Debug.Log($"[ScreenshotStudio] Exported {turntableFrames}-frame turntable → {folder}");
|
||||
ShowNotification(new GUIContent($"Exported {turntableFrames} frames"));
|
||||
Debug.Log($"[ScreenshotStudio] Exported {written.Count}-frame turntable → {folder}");
|
||||
ShowNotification(new GUIContent($"Exported {written.Count} frames"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -752,15 +770,40 @@ namespace Ashwild.EditorTools
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Imports the written file so it appears in the Project window when it lives under Assets.
|
||||
/// The project-relative "Assets/…" path for a written file, or null when it lives outside the
|
||||
/// project — so external-folder exports skip the in-project import step entirely.
|
||||
/// </summary>
|
||||
private static void RefreshIfInProject(string path)
|
||||
private static string ToAssetPath(string path)
|
||||
{
|
||||
string full = Path.GetFullPath(path).Replace('\\', '/');
|
||||
string root = Path.GetFullPath(Application.dataPath).Replace('\\', '/');
|
||||
if (!full.StartsWith(root, StringComparison.OrdinalIgnoreCase)) return;
|
||||
if (!full.StartsWith(root, StringComparison.OrdinalIgnoreCase)) return null;
|
||||
|
||||
AssetDatabase.Refresh();
|
||||
return "Assets" + full.Substring(root.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Imports the written file when it lives under Assets and retypes it as a single-mode Sprite, so
|
||||
/// studio exports drop straight into UI/inventory work without a manual texture-type change in the
|
||||
/// inspector. Files exported to an external folder are left untouched.
|
||||
/// </summary>
|
||||
private static void ImportAsSprite(string path)
|
||||
{
|
||||
string assetPath = ToAssetPath(path);
|
||||
if (assetPath == null) return;
|
||||
|
||||
AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.ForceSynchronousImport);
|
||||
|
||||
TextureImporter importer = AssetImporter.GetAtPath(assetPath) as TextureImporter;
|
||||
if (importer == null)
|
||||
{
|
||||
Debug.LogError($"[ScreenshotStudio] No TextureImporter for '{assetPath}' — cannot set Sprite type.");
|
||||
return;
|
||||
}
|
||||
|
||||
importer.textureType = TextureImporterType.Sprite;
|
||||
importer.spriteImportMode = SpriteImportMode.Single;
|
||||
importer.SaveAndReimport();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,47 +1,268 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using Ashwild.Network;
|
||||
|
||||
namespace Ashwild.EditorTools
|
||||
{
|
||||
/// <summary>
|
||||
/// Editor tool that assigns a unique, stable id (>= 0) to every scene WorldObject in the open
|
||||
/// scene — Pickables and Harvestables alike, in one shared sequence. The ids are baked into the
|
||||
/// scene asset, so all clients load the exact same mapping (the scene is shared). Run after
|
||||
/// scattering or whenever scene objects are added/removed.
|
||||
/// The one menu item that keeps the baked ids of scene WorldObjects — Pickables and Harvestables
|
||||
/// alike — correct. The id is the object's identity across the network: every client loads the same
|
||||
/// scene, so the same id must designate the same object on every machine.
|
||||
///
|
||||
/// It does the whole job in one click, in the order the three steps have to run:
|
||||
/// strip the ids that ended up baked in prefab <i>assets</i>, give an id to every scene object that
|
||||
/// lacks a valid one, then verify the result and report it. Splitting them into separate menu items
|
||||
/// only creates ways to run them in the wrong order, or to forget one.
|
||||
///
|
||||
/// Assignment is <b>stable and additive</b>: ids already baked into the scene are kept, and only the
|
||||
/// objects that need one (never assigned, or colliding with another object) are given the next free
|
||||
/// number. An earlier version renumbered everything from zero on each run, ordered by
|
||||
/// <c>InstanceID</c> — an order that is not stable between editor sessions — so a single new bush
|
||||
/// reshuffled every id in the scene and produced an unreviewable diff.
|
||||
///
|
||||
/// Ids belong to a scene, never to a prefab asset: an id baked into a prefab is inherited by every
|
||||
/// instance placed from it, so all of them collide on that one id from the moment they are created —
|
||||
/// which reads, in game, as objects that play their interaction and give nothing.
|
||||
/// </summary>
|
||||
public static class WorldObjectIdAssigner
|
||||
{
|
||||
#region Constants
|
||||
|
||||
private const string LogPrefix = "[WorldObjectIds]";
|
||||
|
||||
/// <summary>
|
||||
/// How many fixed objects are named in the summary before it collapses into a count — a freshly
|
||||
/// scattered zone can need hundreds of ids, and an unreadable console helps nobody.
|
||||
/// </summary>
|
||||
private const int MaxNamesInSummary = 8;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Menu
|
||||
|
||||
/// <summary>
|
||||
/// Finds all WorldObjects in the open scene and assigns them sequential unique ids.
|
||||
/// Clears prefab-baked ids, assigns an id to every scene WorldObject that lacks a valid one, then
|
||||
/// validates the outcome. Reports what changed and, if anything is still wrong, logs each culprit
|
||||
/// with its object as context so clicking the message selects it in the hierarchy.
|
||||
/// </summary>
|
||||
[MenuItem("Tools/Ashwild/Assign World Object IDs")]
|
||||
public static void Assign()
|
||||
[MenuItem("Tools/Ashwild/Setup World Object IDs")]
|
||||
public static void Setup()
|
||||
{
|
||||
WorldObject[] objects = Object.FindObjectsByType<WorldObject>(FindObjectsInactive.Include, FindObjectsSortMode.None);
|
||||
if (objects.Length == 0)
|
||||
if (PrefabStageUtility.GetCurrentPrefabStage() != null)
|
||||
{
|
||||
Debug.Log("[WorldObjectIdAssigner] No WorldObject found in the open scene.");
|
||||
Debug.LogError($"{LogPrefix} Ids are per scene, not per prefab. Close the prefab stage and run this " +
|
||||
"from the scene that contains the objects.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Deterministic order so re-runs are stable within an editor session.
|
||||
System.Array.Sort(objects, (a, b) => a.GetInstanceID().CompareTo(b.GetInstanceID()));
|
||||
int clearedPrefabs = ClearPrefabIds();
|
||||
|
||||
int next = 0;
|
||||
foreach (WorldObject obj in objects)
|
||||
if (!TryCollect(out List<WorldObject> objects)) return;
|
||||
|
||||
List<WorldObject> assigned = AssignMissingIds(objects);
|
||||
if (assigned.Count > 0) MarkScenesDirty(objects);
|
||||
|
||||
Report(objects, clearedPrefabs, assigned);
|
||||
Validate(objects);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Steps
|
||||
|
||||
/// <summary>
|
||||
/// Strips the id baked into WorldObject prefab <i>assets</i>, so newly placed instances start
|
||||
/// unassigned instead of all inheriting the same id. Scene instances keep their own id, which is
|
||||
/// stored as a prefab override and left untouched — but an instance that had no override now reads
|
||||
/// as unassigned, which is exactly why the assignment step has to run after this one.
|
||||
/// Returns how many prefabs were cleaned.
|
||||
/// </summary>
|
||||
private static int ClearPrefabIds()
|
||||
{
|
||||
string[] guids = AssetDatabase.FindAssets("t:Prefab");
|
||||
int cleared = 0;
|
||||
|
||||
foreach (string guid in guids)
|
||||
{
|
||||
SerializedObject so = new SerializedObject(obj);
|
||||
so.FindProperty("id").intValue = next++;
|
||||
so.ApplyModifiedPropertiesWithoutUndo();
|
||||
EditorUtility.SetDirty(obj);
|
||||
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(AssetDatabase.GUIDToAssetPath(guid));
|
||||
if (prefab == null) continue;
|
||||
|
||||
bool changed = false;
|
||||
foreach (WorldObject obj in prefab.GetComponentsInChildren<WorldObject>(true))
|
||||
{
|
||||
if (obj.Id < 0) continue;
|
||||
WriteId(obj, -1);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (!changed) continue;
|
||||
|
||||
EditorUtility.SetDirty(prefab);
|
||||
cleared++;
|
||||
}
|
||||
|
||||
EditorSceneManager.MarkSceneDirty(objects[0].gameObject.scene);
|
||||
Debug.Log($"[WorldObjectIdAssigner] Assigned ids to {objects.Length} WorldObject(s) (pickables + harvestables). Save the scene to bake them.");
|
||||
if (cleared > 0) AssetDatabase.SaveAssets();
|
||||
return cleared;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gives the next free id to every object that has none or that duplicates an id already taken,
|
||||
/// leaving valid ids untouched. Returns the objects that were changed.
|
||||
/// </summary>
|
||||
private static List<WorldObject> AssignMissingIds(List<WorldObject> objects)
|
||||
{
|
||||
HashSet<int> taken = new HashSet<int>();
|
||||
List<WorldObject> assigned = new List<WorldObject>();
|
||||
int next = 0;
|
||||
|
||||
foreach (WorldObject obj in objects)
|
||||
{
|
||||
if (obj.Id >= 0 && taken.Add(obj.Id)) continue;
|
||||
|
||||
while (!taken.Add(next)) next++;
|
||||
WriteId(obj, next);
|
||||
assigned.Add(obj);
|
||||
}
|
||||
|
||||
return assigned;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Re-checks the ids after the fact and logs every remaining problem against its object. Nothing
|
||||
/// should ever be reported here — it is the proof that the run actually worked, rather than a
|
||||
/// summary claiming it did.
|
||||
/// </summary>
|
||||
private static void Validate(List<WorldObject> objects)
|
||||
{
|
||||
Dictionary<int, WorldObject> byId = new Dictionary<int, WorldObject>();
|
||||
int problems = 0;
|
||||
|
||||
foreach (WorldObject obj in objects)
|
||||
{
|
||||
if (obj.Id < 0)
|
||||
{
|
||||
problems++;
|
||||
Debug.LogError($"{LogPrefix} '{GetPath(obj)}' still has no id — it will not be interactable in game.", obj);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (byId.TryGetValue(obj.Id, out WorldObject other))
|
||||
{
|
||||
problems++;
|
||||
Debug.LogError($"{LogPrefix} '{GetPath(obj)}' still shares id {obj.Id} with '{GetPath(other)}' — " +
|
||||
"using one silently disables the other.", obj);
|
||||
continue;
|
||||
}
|
||||
|
||||
byId.Add(obj.Id, obj);
|
||||
}
|
||||
|
||||
if (problems > 0)
|
||||
Debug.LogError($"{LogPrefix} {problems} id problem(s) survived the setup — this should not happen; " +
|
||||
"check the errors above.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs what the run changed, in one line, and reminds to save when the scene was touched.
|
||||
/// </summary>
|
||||
private static void Report(List<WorldObject> objects, int clearedPrefabs, List<WorldObject> assigned)
|
||||
{
|
||||
string prefabs = clearedPrefabs > 0 ? $"Cleared the baked id of {clearedPrefabs} prefab(s). " : string.Empty;
|
||||
|
||||
if (assigned.Count == 0)
|
||||
{
|
||||
Debug.Log($"{LogPrefix} {prefabs}All {objects.Count} scene WorldObject(s) already have a unique id — nothing to assign.");
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.Log($"{LogPrefix} {prefabs}Assigned an id to {assigned.Count} of {objects.Count} scene WorldObject(s): " +
|
||||
$"{Describe(assigned)}. Save the scene to bake them.");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Gathers the WorldObjects of the open scenes, in a deterministic order (scene, then hierarchy
|
||||
/// path) so two runs walk them the same way and hand out the same ids.
|
||||
/// </summary>
|
||||
private static bool TryCollect(out List<WorldObject> objects)
|
||||
{
|
||||
objects = null;
|
||||
|
||||
WorldObject[] found = Object.FindObjectsByType<WorldObject>(FindObjectsInactive.Include, FindObjectsSortMode.None);
|
||||
if (found.Length == 0)
|
||||
{
|
||||
Debug.Log($"{LogPrefix} No WorldObject found in the open scene(s).");
|
||||
return false;
|
||||
}
|
||||
|
||||
objects = new List<WorldObject>(found);
|
||||
objects.Sort((a, b) =>
|
||||
{
|
||||
int byScene = string.CompareOrdinal(a.gameObject.scene.path, b.gameObject.scene.path);
|
||||
return byScene != 0 ? byScene : string.CompareOrdinal(GetPath(a), GetPath(b));
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the id through SerializedObject so the change is recorded as a prefab override on an
|
||||
/// instance, rather than silently editing the shared prefab value.
|
||||
/// </summary>
|
||||
private static void WriteId(WorldObject obj, int id)
|
||||
{
|
||||
SerializedObject so = new SerializedObject(obj);
|
||||
so.FindProperty("id").intValue = id;
|
||||
so.ApplyModifiedPropertiesWithoutUndo();
|
||||
EditorUtility.SetDirty(obj);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks every scene that owns one of these objects dirty, since the open scenes may be several
|
||||
/// (additive loading) and only the touched ones need saving.
|
||||
/// </summary>
|
||||
private static void MarkScenesDirty(List<WorldObject> objects)
|
||||
{
|
||||
HashSet<Scene> scenes = new HashSet<Scene>();
|
||||
foreach (WorldObject obj in objects)
|
||||
scenes.Add(obj.gameObject.scene);
|
||||
|
||||
foreach (Scene scene in scenes)
|
||||
if (scene.IsValid()) EditorSceneManager.MarkSceneDirty(scene);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A short, readable summary of the objects that were given an id.
|
||||
/// </summary>
|
||||
private static string Describe(List<WorldObject> objects)
|
||||
{
|
||||
List<string> names = new List<string>();
|
||||
for (int i = 0; i < objects.Count && i < MaxNamesInSummary; i++)
|
||||
names.Add($"{objects[i].name} (id {objects[i].Id})");
|
||||
|
||||
if (objects.Count > MaxNamesInSummary) names.Add($"… and {objects.Count - MaxNamesInSummary} more");
|
||||
return string.Join(", ", names);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hierarchy path of an object, used both for the deterministic ordering and for error messages
|
||||
/// that must point at one specific object among many identically named ones.
|
||||
/// </summary>
|
||||
private static string GetPath(WorldObject obj)
|
||||
{
|
||||
string path = obj.name;
|
||||
Transform current = obj.transform.parent;
|
||||
while (current != null)
|
||||
{
|
||||
path = $"{current.name}/{path}";
|
||||
current = current.parent;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using INab.BetterFog.URP;
|
||||
|
||||
namespace Ashwild.Environment
|
||||
{
|
||||
/// <summary>
|
||||
/// Ties INab Studio's Better Fog to the day/night cycle: every frame it copies the
|
||||
/// TimeOfDayManager's current sky colors into the Better Fog volume override so the fog
|
||||
/// warms at dawn/dusk and turns cool/dark at night along with the sky. This is the ONLY
|
||||
/// script that references the Better Fog asset — the manager itself stays fog-agnostic, so
|
||||
/// swapping the fog solution later only touches this file. Runs in edit mode so the fog
|
||||
/// tracks the time slider live.
|
||||
/// </summary>
|
||||
[ExecuteAlways]
|
||||
[DisallowMultipleComponent]
|
||||
[AddComponentMenu("GAME/Environment/Better Fog Day-Night Driver")]
|
||||
public class BetterFogDayNightDriver : MonoBehaviour
|
||||
{
|
||||
#region Serialized Fields
|
||||
|
||||
[Tooltip("Global Volume whose profile holds a Better Fog override.")]
|
||||
[SerializeField] private Volume fogVolume;
|
||||
[Tooltip("Push the current horizon color into Better Fog's Fog Color.")]
|
||||
[SerializeField] private bool driveFogColor = true;
|
||||
[Tooltip("Push the current sun color into Better Fog's Sun Color (only visible when Better Fog's Sun Light is enabled).")]
|
||||
[SerializeField] private bool driveSunColor = true;
|
||||
|
||||
#endregion
|
||||
|
||||
#region State
|
||||
|
||||
private BetterFogVolumeComponent fog;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity Lifecycle
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the Better Fog override once so Update can drive it cheaply, and warns if the
|
||||
/// wiring is incomplete instead of silently doing nothing.
|
||||
/// </summary>
|
||||
private void OnEnable()
|
||||
{
|
||||
ResolveFog();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the manager's current palette into the fog override. No-op until both the fog
|
||||
/// volume and the manager exist, re-resolving the override if it was lost (profile swap).
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
if (fog == null && !ResolveFog()) return;
|
||||
if (TimeOfDayManager.Instance == null) return;
|
||||
|
||||
if (driveFogColor)
|
||||
{
|
||||
fog._FogColor.overrideState = true;
|
||||
fog._FogColor.value = TimeOfDayManager.Instance.CurrentHorizonColor;
|
||||
}
|
||||
|
||||
if (driveSunColor)
|
||||
{
|
||||
fog._SunColor.overrideState = true;
|
||||
fog._SunColor.value = TimeOfDayManager.Instance.CurrentSunColor;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Fetches the Better Fog override from the assigned volume's profile, logging a clear
|
||||
/// error when it is missing so the setup mistake is obvious. Returns true on success.
|
||||
/// </summary>
|
||||
private bool ResolveFog()
|
||||
{
|
||||
if (fogVolume == null)
|
||||
{
|
||||
Debug.LogError($"[BetterFogDayNightDriver] '{name}' has no Fog Volume assigned.", this);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fogVolume.profile == null || !fogVolume.profile.TryGet(out fog))
|
||||
{
|
||||
Debug.LogError($"[BetterFogDayNightDriver] '{name}' volume profile has no Better Fog override.", this);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0a126fd21c4b3064987e3214346aa8a2
|
||||
@@ -1,15 +1,21 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace Ashwild.Environment
|
||||
{
|
||||
/// <summary>
|
||||
/// Drives the day/night cycle for the "GAME/StylizedSky" skybox. Rotates the sun light and
|
||||
/// pushes every time-dependent value (gradient colors, sun/moon direction, cloud params,
|
||||
/// star opacity) into shader globals — mirroring how SeasonManager broadcasts "_Season".
|
||||
/// The skybox shader itself is dumb: it only renders whatever globals this manager writes.
|
||||
/// Runs in edit mode (ExecuteAlways) so the sky updates live while scrubbing the time slider.
|
||||
/// Purely visual and local; when co-op time-sync is added, only the driving time value needs
|
||||
/// to come from the server (see the "not yet networked" note in CLAUDE.md, like SeasonManager).
|
||||
/// Drives the day/night cycle for the self-contained "GAME/StylizedSky" skybox. Rotates
|
||||
/// the sun Directional Light and evaluates one palette per moment of day (sky gradient,
|
||||
/// sun/moon color, cloud colors, star opacity) straight onto the sky MATERIAL's
|
||||
/// properties — the shader stays fully self-contained, so with no manager the material
|
||||
/// still renders its own authored look and this component only overrides the
|
||||
/// time-dependent values on top. Runs in edit mode (ExecuteAlways) so the sky updates
|
||||
/// live while scrubbing the time slider.
|
||||
///
|
||||
/// Purely visual and LOCAL for now: like SeasonManager it is not yet networked. The
|
||||
/// single seam to sync in co-op is the time value itself — feed it through
|
||||
/// <see cref="SetTimeOfDay"/> from a server world-clock (SyncVar) instead of the local
|
||||
/// auto-advance, and every client's sky and lighting follows (see CLAUDE.md §3).
|
||||
/// </summary>
|
||||
[ExecuteAlways]
|
||||
[DisallowMultipleComponent]
|
||||
@@ -27,14 +33,16 @@ namespace Ashwild.Environment
|
||||
[Header("References")]
|
||||
[Tooltip("Directional light rotated to act as the sun. Optional but recommended.")]
|
||||
[SerializeField] private Light sunLight;
|
||||
[Tooltip("Skybox material using GAME/StylizedSky. Assigned to RenderSettings on enable when set.")]
|
||||
[SerializeField] private Material skyboxMaterial;
|
||||
[Tooltip("Second directional light acting as the moon: cool, dim, lit only at night, aimed opposite the sun. Optional.")]
|
||||
[SerializeField] private Light moonLight;
|
||||
[Tooltip("Material using GAME/StylizedSky. Assigned to RenderSettings.skybox on enable.")]
|
||||
[SerializeField] private Material skyMaterial;
|
||||
|
||||
[Header("Time")]
|
||||
[Tooltip("0 = midnight, 0.25 = sunrise, 0.5 = noon, 0.75 = sunset.")]
|
||||
[Range(0f, 1f)] [SerializeField] private float timeOfDay = 0.5f;
|
||||
[SerializeField] private bool autoAdvance = true;
|
||||
[Tooltip("Real seconds for one full 24h cycle. Only advances in Play.")]
|
||||
[Tooltip("Real seconds for one full day/night cycle. Only advances in Play.")]
|
||||
[SerializeField] private float dayLengthSeconds = 300f;
|
||||
[Tooltip("Compass offset of the sun's arc, in degrees.")]
|
||||
[Range(0f, 360f)] [SerializeField] private float sunYaw = 20f;
|
||||
@@ -45,68 +53,70 @@ namespace Ashwild.Environment
|
||||
[Tooltip("Peak directional-light intensity reached around noon.")]
|
||||
[SerializeField] private float maxSunIntensity = 1.3f;
|
||||
|
||||
[Header("Sky Gradient")]
|
||||
[Header("Sky Gradient Over Day")]
|
||||
[SerializeField] private Gradient zenithColorOverDay = new Gradient();
|
||||
[SerializeField] private Gradient horizonColorOverDay = new Gradient();
|
||||
[SerializeField] private Gradient groundColorOverDay = new Gradient();
|
||||
|
||||
[Header("Moon")]
|
||||
[SerializeField] private Color moonColor = new Color(0.85f, 0.88f, 0.96f);
|
||||
|
||||
[Header("Clouds")]
|
||||
[Header("Clouds Over Day")]
|
||||
[SerializeField] private Gradient cloudColorOverDay = new Gradient();
|
||||
[SerializeField] private Gradient cloudShadowColorOverDay = new Gradient();
|
||||
[Tooltip("Coverage threshold over the day. Higher = fewer clouds.")]
|
||||
[SerializeField] private AnimationCurve cloudCoverageOverDay = AnimationCurve.Constant(0f, 1f, 0.48f);
|
||||
[Range(0f, 1f)] [SerializeField] private float cloudOpacity = 1f;
|
||||
[SerializeField] private Vector2 windDirection = new Vector2(1f, 0.35f);
|
||||
[SerializeField] private float windSpeed = 0.01f;
|
||||
|
||||
[Header("Stars")]
|
||||
[Tooltip("Sun elevation at which stars start fading in (upper) and are fully visible (lower).")]
|
||||
[SerializeField] private float starFadeInElevation = 0.05f;
|
||||
[SerializeField] private float starFullElevation = -0.12f;
|
||||
[Header("Moon and Stars")]
|
||||
[SerializeField] private Color moonColor = new Color(0.85f, 0.88f, 0.96f);
|
||||
[Tooltip("Cool tint of the moonlight cast on the scene at night.")]
|
||||
[SerializeField] private Color moonLightColor = new Color(0.55f, 0.62f, 0.85f);
|
||||
[Tooltip("Peak moonlight intensity reached in the dead of night.")]
|
||||
[SerializeField] private float maxMoonIntensity = 0.3f;
|
||||
[Tooltip("Sun height at which stars/moon start fading in (upper) and are fully visible (lower).")]
|
||||
[SerializeField] private float starFadeInElevation = 0.08f;
|
||||
[SerializeField] private float starFullElevation = -0.15f;
|
||||
|
||||
#endregion
|
||||
|
||||
#region State
|
||||
|
||||
private Vector2 cloudOffset;
|
||||
[Header("Fog")]
|
||||
[Tooltip("Tint the built-in RenderSettings fog with the horizon color. Leave off when an external fog (e.g. Better Fog) is driven from CurrentHorizonColor instead.")]
|
||||
[SerializeField] private bool driveFogColor = true;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Shader Property IDs
|
||||
|
||||
private static readonly int ZenithColorId = Shader.PropertyToID("_SkyZenithColor");
|
||||
private static readonly int HorizonColorId = Shader.PropertyToID("_SkyHorizonColor");
|
||||
private static readonly int GroundColorId = Shader.PropertyToID("_SkyGroundColor");
|
||||
private static readonly int SunDirectionId = Shader.PropertyToID("_SkySunDirection");
|
||||
private static readonly int SunColorId = Shader.PropertyToID("_SkySunColor");
|
||||
private static readonly int MoonDirectionId = Shader.PropertyToID("_SkyMoonDirection");
|
||||
private static readonly int MoonColorId = Shader.PropertyToID("_SkyMoonColor");
|
||||
private static readonly int CloudColorId = Shader.PropertyToID("_SkyCloudColor");
|
||||
private static readonly int CloudShadowColorId = Shader.PropertyToID("_SkyCloudShadowColor");
|
||||
private static readonly int CloudParamsId = Shader.PropertyToID("_SkyCloudParams");
|
||||
private static readonly int StarOpacityId = Shader.PropertyToID("_SkyStarOpacity");
|
||||
private static readonly int ZenithColorId = Shader.PropertyToID("_ZenithColor");
|
||||
private static readonly int HorizonColorId = Shader.PropertyToID("_HorizonColor");
|
||||
private static readonly int GroundColorId = Shader.PropertyToID("_GroundColor");
|
||||
private static readonly int SunDirectionId = Shader.PropertyToID("_SunDirection");
|
||||
private static readonly int SunColorId = Shader.PropertyToID("_SunColor");
|
||||
private static readonly int MoonDirectionId = Shader.PropertyToID("_MoonDirection");
|
||||
private static readonly int MoonColorId = Shader.PropertyToID("_MoonColor");
|
||||
private static readonly int CloudColorId = Shader.PropertyToID("_CloudColor");
|
||||
private static readonly int CloudShadowColorId = Shader.PropertyToID("_CloudShadowColor");
|
||||
private static readonly int StarOpacityId = Shader.PropertyToID("_StarOpacity");
|
||||
|
||||
#endregion
|
||||
|
||||
#region State
|
||||
|
||||
private float lastAmbientRefresh;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity Lifecycle
|
||||
|
||||
/// <summary>
|
||||
/// Registers the instance, assigns the skybox and pushes an initial state so the sky is
|
||||
/// correct the moment the component becomes active (including in the editor).
|
||||
/// Registers the instance, makes the sky material active and pushes an initial state
|
||||
/// so the sky is correct the moment the component becomes active (including in edit).
|
||||
/// </summary>
|
||||
private void OnEnable()
|
||||
{
|
||||
Instance = this;
|
||||
if (skyboxMaterial != null)
|
||||
RenderSettings.skybox = skyboxMaterial;
|
||||
if (skyMaterial != null)
|
||||
RenderSettings.skybox = skyMaterial;
|
||||
RenderSettings.ambientMode = AmbientMode.Skybox;
|
||||
RenderSettings.defaultReflectionMode = DefaultReflectionMode.Skybox;
|
||||
Apply();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the shared instance so a disabled manager never keeps answering as the current one.
|
||||
/// Clears the shared instance so a disabled manager never keeps answering as current.
|
||||
/// </summary>
|
||||
private void OnDisable()
|
||||
{
|
||||
@@ -124,14 +134,13 @@ namespace Ashwild.Environment
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Advances time (Play only) and scrolls the clouds, then re-applies every frame.
|
||||
/// Advances time (Play only) and re-applies the palette every frame.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
if (Application.isPlaying && autoAdvance)
|
||||
timeOfDay = Mathf.Repeat(timeOfDay + Time.deltaTime / dayLengthSeconds, 1f);
|
||||
|
||||
cloudOffset += windDirection.normalized * (windSpeed * Time.deltaTime);
|
||||
Apply();
|
||||
}
|
||||
|
||||
@@ -140,42 +149,70 @@ namespace Ashwild.Environment
|
||||
#region Apply
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the current time of day into sun orientation and shader globals, and mirrors
|
||||
/// the resolved sun color/intensity onto the directional light so scene lighting matches
|
||||
/// the sky. This is the single place every time-dependent value is broadcast.
|
||||
/// Resolves the current time of day into sun orientation and the full sky palette,
|
||||
/// writes it onto the sky material, and mirrors the sun color/intensity onto the
|
||||
/// directional light so scene lighting matches the sky. Single source of every
|
||||
/// time-dependent value. No-op without a material to drive.
|
||||
/// </summary>
|
||||
private void Apply()
|
||||
{
|
||||
Vector3 sunDir = OrientSun();
|
||||
Vector3 moonDir = -sunDir;
|
||||
|
||||
float nightFactor = Mathf.SmoothStep(0f, 1f,
|
||||
Mathf.InverseLerp(starFadeInElevation, starFullElevation, sunDir.y));
|
||||
Color sunColor = sunColorOverDay.Evaluate(timeOfDay);
|
||||
DriveSunLight(sunColor);
|
||||
DriveMoonLight(sunDir, nightFactor);
|
||||
PromoteMainLight(nightFactor);
|
||||
|
||||
Shader.SetGlobalColor(ZenithColorId, ToRendered(zenithColorOverDay.Evaluate(timeOfDay)));
|
||||
Shader.SetGlobalColor(HorizonColorId, ToRendered(horizonColorOverDay.Evaluate(timeOfDay)));
|
||||
Shader.SetGlobalColor(GroundColorId, ToRendered(groundColorOverDay.Evaluate(timeOfDay)));
|
||||
Color horizon = horizonColorOverDay.Evaluate(timeOfDay);
|
||||
CurrentHorizonColor = horizon;
|
||||
CurrentSunColor = sunColor;
|
||||
CurrentNightFactor = nightFactor;
|
||||
if (driveFogColor)
|
||||
RenderSettings.fogColor = ToRendered(horizon);
|
||||
|
||||
Shader.SetGlobalVector(SunDirectionId, sunDir);
|
||||
Shader.SetGlobalColor(SunColorId, ToRendered(sunColor));
|
||||
Shader.SetGlobalVector(MoonDirectionId, moonDir);
|
||||
Shader.SetGlobalColor(MoonColorId, ToRendered(moonColor));
|
||||
if (skyMaterial == null)
|
||||
{
|
||||
RepaintEditor();
|
||||
return;
|
||||
}
|
||||
|
||||
Shader.SetGlobalColor(CloudColorId, ToRendered(cloudColorOverDay.Evaluate(timeOfDay)));
|
||||
Shader.SetGlobalColor(CloudShadowColorId, ToRendered(cloudShadowColorOverDay.Evaluate(timeOfDay)));
|
||||
Shader.SetGlobalVector(CloudParamsId, new Vector4(
|
||||
cloudCoverageOverDay.Evaluate(timeOfDay), cloudOpacity, cloudOffset.x, cloudOffset.y));
|
||||
skyMaterial.SetColor(ZenithColorId, ToRendered(zenithColorOverDay.Evaluate(timeOfDay)));
|
||||
skyMaterial.SetColor(HorizonColorId, ToRendered(horizon));
|
||||
skyMaterial.SetColor(GroundColorId, ToRendered(groundColorOverDay.Evaluate(timeOfDay)));
|
||||
|
||||
Shader.SetGlobalFloat(StarOpacityId,
|
||||
Mathf.SmoothStep(0f, 1f, Mathf.InverseLerp(starFadeInElevation, starFullElevation, sunDir.y)));
|
||||
skyMaterial.SetVector(SunDirectionId, sunDir);
|
||||
skyMaterial.SetColor(SunColorId, ToRendered(sunColorOverDay.Evaluate(timeOfDay)));
|
||||
skyMaterial.SetVector(MoonDirectionId, -sunDir);
|
||||
skyMaterial.SetColor(MoonColorId, ToRendered(moonColor));
|
||||
|
||||
skyMaterial.SetColor(CloudColorId, ToRendered(cloudColorOverDay.Evaluate(timeOfDay)));
|
||||
skyMaterial.SetColor(CloudShadowColorId, ToRendered(cloudShadowColorOverDay.Evaluate(timeOfDay)));
|
||||
|
||||
skyMaterial.SetFloat(StarOpacityId, nightFactor);
|
||||
|
||||
RefreshAmbient();
|
||||
RepaintEditor();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recomputes ambient light and the skybox reflection from the current (procedural)
|
||||
/// sky so scene lighting tracks the day — Unity does NOT do this automatically for a
|
||||
/// material that changes every frame. Throttled to a few times a second because
|
||||
/// rebaking the environment probe every frame would stutter the editor.
|
||||
/// </summary>
|
||||
private void RefreshAmbient()
|
||||
{
|
||||
float now = Time.realtimeSinceStartup;
|
||||
if (now - lastAmbientRefresh < 0.15f) return;
|
||||
lastAmbientRefresh = now;
|
||||
DynamicGI.UpdateEnvironment();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rotates the sun light so noon sits overhead and sunrise/sunset sit on the horizon,
|
||||
/// and returns the world-space direction pointing toward the sun (what the shader wants).
|
||||
/// Works from the current time even when no light is assigned.
|
||||
/// and returns the world-space direction pointing toward the sun (what the shader
|
||||
/// wants). Works from the current time even when no light is assigned.
|
||||
/// </summary>
|
||||
private Vector3 OrientSun()
|
||||
{
|
||||
@@ -198,9 +235,35 @@ namespace Ashwild.Environment
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an authored sRGB color to the value the shader must receive. Colors pushed via
|
||||
/// Shader.SetGlobalColor are NOT auto-gamma-corrected (unlike material properties), so in a
|
||||
/// linear project they must be pre-converted or the whole sky washes out to white.
|
||||
/// Aims the moon light opposite the sun (its light travels toward the sun direction, i.e.
|
||||
/// comes from the moon), tints it cool, and fades its intensity in with nightFactor so it
|
||||
/// only lights the scene once the sun is down. No-op when no moon light is wired up.
|
||||
/// </summary>
|
||||
private void DriveMoonLight(Vector3 sunDir, float nightFactor)
|
||||
{
|
||||
if (moonLight == null) return;
|
||||
moonLight.transform.rotation = Quaternion.LookRotation(sunDir);
|
||||
moonLight.color = moonLightColor;
|
||||
moonLight.intensity = nightFactor * maxMoonIntensity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Points URP's main (shadow-casting) light at whichever body is currently up — sun by
|
||||
/// day, moon by night. Without this, a pinned Sun Source keeps the dark night sun as the
|
||||
/// main light and shadows vanish once it sets; swapping to the moon keeps shadows going.
|
||||
/// </summary>
|
||||
private void PromoteMainLight(float nightFactor)
|
||||
{
|
||||
Light mainLight = nightFactor > 0.5f && moonLight != null ? moonLight : sunLight;
|
||||
if (mainLight != null)
|
||||
RenderSettings.sun = mainLight;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an authored sRGB color to the value the shader must receive. Colors
|
||||
/// pushed via Material.SetColor are NOT auto-gamma-corrected (unlike the inspector
|
||||
/// color field), so in a linear project they must be pre-converted or the whole sky
|
||||
/// washes out to white.
|
||||
/// </summary>
|
||||
private static Color ToRendered(Color c)
|
||||
{
|
||||
@@ -208,7 +271,8 @@ namespace Ashwild.Environment
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Forces the Scene view to redraw while scrubbing values outside Play, matching SeasonManager.
|
||||
/// Forces the Scene view to redraw while scrubbing values outside Play, matching
|
||||
/// SeasonManager, so the sky updates live without entering Play mode.
|
||||
/// </summary>
|
||||
private void RepaintEditor()
|
||||
{
|
||||
@@ -228,7 +292,26 @@ namespace Ashwild.Environment
|
||||
public float TimeOfDay => timeOfDay;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the time of day directly (wrapped into [0,1)) and refreshes the sky immediately.
|
||||
/// The horizon color resolved for the current time (authored/gamma space). External
|
||||
/// atmosphere systems (e.g. the Better Fog driver) read this to tint fog with the sky.
|
||||
/// </summary>
|
||||
public Color CurrentHorizonColor { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The sun color resolved for the current time (authored/gamma space) — for fog sun
|
||||
/// scattering or any effect that should match the sun tint.
|
||||
/// </summary>
|
||||
public Color CurrentSunColor { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 0 by day, 1 in the dead of night (same curve that fades in the stars and the moon).
|
||||
/// </summary>
|
||||
public float CurrentNightFactor { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets the time of day directly (wrapped into [0,1)) and refreshes the sky. This is
|
||||
/// the seam a networked world-clock drives from — call it on every client with the
|
||||
/// server time instead of relying on the local auto-advance.
|
||||
/// </summary>
|
||||
public void SetTimeOfDay(float normalized)
|
||||
{
|
||||
@@ -241,9 +324,9 @@ namespace Ashwild.Environment
|
||||
#region Defaults
|
||||
|
||||
/// <summary>
|
||||
/// Seeds all gradients/curves with a bright, soft daytime palette matched to the Raygeas
|
||||
/// stylized skybox reference, so a freshly added manager looks right without hand-authoring.
|
||||
/// Invoked by the editor when the component is added or reset.
|
||||
/// Seeds every gradient/curve with a bright stylized daytime palette (blue noon,
|
||||
/// warm dawn/dusk, deep-blue night) so a freshly added manager looks right without
|
||||
/// hand-authoring. Invoked by the editor when the component is added or reset.
|
||||
/// </summary>
|
||||
private void Reset()
|
||||
{
|
||||
@@ -254,70 +337,68 @@ namespace Ashwild.Environment
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Re-applies the tuned color palette WITHOUT clearing the wired references (Sun Light,
|
||||
/// Skybox Material) or the current time — unlike Reset, which nulls everything. Exposed on
|
||||
/// the component's context menu so the palette can be re-seeded during iteration.
|
||||
/// Re-applies the tuned palette WITHOUT clearing the wired references (Sun Light,
|
||||
/// Sky Material) or the current time — unlike Reset, which nulls everything. Exposed
|
||||
/// on the context menu so the palette can be re-seeded during iteration.
|
||||
/// </summary>
|
||||
[ContextMenu("Apply Stylized Sky Preset")]
|
||||
private void SeedStylizedPreset()
|
||||
{
|
||||
zenithColorOverDay = BuildGradient(
|
||||
(0.00f, new Color(0.02f, 0.03f, 0.07f)),
|
||||
(0.23f, new Color(0.18f, 0.28f, 0.40f)),
|
||||
(0.30f, new Color(0.14f, 0.40f, 0.56f)),
|
||||
(0.50f, new Color(0.13f, 0.42f, 0.58f)),
|
||||
(0.70f, new Color(0.14f, 0.38f, 0.53f)),
|
||||
(0.78f, new Color(0.22f, 0.26f, 0.40f)),
|
||||
(1.00f, new Color(0.02f, 0.03f, 0.07f)));
|
||||
(0.00f, new Color(0.03f, 0.05f, 0.12f)),
|
||||
(0.23f, new Color(0.22f, 0.30f, 0.52f)),
|
||||
(0.32f, new Color(0.20f, 0.42f, 0.80f)),
|
||||
(0.50f, new Color(0.20f, 0.45f, 0.85f)),
|
||||
(0.68f, new Color(0.20f, 0.40f, 0.78f)),
|
||||
(0.78f, new Color(0.24f, 0.28f, 0.50f)),
|
||||
(1.00f, new Color(0.03f, 0.05f, 0.12f)));
|
||||
|
||||
horizonColorOverDay = BuildGradient(
|
||||
(0.00f, new Color(0.04f, 0.05f, 0.10f)),
|
||||
(0.23f, new Color(0.86f, 0.50f, 0.36f)),
|
||||
(0.32f, new Color(0.32f, 0.56f, 0.68f)),
|
||||
(0.50f, new Color(0.26f, 0.52f, 0.66f)),
|
||||
(0.68f, new Color(0.30f, 0.50f, 0.60f)),
|
||||
(0.78f, new Color(0.86f, 0.48f, 0.32f)),
|
||||
(1.00f, new Color(0.04f, 0.05f, 0.10f)));
|
||||
(0.00f, new Color(0.05f, 0.07f, 0.14f)),
|
||||
(0.23f, new Color(0.95f, 0.58f, 0.40f)),
|
||||
(0.32f, new Color(0.75f, 0.86f, 0.95f)),
|
||||
(0.50f, new Color(0.72f, 0.85f, 0.95f)),
|
||||
(0.68f, new Color(0.78f, 0.84f, 0.92f)),
|
||||
(0.78f, new Color(0.97f, 0.52f, 0.35f)),
|
||||
(1.00f, new Color(0.05f, 0.07f, 0.14f)));
|
||||
|
||||
groundColorOverDay = BuildGradient(
|
||||
(0.00f, new Color(0.03f, 0.04f, 0.08f)),
|
||||
(0.25f, new Color(0.26f, 0.30f, 0.34f)),
|
||||
(0.50f, new Color(0.22f, 0.44f, 0.56f)),
|
||||
(0.75f, new Color(0.28f, 0.28f, 0.30f)),
|
||||
(0.25f, new Color(0.35f, 0.33f, 0.32f)),
|
||||
(0.50f, new Color(0.40f, 0.44f, 0.48f)),
|
||||
(0.75f, new Color(0.36f, 0.31f, 0.30f)),
|
||||
(1.00f, new Color(0.03f, 0.04f, 0.08f)));
|
||||
|
||||
sunColorOverDay = BuildGradient(
|
||||
(0.00f, new Color(0.40f, 0.40f, 0.50f)),
|
||||
(0.00f, new Color(0.35f, 0.40f, 0.55f)),
|
||||
(0.23f, new Color(1.00f, 0.55f, 0.30f)),
|
||||
(0.50f, new Color(1.00f, 0.95f, 0.85f)),
|
||||
(0.77f, new Color(1.00f, 0.50f, 0.28f)),
|
||||
(1.00f, new Color(0.40f, 0.40f, 0.50f)));
|
||||
(0.50f, new Color(1.00f, 0.96f, 0.88f)),
|
||||
(0.77f, new Color(1.00f, 0.52f, 0.28f)),
|
||||
(1.00f, new Color(0.35f, 0.40f, 0.55f)));
|
||||
|
||||
cloudColorOverDay = BuildGradient(
|
||||
(0.00f, new Color(0.10f, 0.11f, 0.18f)),
|
||||
(0.25f, new Color(0.98f, 0.78f, 0.66f)),
|
||||
(0.00f, new Color(0.14f, 0.17f, 0.26f)),
|
||||
(0.25f, new Color(1.00f, 0.74f, 0.58f)),
|
||||
(0.50f, new Color(1.00f, 1.00f, 1.00f)),
|
||||
(0.75f, new Color(0.98f, 0.76f, 0.64f)),
|
||||
(1.00f, new Color(0.10f, 0.11f, 0.18f)));
|
||||
(0.75f, new Color(1.00f, 0.72f, 0.55f)),
|
||||
(1.00f, new Color(0.14f, 0.17f, 0.26f)));
|
||||
|
||||
cloudShadowColorOverDay = BuildGradient(
|
||||
(0.00f, new Color(0.05f, 0.06f, 0.10f)),
|
||||
(0.25f, new Color(0.55f, 0.45f, 0.50f)),
|
||||
(0.50f, new Color(0.70f, 0.76f, 0.84f)),
|
||||
(0.75f, new Color(0.56f, 0.44f, 0.48f)),
|
||||
(1.00f, new Color(0.05f, 0.06f, 0.10f)));
|
||||
(0.00f, new Color(0.06f, 0.08f, 0.16f)),
|
||||
(0.25f, new Color(0.60f, 0.45f, 0.52f)),
|
||||
(0.50f, new Color(0.58f, 0.65f, 0.82f)),
|
||||
(0.75f, new Color(0.58f, 0.43f, 0.50f)),
|
||||
(1.00f, new Color(0.06f, 0.08f, 0.16f)));
|
||||
|
||||
sunIntensityOverDay = new AnimationCurve(
|
||||
new Keyframe(0.00f, 0f), new Keyframe(0.23f, 0.05f), new Keyframe(0.30f, 0.8f),
|
||||
new Keyframe(0.50f, 1f), new Keyframe(0.70f, 0.8f), new Keyframe(0.78f, 0.05f),
|
||||
new Keyframe(1.00f, 0f));
|
||||
|
||||
cloudCoverageOverDay = AnimationCurve.Constant(0f, 1f, 0.55f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a Gradient from (time, color) tuples with full alpha, keeping color authoring
|
||||
/// in one compact place. Alpha is unused by the sky (every color is opaque).
|
||||
/// Builds a Gradient from (time, color) tuples with full alpha, keeping color
|
||||
/// authoring in one compact place. Alpha is unused by the sky (every color opaque).
|
||||
/// </summary>
|
||||
private static Gradient BuildGradient(params (float time, Color color)[] stops)
|
||||
{
|
||||
|
||||
@@ -2,18 +2,25 @@ using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using DG.Tweening;
|
||||
using Ashwild.Interaction;
|
||||
using Ashwild.Inventory;
|
||||
using Ashwild.Network;
|
||||
using Ashwild.Player;
|
||||
|
||||
namespace Ashwild.Harvesting
|
||||
{
|
||||
/// <summary>
|
||||
/// A harvestable world object (tree, rock, …). A WorldObject backed by the HarvestableRegistry:
|
||||
/// A harvestable world object (tree, rock, bush, …). A WorldObject backed by the HarvestableRegistry:
|
||||
/// the registry owns the authoritative health, loot grant, depletion and respawn, while this
|
||||
/// component holds the authored data (max health, drop tables, respawn settings) and plays the
|
||||
/// local feedback (squash/stretch on hit, blocked "clunk", fall on depletion).
|
||||
/// local hit feedback (the springy scale "pop", mirroring the buildable appear bump).
|
||||
///
|
||||
/// A hit is always applied through <see cref="ApplyHit"/> — the single path shared by tool swings
|
||||
/// (ToolBehaviour) and, when <see cref="allowBareHandHarvest"/> is set, bare-hand gathering via the
|
||||
/// interact key (IInteractable). Loot is authored explicitly per source: a dedicated bare-hand table
|
||||
/// and one table per listed tool. Nothing is implicit — a tool that is not listed yields no drops.
|
||||
/// </summary>
|
||||
public class Harvestable : WorldObject
|
||||
public class Harvestable : WorldObject, IInteractable
|
||||
{
|
||||
#region Serialized Fields
|
||||
|
||||
@@ -21,23 +28,31 @@ namespace Ashwild.Harvesting
|
||||
[SerializeField] private HarvestType harvestType = HarvestType.Tree;
|
||||
[SerializeField] private float maxHealth = 100f;
|
||||
|
||||
[Header("Drops (per tool)")]
|
||||
[Tooltip("Each entry is the loot for one tool. Add an entry with no tool as a fallback for unlisted tools.")]
|
||||
[Header("Bare-Hand Harvest")]
|
||||
[Tooltip("When set, the player can gather this by hand with the interact key (no tool needed). Leave off for trees/rocks.")]
|
||||
[SerializeField] private bool allowBareHandHarvest = false;
|
||||
[Tooltip("Damage dealt by one bare-hand gather. Combined with Max Health this sets how many hand-pulls it takes.")]
|
||||
[SerializeField] private float bareHandDamage = 25f;
|
||||
[Tooltip("Verb shown under the crosshair for bare-hand gathering (e.g. \"Gather\").")]
|
||||
[SerializeField] private string interactionVerb = "Gather";
|
||||
[Tooltip("Loot rolled when gathered by hand. Each row has its own drop chance and quantity range.")]
|
||||
[SerializeField] private ResourceDrop[] bareHandDrops;
|
||||
|
||||
[Header("Tool Drops")]
|
||||
[Tooltip("Loot per tool. Every entry MUST have a tool assigned — a tool not listed here yields no drops.")]
|
||||
[SerializeField] private ToolDropTable[] toolDrops;
|
||||
|
||||
[Header("Hit Feedback")]
|
||||
[Tooltip("Squash & stretch amount on impact (0.1–0.2 reads well).")]
|
||||
[SerializeField] private float punchScale = 0.15f;
|
||||
[Tooltip("Bend angle (degrees) the object tips away from the hit.")]
|
||||
[SerializeField] private float punchAngle = 8f;
|
||||
[SerializeField] private float punchDuration = 0.3f;
|
||||
[SerializeField] private int punchVibrato = 6;
|
||||
[SerializeField, Range(0f, 1f)] private float punchElasticity = 0.5f;
|
||||
|
||||
[Header("Blocked Feedback (wrong / no tool)")]
|
||||
[Tooltip("Tiny wobble played when the hit deals no damage (no proper tool).")]
|
||||
[SerializeField] private float blockedAngle = 2.5f;
|
||||
[SerializeField] private float blockedDuration = 0.2f;
|
||||
[Tooltip("Fraction of the authored scale a hit shrinks to before springing back (0.85 = squash to 85%).")]
|
||||
[SerializeField] private float hitStartScaleFactor = DefaultHitStartScaleFactor;
|
||||
[Tooltip("How long the hit pop lasts — short and snappy reads as an impact.")]
|
||||
[SerializeField] private float hitBumpDuration = DefaultHitBumpDuration;
|
||||
[Tooltip("OutBack gives the springy overshoot that sells the hit.")]
|
||||
[SerializeField] private Ease hitBumpEase = Ease.OutBack;
|
||||
[Tooltip("Shrink fraction for a blocked (wrong-tool) clunk — subtler than a real hit.")]
|
||||
[SerializeField] private float blockedStartScaleFactor = 0.95f;
|
||||
[Tooltip("Duration of the blocked clunk.")]
|
||||
[SerializeField] private float blockedBumpDuration = 0.1f;
|
||||
|
||||
[Header("Respawn")]
|
||||
[SerializeField] private bool canRespawn = true;
|
||||
@@ -49,6 +64,39 @@ namespace Ashwild.Harvesting
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constants
|
||||
|
||||
/// <summary>
|
||||
/// Fallback pop values, used both as field initializers and as guard defaults in
|
||||
/// <see cref="PlayScalePop"/> — so a prefab predating these fields (Unity deserializes the
|
||||
/// missing values to 0) still pops instead of snapping.
|
||||
/// </summary>
|
||||
private const float DefaultHitBumpDuration = 0.18f;
|
||||
private const float DefaultHitStartScaleFactor = 0.85f;
|
||||
|
||||
#endregion
|
||||
|
||||
#region State
|
||||
|
||||
/// <summary>
|
||||
/// The authored scale captured once, so every hit pop springs back to the true size even when a
|
||||
/// previous pop is interrupted mid-tween (it would otherwise shrink from an already-shrunk scale).
|
||||
/// </summary>
|
||||
private Vector3 baseScale = Vector3.one;
|
||||
|
||||
/// <summary>
|
||||
/// The running pop tween, killed before restarting and on teardown.
|
||||
/// </summary>
|
||||
private Tween hitTween;
|
||||
|
||||
/// <summary>
|
||||
/// Reusable buffer holding the worst-case loot of one hit, so the per-swing capacity check does
|
||||
/// not allocate. Never holds meaningful state between calls.
|
||||
/// </summary>
|
||||
private readonly List<SlotContent> potentialDrops = new List<SlotContent>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Data (read by the registry, server-side)
|
||||
|
||||
/// <summary>
|
||||
@@ -72,12 +120,13 @@ namespace Ashwild.Harvesting
|
||||
public float RespawnDelay => respawnDelay;
|
||||
|
||||
/// <summary>
|
||||
/// Rolls the loot for a hit with the given tool (server-side). Returns the items + quantities
|
||||
/// to grant; the registry does the actual granting. No inventory access here.
|
||||
/// Rolls the loot for a hit (server-side). A null <paramref name="tool"/> means bare hands and
|
||||
/// rolls the bare-hand table; a tool rolls its own listed table, or nothing when it is not listed.
|
||||
/// Returns the items + quantities to grant; the registry does the actual granting.
|
||||
/// </summary>
|
||||
public IEnumerable<(ItemData item, int quantity)> RollDrops(ItemData tool)
|
||||
{
|
||||
ResourceDrop[] table = GetDropsFor(tool);
|
||||
ResourceDrop[] table = tool == null ? bareHandDrops : GetToolDrops(tool);
|
||||
if (table == null) yield break;
|
||||
|
||||
for (int i = 0; i < table.Length; i++)
|
||||
@@ -93,39 +142,158 @@ namespace Ashwild.Harvesting
|
||||
|
||||
#endregion
|
||||
|
||||
#region Hit Application (shared)
|
||||
|
||||
/// <summary>
|
||||
/// The single path for applying a harvest hit — used by tool swings (ToolBehaviour) and by
|
||||
/// bare-hand gathering (Interact). Asks the registry (server) to apply the damage/loot/depletion
|
||||
/// authoritatively, plays the local pop for instant response, and raises the bus event. A null
|
||||
/// <paramref name="tool"/> means bare hands and rolls the bare-hand table.
|
||||
///
|
||||
/// Every reason the hit cannot land is checked *before* any feedback is played, and returns false
|
||||
/// so the caller skips its follow-up work (tool wear). That ordering is the whole point: the pop
|
||||
/// used to play unconditionally, ahead of a request the server could still refuse for half a dozen
|
||||
/// silent reasons — which is precisely what made a refused harvest indistinguishable from a
|
||||
/// successful one that rolled nothing. A hit that plays now is a hit the server will honour.
|
||||
/// </summary>
|
||||
public bool ApplyHit(ItemData tool, float damage)
|
||||
{
|
||||
if (HarvestableRegistry.Instance == null)
|
||||
{
|
||||
Debug.LogError("[Harvestable] No HarvestableRegistry in the scene — cannot harvest.", this);
|
||||
return false;
|
||||
}
|
||||
if (HarvestableRegistry.Instance.IsInactive(Id)) return false;
|
||||
|
||||
if (!HarvestableRegistry.Instance.IsRegistered(Id))
|
||||
{
|
||||
Debug.LogError($"[Harvestable] '{name}' (id {Id}) is not tracked by the registry — the server " +
|
||||
"would drop this hit. Check the console for an id error at startup.", this);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CanCarryDrops(tool))
|
||||
{
|
||||
PlayerEvents.RaiseInteractionRefused("Inventory full");
|
||||
PlayBlockedFeedback();
|
||||
return false;
|
||||
}
|
||||
|
||||
PlayHitEffect();
|
||||
|
||||
ushort toolId = ItemDatabase.Instance != null ? ItemDatabase.Instance.GetId(tool) : (ushort)0;
|
||||
HarvestableRegistry.Instance.RequestHitServerRpc(Id, damage, toolId);
|
||||
|
||||
PlayerEvents.RaiseHarvestableHit(this, damage);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the local player could store everything this source is able to roll. The loot itself is
|
||||
/// rolled by the server, which cannot answer this — the inventory is client-authoritative, so the
|
||||
/// server's copy of it is empty — hence the check runs here, on the only machine that knows, and
|
||||
/// mirrors the pre-check pickups already do.
|
||||
///
|
||||
/// It deliberately tests the *worst case* (every row at its maximum quantity, taken together): the
|
||||
/// alternative is discovering mid-grant that the roll does not fit, at which point the resource is
|
||||
/// already spent. Being pessimistic costs the player a hit they can retry after making room; being
|
||||
/// optimistic costs them loot. Returns true when there is no inventory (offline/editor) or when
|
||||
/// the source has nothing to give, so an empty table never blocks a swing.
|
||||
/// </summary>
|
||||
private bool CanCarryDrops(ItemData tool)
|
||||
{
|
||||
PlayerInventory inventory = PlayerInventory.Instance;
|
||||
if (inventory == null) return true;
|
||||
|
||||
ResourceDrop[] table = tool == null ? bareHandDrops : GetToolDrops(tool);
|
||||
if (table == null || table.Length == 0) return true;
|
||||
|
||||
potentialDrops.Clear();
|
||||
for (int i = 0; i < table.Length; i++)
|
||||
{
|
||||
if (table[i].item == null || table[i].dropChance <= 0f) continue;
|
||||
|
||||
int quantity = Mathf.Max(table[i].minQuantity, table[i].maxQuantity);
|
||||
if (quantity > 0) potentialDrops.Add(SlotContent.Of(table[i].item, quantity, -1));
|
||||
}
|
||||
|
||||
return inventory.CanFitAll(potentialDrops);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IInteractable (bare-hand gathering)
|
||||
|
||||
/// <summary>
|
||||
/// Crosshair label while aiming at this object. Only bare-hand-harvestable objects advertise a
|
||||
/// prompt; trees/rocks return null so the crosshair shows nothing (they need a tool swing).
|
||||
/// </summary>
|
||||
public string InteractionPrompt => allowBareHandHarvest ? interactionVerb : null;
|
||||
|
||||
/// <summary>
|
||||
/// Bare-hand gather via the interact key. Ignored on non-hand-harvestable objects and while the
|
||||
/// player is building (so an interact press mid-placement never snatches a gather). Routes through
|
||||
/// <see cref="ApplyHit"/> with a null tool, yielding the bare-hand loot table.
|
||||
/// </summary>
|
||||
public void Interact()
|
||||
{
|
||||
if (!allowBareHandHarvest) return;
|
||||
if (PlayerEvents.IsBuilding) return;
|
||||
|
||||
ApplyHit(null, bareHandDamage);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Feedback (client-side)
|
||||
|
||||
/// <summary>
|
||||
/// Squash & stretch + bend away from the hit. Played on every client (via the registry) and
|
||||
/// locally on the hitter for instant response.
|
||||
/// The springy scale pop on impact — the same "shrink then spring back with OutBack" look as the
|
||||
/// buildable appear bump. Played on every client (via the registry) and locally on the hitter for
|
||||
/// instant response.
|
||||
/// </summary>
|
||||
public void PlayHitEffect(Vector3 hitDirection)
|
||||
public void PlayHitEffect()
|
||||
{
|
||||
transform.DOComplete();
|
||||
|
||||
Vector3 tiltAxis = TiltAxisFor(hitDirection);
|
||||
transform.DOPunchRotation(tiltAxis * punchAngle, punchDuration, punchVibrato, punchElasticity);
|
||||
|
||||
Vector3 squash = new Vector3(punchScale, -punchScale, punchScale);
|
||||
transform.DOPunchScale(squash, punchDuration, punchVibrato, punchElasticity);
|
||||
|
||||
PlayScalePop(hitStartScaleFactor, hitBumpDuration, hitBumpEase);
|
||||
onHit?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Small "clunk" when a hit deals no damage (player lacks the proper tool). Local only.
|
||||
/// A subtler, non-springy pop when a hit deals no damage (player lacks the proper tool). Local only.
|
||||
/// </summary>
|
||||
public void PlayBlockedFeedback(Vector3 hitDirection)
|
||||
public void PlayBlockedFeedback()
|
||||
{
|
||||
transform.DOComplete();
|
||||
Vector3 tiltAxis = TiltAxisFor(hitDirection);
|
||||
transform.DOPunchRotation(tiltAxis * blockedAngle, blockedDuration, punchVibrato, punchElasticity);
|
||||
PlayScalePop(blockedStartScaleFactor, blockedBumpDuration, Ease.OutQuad);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Kills any running pop, snaps to the shrunk start scale, then tweens back to the authored scale.
|
||||
/// Non-positive serialized values fall back to the constants so the pop still plays.
|
||||
/// </summary>
|
||||
private void PlayScalePop(float factor, float duration, Ease ease)
|
||||
{
|
||||
hitTween?.Kill();
|
||||
|
||||
float f = (factor > 0f && factor < 1f) ? factor : DefaultHitStartScaleFactor;
|
||||
float d = duration > 0f ? duration : DefaultHitBumpDuration;
|
||||
|
||||
transform.localScale = baseScale * f;
|
||||
hitTween = transform.DOScale(baseScale, d).SetEase(ease);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region WorldObject
|
||||
|
||||
/// <summary>
|
||||
/// Captures the authored scale for the pop, then self-registers with the registry as usual.
|
||||
/// </summary>
|
||||
protected override void Start()
|
||||
{
|
||||
baseScale = transform.localScale;
|
||||
base.Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This harvestable belongs to the HarvestableRegistry.
|
||||
/// </summary>
|
||||
@@ -141,43 +309,40 @@ namespace Ashwild.Harvesting
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Brings the object back on respawn.
|
||||
/// Brings the object back on respawn, restoring the authored scale in case a pop was interrupted.
|
||||
/// </summary>
|
||||
public override void ShowActive()
|
||||
{
|
||||
hitTween?.Kill();
|
||||
transform.localScale = baseScale;
|
||||
gameObject.SetActive(true);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity Lifecycle
|
||||
|
||||
/// <summary>
|
||||
/// Kills the pop tween so it never targets a destroyed transform.
|
||||
/// </summary>
|
||||
private void OnDestroy() => hitTween?.Kill();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Drop table matching the given tool, or the fallback entry (tool == null) when the tool
|
||||
/// isn't listed explicitly. Returns null if nothing matches.
|
||||
/// The drop table listed for the given tool, or null when that tool is not listed (no drops).
|
||||
/// </summary>
|
||||
private ResourceDrop[] GetDropsFor(ItemData tool)
|
||||
private ResourceDrop[] GetToolDrops(ItemData tool)
|
||||
{
|
||||
ResourceDrop[] fallback = null;
|
||||
if (toolDrops == null) return null;
|
||||
|
||||
for (int i = 0; i < toolDrops.Length; i++)
|
||||
{
|
||||
if (toolDrops[i].tool == tool) return toolDrops[i].drops;
|
||||
if (toolDrops[i].tool == null) fallback = toolDrops[i].drops;
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rotation axis that tips the object's top in the direction of the hit. Falls back to a fixed
|
||||
/// axis when the hit is vertical or has no horizontal component.
|
||||
/// </summary>
|
||||
private Vector3 TiltAxisFor(Vector3 hitDirection)
|
||||
{
|
||||
Vector3 horizontal = new Vector3(hitDirection.x, 0f, hitDirection.z);
|
||||
if (horizontal.sqrMagnitude < 0.0001f)
|
||||
return Vector3.right;
|
||||
|
||||
return Vector3.Cross(Vector3.up, horizontal.normalized);
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -11,7 +11,11 @@ namespace Ashwild.Interaction
|
||||
{
|
||||
/// <summary>
|
||||
/// Short label describing the action, shown under the crosshair while the
|
||||
/// player is aiming at this target (e.g. "Pick up Wood", "Open").
|
||||
/// player is aiming at this target (e.g. "Pick up Wood", "Open"). Returning
|
||||
/// null or empty means "no interaction offered right now" — PlayerInteractor
|
||||
/// skips such targets entirely (no hover, no Interact call), so a component may
|
||||
/// implement IInteractable yet stay inert until it opts in (e.g. a Harvestable
|
||||
/// that is only gatherable by hand when its bare-hand flag is set).
|
||||
/// </summary>
|
||||
string InteractionPrompt { get; }
|
||||
|
||||
|
||||
@@ -3,111 +3,225 @@ using Ashwild.Player;
|
||||
|
||||
namespace Ashwild.Inventory
|
||||
{
|
||||
/// <summary>
|
||||
/// Keeps the item in the player's hand in sync with the selected hotbar slot. It owns the *timing*
|
||||
/// of a swap, not its look: the put-away and draw animations live in the arms rig, and this
|
||||
/// controller simply waits for them.
|
||||
///
|
||||
/// The wait is what makes the swap read correctly. Destroying the old prefab the instant the
|
||||
/// selection changes makes the item vanish mid-motion, so instead the sequence is: ask for the
|
||||
/// put-away animation, wait for its Animation Event, then destroy, announce the new item (which
|
||||
/// lets the animation binder load its clips first), spawn it, and ask for the draw animation.
|
||||
///
|
||||
/// That wait is guarded by a timeout, because the completion event only exists if someone plays the
|
||||
/// clip. An item whose set has no put-away clip, a rig that was never wired, a disabled arms
|
||||
/// object — any of those would otherwise leave the player stuck holding an item he already
|
||||
/// switched away from, with no error to explain it. The timeout turns a silent deadlock into a
|
||||
/// slightly abrupt swap.
|
||||
/// </summary>
|
||||
[DisallowMultipleComponent]
|
||||
public class HotbarController : MonoBehaviour
|
||||
{
|
||||
#region Serialized Fields
|
||||
|
||||
[Header("References")]
|
||||
[SerializeField] private Transform toolHolder;
|
||||
[SerializeField] private PlayerToolHolder toolHolderAnim;
|
||||
[Tooltip("Attachment joint in the arms rig's right hand (RightHand_Holder_JTN). Held prefabs are parented here so they follow the animated hand.")]
|
||||
[SerializeField] private Transform handHolder;
|
||||
[Tooltip("Origin and forward of the aim ray handed to held items — normally the first-person camera.")]
|
||||
[SerializeField] private Transform raycastOrigin;
|
||||
|
||||
[Header("Swap")]
|
||||
[Tooltip("Seconds to wait for the put-away animation before swapping anyway. Safety net only — a wired rig always completes first.")]
|
||||
[SerializeField] private float stowTimeout = 0.5f;
|
||||
|
||||
#endregion
|
||||
|
||||
#region State
|
||||
|
||||
private PlayerInventory inventory;
|
||||
private GameObject currentHeldObject;
|
||||
private ItemData currentHeldItem;
|
||||
private bool isSwitching;
|
||||
private HeldItemContext heldContext;
|
||||
|
||||
private GameObject currentHeldObject;
|
||||
private ItemData currentHeldItem;
|
||||
|
||||
private bool isStowing;
|
||||
private ItemData pendingItem;
|
||||
private float stowDeadline;
|
||||
private bool stowTimeoutReported;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity Lifecycle
|
||||
|
||||
/// <summary>
|
||||
/// Binds to the local inventory and draws whatever is already selected, without animation —
|
||||
/// spawning into the world should not look like the player just swapped weapons.
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
inventory = PlayerInventory.Instance;
|
||||
inventory.onSelectedSlotChanged.AddListener(OnSelectionChanged);
|
||||
inventory.onSlotChanged.AddListener(OnSlotChanged);
|
||||
if (inventory == null)
|
||||
{
|
||||
Debug.LogError("[HotbarController] No local PlayerInventory — the hand will stay empty.", this);
|
||||
return;
|
||||
}
|
||||
|
||||
inventory.onSelectedSlotChanged.AddListener(HandleSelectionChanged);
|
||||
inventory.onSlotChanged.AddListener(HandleSlotChanged);
|
||||
|
||||
heldContext = new HeldItemContext { RaycastOrigin = raycastOrigin };
|
||||
UpdateHeldItem(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Listens for the put-away animation finishing. Paired with OnDisable.
|
||||
/// </summary>
|
||||
private void OnEnable()
|
||||
{
|
||||
PlayerEvents.UnequipAnimComplete += HandleUnequipAnimComplete;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribes — mirrors OnEnable exactly.
|
||||
/// </summary>
|
||||
private void OnDisable()
|
||||
{
|
||||
PlayerEvents.UnequipAnimComplete -= HandleUnequipAnimComplete;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Drops the inventory listeners; the bus subscription is already handled by OnDisable.
|
||||
/// </summary>
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (inventory != null)
|
||||
if (inventory == null) return;
|
||||
inventory.onSelectedSlotChanged.RemoveListener(HandleSelectionChanged);
|
||||
inventory.onSlotChanged.RemoveListener(HandleSlotChanged);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Forces the swap through when the put-away animation never reports back. Runs only while a
|
||||
/// swap is actually pending, so an idle player costs nothing. The diagnostic is logged once per
|
||||
/// session rather than per swap: the cause is always a wiring or authoring gap that will repeat
|
||||
/// on every single hotbar change, and a console flooded with the same warning hides the rest.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
if (!isStowing) return;
|
||||
if (Time.time < stowDeadline) return;
|
||||
|
||||
if (!stowTimeoutReported)
|
||||
{
|
||||
inventory.onSelectedSlotChanged.RemoveListener(OnSelectionChanged);
|
||||
inventory.onSlotChanged.RemoveListener(OnSlotChanged);
|
||||
stowTimeoutReported = true;
|
||||
Debug.LogWarning("[HotbarController] The put-away animation never completed — swapping anyway " +
|
||||
"(logged once). Check that the arms rig has an ArmsAnimationEvents component and " +
|
||||
"that the Unequip clip carries an AnimUnequipComplete event. Until those clips " +
|
||||
"exist, lower Stow Timeout so the swap stays snappy.", this);
|
||||
}
|
||||
|
||||
CommitSwap();
|
||||
}
|
||||
|
||||
private void OnSelectionChanged(int index)
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
private void HandleSelectionChanged(int index) => UpdateHeldItem(true);
|
||||
|
||||
/// <summary>
|
||||
/// The held item's own stack changed (it was consumed, worn out, or refilled) — redraw only when
|
||||
/// it is the selected slot.
|
||||
/// </summary>
|
||||
private void HandleSlotChanged(int index)
|
||||
{
|
||||
UpdateHeldItem(true);
|
||||
if (index == inventory.SelectedHotbarIndex) UpdateHeldItem(true);
|
||||
}
|
||||
|
||||
private void OnSlotChanged(int index)
|
||||
/// <summary>
|
||||
/// The arms finished putting the old item away, so the swap can complete.
|
||||
/// </summary>
|
||||
private void HandleUnequipAnimComplete()
|
||||
{
|
||||
if (index == inventory.SelectedHotbarIndex)
|
||||
UpdateHeldItem(true);
|
||||
if (isStowing) CommitSwap();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Swap Sequence
|
||||
|
||||
/// <summary>
|
||||
/// Entry point for every reason the hand might need to change. Swaps straight away when there is
|
||||
/// nothing to put away (empty hands, or the very first draw), otherwise starts the put-away
|
||||
/// animation and defers. A selection that changes again mid-stow only updates the target: the
|
||||
/// player who scrolls three slots quickly plays one put-away, not three.
|
||||
/// </summary>
|
||||
private void UpdateHeldItem(bool animate)
|
||||
{
|
||||
InventorySlot selected = inventory.GetSelectedSlot();
|
||||
ItemData newItem = selected.IsEmpty ? null : selected.ItemData;
|
||||
|
||||
if (newItem == currentHeldItem)
|
||||
if (newItem == currentHeldItem && !isStowing) return;
|
||||
|
||||
pendingItem = newItem;
|
||||
|
||||
if (isStowing) return;
|
||||
|
||||
if (!animate || currentHeldObject == null)
|
||||
{
|
||||
CommitSwap();
|
||||
return;
|
||||
|
||||
if (isSwitching)
|
||||
{
|
||||
if (currentHeldObject != null)
|
||||
{
|
||||
Destroy(currentHeldObject);
|
||||
currentHeldObject = null;
|
||||
}
|
||||
isSwitching = false;
|
||||
}
|
||||
|
||||
if (animate && currentHeldObject != null && toolHolderAnim != null)
|
||||
{
|
||||
isSwitching = true;
|
||||
toolHolderAnim.PlayUnequipAnimation(() =>
|
||||
{
|
||||
if (currentHeldObject != null)
|
||||
{
|
||||
Destroy(currentHeldObject);
|
||||
currentHeldObject = null;
|
||||
}
|
||||
currentHeldItem = newItem;
|
||||
SpawnItem(newItem);
|
||||
isSwitching = false;
|
||||
|
||||
if (currentHeldObject != null)
|
||||
toolHolderAnim.PlayEquipAnimation();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
if (currentHeldObject != null)
|
||||
{
|
||||
Destroy(currentHeldObject);
|
||||
currentHeldObject = null;
|
||||
}
|
||||
currentHeldItem = newItem;
|
||||
SpawnItem(newItem);
|
||||
|
||||
if (animate && toolHolderAnim != null && currentHeldObject != null)
|
||||
toolHolderAnim.PlayEquipAnimation();
|
||||
}
|
||||
isStowing = true;
|
||||
stowDeadline = Time.time + stowTimeout;
|
||||
PlayerEvents.RaiseUnequipStarted();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs the actual exchange. HeldItemChanged is raised *before* the new prefab is spawned so
|
||||
/// the animation binder has already loaded the item's clips by the time the draw trigger fires —
|
||||
/// otherwise the first frame of the draw would play the previous item's animation.
|
||||
/// </summary>
|
||||
private void CommitSwap()
|
||||
{
|
||||
isStowing = false;
|
||||
|
||||
if (currentHeldObject != null)
|
||||
{
|
||||
Destroy(currentHeldObject);
|
||||
currentHeldObject = null;
|
||||
}
|
||||
|
||||
currentHeldItem = pendingItem;
|
||||
PlayerEvents.RaiseHeldItemChanged(currentHeldItem);
|
||||
|
||||
SpawnItem(currentHeldItem);
|
||||
|
||||
if (currentHeldItem != null) PlayerEvents.RaiseEquipStarted();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates the item's hand prefab on the rig's hand joint and links its behaviour to the
|
||||
/// player. Parenting to the joint rather than to a free-floating holder is what keeps a tool
|
||||
/// locked in the palm through every animation, with no code following the hand each frame.
|
||||
/// </summary>
|
||||
private void SpawnItem(ItemData item)
|
||||
{
|
||||
if (item == null || item.HandPrefab == null || toolHolder == null)
|
||||
if (item == null || item.HandPrefab == null) return;
|
||||
|
||||
if (handHolder == null)
|
||||
{
|
||||
Debug.LogError($"[HotbarController] No hand holder assigned — '{item.ItemName}' cannot be drawn. " +
|
||||
"Wire the arms rig's RightHand_Holder_JTN.", this);
|
||||
return;
|
||||
}
|
||||
|
||||
currentHeldObject = Instantiate(item.HandPrefab, toolHolder);
|
||||
currentHeldObject = Instantiate(item.HandPrefab, handHolder);
|
||||
|
||||
// Link the freshly spawned held item to the player so its behaviour
|
||||
// (tool, consumable, ...) can interact — only the equipped item is alive.
|
||||
IHeldItemBehaviour behaviour = currentHeldObject.GetComponentInChildren<IHeldItemBehaviour>(true);
|
||||
if (behaviour != null)
|
||||
behaviour.Setup(heldContext, item);
|
||||
behaviour?.Setup(heldContext, item);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Ashwild.Inventory
|
||||
{
|
||||
/// <summary>
|
||||
/// The tabs of the inventory window, used to address a category by name instead of by its position
|
||||
/// in an inspector array — so code that opens the window on a given tab (a crafting station opening
|
||||
/// straight on the craft panel) can never be silently broken by a reorder.
|
||||
/// </summary>
|
||||
public enum InventoryCategory
|
||||
{
|
||||
Inventory = 0,
|
||||
Crafting = 1,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2e1721783444e304d92ede84fdb40b7a
|
||||
@@ -16,13 +16,16 @@ namespace Ashwild.Inventory
|
||||
[SerializeField] private Ease unfillEase = Ease.InQuad;
|
||||
|
||||
private Tweener fillTween;
|
||||
private int categoryIndex;
|
||||
private InventoryCategory category;
|
||||
|
||||
public void Initialize(int index, InventoryCategoryManager manager)
|
||||
/// <summary>
|
||||
/// Binds this button to the category it selects on the strip manager.
|
||||
/// </summary>
|
||||
public void Initialize(InventoryCategory category, InventoryCategoryManager manager)
|
||||
{
|
||||
categoryIndex = index;
|
||||
this.category = category;
|
||||
fillImage.fillAmount = 0f;
|
||||
button.onClick.AddListener(() => manager.SelectCategory(categoryIndex));
|
||||
button.onClick.AddListener(() => manager.SelectCategory(this.category));
|
||||
}
|
||||
|
||||
public void Select()
|
||||
|
||||
@@ -1,41 +1,96 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Ashwild.Inventory
|
||||
{
|
||||
/// <summary>
|
||||
/// Owns the tab strip of the inventory window: which category is selected, which panel is shown, and
|
||||
/// the button fill that goes with it. Categories are addressed by <see cref="InventoryCategory"/>
|
||||
/// rather than by array position, so code that opens the window on a given tab (a crafting station
|
||||
/// opening straight on the craft panel) stays correct if the strip is reordered.
|
||||
/// </summary>
|
||||
public class InventoryCategoryManager : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private InventoryCategoryButton[] categoryButtons;
|
||||
[SerializeField] private GameObject[] panels;
|
||||
[SerializeField] private int defaultCategoryIndex;
|
||||
#region Types
|
||||
|
||||
/// <summary>
|
||||
/// One tab: the category it stands for, its strip button and the panel it shows. Keeping the
|
||||
/// three together in one entry removes the parallel-array class of bug, where a button and a
|
||||
/// panel silently drift out of alignment.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
private class CategoryEntry
|
||||
{
|
||||
public InventoryCategory category;
|
||||
public InventoryCategoryButton button;
|
||||
public GameObject panel;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Serialized Fields
|
||||
|
||||
[Header("Tabs")]
|
||||
[SerializeField] private CategoryEntry[] categories;
|
||||
|
||||
[Header("Default")]
|
||||
[Tooltip("The tab the window lands on when opened without an explicit category.")]
|
||||
[SerializeField] private InventoryCategory defaultCategory = InventoryCategory.Inventory;
|
||||
|
||||
#endregion
|
||||
|
||||
#region State
|
||||
|
||||
private int currentIndex = -1;
|
||||
private bool locked;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
for (int i = 0; i < categoryButtons.Length; i++)
|
||||
{
|
||||
categoryButtons[i].Initialize(i, this);
|
||||
categoryButtons[i].SetFillImmediate(false);
|
||||
}
|
||||
#endregion
|
||||
|
||||
for (int i = 0; i < panels.Length; i++)
|
||||
panels[i].SetActive(false);
|
||||
}
|
||||
#region Unity Lifecycle
|
||||
|
||||
/// <summary>
|
||||
/// Opens the category strip on the inventory category every time — the window must always land on
|
||||
/// the inventory, never stay on craft from a previous session (and chest mode needs the grid).
|
||||
/// Wires each button to its category and parks every panel closed.
|
||||
/// </summary>
|
||||
public void Open()
|
||||
private void Awake()
|
||||
{
|
||||
SelectCategoryImmediate(defaultCategoryIndex);
|
||||
for (int i = 0; i < categories.Length; i++)
|
||||
{
|
||||
CategoryEntry entry = categories[i];
|
||||
if (entry.button != null)
|
||||
{
|
||||
entry.button.Initialize(entry.category, this);
|
||||
entry.button.SetFillImmediate(false);
|
||||
}
|
||||
|
||||
if (entry.panel != null)
|
||||
entry.panel.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public API
|
||||
|
||||
/// <summary>
|
||||
/// Opens the strip on the default category — the window must always land on the inventory, never
|
||||
/// stay on craft from a previous session (and chest mode needs the grid).
|
||||
/// </summary>
|
||||
public void Open() => Open(defaultCategory);
|
||||
|
||||
/// <summary>
|
||||
/// Opens the strip directly on the given category, bypassing the lock (this is the window
|
||||
/// deciding where it lands, not the player switching tabs).
|
||||
/// </summary>
|
||||
public void Open(InventoryCategory category) => SelectCategoryImmediate(IndexOf(category));
|
||||
|
||||
/// <summary>
|
||||
/// Closes every panel.
|
||||
/// </summary>
|
||||
public void Close()
|
||||
{
|
||||
for (int i = 0; i < panels.Length; i++)
|
||||
panels[i].SetActive(false);
|
||||
for (int i = 0; i < categories.Length; i++)
|
||||
if (categories[i].panel != null)
|
||||
categories[i].panel.SetActive(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -44,38 +99,63 @@ namespace Ashwild.Inventory
|
||||
/// </summary>
|
||||
public void SetLocked(bool value) => locked = value;
|
||||
|
||||
public void SelectCategory(int index)
|
||||
/// <summary>
|
||||
/// Switches to a category on the player's request (a strip button), animating the fill. Ignored
|
||||
/// while locked or when the category is already shown.
|
||||
/// </summary>
|
||||
public void SelectCategory(InventoryCategory category)
|
||||
{
|
||||
if (locked) return;
|
||||
if (index == currentIndex) return;
|
||||
if (index < 0 || index >= panels.Length) return;
|
||||
|
||||
// Deselect old
|
||||
if (currentIndex >= 0 && currentIndex < categoryButtons.Length)
|
||||
int index = IndexOf(category);
|
||||
if (index < 0 || index == currentIndex) return;
|
||||
|
||||
if (currentIndex >= 0)
|
||||
{
|
||||
categoryButtons[currentIndex].Deselect();
|
||||
panels[currentIndex].SetActive(false);
|
||||
if (categories[currentIndex].button != null) categories[currentIndex].button.Deselect();
|
||||
if (categories[currentIndex].panel != null) categories[currentIndex].panel.SetActive(false);
|
||||
}
|
||||
|
||||
// Select new
|
||||
currentIndex = index;
|
||||
categoryButtons[currentIndex].Select();
|
||||
panels[currentIndex].SetActive(true);
|
||||
if (categories[currentIndex].button != null) categories[currentIndex].button.Select();
|
||||
if (categories[currentIndex].panel != null) categories[currentIndex].panel.SetActive(true);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Selects a tab without the fill animation, for the frame the window opens on.
|
||||
/// </summary>
|
||||
private void SelectCategoryImmediate(int index)
|
||||
{
|
||||
if (index < 0 || index >= panels.Length) return;
|
||||
if (index < 0 || index >= categories.Length) return;
|
||||
|
||||
if (currentIndex >= 0 && currentIndex < categoryButtons.Length)
|
||||
if (currentIndex >= 0)
|
||||
{
|
||||
categoryButtons[currentIndex].SetFillImmediate(false);
|
||||
panels[currentIndex].SetActive(false);
|
||||
if (categories[currentIndex].button != null) categories[currentIndex].button.SetFillImmediate(false);
|
||||
if (categories[currentIndex].panel != null) categories[currentIndex].panel.SetActive(false);
|
||||
}
|
||||
|
||||
currentIndex = index;
|
||||
categoryButtons[currentIndex].SetFillImmediate(true);
|
||||
panels[currentIndex].SetActive(true);
|
||||
if (categories[currentIndex].button != null) categories[currentIndex].button.SetFillImmediate(true);
|
||||
if (categories[currentIndex].panel != null) categories[currentIndex].panel.SetActive(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Position of a category in the strip, or -1 when it was never wired — logged so a missing tab
|
||||
/// is obvious instead of silently doing nothing.
|
||||
/// </summary>
|
||||
private int IndexOf(InventoryCategory category)
|
||||
{
|
||||
for (int i = 0; i < categories.Length; i++)
|
||||
if (categories[i].category == category) return i;
|
||||
|
||||
Debug.LogError($"[InventoryCategoryManager] No tab wired for category '{category}'.", this);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +72,13 @@ namespace Ashwild.Inventory
|
||||
/// </summary>
|
||||
private Chest boundChest;
|
||||
|
||||
/// <summary>
|
||||
/// The tab the next open must land on, or null for the default. Set by whoever opens the window
|
||||
/// on a specific module (a crafting station wanting the craft tab) and consumed by Show(), so it
|
||||
/// applies to exactly one opening and never leaks into the next.
|
||||
/// </summary>
|
||||
private InventoryCategory? pendingCategory;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity Lifecycle
|
||||
@@ -175,10 +182,24 @@ namespace Ashwild.Inventory
|
||||
UIManager.Instance.OpenPanel(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the window on a specific tab — used by a crafting station, which wants the craft panel
|
||||
/// straight away rather than making the player click across. The tab is picked up in Show().
|
||||
/// </summary>
|
||||
public void OpenAtCategory(InventoryCategory category)
|
||||
{
|
||||
pendingCategory = category;
|
||||
|
||||
if (UIManager.Instance != null)
|
||||
UIManager.Instance.OpenPanel(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the inventory window. Enters chest mode when a chest was bound (right side shows the
|
||||
/// chest module, category is forced to and locked on the inventory), otherwise normal browsing
|
||||
/// (right side shows the description). Always lands on the inventory category, never craft.
|
||||
/// (right side shows the description). Lands on the tab a caller requested, or on the default
|
||||
/// (inventory) otherwise — a plain open never stays on craft from a previous session, and chest
|
||||
/// mode always wins since it needs the grid.
|
||||
/// </summary>
|
||||
public override void Show()
|
||||
{
|
||||
@@ -187,10 +208,14 @@ namespace Ashwild.Inventory
|
||||
|
||||
if (categoryManager != null)
|
||||
{
|
||||
categoryManager.Open();
|
||||
if (!chestMode && pendingCategory.HasValue) categoryManager.Open(pendingCategory.Value);
|
||||
else categoryManager.Open();
|
||||
|
||||
categoryManager.SetLocked(chestMode);
|
||||
}
|
||||
|
||||
pendingCategory = null;
|
||||
|
||||
if (chestMode)
|
||||
{
|
||||
if (descriptionModule != null) descriptionModule.SetActive(false);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using UnityEngine;
|
||||
using Ashwild.Player;
|
||||
|
||||
namespace Ashwild.Inventory
|
||||
{
|
||||
@@ -59,6 +60,12 @@ namespace Ashwild.Inventory
|
||||
[SerializeField] private GameObject worldPrefab;
|
||||
[SerializeField] private GameObject handPrefab;
|
||||
|
||||
[Header("Animation")]
|
||||
[Tooltip("Clips the owner's first-person arms play while holding this item. Empty = bare-hand set.")]
|
||||
[SerializeField] private PlayerAnimationSet armsAnimationSet;
|
||||
[Tooltip("Clips the third-person body plays while holding this item, as seen by other players.")]
|
||||
[SerializeField] private PlayerAnimationSet bodyAnimationSet;
|
||||
|
||||
public string ItemName => itemName;
|
||||
public Sprite Icon => icon;
|
||||
public string Description => description;
|
||||
@@ -92,5 +99,16 @@ namespace Ashwild.Inventory
|
||||
public float FuelSeconds => fuelSeconds;
|
||||
public GameObject WorldPrefab => worldPrefab;
|
||||
public GameObject HandPrefab => handPrefab;
|
||||
|
||||
/// <summary>
|
||||
/// The clip set this item imposes on one of the player's two rigs. Kept as a lookup rather than
|
||||
/// two public properties so a binder can be pointed at either rig from the inspector and stay
|
||||
/// the same component — the arms and the body run identical code on different data.
|
||||
/// Returns null when the item authors nothing, which makes the binder fall back to bare hands.
|
||||
/// </summary>
|
||||
public PlayerAnimationSet GetAnimationSet(PlayerAnimationRig rig)
|
||||
{
|
||||
return rig == PlayerAnimationRig.Arms ? armsAnimationSet : bodyAnimationSet;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using FishNet.Connection;
|
||||
@@ -47,6 +48,18 @@ namespace Ashwild.Inventory
|
||||
private InventorySlot[] slots;
|
||||
private int selectedHotbarIndex;
|
||||
|
||||
/// <summary>
|
||||
/// Scratch copy of the slots used by the capacity dry run, kept as a field so a per-swing
|
||||
/// capacity check does not allocate. Never holds meaningful state between calls.
|
||||
/// </summary>
|
||||
private SlotContent[] fitSnapshot;
|
||||
|
||||
/// <summary>
|
||||
/// One-element buffer so the single-item <see cref="CanFit"/> can reuse the multi-item dry run
|
||||
/// without allocating an array on every call.
|
||||
/// </summary>
|
||||
private readonly SlotContent[] singleFitBuffer = new SlotContent[1];
|
||||
|
||||
public int InventorySize => inventorySize;
|
||||
public int HotbarSize => hotbarSize;
|
||||
public int SelectedHotbarIndex => selectedHotbarIndex;
|
||||
@@ -156,6 +169,12 @@ namespace Ashwild.Inventory
|
||||
/// <summary>
|
||||
/// Runs on the owning client: resolves the granted item and adds it locally, restoring its
|
||||
/// remaining uses and honouring the slot the player aimed at when one was requested.
|
||||
///
|
||||
/// A grant has already left its source by the time it arrives, so anything that does not fit must
|
||||
/// not simply evaporate the way it used to. The interactions that can be refused pre-check their
|
||||
/// capacity before asking the server, which makes an overflow here a last-resort case (the
|
||||
/// inventory filled up while the request was in flight); it is put back into the world at the
|
||||
/// player's feet, announced, and logged — never destroyed.
|
||||
/// </summary>
|
||||
[TargetRpc]
|
||||
private void TargetGrantItem(NetworkConnection conn, ushort itemId, int quantity, int uses, int preferredIndex, bool isTransfer)
|
||||
@@ -167,7 +186,13 @@ namespace Ashwild.Inventory
|
||||
return;
|
||||
}
|
||||
|
||||
AddItem(item, quantity, uses, preferredIndex, isTransfer);
|
||||
AddItem(item, quantity, uses, preferredIndex, isTransfer, out int leftover);
|
||||
if (leftover <= 0) return;
|
||||
|
||||
Debug.LogWarning($"[PlayerInventory] Inventory full — {leftover}x '{item.ItemName}' could not be " +
|
||||
"stored and was returned to the world.", this);
|
||||
PlayerEvents.RaiseInteractionRefused("Inventory full");
|
||||
SpawnInWorld(item, leftover, uses);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -269,7 +294,19 @@ namespace Ashwild.Inventory
|
||||
/// pulling an item a co-op partner left in a chest is a genuine first acquisition.
|
||||
/// </summary>
|
||||
public bool AddItem(ItemData item, int quantity = 1, int uses = -1, int preferredIndex = -1, bool isTransfer = false)
|
||||
=> AddItem(item, quantity, uses, preferredIndex, isTransfer, out _);
|
||||
|
||||
/// <summary>
|
||||
/// Same as <see cref="AddItem(ItemData,int,int,int,bool)"/> but reports how many units could not be
|
||||
/// placed. Callers that received the stack from the server need that number: whatever is left over
|
||||
/// has already left its source (the harvestable is damaged, the pickup is claimed) and would simply
|
||||
/// cease to exist if it were dropped on the floor here, which is exactly how loot used to go
|
||||
/// missing without a single log line.
|
||||
/// </summary>
|
||||
public bool AddItem(ItemData item, int quantity, int uses, int preferredIndex, bool isTransfer, out int leftover)
|
||||
{
|
||||
leftover = quantity;
|
||||
|
||||
if (item == null)
|
||||
{
|
||||
Debug.LogError("[PlayerInventory] AddItem called with no ItemData — pickup ignored.", this);
|
||||
@@ -287,7 +324,9 @@ namespace Ashwild.Inventory
|
||||
for (int i = 0; i < inventorySize && !incoming.IsEmpty; i++)
|
||||
if (slots[i].IsEmpty) StackInto(i, ref incoming);
|
||||
|
||||
int added = quantity - (incoming.IsEmpty ? 0 : incoming.Quantity);
|
||||
leftover = incoming.IsEmpty ? 0 : incoming.Quantity;
|
||||
|
||||
int added = quantity - leftover;
|
||||
if (added > 0)
|
||||
{
|
||||
onItemAdded?.Invoke(item, added);
|
||||
@@ -317,23 +356,43 @@ namespace Ashwild.Inventory
|
||||
{
|
||||
if (item == null) return false;
|
||||
|
||||
SlotContent incoming = SlotContent.Of(item, quantity, -1);
|
||||
singleFitBuffer[0] = SlotContent.Of(item, quantity, -1);
|
||||
return CanFitAll(singleFitBuffer);
|
||||
}
|
||||
|
||||
for (int i = 0; i < inventorySize && !incoming.IsEmpty; i++)
|
||||
/// <summary>
|
||||
/// Returns whether several stacks would fit *together*, without mutating anything. Asking
|
||||
/// <see cref="CanFit"/> once per item is not equivalent and quietly over-promises: with a single
|
||||
/// empty slot left, two different items each answer "yes" on their own, then the second one has
|
||||
/// nowhere to go. Harvest loot rolls several items at once, so it needs the combined answer — the
|
||||
/// dry run therefore places each stack into a running snapshot of the slots, exactly as the real
|
||||
/// add would, and fails as soon as one leftover cannot be placed.
|
||||
/// </summary>
|
||||
public bool CanFitAll(IReadOnlyList<SlotContent> incoming)
|
||||
{
|
||||
if (incoming == null || incoming.Count == 0) return true;
|
||||
|
||||
if (fitSnapshot == null || fitSnapshot.Length != inventorySize)
|
||||
fitSnapshot = new SlotContent[inventorySize];
|
||||
|
||||
for (int i = 0; i < inventorySize; i++)
|
||||
fitSnapshot[i] = ReadSlot(i);
|
||||
|
||||
for (int n = 0; n < incoming.Count; n++)
|
||||
{
|
||||
if (slots[i].IsEmpty) continue;
|
||||
SlotContent target = ReadSlot(i);
|
||||
SlotTransfer.TryStack(ref incoming, ref target);
|
||||
SlotContent pending = incoming[n];
|
||||
if (pending.IsEmpty) continue;
|
||||
|
||||
for (int i = 0; i < inventorySize && !pending.IsEmpty; i++)
|
||||
if (!fitSnapshot[i].IsEmpty) SlotTransfer.TryStack(ref pending, ref fitSnapshot[i]);
|
||||
|
||||
for (int i = 0; i < inventorySize && !pending.IsEmpty; i++)
|
||||
if (fitSnapshot[i].IsEmpty) SlotTransfer.TryStack(ref pending, ref fitSnapshot[i]);
|
||||
|
||||
if (!pending.IsEmpty) return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < inventorySize && !incoming.IsEmpty; i++)
|
||||
{
|
||||
if (!slots[i].IsEmpty) continue;
|
||||
SlotContent target = SlotContent.Empty;
|
||||
SlotTransfer.TryStack(ref incoming, ref target);
|
||||
}
|
||||
|
||||
return incoming.IsEmpty;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void RemoveItem(int index, int quantity = 1)
|
||||
@@ -408,21 +467,36 @@ namespace Ashwild.Inventory
|
||||
|
||||
ItemData item = slots[index].ItemData;
|
||||
int uses = item.HasUses ? slots[index].CurrentUses : -1;
|
||||
Transform origin = dropOrigin != null ? dropOrigin : transform;
|
||||
if (item.WorldPrefab != null && origin != null && PickableRegistry.Instance != null)
|
||||
{
|
||||
Vector3 dropPos = origin.position + origin.forward * dropForwardDistance;
|
||||
ushort id = ItemDatabase.Instance != null ? ItemDatabase.Instance.GetId(item) : (ushort)0;
|
||||
if (id != 0)
|
||||
PickableRegistry.Instance.RequestDropServerRpc(id, quantity, uses, dropPos, origin.rotation);
|
||||
else
|
||||
Debug.LogWarning($"[PlayerInventory] '{item.ItemName}' is not in the ItemDatabase — drop not spawned. Run Rebuild Item Database.", this);
|
||||
}
|
||||
|
||||
SpawnInWorld(item, quantity, uses);
|
||||
|
||||
RemoveItem(index, quantity);
|
||||
PlayerEvents.RaiseItemDropped(item, quantity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asks the server to spawn a stack in front of the player, without touching the inventory. Shared
|
||||
/// by the deliberate drop (which removes the stack first) and by the overflow safety net (whose
|
||||
/// stack was never stored), so both go through the same authoritative drop path.
|
||||
/// </summary>
|
||||
private void SpawnInWorld(ItemData item, int quantity, int uses)
|
||||
{
|
||||
if (item == null || quantity <= 0) return;
|
||||
|
||||
Transform origin = dropOrigin != null ? dropOrigin : transform;
|
||||
if (item.WorldPrefab == null || origin == null || PickableRegistry.Instance == null) return;
|
||||
|
||||
ushort id = ItemDatabase.Instance != null ? ItemDatabase.Instance.GetId(item) : (ushort)0;
|
||||
if (id == 0)
|
||||
{
|
||||
Debug.LogWarning($"[PlayerInventory] '{item.ItemName}' is not in the ItemDatabase — drop not spawned. Run Rebuild Item Database.", this);
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 dropPos = origin.position + origin.forward * dropForwardDistance;
|
||||
PickableRegistry.Instance.RequestDropServerRpc(id, quantity, uses, dropPos, origin.rotation);
|
||||
}
|
||||
|
||||
public void SelectHotbarSlot(int index)
|
||||
{
|
||||
if (index < 0 || index >= hotbarSize) return;
|
||||
|
||||
@@ -60,29 +60,43 @@ namespace Ashwild.Network
|
||||
/// <summary>
|
||||
/// Owner-side entry: tells the server the local player hit a harvestable. The server applies
|
||||
/// the (client-computed) damage, grants loot, broadcasts feedback, and handles depletion.
|
||||
///
|
||||
/// The two ways this can go wrong are reported back to the requesting player instead of being
|
||||
/// swallowed: an id the server does not track (a scene object with a missing or duplicate id) and
|
||||
/// a connection whose inventory cannot be resolved. Both used to end in a bare `return` — the
|
||||
/// player saw their hit land and gained nothing, with no trace anywhere on their machine. Losing
|
||||
/// the race against another player is the one case left unreported, since the object disappearing
|
||||
/// says it plainly enough.
|
||||
/// </summary>
|
||||
[ServerRpc(RequireOwnership = false)]
|
||||
public void RequestHitServerRpc(int id, float damage, ushort toolItemId, Vector3 hitDirection, NetworkConnection conn = null)
|
||||
public void RequestHitServerRpc(int id, float damage, ushort toolItemId, NetworkConnection conn = null)
|
||||
{
|
||||
if (IsInactive(id)) return;
|
||||
if (!TryGetObject(id, out WorldObject obj) || obj is not Harvestable harvestable) return;
|
||||
|
||||
if (!TryGetObject(id, out WorldObject obj) || obj is not Harvestable harvestable)
|
||||
{
|
||||
ReportFailure(conn, id, "no harvestable is registered with this id on the server");
|
||||
return;
|
||||
}
|
||||
|
||||
PlayerInventory inventory = ResolveInventory(conn);
|
||||
if (inventory == null)
|
||||
{
|
||||
ReportFailure(conn, id, "the requesting player's inventory could not be resolved — the loot would be lost");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!health.TryGetValue(id, out float hp)) hp = harvestable.MaxHealth;
|
||||
hp -= damage;
|
||||
health[id] = hp;
|
||||
|
||||
// Grant the rolled loot to the hitting player.
|
||||
ItemData tool = ItemDatabase.Instance != null ? ItemDatabase.Instance.GetItem(toolItemId) : null;
|
||||
PlayerInventory inventory = ResolveInventory(conn);
|
||||
if (inventory != null)
|
||||
{
|
||||
foreach ((ItemData item, int quantity) in harvestable.RollDrops(tool))
|
||||
inventory.GrantItemFromServer(item, quantity);
|
||||
}
|
||||
foreach ((ItemData item, int quantity) in harvestable.RollDrops(tool))
|
||||
inventory.GrantItemFromServer(item, quantity);
|
||||
|
||||
// Feedback for everyone except the hitter (who already played it locally for responsiveness).
|
||||
int hitterClientId = conn != null ? conn.ClientId : -1;
|
||||
PlayHitObserversRpc(id, hitDirection, hitterClientId);
|
||||
PlayHitObserversRpc(id, hitterClientId);
|
||||
|
||||
if (hp <= 0f)
|
||||
{
|
||||
@@ -101,11 +115,11 @@ namespace Ashwild.Network
|
||||
/// Plays the hit feedback on every client except the one who threw the hit.
|
||||
/// </summary>
|
||||
[ObserversRpc]
|
||||
private void PlayHitObserversRpc(int id, Vector3 hitDirection, int hitterClientId)
|
||||
private void PlayHitObserversRpc(int id, int hitterClientId)
|
||||
{
|
||||
if (LocalConnection != null && LocalConnection.ClientId == hitterClientId) return;
|
||||
if (TryGetObject(id, out WorldObject obj) && obj is Harvestable harvestable)
|
||||
harvestable.PlayHitEffect(hitDirection);
|
||||
harvestable.PlayHitEffect();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user