added inventory and stamina system
This commit is contained in:
8
Assets/Items.meta
Normal file
8
Assets/Items.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 94c8f7f884ccd0e47a66f83dc8fe8b0e
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
20
Assets/Items/BhopBoots_Item.asset
Normal file
20
Assets/Items/BhopBoots_Item.asset
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
%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: b549159f64e1b164082e2e4ae4d28ac1, type: 3}
|
||||||
|
m_Name: BhopBoots_Item
|
||||||
|
m_EditorClassIdentifier: Assembly-CSharp::ItemDefinition
|
||||||
|
itemName: Bunny Hop Boots
|
||||||
|
icon: {fileID: 0}
|
||||||
|
type: 0
|
||||||
|
weaponPrefab: {fileID: 0}
|
||||||
|
isEquippable: 1
|
||||||
|
description: Illegal footwear. Gives 200 stamina while worn. Do not wear near cliffs.
|
||||||
8
Assets/Items/BhopBoots_Item.asset.meta
Normal file
8
Assets/Items/BhopBoots_Item.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e09f7b05a155ba8449a0f0d8412ebe9d
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
19
Assets/Items/GunSplat_Item.asset
Normal file
19
Assets/Items/GunSplat_Item.asset
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
%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: b549159f64e1b164082e2e4ae4d28ac1, type: 3}
|
||||||
|
m_Name: GunSplat_Item
|
||||||
|
m_EditorClassIdentifier: Assembly-CSharp::ItemDefinition
|
||||||
|
itemName: Gun Splat
|
||||||
|
icon: {fileID: 0}
|
||||||
|
type: 2
|
||||||
|
weaponPrefab: {fileID: 8985654986098955209, guid: aa9cce2c129fa1a41bf5a539b136f7a6, type: 3}
|
||||||
|
description: A janky lidar-scanned sidearm. Shoots first, looks weird always.
|
||||||
8
Assets/Items/GunSplat_Item.asset.meta
Normal file
8
Assets/Items/GunSplat_Item.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2b5a641a42945604c9ffa88754a9f6d1
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -1,5 +1,18 @@
|
|||||||
%YAML 1.1
|
%YAML 1.1
|
||||||
%TAG !u! tag:unity3d.com,2011:
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!114 &-809172725653918854
|
||||||
|
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
|
--- !u!21 &2100000
|
||||||
Material:
|
Material:
|
||||||
serializedVersion: 8
|
serializedVersion: 8
|
||||||
@@ -30,8 +43,22 @@ Material:
|
|||||||
m_Texture: {fileID: -5390149515647955136, guid: 24bb6e32322f940e59304941b7849582, type: 3}
|
m_Texture: {fileID: -5390149515647955136, guid: 24bb6e32322f940e59304941b7849582, type: 3}
|
||||||
m_Scale: {x: 1, y: 1}
|
m_Scale: {x: 1, y: 1}
|
||||||
m_Offset: {x: 0, y: 0}
|
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_Ints: []
|
||||||
m_Floats:
|
m_Floats:
|
||||||
|
- _AlphaClip: 0
|
||||||
|
- _AlphaToMask: 0
|
||||||
- _BUILTIN_AlphaClip: 0
|
- _BUILTIN_AlphaClip: 0
|
||||||
- _BUILTIN_Blend: 0
|
- _BUILTIN_Blend: 0
|
||||||
- _BUILTIN_CullMode: 2
|
- _BUILTIN_CullMode: 2
|
||||||
@@ -43,6 +70,19 @@ Material:
|
|||||||
- _BUILTIN_ZTest: 4
|
- _BUILTIN_ZTest: 4
|
||||||
- _BUILTIN_ZWrite: 1
|
- _BUILTIN_ZWrite: 1
|
||||||
- _BUILTIN_ZWriteControl: 0
|
- _BUILTIN_ZWriteControl: 0
|
||||||
|
- _Blend: 0
|
||||||
|
- _CastShadows: 1
|
||||||
|
- _Cull: 2
|
||||||
|
- _DstBlend: 0
|
||||||
|
- _DstBlendAlpha: 0
|
||||||
|
- _QueueControl: 1
|
||||||
|
- _QueueOffset: 0
|
||||||
|
- _SrcBlend: 1
|
||||||
|
- _SrcBlendAlpha: 1
|
||||||
|
- _Surface: 0
|
||||||
|
- _ZTest: 4
|
||||||
|
- _ZWrite: 1
|
||||||
|
- _ZWriteControl: 0
|
||||||
- alphaCutoff: 0
|
- alphaCutoff: 0
|
||||||
- baseColorTexture_texCoord: 0
|
- baseColorTexture_texCoord: 0
|
||||||
m_Colors:
|
m_Colors:
|
||||||
|
|||||||
8
Assets/Prefabs/Pickups.meta
Normal file
8
Assets/Prefabs/Pickups.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c47b29375f392f14385951a3f2e89a56
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
161
Assets/Prefabs/Pickups/BhopBoots_Pickup.prefab
Normal file
161
Assets/Prefabs/Pickups/BhopBoots_Pickup.prefab
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!1 &976112319745431805
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 4275644691617312389}
|
||||||
|
- component: {fileID: 5061255202814967808}
|
||||||
|
- component: {fileID: 1990460956270860801}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: Visual
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!4 &4275644691617312389
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 976112319745431805}
|
||||||
|
serializedVersion: 2
|
||||||
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||||
|
m_LocalScale: {x: 0.4, y: 0.2, z: 0.7}
|
||||||
|
m_ConstrainProportionsScale: 0
|
||||||
|
m_Children: []
|
||||||
|
m_Father: {fileID: 9221872812818225285}
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
--- !u!33 &5061255202814967808
|
||||||
|
MeshFilter:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 976112319745431805}
|
||||||
|
m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
|
||||||
|
--- !u!23 &1990460956270860801
|
||||||
|
MeshRenderer:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 976112319745431805}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_CastShadows: 1
|
||||||
|
m_ReceiveShadows: 1
|
||||||
|
m_DynamicOccludee: 1
|
||||||
|
m_StaticShadowCaster: 0
|
||||||
|
m_MotionVectors: 1
|
||||||
|
m_LightProbeUsage: 1
|
||||||
|
m_ReflectionProbeUsage: 1
|
||||||
|
m_RayTracingMode: 2
|
||||||
|
m_RayTraceProcedural: 0
|
||||||
|
m_RayTracingAccelStructBuildFlagsOverride: 0
|
||||||
|
m_RayTracingAccelStructBuildFlags: 1
|
||||||
|
m_SmallMeshCulling: 1
|
||||||
|
m_ForceMeshLod: -1
|
||||||
|
m_MeshLodSelectionBias: 0
|
||||||
|
m_RenderingLayerMask: 1
|
||||||
|
m_RendererPriority: 0
|
||||||
|
m_Materials:
|
||||||
|
- {fileID: 0}
|
||||||
|
m_StaticBatchInfo:
|
||||||
|
firstSubMesh: 0
|
||||||
|
subMeshCount: 0
|
||||||
|
m_StaticBatchRoot: {fileID: 0}
|
||||||
|
m_ProbeAnchor: {fileID: 0}
|
||||||
|
m_LightProbeVolumeOverride: {fileID: 0}
|
||||||
|
m_ScaleInLightmap: 1
|
||||||
|
m_ReceiveGI: 1
|
||||||
|
m_PreserveUVs: 1
|
||||||
|
m_IgnoreNormalsForChartDetection: 0
|
||||||
|
m_ImportantGI: 0
|
||||||
|
m_StitchLightmapSeams: 1
|
||||||
|
m_SelectedEditorRenderState: 3
|
||||||
|
m_MinimumChartSize: 4
|
||||||
|
m_AutoUVMaxDistance: 0.5
|
||||||
|
m_AutoUVMaxAngle: 89
|
||||||
|
m_LightmapParameters: {fileID: 0}
|
||||||
|
m_GlobalIlluminationMeshLod: 0
|
||||||
|
m_SortingLayerID: 0
|
||||||
|
m_SortingLayer: 0
|
||||||
|
m_SortingOrder: 0
|
||||||
|
m_MaskInteraction: 0
|
||||||
|
m_AdditionalVertexStreams: {fileID: 0}
|
||||||
|
--- !u!1 &5967924902892500278
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 9221872812818225285}
|
||||||
|
- component: {fileID: 8317664859568879488}
|
||||||
|
- component: {fileID: 6478899211359510473}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: BhopBoots_Pickup
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!4 &9221872812818225285
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 5967924902892500278}
|
||||||
|
serializedVersion: 2
|
||||||
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||||
|
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||||
|
m_ConstrainProportionsScale: 0
|
||||||
|
m_Children:
|
||||||
|
- {fileID: 4275644691617312389}
|
||||||
|
m_Father: {fileID: 0}
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
--- !u!114 &8317664859568879488
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 5967924902892500278}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 95709fa8fd6c75045b1ef8e4f505c152, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: Assembly-CSharp::PickupItem
|
||||||
|
definition: {fileID: 11400000, guid: e09f7b05a155ba8449a0f0d8412ebe9d, type: 2}
|
||||||
|
itemName: Item
|
||||||
|
itemIcon: {fileID: 0}
|
||||||
|
spinSpeed: 140
|
||||||
|
bobHeight: 0.2
|
||||||
|
bobSpeed: 2.8
|
||||||
|
pickupRadius: 2.2
|
||||||
|
pickupKey: 101
|
||||||
|
pickupParticlesPrefab: {fileID: 0}
|
||||||
|
pickupSound: {fileID: 0}
|
||||||
|
--- !u!114 &6478899211359510473
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 5967924902892500278}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 1ce88c8ee6fd8a0469215a541f72f835, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: Assembly-CSharp::StaminaBoostPickup
|
||||||
|
newMaxStamina: 200
|
||||||
7
Assets/Prefabs/Pickups/BhopBoots_Pickup.prefab.meta
Normal file
7
Assets/Prefabs/Pickups/BhopBoots_Pickup.prefab.meta
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9ead08e094f7bc74c86e58083dfcdf75
|
||||||
|
PrefabImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Prefabs/Weapons.meta
Normal file
8
Assets/Prefabs/Weapons.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ef5d1bcacc1d9e346b6e53fbcec62ad8
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
141
Assets/Prefabs/Weapons/GunSplat.prefab
Normal file
141
Assets/Prefabs/Weapons/GunSplat.prefab
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!1 &8985654986098955209
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 6346070617587720957}
|
||||||
|
- component: {fileID: 8585139455581857007}
|
||||||
|
- component: {fileID: 5387598106901245531}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: GunSplat
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!4 &6346070617587720957
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 8985654986098955209}
|
||||||
|
serializedVersion: 2
|
||||||
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||||
|
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||||
|
m_ConstrainProportionsScale: 0
|
||||||
|
m_Children:
|
||||||
|
- {fileID: 6241336930636794420}
|
||||||
|
m_Father: {fileID: 0}
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
--- !u!114 &8585139455581857007
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 8985654986098955209}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: e348b4e172ba23d45960c0071cc09b1f, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: Assembly-CSharp::SimpleGun
|
||||||
|
damage: 30
|
||||||
|
range: 120
|
||||||
|
fireRate: 6
|
||||||
|
maxAmmo: 24
|
||||||
|
currentAmmo: 0
|
||||||
|
isAutomatic: 0
|
||||||
|
recoilKickback: 0.08
|
||||||
|
recoilKickUp: 0.04
|
||||||
|
recoilRecoverySpeed: 12
|
||||||
|
enableInternalBob: 0
|
||||||
|
bobFrequency: 10
|
||||||
|
bobHorizontalAmplitude: 0.05
|
||||||
|
bobVerticalAmplitude: 0.03
|
||||||
|
sprintBobMultiplier: 1.5
|
||||||
|
bobReturnSpeed: 6
|
||||||
|
fpsCam: {fileID: 0}
|
||||||
|
--- !u!114 &5387598106901245531
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 8985654986098955209}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 59433c687536a60418803799027ddcc3, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: Assembly-CSharp::WeaponViewmodel
|
||||||
|
positionOffset: {x: 0.13, y: -0.25, z: 0.45}
|
||||||
|
rotationOffset: {x: 0.03, y: 0, z: 0}
|
||||||
|
scale: {x: 1, y: 1, z: 1}
|
||||||
|
--- !u!1001 &8738524764220850214
|
||||||
|
PrefabInstance:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Modification:
|
||||||
|
serializedVersion: 3
|
||||||
|
m_TransformParent: {fileID: 6346070617587720957}
|
||||||
|
m_Modifications:
|
||||||
|
- target: {fileID: -8098169881513260187, guid: 4f15ea45bfb5e4f11bc2bce50e39fc10, type: 3}
|
||||||
|
propertyPath: m_Name
|
||||||
|
value: Visual
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 3447742079981357586, guid: 4f15ea45bfb5e4f11bc2bce50e39fc10, type: 3}
|
||||||
|
propertyPath: m_LocalPosition.x
|
||||||
|
value: 0
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 3447742079981357586, guid: 4f15ea45bfb5e4f11bc2bce50e39fc10, type: 3}
|
||||||
|
propertyPath: m_LocalPosition.y
|
||||||
|
value: 0
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 3447742079981357586, guid: 4f15ea45bfb5e4f11bc2bce50e39fc10, type: 3}
|
||||||
|
propertyPath: m_LocalPosition.z
|
||||||
|
value: 0
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 3447742079981357586, guid: 4f15ea45bfb5e4f11bc2bce50e39fc10, type: 3}
|
||||||
|
propertyPath: m_LocalRotation.w
|
||||||
|
value: 1
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 3447742079981357586, guid: 4f15ea45bfb5e4f11bc2bce50e39fc10, type: 3}
|
||||||
|
propertyPath: m_LocalRotation.x
|
||||||
|
value: 0
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 3447742079981357586, guid: 4f15ea45bfb5e4f11bc2bce50e39fc10, type: 3}
|
||||||
|
propertyPath: m_LocalRotation.y
|
||||||
|
value: 0
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 3447742079981357586, guid: 4f15ea45bfb5e4f11bc2bce50e39fc10, type: 3}
|
||||||
|
propertyPath: m_LocalRotation.z
|
||||||
|
value: 0
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 3447742079981357586, guid: 4f15ea45bfb5e4f11bc2bce50e39fc10, type: 3}
|
||||||
|
propertyPath: m_LocalEulerAnglesHint.x
|
||||||
|
value: 0
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 3447742079981357586, guid: 4f15ea45bfb5e4f11bc2bce50e39fc10, type: 3}
|
||||||
|
propertyPath: m_LocalEulerAnglesHint.y
|
||||||
|
value: 0
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 3447742079981357586, guid: 4f15ea45bfb5e4f11bc2bce50e39fc10, type: 3}
|
||||||
|
propertyPath: m_LocalEulerAnglesHint.z
|
||||||
|
value: 0
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
m_RemovedComponents: []
|
||||||
|
m_RemovedGameObjects: []
|
||||||
|
m_AddedGameObjects: []
|
||||||
|
m_AddedComponents: []
|
||||||
|
m_SourcePrefab: {fileID: 3150474306388093854, guid: 4f15ea45bfb5e4f11bc2bce50e39fc10, type: 3}
|
||||||
|
--- !u!4 &6241336930636794420 stripped
|
||||||
|
Transform:
|
||||||
|
m_CorrespondingSourceObject: {fileID: 3447742079981357586, guid: 4f15ea45bfb5e4f11bc2bce50e39fc10, type: 3}
|
||||||
|
m_PrefabInstance: {fileID: 8738524764220850214}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
7
Assets/Prefabs/Weapons/GunSplat.prefab.meta
Normal file
7
Assets/Prefabs/Weapons/GunSplat.prefab.meta
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: aa9cce2c129fa1a41bf5a539b136f7a6
|
||||||
|
PrefabImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -340,9 +340,22 @@ PrefabInstance:
|
|||||||
value: 0
|
value: 0
|
||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
m_RemovedComponents: []
|
m_RemovedComponents: []
|
||||||
m_RemovedGameObjects: []
|
m_RemovedGameObjects:
|
||||||
|
- {fileID: 2029899304289424255, guid: 33de4b621d70a49aab5df775f2b826ef, type: 3}
|
||||||
m_AddedGameObjects: []
|
m_AddedGameObjects: []
|
||||||
m_AddedComponents: []
|
m_AddedComponents:
|
||||||
|
- targetCorrespondingSourceObject: {fileID: 6882903375964528458, guid: 33de4b621d70a49aab5df775f2b826ef, type: 3}
|
||||||
|
insertIndex: -1
|
||||||
|
addedObject: {fileID: 2039163533}
|
||||||
|
- targetCorrespondingSourceObject: {fileID: 6882903375964528458, guid: 33de4b621d70a49aab5df775f2b826ef, type: 3}
|
||||||
|
insertIndex: -1
|
||||||
|
addedObject: {fileID: 2039163534}
|
||||||
|
- targetCorrespondingSourceObject: {fileID: 6882903375964528458, guid: 33de4b621d70a49aab5df775f2b826ef, type: 3}
|
||||||
|
insertIndex: -1
|
||||||
|
addedObject: {fileID: 2039163535}
|
||||||
|
- targetCorrespondingSourceObject: {fileID: 6882903375964528458, guid: 33de4b621d70a49aab5df775f2b826ef, type: 3}
|
||||||
|
insertIndex: -1
|
||||||
|
addedObject: {fileID: 2039163536}
|
||||||
m_SourcePrefab: {fileID: 100100000, guid: 33de4b621d70a49aab5df775f2b826ef, type: 3}
|
m_SourcePrefab: {fileID: 100100000, guid: 33de4b621d70a49aab5df775f2b826ef, type: 3}
|
||||||
--- !u!1001 &197010634
|
--- !u!1001 &197010634
|
||||||
PrefabInstance:
|
PrefabInstance:
|
||||||
@@ -6459,52 +6472,6 @@ MeshCollider:
|
|||||||
m_Convex: 0
|
m_Convex: 0
|
||||||
m_CookingOptions: 30
|
m_CookingOptions: 30
|
||||||
m_Mesh: {fileID: 0}
|
m_Mesh: {fileID: 0}
|
||||||
--- !u!1 &487503082
|
|
||||||
GameObject:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
serializedVersion: 6
|
|
||||||
m_Component:
|
|
||||||
- component: {fileID: 487503084}
|
|
||||||
- component: {fileID: 487503083}
|
|
||||||
m_Layer: 0
|
|
||||||
m_Name: GameObject (1)
|
|
||||||
m_TagString: Untagged
|
|
||||||
m_Icon: {fileID: 0}
|
|
||||||
m_NavMeshLayer: 0
|
|
||||||
m_StaticEditorFlags: 0
|
|
||||||
m_IsActive: 1
|
|
||||||
--- !u!114 &487503083
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 487503082}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: 180c6606991a4b64f812f9713907fd0c, type: 3}
|
|
||||||
m_Name:
|
|
||||||
m_EditorClassIdentifier: Assembly-CSharp::EnemySpawner
|
|
||||||
enemyType: 2
|
|
||||||
healthOverride: 0
|
|
||||||
--- !u!4 &487503084
|
|
||||||
Transform:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 487503082}
|
|
||||||
serializedVersion: 2
|
|
||||||
m_LocalRotation: {x: 0.07095997, y: 0.5789249, z: -0.050682172, w: 0.8107047}
|
|
||||||
m_LocalPosition: {x: 243.82, y: -35.34, z: 91.07}
|
|
||||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
|
||||||
m_ConstrainProportionsScale: 0
|
|
||||||
m_Children: []
|
|
||||||
m_Father: {fileID: 0}
|
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
|
||||||
--- !u!1 &571790233 stripped
|
--- !u!1 &571790233 stripped
|
||||||
GameObject:
|
GameObject:
|
||||||
m_CorrespondingSourceObject: {fileID: -8098169881513260187, guid: 3df8bb31de452e64fa85a1265898d698, type: 3}
|
m_CorrespondingSourceObject: {fileID: -8098169881513260187, guid: 3df8bb31de452e64fa85a1265898d698, type: 3}
|
||||||
@@ -6532,52 +6499,6 @@ MeshCollider:
|
|||||||
m_Convex: 0
|
m_Convex: 0
|
||||||
m_CookingOptions: 30
|
m_CookingOptions: 30
|
||||||
m_Mesh: {fileID: 1050908021570271116, guid: 3df8bb31de452e64fa85a1265898d698, type: 3}
|
m_Mesh: {fileID: 1050908021570271116, guid: 3df8bb31de452e64fa85a1265898d698, type: 3}
|
||||||
--- !u!1 &606146705
|
|
||||||
GameObject:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
serializedVersion: 6
|
|
||||||
m_Component:
|
|
||||||
- component: {fileID: 606146707}
|
|
||||||
- component: {fileID: 606146706}
|
|
||||||
m_Layer: 0
|
|
||||||
m_Name: GameObject (2)
|
|
||||||
m_TagString: Untagged
|
|
||||||
m_Icon: {fileID: 0}
|
|
||||||
m_NavMeshLayer: 0
|
|
||||||
m_StaticEditorFlags: 0
|
|
||||||
m_IsActive: 1
|
|
||||||
--- !u!114 &606146706
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 606146705}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: 180c6606991a4b64f812f9713907fd0c, type: 3}
|
|
||||||
m_Name:
|
|
||||||
m_EditorClassIdentifier: Assembly-CSharp::EnemySpawner
|
|
||||||
enemyType: 1
|
|
||||||
healthOverride: 0
|
|
||||||
--- !u!4 &606146707
|
|
||||||
Transform:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 606146705}
|
|
||||||
serializedVersion: 2
|
|
||||||
m_LocalRotation: {x: 0.07095997, y: 0.5789249, z: -0.050682172, w: 0.8107047}
|
|
||||||
m_LocalPosition: {x: 244.40219, y: -35.34, z: 89.64255}
|
|
||||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
|
||||||
m_ConstrainProportionsScale: 0
|
|
||||||
m_Children: []
|
|
||||||
m_Father: {fileID: 0}
|
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
|
||||||
--- !u!1 &624019478 stripped
|
--- !u!1 &624019478 stripped
|
||||||
GameObject:
|
GameObject:
|
||||||
m_CorrespondingSourceObject: {fileID: -8220620780705863596, guid: 9e848a421785b4212abcb7cf4ae082f3, type: 3}
|
m_CorrespondingSourceObject: {fileID: -8220620780705863596, guid: 9e848a421785b4212abcb7cf4ae082f3, type: 3}
|
||||||
@@ -6732,6 +6653,33 @@ MonoBehaviour:
|
|||||||
m_ShadowLayerMask: 1
|
m_ShadowLayerMask: 1
|
||||||
m_RenderingLayers: 1
|
m_RenderingLayers: 1
|
||||||
m_ShadowRenderingLayers: 1
|
m_ShadowRenderingLayers: 1
|
||||||
|
--- !u!1 &715417260 stripped
|
||||||
|
GameObject:
|
||||||
|
m_CorrespondingSourceObject: {fileID: -8098169881513260187, guid: 681011e79237b4409ad76bc628bdf5b3, type: 3}
|
||||||
|
m_PrefabInstance: {fileID: 791320992}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
--- !u!114 &715417264
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 715417260}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 95709fa8fd6c75045b1ef8e4f505c152, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: Assembly-CSharp::PickupItem
|
||||||
|
definition: {fileID: 11400000, guid: e09f7b05a155ba8449a0f0d8412ebe9d, type: 2}
|
||||||
|
itemName: Item
|
||||||
|
itemIcon: {fileID: 0}
|
||||||
|
spinSpeed: 120
|
||||||
|
bobHeight: 0.18
|
||||||
|
bobSpeed: 2.2
|
||||||
|
pickupRadius: 2
|
||||||
|
pickupKey: 101
|
||||||
|
pickupParticlesPrefab: {fileID: 0}
|
||||||
|
pickupSound: {fileID: 0}
|
||||||
--- !u!1001 &791320992
|
--- !u!1001 &791320992
|
||||||
PrefabInstance:
|
PrefabInstance:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -6758,7 +6706,7 @@ PrefabInstance:
|
|||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
- target: {fileID: 3447742079981357586, guid: 681011e79237b4409ad76bc628bdf5b3, type: 3}
|
- target: {fileID: 3447742079981357586, guid: 681011e79237b4409ad76bc628bdf5b3, type: 3}
|
||||||
propertyPath: m_LocalPosition.x
|
propertyPath: m_LocalPosition.x
|
||||||
value: 365.609
|
value: 363.888
|
||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
- target: {fileID: 3447742079981357586, guid: 681011e79237b4409ad76bc628bdf5b3, type: 3}
|
- target: {fileID: 3447742079981357586, guid: 681011e79237b4409ad76bc628bdf5b3, type: 3}
|
||||||
propertyPath: m_LocalPosition.y
|
propertyPath: m_LocalPosition.y
|
||||||
@@ -6766,7 +6714,7 @@ PrefabInstance:
|
|||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
- target: {fileID: 3447742079981357586, guid: 681011e79237b4409ad76bc628bdf5b3, type: 3}
|
- target: {fileID: 3447742079981357586, guid: 681011e79237b4409ad76bc628bdf5b3, type: 3}
|
||||||
propertyPath: m_LocalPosition.z
|
propertyPath: m_LocalPosition.z
|
||||||
value: 156.609
|
value: 157.267
|
||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
- target: {fileID: 3447742079981357586, guid: 681011e79237b4409ad76bc628bdf5b3, type: 3}
|
- target: {fileID: 3447742079981357586, guid: 681011e79237b4409ad76bc628bdf5b3, type: 3}
|
||||||
propertyPath: m_LocalRotation.w
|
propertyPath: m_LocalRotation.w
|
||||||
@@ -6799,7 +6747,10 @@ PrefabInstance:
|
|||||||
m_RemovedComponents: []
|
m_RemovedComponents: []
|
||||||
m_RemovedGameObjects: []
|
m_RemovedGameObjects: []
|
||||||
m_AddedGameObjects: []
|
m_AddedGameObjects: []
|
||||||
m_AddedComponents: []
|
m_AddedComponents:
|
||||||
|
- targetCorrespondingSourceObject: {fileID: -8098169881513260187, guid: 681011e79237b4409ad76bc628bdf5b3, type: 3}
|
||||||
|
insertIndex: -1
|
||||||
|
addedObject: {fileID: 715417264}
|
||||||
m_SourcePrefab: {fileID: 3150474306388093854, guid: 681011e79237b4409ad76bc628bdf5b3, type: 3}
|
m_SourcePrefab: {fileID: 3150474306388093854, guid: 681011e79237b4409ad76bc628bdf5b3, type: 3}
|
||||||
--- !u!1001 &806276174
|
--- !u!1001 &806276174
|
||||||
PrefabInstance:
|
PrefabInstance:
|
||||||
@@ -6919,6 +6870,66 @@ MeshCollider:
|
|||||||
m_Convex: 0
|
m_Convex: 0
|
||||||
m_CookingOptions: 30
|
m_CookingOptions: 30
|
||||||
m_Mesh: {fileID: 1050908021570271116, guid: 1e096925873fc492b854ee214231ab86, type: 3}
|
m_Mesh: {fileID: 1050908021570271116, guid: 1e096925873fc492b854ee214231ab86, type: 3}
|
||||||
|
--- !u!1001 &1007367441
|
||||||
|
PrefabInstance:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Modification:
|
||||||
|
serializedVersion: 3
|
||||||
|
m_TransformParent: {fileID: 0}
|
||||||
|
m_Modifications:
|
||||||
|
- target: {fileID: 6346070617587720957, guid: aa9cce2c129fa1a41bf5a539b136f7a6, type: 3}
|
||||||
|
propertyPath: m_LocalPosition.x
|
||||||
|
value: 355.01
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 6346070617587720957, guid: aa9cce2c129fa1a41bf5a539b136f7a6, type: 3}
|
||||||
|
propertyPath: m_LocalPosition.y
|
||||||
|
value: -18.848
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 6346070617587720957, guid: aa9cce2c129fa1a41bf5a539b136f7a6, type: 3}
|
||||||
|
propertyPath: m_LocalPosition.z
|
||||||
|
value: 167.46
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 6346070617587720957, guid: aa9cce2c129fa1a41bf5a539b136f7a6, type: 3}
|
||||||
|
propertyPath: m_LocalRotation.w
|
||||||
|
value: 1
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 6346070617587720957, guid: aa9cce2c129fa1a41bf5a539b136f7a6, type: 3}
|
||||||
|
propertyPath: m_LocalRotation.x
|
||||||
|
value: 0
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 6346070617587720957, guid: aa9cce2c129fa1a41bf5a539b136f7a6, type: 3}
|
||||||
|
propertyPath: m_LocalRotation.y
|
||||||
|
value: 0
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 6346070617587720957, guid: aa9cce2c129fa1a41bf5a539b136f7a6, type: 3}
|
||||||
|
propertyPath: m_LocalRotation.z
|
||||||
|
value: 0
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 6346070617587720957, guid: aa9cce2c129fa1a41bf5a539b136f7a6, type: 3}
|
||||||
|
propertyPath: m_LocalEulerAnglesHint.x
|
||||||
|
value: 0
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 6346070617587720957, guid: aa9cce2c129fa1a41bf5a539b136f7a6, type: 3}
|
||||||
|
propertyPath: m_LocalEulerAnglesHint.y
|
||||||
|
value: 0
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 6346070617587720957, guid: aa9cce2c129fa1a41bf5a539b136f7a6, type: 3}
|
||||||
|
propertyPath: m_LocalEulerAnglesHint.z
|
||||||
|
value: 0
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 8985654986098955209, guid: aa9cce2c129fa1a41bf5a539b136f7a6, type: 3}
|
||||||
|
propertyPath: m_Name
|
||||||
|
value: GunSplat
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
m_RemovedComponents: []
|
||||||
|
m_RemovedGameObjects: []
|
||||||
|
m_AddedGameObjects: []
|
||||||
|
m_AddedComponents:
|
||||||
|
- targetCorrespondingSourceObject: {fileID: 8985654986098955209, guid: aa9cce2c129fa1a41bf5a539b136f7a6, type: 3}
|
||||||
|
insertIndex: -1
|
||||||
|
addedObject: {fileID: 2128421432}
|
||||||
|
m_SourcePrefab: {fileID: 100100000, guid: aa9cce2c129fa1a41bf5a539b136f7a6, type: 3}
|
||||||
--- !u!1001 &1022368674
|
--- !u!1001 &1022368674
|
||||||
PrefabInstance:
|
PrefabInstance:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -7125,6 +7136,60 @@ MeshCollider:
|
|||||||
m_Convex: 0
|
m_Convex: 0
|
||||||
m_CookingOptions: 30
|
m_CookingOptions: 30
|
||||||
m_Mesh: {fileID: 1050908021570271116, guid: d26387bf4a33f4f03b916907f6b50828, type: 3}
|
m_Mesh: {fileID: 1050908021570271116, guid: d26387bf4a33f4f03b916907f6b50828, type: 3}
|
||||||
|
--- !u!1 &1114521747
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 1114521749}
|
||||||
|
- component: {fileID: 1114521748}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: gunsplat
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!114 &1114521748
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 1114521747}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 95709fa8fd6c75045b1ef8e4f505c152, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: Assembly-CSharp::PickupItem
|
||||||
|
definition: {fileID: 11400000, guid: 2b5a641a42945604c9ffa88754a9f6d1, type: 2}
|
||||||
|
itemName: Weapon
|
||||||
|
itemIcon: {fileID: 0}
|
||||||
|
spinSpeed: 120
|
||||||
|
bobHeight: 0.18
|
||||||
|
bobSpeed: 2.2
|
||||||
|
pickupRadius: 2
|
||||||
|
pickupKey: 101
|
||||||
|
pickupParticlesPrefab: {fileID: 0}
|
||||||
|
pickupSound: {fileID: 0}
|
||||||
|
--- !u!4 &1114521749
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 1114521747}
|
||||||
|
serializedVersion: 2
|
||||||
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_LocalPosition: {x: 355.188, y: -16.728, z: 155.372}
|
||||||
|
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||||
|
m_ConstrainProportionsScale: 0
|
||||||
|
m_Children: []
|
||||||
|
m_Father: {fileID: 0}
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
--- !u!1 &1121149997 stripped
|
--- !u!1 &1121149997 stripped
|
||||||
GameObject:
|
GameObject:
|
||||||
m_CorrespondingSourceObject: {fileID: -8098169881513260187, guid: 3c8a5d44028bd40d3abe5e22cafcb901, type: 3}
|
m_CorrespondingSourceObject: {fileID: -8098169881513260187, guid: 3c8a5d44028bd40d3abe5e22cafcb901, type: 3}
|
||||||
@@ -7569,11 +7634,11 @@ PrefabInstance:
|
|||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
- target: {fileID: 3447742079981357586, guid: 4f15ea45bfb5e4f11bc2bce50e39fc10, type: 3}
|
- target: {fileID: 3447742079981357586, guid: 4f15ea45bfb5e4f11bc2bce50e39fc10, type: 3}
|
||||||
propertyPath: m_LocalPosition.x
|
propertyPath: m_LocalPosition.x
|
||||||
value: 304.18
|
value: -186547
|
||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
- target: {fileID: 3447742079981357586, guid: 4f15ea45bfb5e4f11bc2bce50e39fc10, type: 3}
|
- target: {fileID: 3447742079981357586, guid: 4f15ea45bfb5e4f11bc2bce50e39fc10, type: 3}
|
||||||
propertyPath: m_LocalPosition.y
|
propertyPath: m_LocalPosition.y
|
||||||
value: -146.63
|
value: -74119
|
||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
- target: {fileID: 3447742079981357586, guid: 4f15ea45bfb5e4f11bc2bce50e39fc10, type: 3}
|
- target: {fileID: 3447742079981357586, guid: 4f15ea45bfb5e4f11bc2bce50e39fc10, type: 3}
|
||||||
propertyPath: m_LocalPosition.z
|
propertyPath: m_LocalPosition.z
|
||||||
@@ -8155,52 +8220,6 @@ PrefabInstance:
|
|||||||
insertIndex: -1
|
insertIndex: -1
|
||||||
addedObject: {fileID: 277384082}
|
addedObject: {fileID: 277384082}
|
||||||
m_SourcePrefab: {fileID: 3150474306388093854, guid: 4e219defa86804242a4efe4306a4b26b, type: 3}
|
m_SourcePrefab: {fileID: 3150474306388093854, guid: 4e219defa86804242a4efe4306a4b26b, type: 3}
|
||||||
--- !u!1 &1882479419
|
|
||||||
GameObject:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
serializedVersion: 6
|
|
||||||
m_Component:
|
|
||||||
- component: {fileID: 1882479421}
|
|
||||||
- component: {fileID: 1882479420}
|
|
||||||
m_Layer: 0
|
|
||||||
m_Name: GameObject (3)
|
|
||||||
m_TagString: Untagged
|
|
||||||
m_Icon: {fileID: 0}
|
|
||||||
m_NavMeshLayer: 0
|
|
||||||
m_StaticEditorFlags: 0
|
|
||||||
m_IsActive: 1
|
|
||||||
--- !u!114 &1882479420
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 1882479419}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: 180c6606991a4b64f812f9713907fd0c, type: 3}
|
|
||||||
m_Name:
|
|
||||||
m_EditorClassIdentifier: Assembly-CSharp::EnemySpawner
|
|
||||||
enemyType: 0
|
|
||||||
healthOverride: 0
|
|
||||||
--- !u!4 &1882479421
|
|
||||||
Transform:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 1882479419}
|
|
||||||
serializedVersion: 2
|
|
||||||
m_LocalRotation: {x: 0.07095997, y: 0.5789249, z: -0.050682172, w: 0.8107047}
|
|
||||||
m_LocalPosition: {x: 243.82, y: -35.34, z: 91.07}
|
|
||||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
|
||||||
m_ConstrainProportionsScale: 0
|
|
||||||
m_Children: []
|
|
||||||
m_Father: {fileID: 0}
|
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
|
||||||
--- !u!1001 &2026185005
|
--- !u!1001 &2026185005
|
||||||
PrefabInstance:
|
PrefabInstance:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -8277,52 +8296,115 @@ PrefabInstance:
|
|||||||
insertIndex: -1
|
insertIndex: -1
|
||||||
addedObject: {fileID: 990201665}
|
addedObject: {fileID: 990201665}
|
||||||
m_SourcePrefab: {fileID: 3150474306388093854, guid: 1e096925873fc492b854ee214231ab86, type: 3}
|
m_SourcePrefab: {fileID: 3150474306388093854, guid: 1e096925873fc492b854ee214231ab86, type: 3}
|
||||||
--- !u!1 &2073272903
|
--- !u!1 &2039163524 stripped
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_CorrespondingSourceObject: {fileID: 6882903375964528458, guid: 33de4b621d70a49aab5df775f2b826ef, type: 3}
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
m_PrefabInstance: {fileID: 110786869}
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
m_PrefabAsset: {fileID: 0}
|
||||||
serializedVersion: 6
|
--- !u!114 &2039163533
|
||||||
m_Component:
|
|
||||||
- component: {fileID: 2073272905}
|
|
||||||
- component: {fileID: 2073272904}
|
|
||||||
m_Layer: 0
|
|
||||||
m_Name: GameObject
|
|
||||||
m_TagString: Untagged
|
|
||||||
m_Icon: {fileID: 0}
|
|
||||||
m_NavMeshLayer: 0
|
|
||||||
m_StaticEditorFlags: 0
|
|
||||||
m_IsActive: 1
|
|
||||||
--- !u!114 &2073272904
|
|
||||||
MonoBehaviour:
|
MonoBehaviour:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
m_PrefabInstance: {fileID: 0}
|
m_PrefabInstance: {fileID: 0}
|
||||||
m_PrefabAsset: {fileID: 0}
|
m_PrefabAsset: {fileID: 0}
|
||||||
m_GameObject: {fileID: 2073272903}
|
m_GameObject: {fileID: 2039163524}
|
||||||
m_Enabled: 1
|
m_Enabled: 1
|
||||||
m_EditorHideFlags: 0
|
m_EditorHideFlags: 0
|
||||||
m_Script: {fileID: 11500000, guid: 180c6606991a4b64f812f9713907fd0c, type: 3}
|
m_Script: {fileID: 11500000, guid: 514ba78e0d4cf8b4787471c2db0c6976, type: 3}
|
||||||
m_Name:
|
m_Name:
|
||||||
m_EditorClassIdentifier: Assembly-CSharp::EnemySpawner
|
m_EditorClassIdentifier: Assembly-CSharp::Inventory
|
||||||
enemyType: 1
|
items: []
|
||||||
healthOverride: 0
|
toggleKey: 105
|
||||||
--- !u!4 &2073272905
|
equipKey: 102
|
||||||
Transform:
|
columns: 4
|
||||||
|
maxSlots: 20
|
||||||
|
colBackground: {r: 0.05, g: 0.05, b: 0.05, a: 0.92}
|
||||||
|
colPanel: {r: 0.1, g: 0.1, b: 0.1, a: 0.95}
|
||||||
|
colSlotEmpty: {r: 0.18, g: 0.18, b: 0.18, a: 1}
|
||||||
|
colSlotFilled: {r: 0.22, g: 0.28, b: 0.22, a: 1}
|
||||||
|
colSlotHover: {r: 0.35, g: 0.45, b: 0.35, a: 1}
|
||||||
|
colSlotSelected: {r: 0.45, g: 0.75, b: 0.45, a: 1}
|
||||||
|
colSlotEquipped: {r: 0.6, g: 0.4, b: 0.1, a: 1}
|
||||||
|
colBorder: {r: 0.4, g: 0.65, b: 0.4, a: 1}
|
||||||
|
colText: {r: 0.85, g: 0.95, b: 0.85, a: 1}
|
||||||
|
colDim: {r: 0.5, g: 0.6, b: 0.5, a: 1}
|
||||||
|
colAccent: {r: 0.5, g: 0.9, b: 0.5, a: 1}
|
||||||
|
colWeaponAccent: {r: 0.95, g: 0.75, b: 0.2, a: 1}
|
||||||
|
colCtxBg: {r: 0.08, g: 0.1, b: 0.08, a: 0.98}
|
||||||
|
colCtxBorder: {r: 0.4, g: 0.65, b: 0.4, a: 1}
|
||||||
|
colCtxHover: {r: 0.2, g: 0.35, b: 0.2, a: 1}
|
||||||
|
colCtxText: {r: 0.85, g: 0.95, b: 0.85, a: 1}
|
||||||
|
colCtxDestructive: {r: 0.85, g: 0.25, b: 0.2, a: 1}
|
||||||
|
--- !u!114 &2039163534
|
||||||
|
MonoBehaviour:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
m_PrefabInstance: {fileID: 0}
|
m_PrefabInstance: {fileID: 0}
|
||||||
m_PrefabAsset: {fileID: 0}
|
m_PrefabAsset: {fileID: 0}
|
||||||
m_GameObject: {fileID: 2073272903}
|
m_GameObject: {fileID: 2039163524}
|
||||||
serializedVersion: 2
|
m_Enabled: 1
|
||||||
m_LocalRotation: {x: 0.07095997, y: 0.5789249, z: -0.050682172, w: 0.8107047}
|
m_EditorHideFlags: 0
|
||||||
m_LocalPosition: {x: 243.82, y: -35.34, z: 91.07}
|
m_Script: {fileID: 11500000, guid: 18715d388d5da7544b10c5d7a385cbee, type: 3}
|
||||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
m_Name:
|
||||||
m_ConstrainProportionsScale: 0
|
m_EditorClassIdentifier: Assembly-CSharp::WeaponManager
|
||||||
m_Children: []
|
weaponHolder: {fileID: 0}
|
||||||
m_Father: {fileID: 0}
|
weaponPositionOffset: {x: 0.2, y: -0.25, z: 0.45}
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
weaponRotationOffset: {x: 0, y: 0, z: 0}
|
||||||
|
weaponScale: {x: 1, y: 1, z: 1}
|
||||||
|
allowScrollSwitch: 1
|
||||||
|
allowNumberKeys: 1
|
||||||
|
--- !u!114 &2039163535
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 2039163524}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 1d84d3e6c1cf6794da6ff84203edb291, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: Assembly-CSharp::PlayerHUD
|
||||||
|
barWidth: 220
|
||||||
|
barHeight: 18
|
||||||
|
barSpacing: 10
|
||||||
|
edgePadX: 18
|
||||||
|
edgePadY: 18
|
||||||
|
anchor: 0
|
||||||
|
colHealthFull: {r: 0.15, g: 0.8, b: 0.25, a: 1}
|
||||||
|
colHealthMid: {r: 0.9, g: 0.75, b: 0.1, a: 1}
|
||||||
|
colHealthLow: {r: 0.9, g: 0.15, b: 0.1, a: 1}
|
||||||
|
healthMidThreshold: 0.5
|
||||||
|
healthLowThreshold: 0.25
|
||||||
|
colStaminaNormal: {r: 0.2, g: 0.55, b: 0.95, a: 1}
|
||||||
|
colStaminaRegen: {r: 0.2, g: 0.55, b: 0.95, a: 0.45}
|
||||||
|
colStaminaExhaust: {r: 0.6, g: 0.18, b: 0.18, a: 1}
|
||||||
|
colBarBackground: {r: 0.06, g: 0.06, b: 0.06, a: 0.9}
|
||||||
|
colBarBorder: {r: 0.28, g: 0.28, b: 0.28, a: 1}
|
||||||
|
colLabel: {r: 0.8, g: 0.8, b: 0.8, a: 1}
|
||||||
|
colLabelCritical: {r: 1, g: 0.25, b: 0.25, a: 1}
|
||||||
|
borderThickness: 1.5
|
||||||
|
pulseSpeed: 4
|
||||||
|
showSpeedometer: 1
|
||||||
|
colSpeedo: {r: 0.2, g: 0.95, b: 0.4, a: 1}
|
||||||
|
colSpeedoFast: {r: 0.95, g: 0.8, b: 0.1, a: 1}
|
||||||
|
colSpeedoCritical: {r: 0.95, g: 0.2, b: 0.2, a: 1}
|
||||||
|
speedoFastThreshold: 60
|
||||||
|
speedoCritThreshold: 75
|
||||||
|
--- !u!114 &2039163536
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 2039163524}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 5680222822977cd4a988c93d7b8881bc, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: Assembly-CSharp::BootsEffect
|
||||||
|
bootsItemName: Bunny Hop Boots
|
||||||
|
boostedMaxStamina: 200
|
||||||
--- !u!1001 &2100225160
|
--- !u!1001 &2100225160
|
||||||
PrefabInstance:
|
PrefabInstance:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -8445,6 +8527,33 @@ MeshCollider:
|
|||||||
m_Convex: 0
|
m_Convex: 0
|
||||||
m_CookingOptions: 30
|
m_CookingOptions: 30
|
||||||
m_Mesh: {fileID: 0}
|
m_Mesh: {fileID: 0}
|
||||||
|
--- !u!1 &2128421431 stripped
|
||||||
|
GameObject:
|
||||||
|
m_CorrespondingSourceObject: {fileID: 8985654986098955209, guid: aa9cce2c129fa1a41bf5a539b136f7a6, type: 3}
|
||||||
|
m_PrefabInstance: {fileID: 1007367441}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
--- !u!114 &2128421432
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 2128421431}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 95709fa8fd6c75045b1ef8e4f505c152, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: Assembly-CSharp::PickupItem
|
||||||
|
definition: {fileID: 11400000, guid: 2b5a641a42945604c9ffa88754a9f6d1, type: 2}
|
||||||
|
itemName: Item
|
||||||
|
itemIcon: {fileID: 0}
|
||||||
|
spinSpeed: 120
|
||||||
|
bobHeight: 0.18
|
||||||
|
bobSpeed: 2.2
|
||||||
|
pickupRadius: 2
|
||||||
|
pickupKey: 101
|
||||||
|
pickupParticlesPrefab: {fileID: 0}
|
||||||
|
pickupSound: {fileID: 0}
|
||||||
--- !u!1660057539 &9223372036854775807
|
--- !u!1660057539 &9223372036854775807
|
||||||
SceneRoots:
|
SceneRoots:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -8454,10 +8563,6 @@ SceneRoots:
|
|||||||
- {fileID: 1719075356}
|
- {fileID: 1719075356}
|
||||||
- {fileID: 1410308539}
|
- {fileID: 1410308539}
|
||||||
- {fileID: 343869061}
|
- {fileID: 343869061}
|
||||||
- {fileID: 2073272905}
|
|
||||||
- {fileID: 487503084}
|
|
||||||
- {fileID: 606146707}
|
|
||||||
- {fileID: 1882479421}
|
|
||||||
- {fileID: 1360218911}
|
- {fileID: 1360218911}
|
||||||
- {fileID: 271705868}
|
- {fileID: 271705868}
|
||||||
- {fileID: 1022368674}
|
- {fileID: 1022368674}
|
||||||
@@ -8478,3 +8583,5 @@ SceneRoots:
|
|||||||
- {fileID: 1716730849}
|
- {fileID: 1716730849}
|
||||||
- {fileID: 2026185005}
|
- {fileID: 2026185005}
|
||||||
- {fileID: 65142585}
|
- {fileID: 65142585}
|
||||||
|
- {fileID: 1114521749}
|
||||||
|
- {fileID: 1007367441}
|
||||||
|
|||||||
46
Assets/Scripts/BootsEffect.cs
Normal file
46
Assets/Scripts/BootsEffect.cs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attach to the Player. Watches the inventory for the Bunny Hop Boots being
|
||||||
|
/// equipped/unequipped and adjusts max stamina accordingly.
|
||||||
|
/// </summary>
|
||||||
|
public class BootsEffect : MonoBehaviour
|
||||||
|
{
|
||||||
|
[Tooltip("Must match the ItemDefinition itemName exactly.")]
|
||||||
|
public string bootsItemName = "Bunny Hop Boots";
|
||||||
|
public float boostedMaxStamina = 200f;
|
||||||
|
|
||||||
|
private Inventory _inventory;
|
||||||
|
private Player _player;
|
||||||
|
private bool _boosted = false;
|
||||||
|
|
||||||
|
void Start()
|
||||||
|
{
|
||||||
|
_inventory = GetComponent<Inventory>();
|
||||||
|
_player = GetComponent<Player>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Update()
|
||||||
|
{
|
||||||
|
if (_inventory == null || _player == null) return;
|
||||||
|
|
||||||
|
// Find the boots entry in inventory
|
||||||
|
var entry = _inventory.items.Find(e => e.DisplayName == bootsItemName);
|
||||||
|
bool shouldBoost = entry != null && entry.isEquipped;
|
||||||
|
|
||||||
|
if (shouldBoost && !_boosted)
|
||||||
|
{
|
||||||
|
_player.maxStamina = boostedMaxStamina;
|
||||||
|
_player.stamina = Mathf.Min(_player.stamina + (boostedMaxStamina - 100f), boostedMaxStamina);
|
||||||
|
_boosted = true;
|
||||||
|
Debug.Log("[BootsEffect] Boots equipped — stamina boosted to " + boostedMaxStamina);
|
||||||
|
}
|
||||||
|
else if (!shouldBoost && _boosted)
|
||||||
|
{
|
||||||
|
_player.maxStamina = 100f;
|
||||||
|
_player.stamina = Mathf.Min(_player.stamina, 100f);
|
||||||
|
_boosted = false;
|
||||||
|
Debug.Log("[BootsEffect] Boots unequipped — stamina restored to 100");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/Scripts/BootsEffect.cs.meta
Normal file
2
Assets/Scripts/BootsEffect.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5680222822977cd4a988c93d7b8881bc
|
||||||
8
Assets/Scripts/Editor.meta
Normal file
8
Assets/Scripts/Editor.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 384e9957bd5199549870be11d0838d80
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
100
Assets/Scripts/Editor/BhopBootsSetup.cs
Normal file
100
Assets/Scripts/Editor/BhopBootsSetup.cs
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// OGG → Setup → Create Bunny Hop Boots
|
||||||
|
/// Creates the ItemDefinition and a world pickup for the bhop boots.
|
||||||
|
/// </summary>
|
||||||
|
public static class BhopBootsSetup
|
||||||
|
{
|
||||||
|
private const string ITEM_DEF_PATH = "Assets/Items/BhopBoots_Item.asset";
|
||||||
|
private const string PICKUP_PREFAB = "Assets/Prefabs/Pickups/BhopBoots_Pickup.prefab";
|
||||||
|
|
||||||
|
[MenuItem("OGG/Setup/Create Bunny Hop Boots")]
|
||||||
|
public static void Create()
|
||||||
|
{
|
||||||
|
EnsureFolder("Assets/Items");
|
||||||
|
EnsureFolder("Assets/Prefabs/Pickups");
|
||||||
|
|
||||||
|
// ── ItemDefinition ────────────────────────────────────────────
|
||||||
|
ItemDefinition item = AssetDatabase.LoadAssetAtPath<ItemDefinition>(ITEM_DEF_PATH);
|
||||||
|
bool isNew = item == null;
|
||||||
|
if (isNew) item = ScriptableObject.CreateInstance<ItemDefinition>();
|
||||||
|
|
||||||
|
item.itemName = "Bunny Hop Boots";
|
||||||
|
item.type = ItemDefinition.ItemType.Misc;
|
||||||
|
item.isEquippable = true;
|
||||||
|
item.description = "Illegal footwear. Gives 200 stamina while worn. Do not wear near cliffs.";
|
||||||
|
|
||||||
|
if (isNew)
|
||||||
|
AssetDatabase.CreateAsset(item, ITEM_DEF_PATH);
|
||||||
|
else
|
||||||
|
EditorUtility.SetDirty(item);
|
||||||
|
|
||||||
|
AssetDatabase.SaveAssets();
|
||||||
|
|
||||||
|
// ── Pickup prefab — procedural boot shape ─────────────────────
|
||||||
|
GameObject root = new GameObject("BhopBoots_Pickup");
|
||||||
|
|
||||||
|
// Simple visual: a squashed cube (boot-ish)
|
||||||
|
GameObject body = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
||||||
|
body.name = "Visual";
|
||||||
|
body.transform.SetParent(root.transform, false);
|
||||||
|
body.transform.localPosition = new Vector3(0f, 0f, 0f);
|
||||||
|
body.transform.localScale = new Vector3(0.4f, 0.2f, 0.7f);
|
||||||
|
Object.DestroyImmediate(body.GetComponent<Collider>());
|
||||||
|
|
||||||
|
// Bright accent colour so it stands out
|
||||||
|
var rend = body.GetComponent<Renderer>();
|
||||||
|
if (rend != null)
|
||||||
|
{
|
||||||
|
rend.material = new Material(Shader.Find("Universal Render Pipeline/Lit"));
|
||||||
|
rend.material.color = new Color(0.1f, 0.9f, 0.4f);
|
||||||
|
}
|
||||||
|
|
||||||
|
PickupItem pickup = root.AddComponent<PickupItem>();
|
||||||
|
pickup.definition = item;
|
||||||
|
pickup.spinSpeed = 140f;
|
||||||
|
pickup.bobHeight = 0.2f;
|
||||||
|
pickup.bobSpeed = 2.8f;
|
||||||
|
pickup.pickupRadius = 2.2f;
|
||||||
|
pickup.pickupKey = KeyCode.E;
|
||||||
|
|
||||||
|
StaminaBoostPickup boost = root.AddComponent<StaminaBoostPickup>();
|
||||||
|
boost.newMaxStamina = 200f;
|
||||||
|
|
||||||
|
if (File.Exists(Application.dataPath + "/../" + PICKUP_PREFAB))
|
||||||
|
AssetDatabase.DeleteAsset(PICKUP_PREFAB);
|
||||||
|
|
||||||
|
GameObject saved = PrefabUtility.SaveAsPrefabAsset(root, PICKUP_PREFAB);
|
||||||
|
Object.DestroyImmediate(root);
|
||||||
|
|
||||||
|
AssetDatabase.Refresh();
|
||||||
|
|
||||||
|
EditorUtility.DisplayDialog(
|
||||||
|
"Bunny Hop Boots ✓",
|
||||||
|
$"Created:\n• {ITEM_DEF_PATH}\n• {PICKUP_PREFAB}\n\n" +
|
||||||
|
"Drag BhopBoots_Pickup into the scene.\n" +
|
||||||
|
"Pick it up with [E] to unlock bunny hopping.\n\n" +
|
||||||
|
"Hold SPACE while landing to chain hops and build speed.\n" +
|
||||||
|
"Strafe left/right mid-air to steer.",
|
||||||
|
"Let's go");
|
||||||
|
|
||||||
|
EditorGUIUtility.PingObject(saved);
|
||||||
|
Selection.activeObject = saved;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void EnsureFolder(string path)
|
||||||
|
{
|
||||||
|
string[] parts = path.Split('/');
|
||||||
|
string current = parts[0];
|
||||||
|
for (int i = 1; i < parts.Length; i++)
|
||||||
|
{
|
||||||
|
string next = current + "/" + parts[i];
|
||||||
|
if (!AssetDatabase.IsValidFolder(next))
|
||||||
|
AssetDatabase.CreateFolder(current, parts[i]);
|
||||||
|
current = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/Scripts/Editor/BhopBootsSetup.cs.meta
Normal file
2
Assets/Scripts/Editor/BhopBootsSetup.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 92ff81f98e809934bab9c5b8f160276a
|
||||||
151
Assets/Scripts/Editor/GunSplatSetup.cs
Normal file
151
Assets/Scripts/Editor/GunSplatSetup.cs
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// OGG → Setup → Create Gun Splat Weapon
|
||||||
|
/// Builds:
|
||||||
|
/// • Assets/Prefabs/Weapons/GunSplat.prefab — the held weapon (SimpleGun)
|
||||||
|
/// • Assets/Prefabs/Pickups/GunSplat_Pickup.prefab — world pickup (spins/bobs, press E)
|
||||||
|
/// • Assets/Items/GunSplat_Item.asset — ItemDefinition (type = Weapon)
|
||||||
|
/// </summary>
|
||||||
|
public static class GunSplatSetup
|
||||||
|
{
|
||||||
|
private const string GLB_PATH = "Assets/Models/LidarScans/Gun Splat.glb";
|
||||||
|
private const string WEAPON_PREFAB = "Assets/Prefabs/Weapons/GunSplat.prefab";
|
||||||
|
private const string PICKUP_PREFAB = "Assets/Prefabs/Pickups/GunSplat_Pickup.prefab";
|
||||||
|
private const string ITEM_DEF_PATH = "Assets/Items/GunSplat_Item.asset";
|
||||||
|
|
||||||
|
[MenuItem("OGG/Setup/Create Gun Splat Weapon")]
|
||||||
|
public static void CreateGunSplat()
|
||||||
|
{
|
||||||
|
// ── 1. Ensure output folders exist ──────────────────────────
|
||||||
|
EnsureFolder("Assets/Prefabs/Weapons");
|
||||||
|
EnsureFolder("Assets/Prefabs/Pickups");
|
||||||
|
EnsureFolder("Assets/Items");
|
||||||
|
|
||||||
|
// ── 2. Load the GLB model ────────────────────────────────────
|
||||||
|
GameObject modelAsset = AssetDatabase.LoadAssetAtPath<GameObject>(GLB_PATH);
|
||||||
|
if (modelAsset == null)
|
||||||
|
{
|
||||||
|
Debug.LogError($"[GunSplatSetup] Could not find GLB at: {GLB_PATH}");
|
||||||
|
EditorUtility.DisplayDialog("Gun Splat Setup", $"Could not find model at:\n{GLB_PATH}", "OK");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 3. Build the held-weapon prefab ──────────────────────────
|
||||||
|
GameObject weaponRoot = new GameObject("GunSplat");
|
||||||
|
AttachVisual(weaponRoot, modelAsset);
|
||||||
|
|
||||||
|
SimpleGun gun = weaponRoot.AddComponent<SimpleGun>();
|
||||||
|
gun.damage = 30f;
|
||||||
|
gun.range = 120f;
|
||||||
|
gun.fireRate = 6f;
|
||||||
|
gun.maxAmmo = 24;
|
||||||
|
gun.isAutomatic = false;
|
||||||
|
|
||||||
|
if (File.Exists(DataRelative(WEAPON_PREFAB)))
|
||||||
|
AssetDatabase.DeleteAsset(WEAPON_PREFAB);
|
||||||
|
|
||||||
|
GameObject savedWeapon = PrefabUtility.SaveAsPrefabAsset(weaponRoot, WEAPON_PREFAB);
|
||||||
|
Object.DestroyImmediate(weaponRoot);
|
||||||
|
|
||||||
|
if (savedWeapon == null)
|
||||||
|
{
|
||||||
|
Debug.LogError("[GunSplatSetup] Failed to save weapon prefab.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Debug.Log($"[GunSplatSetup] Weapon prefab → {WEAPON_PREFAB}");
|
||||||
|
|
||||||
|
// ── 4. Create / update ItemDefinition ────────────────────────
|
||||||
|
ItemDefinition item = AssetDatabase.LoadAssetAtPath<ItemDefinition>(ITEM_DEF_PATH);
|
||||||
|
bool isNew = item == null;
|
||||||
|
if (isNew) item = ScriptableObject.CreateInstance<ItemDefinition>();
|
||||||
|
|
||||||
|
item.itemName = "Gun Splat";
|
||||||
|
item.type = ItemDefinition.ItemType.Weapon;
|
||||||
|
item.weaponPrefab = savedWeapon;
|
||||||
|
item.description = "A janky lidar-scanned sidearm. Shoots first, looks weird always.";
|
||||||
|
|
||||||
|
if (isNew)
|
||||||
|
AssetDatabase.CreateAsset(item, ITEM_DEF_PATH);
|
||||||
|
else
|
||||||
|
EditorUtility.SetDirty(item);
|
||||||
|
|
||||||
|
AssetDatabase.SaveAssets();
|
||||||
|
Debug.Log($"[GunSplatSetup] ItemDefinition → {ITEM_DEF_PATH}");
|
||||||
|
|
||||||
|
// ── 5. Build the world pickup prefab ─────────────────────────
|
||||||
|
GameObject pickupRoot = new GameObject("GunSplat_Pickup");
|
||||||
|
|
||||||
|
// Visual child — the GLB model
|
||||||
|
AttachVisual(pickupRoot, modelAsset);
|
||||||
|
|
||||||
|
// PickupItem component
|
||||||
|
PickupItem pickup = pickupRoot.AddComponent<PickupItem>();
|
||||||
|
pickup.definition = item;
|
||||||
|
pickup.spinSpeed = 90f;
|
||||||
|
pickup.bobHeight = 0.15f;
|
||||||
|
pickup.bobSpeed = 2.0f;
|
||||||
|
pickup.pickupRadius = 2.2f;
|
||||||
|
pickup.pickupKey = KeyCode.E;
|
||||||
|
|
||||||
|
if (File.Exists(DataRelative(PICKUP_PREFAB)))
|
||||||
|
AssetDatabase.DeleteAsset(PICKUP_PREFAB);
|
||||||
|
|
||||||
|
GameObject savedPickup = PrefabUtility.SaveAsPrefabAsset(pickupRoot, PICKUP_PREFAB);
|
||||||
|
Object.DestroyImmediate(pickupRoot);
|
||||||
|
|
||||||
|
if (savedPickup == null)
|
||||||
|
{
|
||||||
|
Debug.LogError("[GunSplatSetup] Failed to save pickup prefab.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Debug.Log($"[GunSplatSetup] Pickup prefab → {PICKUP_PREFAB}");
|
||||||
|
|
||||||
|
AssetDatabase.Refresh();
|
||||||
|
|
||||||
|
// ── 6. Done ──────────────────────────────────────────────────
|
||||||
|
EditorUtility.DisplayDialog(
|
||||||
|
"Gun Splat Setup ✓",
|
||||||
|
"Created:\n" +
|
||||||
|
$"• {WEAPON_PREFAB}\n" +
|
||||||
|
$"• {PICKUP_PREFAB}\n" +
|
||||||
|
$"• {ITEM_DEF_PATH}\n\n" +
|
||||||
|
"Drag GunSplat_Pickup into the scene wherever you want it to spawn.\n" +
|
||||||
|
"It will spin, bob, and show an [E] prompt when the player gets close.",
|
||||||
|
"Sick");
|
||||||
|
|
||||||
|
EditorGUIUtility.PingObject(savedPickup);
|
||||||
|
Selection.activeObject = savedPickup;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Helpers ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
static void AttachVisual(GameObject parent, GameObject modelAsset)
|
||||||
|
{
|
||||||
|
GameObject visual = (GameObject)PrefabUtility.InstantiatePrefab(modelAsset);
|
||||||
|
if (visual == null) visual = Object.Instantiate(modelAsset);
|
||||||
|
visual.name = "Visual";
|
||||||
|
visual.transform.SetParent(parent.transform, false);
|
||||||
|
visual.transform.localPosition = Vector3.zero;
|
||||||
|
visual.transform.localRotation = Quaternion.identity;
|
||||||
|
visual.transform.localScale = Vector3.one;
|
||||||
|
}
|
||||||
|
|
||||||
|
static string DataRelative(string assetPath) =>
|
||||||
|
Application.dataPath + "/../" + assetPath;
|
||||||
|
|
||||||
|
static void EnsureFolder(string path)
|
||||||
|
{
|
||||||
|
string[] parts = path.Split('/');
|
||||||
|
string current = parts[0];
|
||||||
|
for (int i = 1; i < parts.Length; i++)
|
||||||
|
{
|
||||||
|
string next = current + "/" + parts[i];
|
||||||
|
if (!AssetDatabase.IsValidFolder(next))
|
||||||
|
AssetDatabase.CreateFolder(current, parts[i]);
|
||||||
|
current = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/Scripts/Editor/GunSplatSetup.cs.meta
Normal file
2
Assets/Scripts/Editor/GunSplatSetup.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a0ceea0a64e3fa344b83004be51fe457
|
||||||
92
Assets/Scripts/Editor/WeaponManagerEditor.cs
Normal file
92
Assets/Scripts/Editor/WeaponManagerEditor.cs
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
|
||||||
|
[CustomEditor(typeof(WeaponManager))]
|
||||||
|
public class WeaponManagerEditor : Editor
|
||||||
|
{
|
||||||
|
public override void OnInspectorGUI()
|
||||||
|
{
|
||||||
|
DrawDefaultInspector();
|
||||||
|
|
||||||
|
WeaponManager wm = (WeaponManager)target;
|
||||||
|
|
||||||
|
if (!Application.isPlaying) return;
|
||||||
|
|
||||||
|
EditorGUILayout.Space();
|
||||||
|
EditorGUILayout.LabelField("── Live Positioning ──", EditorStyles.boldLabel);
|
||||||
|
|
||||||
|
// Show what's active
|
||||||
|
string activeName = wm.ActiveWeaponName;
|
||||||
|
if (string.IsNullOrEmpty(activeName))
|
||||||
|
{
|
||||||
|
EditorGUILayout.HelpBox("No weapon currently equipped.", MessageType.Info);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.HelpBox($"Active: {activeName}", MessageType.None);
|
||||||
|
|
||||||
|
// Select the live GO so you can drag gizmos in Scene view
|
||||||
|
if (GUILayout.Button("🎯 Select Active Weapon in Scene"))
|
||||||
|
{
|
||||||
|
var slot = wm.slots.Find(s => s.itemName == activeName);
|
||||||
|
if (slot != null)
|
||||||
|
{
|
||||||
|
Selection.activeGameObject = slot.instance;
|
||||||
|
SceneView.lastActiveSceneView?.Focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.Space();
|
||||||
|
|
||||||
|
// Sync the current transform back into the WeaponViewmodel (or fallback global offset)
|
||||||
|
if (GUILayout.Button("⬆ Sync Transform → Offset Fields"))
|
||||||
|
{
|
||||||
|
var slot = wm.slots.Find(s => s.itemName == activeName);
|
||||||
|
if (slot != null)
|
||||||
|
{
|
||||||
|
var vm = slot.instance.GetComponent<WeaponViewmodel>();
|
||||||
|
if (vm != null)
|
||||||
|
{
|
||||||
|
Undo.RecordObject(vm, "Sync Weapon Viewmodel");
|
||||||
|
vm.SyncFromTransform();
|
||||||
|
EditorUtility.SetDirty(vm);
|
||||||
|
|
||||||
|
// Also save back to the source prefab asset
|
||||||
|
var prefabAsset = PrefabUtility.GetCorrespondingObjectFromSource(slot.instance);
|
||||||
|
if (prefabAsset != null)
|
||||||
|
{
|
||||||
|
var prefabVm = prefabAsset.GetComponent<WeaponViewmodel>();
|
||||||
|
if (prefabVm != null)
|
||||||
|
{
|
||||||
|
Undo.RecordObject(prefabVm, "Sync Weapon Viewmodel Prefab");
|
||||||
|
prefabVm.positionOffset = vm.positionOffset;
|
||||||
|
prefabVm.rotationOffset = vm.rotationOffset;
|
||||||
|
prefabVm.scale = vm.scale;
|
||||||
|
EditorUtility.SetDirty(prefabVm);
|
||||||
|
AssetDatabase.SaveAssets();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Debug.Log($"[WeaponManager] Synced to WeaponViewmodel: pos={vm.positionOffset} rot={vm.rotationOffset} scale={vm.scale}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Fallback: no viewmodel, write to global offsets
|
||||||
|
Undo.RecordObject(wm, "Sync Weapon Offset");
|
||||||
|
wm.weaponPositionOffset = slot.instance.transform.localPosition;
|
||||||
|
wm.weaponRotationOffset = slot.instance.transform.localRotation.eulerAngles;
|
||||||
|
wm.weaponScale = slot.instance.transform.localScale;
|
||||||
|
EditorUtility.SetDirty(wm);
|
||||||
|
Debug.Log($"[WeaponManager] Synced to global offset: pos={wm.weaponPositionOffset}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.HelpBox(
|
||||||
|
"1. Hit Play & equip the gun\n" +
|
||||||
|
"2. Click 'Select Active Weapon'\n" +
|
||||||
|
"3. Use Move/Rotate gizmos in Scene view\n" +
|
||||||
|
"4. Click 'Sync Transform → Offset Fields'\n" +
|
||||||
|
"5. Stop Play — values are saved",
|
||||||
|
MessageType.Info);
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/Scripts/Editor/WeaponManagerEditor.cs.meta
Normal file
2
Assets/Scripts/Editor/WeaponManagerEditor.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: fe67de8a32d4bf5449ee8def34a1bb8a
|
||||||
@@ -4,33 +4,31 @@ using UnityEngine;
|
|||||||
public class FirstPersonController : MonoBehaviour
|
public class FirstPersonController : MonoBehaviour
|
||||||
{
|
{
|
||||||
[Header("Movement Settings")]
|
[Header("Movement Settings")]
|
||||||
public float walkSpeed = 50f; // Boosted from 8f to overcome collision issues
|
public float walkSpeed = 50f;
|
||||||
public float runSpeed = 80f; // Boosted from 14f
|
public float runSpeed = 80f;
|
||||||
public float jumpHeight = 2.5f;
|
public float jumpHeight = 2.5f;
|
||||||
public float gravity = -20f;
|
public float gravity = -20f;
|
||||||
|
|
||||||
[Header("Mouse Look Settings")]
|
[Header("Mouse Look Settings")]
|
||||||
public float mouseSensitivity = 3f;
|
public float mouseSensitivity = 3f;
|
||||||
public float maxLookAngle = 90f;
|
public float maxLookAngle = 90f;
|
||||||
|
|
||||||
[Header("References")]
|
[Header("References")]
|
||||||
public Camera playerCamera;
|
public Camera playerCamera;
|
||||||
|
|
||||||
// Private variables
|
// Private variables
|
||||||
private CharacterController controller;
|
private CharacterController controller;
|
||||||
private Vector3 velocity;
|
private Inventory inventory;
|
||||||
private bool isGrounded;
|
private Player player;
|
||||||
private float xRotation = 0f;
|
private Vector3 velocity;
|
||||||
|
private bool isGrounded;
|
||||||
|
private float xRotation = 0f;
|
||||||
|
|
||||||
void Start()
|
void Start()
|
||||||
{
|
{
|
||||||
Debug.Log("Starting game");
|
|
||||||
|
|
||||||
// FORCE NORMAL TIME (in case something external changed it)
|
|
||||||
Time.timeScale = 1f;
|
Time.timeScale = 1f;
|
||||||
|
|
||||||
controller = GetComponent<CharacterController>();
|
controller = GetComponent<CharacterController>();
|
||||||
|
|
||||||
if (controller == null)
|
if (controller == null)
|
||||||
{
|
{
|
||||||
Debug.LogError("FirstPersonController: No CharacterController found!");
|
Debug.LogError("FirstPersonController: No CharacterController found!");
|
||||||
@@ -40,85 +38,58 @@ public class FirstPersonController : MonoBehaviour
|
|||||||
if (playerCamera == null)
|
if (playerCamera == null)
|
||||||
playerCamera = GetComponentInChildren<Camera>();
|
playerCamera = GetComponentInChildren<Camera>();
|
||||||
|
|
||||||
|
inventory = GetComponent<Inventory>();
|
||||||
|
player = GetComponent<Player>();
|
||||||
|
|
||||||
Cursor.lockState = CursorLockMode.Locked;
|
Cursor.lockState = CursorLockMode.Locked;
|
||||||
Cursor.visible = false;
|
Cursor.visible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Update()
|
void Update()
|
||||||
{
|
{
|
||||||
if( controller == null )
|
if (controller == null) return;
|
||||||
return;
|
|
||||||
|
|
||||||
isGrounded = controller.isGrounded;
|
isGrounded = controller.isGrounded;
|
||||||
|
|
||||||
if (isGrounded && velocity.y < 0)
|
if (isGrounded && velocity.y < 0)
|
||||||
velocity.y = -2f;
|
velocity.y = -2f;
|
||||||
|
|
||||||
// ─── Movement via direct KeyCode ───
|
float moveX = 0f, moveZ = 0f;
|
||||||
float moveX = 0f;
|
if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow)) moveZ += 1f;
|
||||||
float moveZ = 0f;
|
if (Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.DownArrow)) moveZ -= 1f;
|
||||||
|
if (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow)) moveX -= 1f;
|
||||||
if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow)) moveZ += 1f;
|
if (Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow)) moveX += 1f;
|
||||||
if (Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.DownArrow)) moveZ -= 1f;
|
|
||||||
if (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow)) moveX -= 1f;
|
|
||||||
if (Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow)) moveX += 1f;
|
|
||||||
|
|
||||||
// DEBUG: Log once when W is first pressed
|
|
||||||
if (Input.GetKeyDown(KeyCode.W))
|
|
||||||
{
|
|
||||||
Debug.Log($"[FPC DEBUG] W pressed | controller.enabled={controller.enabled} | position={transform.position} | isGrounded={isGrounded}");
|
|
||||||
Debug.Log($"[FPC DEBUG] Time.timeScale={Time.timeScale} | Time.deltaTime={Time.deltaTime} | walkSpeed={walkSpeed}");
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector3 move = transform.right * moveX + transform.forward * moveZ;
|
Vector3 move = transform.right * moveX + transform.forward * moveZ;
|
||||||
|
if (move.magnitude > 1f) move.Normalize();
|
||||||
|
|
||||||
if (move.magnitude > 1f)
|
bool wantSprint = Input.GetKey(KeyCode.LeftShift) && move.magnitude > 0f;
|
||||||
move.Normalize();
|
bool isSprinting = wantSprint && (player == null || player.CanSprint());
|
||||||
|
if (player != null) player.isSprinting = isSprinting;
|
||||||
|
|
||||||
float currentSpeed = Input.GetKey(KeyCode.LeftShift) ? runSpeed : walkSpeed;
|
float currentSpeed = isSprinting ? runSpeed : walkSpeed;
|
||||||
|
|
||||||
Vector3 posBefore = transform.position;
|
|
||||||
controller.Move(move * currentSpeed * Time.deltaTime);
|
controller.Move(move * currentSpeed * Time.deltaTime);
|
||||||
|
|
||||||
// DEBUG: Log if we tried to move but didn't
|
|
||||||
if (move.magnitude > 0f && Input.GetKeyDown(KeyCode.W))
|
|
||||||
{
|
|
||||||
Vector3 posAfter = transform.position;
|
|
||||||
//Debug.Log($"[FPC DEBUG] Move attempt: delta={move * currentSpeed * Time.deltaTime} | actualDelta={(posAfter - posBefore)} | controller.height={controller.height} | controller.radius={controller.radius}");
|
|
||||||
|
|
||||||
// Check what we're colliding with
|
|
||||||
Collider[] nearbyColliders = Physics.OverlapSphere(transform.position, controller.radius + 0.5f);
|
|
||||||
//Debug.Log($"[FPC DEBUG] Found {nearbyColliders.Length} colliders near player");
|
|
||||||
foreach (Collider col in nearbyColliders)
|
|
||||||
{
|
|
||||||
if (col != controller && !(col is CharacterController))
|
|
||||||
Debug.LogWarning($"[FPC DEBUG] Nearby collider: {col.gameObject.name} on layer {LayerMask.LayerToName(col.gameObject.layer)}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Jumping
|
|
||||||
if (Input.GetKey(KeyCode.Space) && isGrounded)
|
if (Input.GetKey(KeyCode.Space) && isGrounded)
|
||||||
velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
|
velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
|
||||||
|
|
||||||
// Gravity
|
|
||||||
velocity.y += gravity * Time.deltaTime;
|
velocity.y += gravity * Time.deltaTime;
|
||||||
controller.Move(velocity * Time.deltaTime);
|
controller.Move(new Vector3(0f, velocity.y, 0f) * Time.deltaTime);
|
||||||
|
|
||||||
// Mouse look
|
bool inventoryOpen = inventory != null && inventory.IsOpen;
|
||||||
HandleMouseLook();
|
if (!inventoryOpen)
|
||||||
|
HandleMouseLook();
|
||||||
|
|
||||||
// Escape to unlock cursor
|
|
||||||
if (Input.GetKeyDown(KeyCode.Escape))
|
if (Input.GetKeyDown(KeyCode.Escape))
|
||||||
{
|
{
|
||||||
Cursor.lockState = CursorLockMode.None;
|
Cursor.lockState = CursorLockMode.None;
|
||||||
Cursor.visible = true;
|
Cursor.visible = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Click to re-lock cursor
|
if (Input.GetMouseButtonDown(0) && Cursor.lockState == CursorLockMode.None && !inventoryOpen)
|
||||||
if (Input.GetMouseButtonDown(0) && Cursor.lockState == CursorLockMode.None)
|
|
||||||
{
|
{
|
||||||
Cursor.lockState = CursorLockMode.Locked;
|
Cursor.lockState = CursorLockMode.Locked;
|
||||||
Cursor.visible = false;
|
Cursor.visible = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,9 +99,8 @@ public class FirstPersonController : MonoBehaviour
|
|||||||
float mouseY = Input.GetAxis("Mouse Y") * mouseSensitivity;
|
float mouseY = Input.GetAxis("Mouse Y") * mouseSensitivity;
|
||||||
|
|
||||||
xRotation -= mouseY;
|
xRotation -= mouseY;
|
||||||
xRotation = Mathf.Clamp(xRotation, -maxLookAngle, maxLookAngle);
|
xRotation = Mathf.Clamp(xRotation, -maxLookAngle, maxLookAngle);
|
||||||
playerCamera.transform.localRotation = Quaternion.Euler(xRotation, 0f, 0f);
|
playerCamera.transform.localRotation = Quaternion.Euler(xRotation, 0f, 0f);
|
||||||
|
|
||||||
transform.Rotate(Vector3.up * mouseX);
|
transform.Rotate(Vector3.up * mouseX);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ public static class InputData
|
|||||||
new MouseButton( Defines.Input.kLeftMouseButton),
|
new MouseButton( Defines.Input.kLeftMouseButton),
|
||||||
new KeyboardButton( KeyCode.LeftControl ),
|
new KeyboardButton( KeyCode.LeftControl ),
|
||||||
} ) );
|
} ) );
|
||||||
reload = AddButton( new KeyboardButton( KeyCode.Space ) );
|
reload = AddButton( new KeyboardButton( KeyCode.R ) );
|
||||||
jump = AddButton( new KeyboardButton( KeyCode.Space ) );
|
jump = AddButton( new KeyboardButton( KeyCode.Space ) );
|
||||||
|
|
||||||
horizontalMovement = AddAxis( new MultiAxis(
|
horizontalMovement = AddAxis( new MultiAxis(
|
||||||
|
|||||||
@@ -187,8 +187,13 @@ public class HumanoidEnemy : MonoBehaviour
|
|||||||
// Punch animation
|
// Punch animation
|
||||||
StartCoroutine(PunchAnimation());
|
StartCoroutine(PunchAnimation());
|
||||||
|
|
||||||
// TODO: Hook into player health system when you have one
|
// Damage the player via Player.health
|
||||||
// player.GetComponent<PlayerHealth>()?.TakeDamage(attackDamage);
|
Player playerHealth = player.GetComponent<Player>();
|
||||||
|
if (playerHealth != null)
|
||||||
|
{
|
||||||
|
playerHealth.health -= attackDamage;
|
||||||
|
Debug.Log($"[Enemy] Player health: {playerHealth.health}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
632
Assets/Scripts/Inventory.cs
Normal file
632
Assets/Scripts/Inventory.cs
Normal file
@@ -0,0 +1,632 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Inventory system with a toggleable HUD (press I) and right-click context menu.
|
||||||
|
/// Attach to the Player GameObject alongside WeaponManager.
|
||||||
|
/// </summary>
|
||||||
|
public class Inventory : MonoBehaviour
|
||||||
|
{
|
||||||
|
// ─── Item data ────────────────────────────────────────────────────
|
||||||
|
[System.Serializable]
|
||||||
|
public class InventoryEntry
|
||||||
|
{
|
||||||
|
public ItemDefinition definition;
|
||||||
|
public string itemName;
|
||||||
|
public Sprite icon;
|
||||||
|
public int count;
|
||||||
|
public bool isEquipped;
|
||||||
|
|
||||||
|
public InventoryEntry(string name, Sprite icon)
|
||||||
|
{
|
||||||
|
this.itemName = name;
|
||||||
|
this.icon = icon;
|
||||||
|
this.count = 1;
|
||||||
|
this.isEquipped = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InventoryEntry(ItemDefinition def)
|
||||||
|
{
|
||||||
|
this.definition = def;
|
||||||
|
this.itemName = def.itemName;
|
||||||
|
this.icon = def.icon;
|
||||||
|
this.count = 1;
|
||||||
|
this.isEquipped = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool _isWeaponSlot;
|
||||||
|
|
||||||
|
public bool IsWeapon =>
|
||||||
|
(definition != null && definition.type == ItemDefinition.ItemType.Weapon)
|
||||||
|
|| _isWeaponSlot;
|
||||||
|
|
||||||
|
// True for weapons AND equippable misc items
|
||||||
|
public bool IsEquippable =>
|
||||||
|
IsWeapon || (definition != null && definition.isEquippable);
|
||||||
|
|
||||||
|
public string DisplayName => definition != null ? definition.itemName : itemName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<InventoryEntry> items = new List<InventoryEntry>();
|
||||||
|
|
||||||
|
// ─── HUD config ───────────────────────────────────────────────────
|
||||||
|
[Header("HUD")]
|
||||||
|
public KeyCode toggleKey = KeyCode.I;
|
||||||
|
public KeyCode equipKey = KeyCode.F;
|
||||||
|
public int columns = 4;
|
||||||
|
public int maxSlots = 20;
|
||||||
|
|
||||||
|
[Header("HUD Colours")]
|
||||||
|
public Color colBackground = new Color(0.05f, 0.05f, 0.05f, 0.92f);
|
||||||
|
public Color colPanel = new Color(0.10f, 0.10f, 0.10f, 0.95f);
|
||||||
|
public Color colSlotEmpty = new Color(0.18f, 0.18f, 0.18f, 1.00f);
|
||||||
|
public Color colSlotFilled = new Color(0.22f, 0.28f, 0.22f, 1.00f);
|
||||||
|
public Color colSlotHover = new Color(0.35f, 0.45f, 0.35f, 1.00f);
|
||||||
|
public Color colSlotSelected = new Color(0.45f, 0.75f, 0.45f, 1.00f);
|
||||||
|
public Color colSlotEquipped = new Color(0.60f, 0.40f, 0.10f, 1.00f);
|
||||||
|
public Color colBorder = new Color(0.40f, 0.65f, 0.40f, 1.00f);
|
||||||
|
public Color colText = new Color(0.85f, 0.95f, 0.85f, 1.00f);
|
||||||
|
public Color colDim = new Color(0.50f, 0.60f, 0.50f, 1.00f);
|
||||||
|
public Color colAccent = new Color(0.50f, 0.90f, 0.50f, 1.00f);
|
||||||
|
public Color colWeaponAccent = new Color(0.95f, 0.75f, 0.20f, 1.00f);
|
||||||
|
|
||||||
|
[Header("Context Menu Colours")]
|
||||||
|
public Color colCtxBg = new Color(0.08f, 0.10f, 0.08f, 0.98f);
|
||||||
|
public Color colCtxBorder = new Color(0.40f, 0.65f, 0.40f, 1.00f);
|
||||||
|
public Color colCtxHover = new Color(0.20f, 0.35f, 0.20f, 1.00f);
|
||||||
|
public Color colCtxText = new Color(0.85f, 0.95f, 0.85f, 1.00f);
|
||||||
|
public Color colCtxDestructive = new Color(0.85f, 0.25f, 0.20f, 1.00f);
|
||||||
|
|
||||||
|
// ─── Private state ────────────────────────────────────────────────
|
||||||
|
private bool _open = false;
|
||||||
|
private int _selectedIndex = 0;
|
||||||
|
private int _hoveredIndex = -1;
|
||||||
|
private WeaponManager _weaponManager;
|
||||||
|
|
||||||
|
// ─── Context menu state ───────────────────────────────────────────
|
||||||
|
private bool _ctxOpen = false;
|
||||||
|
private int _ctxItemIndex = -1;
|
||||||
|
private Vector2 _ctxPos;
|
||||||
|
|
||||||
|
private const float kCtxItemH = 28f;
|
||||||
|
private const float kCtxWidth = 140f;
|
||||||
|
private const float kCtxPadX = 10f;
|
||||||
|
|
||||||
|
// Context menu action labels — built per item
|
||||||
|
private struct CtxAction
|
||||||
|
{
|
||||||
|
public string label;
|
||||||
|
public bool isDestructive;
|
||||||
|
public System.Action callback;
|
||||||
|
}
|
||||||
|
private List<CtxAction> _ctxActions = new List<CtxAction>();
|
||||||
|
|
||||||
|
// ─── Layout constants ─────────────────────────────────────────────
|
||||||
|
private const float kSlotSize = 80f;
|
||||||
|
private const float kSlotPad = 8f;
|
||||||
|
private const float kPanelPad = 20f;
|
||||||
|
private const float kHeaderH = 40f;
|
||||||
|
private const float kDetailPanelH = 120f;
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
void Start()
|
||||||
|
{
|
||||||
|
_weaponManager = GetComponent<WeaponManager>();
|
||||||
|
if (_weaponManager != null)
|
||||||
|
Invoke(nameof(SyncStartingWeapons), 0.1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SyncStartingWeapons()
|
||||||
|
{
|
||||||
|
if (_weaponManager == null) return;
|
||||||
|
foreach (var slot in _weaponManager.slots)
|
||||||
|
{
|
||||||
|
if (slot.definition != null)
|
||||||
|
AddItem(slot.definition, autoEquip: false);
|
||||||
|
else
|
||||||
|
AddItemRaw(slot.itemName, null, isWeaponSlot: true);
|
||||||
|
}
|
||||||
|
SyncEquippedState();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Public API ───────────────────────────────────────────────────
|
||||||
|
public void AddItem(ItemDefinition def, bool autoEquip = false)
|
||||||
|
{
|
||||||
|
if (def == null) return;
|
||||||
|
InventoryEntry existing = items.Find(e => e.definition == def);
|
||||||
|
if (existing != null)
|
||||||
|
{
|
||||||
|
if (def.type != ItemDefinition.ItemType.Weapon) existing.count++;
|
||||||
|
Debug.Log($"[Inventory] Already have: {def.itemName}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var entry = new InventoryEntry(def);
|
||||||
|
items.Add(entry);
|
||||||
|
Debug.Log($"[Inventory] Picked up: {def.itemName}");
|
||||||
|
if (def.type == ItemDefinition.ItemType.Weapon)
|
||||||
|
{
|
||||||
|
_weaponManager?.AddWeapon(def);
|
||||||
|
if (autoEquip) EquipItem(items.Count - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddItem(string itemName, Sprite icon = null) => AddItemRaw(itemName, icon, false);
|
||||||
|
|
||||||
|
private void AddItemRaw(string itemName, Sprite icon, bool isWeaponSlot)
|
||||||
|
{
|
||||||
|
InventoryEntry existing = items.Find(e => e.itemName == itemName && e.definition == null);
|
||||||
|
if (existing != null) { existing.count++; return; }
|
||||||
|
var entry = new InventoryEntry(itemName, icon);
|
||||||
|
entry._isWeaponSlot = isWeaponSlot;
|
||||||
|
items.Add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool RemoveItem(string itemName, int amount = 1)
|
||||||
|
{
|
||||||
|
InventoryEntry entry = items.Find(e => e.DisplayName == itemName);
|
||||||
|
if (entry == null) return false;
|
||||||
|
entry.count -= amount;
|
||||||
|
if (entry.count <= 0) items.Remove(entry);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasItem(string itemName) => items.Exists(e => e.DisplayName == itemName);
|
||||||
|
public int CountOf(string itemName)
|
||||||
|
{
|
||||||
|
var e = items.Find(x => x.DisplayName == itemName);
|
||||||
|
return e != null ? e.count : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsOpen => _open;
|
||||||
|
|
||||||
|
// ─── Equip / Unequip ──────────────────────────────────────────────
|
||||||
|
void EquipItem(int index)
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= items.Count) return;
|
||||||
|
InventoryEntry entry = items[index];
|
||||||
|
if (!entry.IsEquippable) return;
|
||||||
|
|
||||||
|
if (entry.IsWeapon)
|
||||||
|
{
|
||||||
|
// Unequip other weapons
|
||||||
|
foreach (var e in items) { if (e.IsWeapon) e.isEquipped = false; }
|
||||||
|
entry.isEquipped = true;
|
||||||
|
_weaponManager?.EquipByName(entry.DisplayName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Toggle equip for misc equippables (boots, etc.)
|
||||||
|
entry.isEquipped = true;
|
||||||
|
}
|
||||||
|
Debug.Log($"[Inventory] Equipped: {entry.DisplayName}");
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnequipItem(int index)
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= items.Count) return;
|
||||||
|
InventoryEntry entry = items[index];
|
||||||
|
if (!entry.IsEquippable || !entry.isEquipped) return;
|
||||||
|
entry.isEquipped = false;
|
||||||
|
|
||||||
|
if (entry.IsWeapon && _weaponManager != null)
|
||||||
|
{
|
||||||
|
var slot = _weaponManager.slots.Find(s => s.itemName == entry.DisplayName);
|
||||||
|
if (slot != null) slot.instance.SetActive(false);
|
||||||
|
_weaponManager.Unequip();
|
||||||
|
}
|
||||||
|
Debug.Log($"[Inventory] Unequipped: {entry.DisplayName}");
|
||||||
|
}
|
||||||
|
|
||||||
|
void DropItem(int index)
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= items.Count) return;
|
||||||
|
InventoryEntry entry = items[index];
|
||||||
|
if (entry.isEquipped) UnequipItem(index);
|
||||||
|
items.RemoveAt(index);
|
||||||
|
if (_selectedIndex >= items.Count) _selectedIndex = items.Count - 1;
|
||||||
|
Debug.Log($"[Inventory] Dropped: {entry.DisplayName}");
|
||||||
|
// TODO: optionally spawn the pickup back in the world here
|
||||||
|
}
|
||||||
|
|
||||||
|
void SyncEquippedState()
|
||||||
|
{
|
||||||
|
if (_weaponManager == null) return;
|
||||||
|
string active = _weaponManager.ActiveWeaponName;
|
||||||
|
foreach (var e in items)
|
||||||
|
{
|
||||||
|
// Only sync weapon equipped state — leave misc equippables alone
|
||||||
|
if (e.IsWeapon)
|
||||||
|
e.isEquipped = (e.DisplayName == active);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Context menu builder ─────────────────────────────────────────
|
||||||
|
void OpenContextMenu(int itemIndex, Vector2 screenPos)
|
||||||
|
{
|
||||||
|
if (itemIndex < 0 || itemIndex >= items.Count) return;
|
||||||
|
|
||||||
|
_ctxItemIndex = itemIndex;
|
||||||
|
_ctxActions.Clear();
|
||||||
|
|
||||||
|
InventoryEntry entry = items[itemIndex];
|
||||||
|
|
||||||
|
if (entry.IsEquippable)
|
||||||
|
{
|
||||||
|
if (entry.isEquipped)
|
||||||
|
{
|
||||||
|
int captured = itemIndex;
|
||||||
|
_ctxActions.Add(new CtxAction
|
||||||
|
{
|
||||||
|
label = "Unequip",
|
||||||
|
isDestructive = false,
|
||||||
|
callback = () => UnequipItem(captured)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int captured = itemIndex;
|
||||||
|
_ctxActions.Add(new CtxAction
|
||||||
|
{
|
||||||
|
label = "Equip",
|
||||||
|
isDestructive = false,
|
||||||
|
callback = () => EquipItem(captured)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consumables / misc could have a "Use" action here in future
|
||||||
|
// For now everyone gets Inspect (just selects and focuses the detail panel)
|
||||||
|
{
|
||||||
|
int captured = itemIndex;
|
||||||
|
_ctxActions.Add(new CtxAction
|
||||||
|
{
|
||||||
|
label = "Inspect",
|
||||||
|
isDestructive = false,
|
||||||
|
callback = () => _selectedIndex = captured
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
int captured = itemIndex;
|
||||||
|
_ctxActions.Add(new CtxAction
|
||||||
|
{
|
||||||
|
label = "Drop",
|
||||||
|
isDestructive = true,
|
||||||
|
callback = () => DropItem(captured)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clamp so the menu doesn't go off screen
|
||||||
|
float menuH = _ctxActions.Count * kCtxItemH + 8f;
|
||||||
|
float clampedX = Mathf.Min(screenPos.x, Screen.width - kCtxWidth - 4f);
|
||||||
|
float clampedY = Mathf.Min(screenPos.y, Screen.height - menuH - 4f);
|
||||||
|
_ctxPos = new Vector2(clampedX, clampedY);
|
||||||
|
_ctxOpen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CloseContextMenu() { _ctxOpen = false; _ctxItemIndex = -1; }
|
||||||
|
|
||||||
|
// ─── Toggle ───────────────────────────────────────────────────────
|
||||||
|
void Update()
|
||||||
|
{
|
||||||
|
if (Input.GetKeyDown(toggleKey))
|
||||||
|
{
|
||||||
|
CloseContextMenu();
|
||||||
|
SetOpen(!_open);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_open && Input.GetKeyDown(equipKey))
|
||||||
|
{
|
||||||
|
if (_selectedIndex < items.Count && items[_selectedIndex].IsWeapon)
|
||||||
|
EquipItem(_selectedIndex);
|
||||||
|
CloseContextMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_open)
|
||||||
|
SyncEquippedState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetOpen(bool open)
|
||||||
|
{
|
||||||
|
_open = open;
|
||||||
|
SyncEquippedState();
|
||||||
|
Cursor.lockState = open ? CursorLockMode.None : CursorLockMode.Locked;
|
||||||
|
Cursor.visible = open;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── GUI ──────────────────────────────────────────────────────────
|
||||||
|
void OnGUI()
|
||||||
|
{
|
||||||
|
if (!_open) return;
|
||||||
|
|
||||||
|
int rows = Mathf.CeilToInt((float)maxSlots / columns);
|
||||||
|
float gridW = columns * (kSlotSize + kSlotPad) - kSlotPad;
|
||||||
|
float gridH = rows * (kSlotSize + kSlotPad) - kSlotPad;
|
||||||
|
float panelW = gridW + kPanelPad * 2f;
|
||||||
|
float panelH = kHeaderH + kPanelPad + gridH + kPanelPad + kDetailPanelH + kPanelPad;
|
||||||
|
float panelX = (Screen.width - panelW) * 0.5f;
|
||||||
|
float panelY = (Screen.height - panelH) * 0.5f;
|
||||||
|
|
||||||
|
Vector2 mouse = Event.current.mousePosition;
|
||||||
|
|
||||||
|
// ── Click outside context menu to close it ────────────────────
|
||||||
|
if (_ctxOpen && Event.current.type == EventType.MouseDown)
|
||||||
|
{
|
||||||
|
Rect ctxRect = GetCtxRect();
|
||||||
|
if (!ctxRect.Contains(mouse))
|
||||||
|
CloseContextMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Backdrop ──────────────────────────────────────────────────
|
||||||
|
DrawRect(new Rect(0, 0, Screen.width, Screen.height), new Color(0, 0, 0, 0.55f));
|
||||||
|
|
||||||
|
// ── Main panel ────────────────────────────────────────────────
|
||||||
|
DrawBorderedBox(new Rect(panelX, panelY, panelW, panelH), colPanel, colBorder, 2f);
|
||||||
|
|
||||||
|
GUIStyle headerStyle = MakeStyle(18, FontStyle.Bold, colAccent, TextAnchor.MiddleCenter);
|
||||||
|
GUI.Label(new Rect(panelX, panelY + 8f, panelW, 28f), "[ INVENTORY ]", headerStyle);
|
||||||
|
DrawRect(new Rect(panelX + 10f, panelY + kHeaderH - 2f, panelW - 20f, 1f), colBorder);
|
||||||
|
|
||||||
|
if (_weaponManager != null && _weaponManager.ActiveWeaponName != "")
|
||||||
|
{
|
||||||
|
GUIStyle activeStyle = MakeStyle(11, FontStyle.Normal, colWeaponAccent, TextAnchor.MiddleRight);
|
||||||
|
GUI.Label(new Rect(panelX, panelY + 10f, panelW - 10f, 20f),
|
||||||
|
$"EQUIPPED: {_weaponManager.ActiveWeaponName}", activeStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
float gx = panelX + kPanelPad;
|
||||||
|
float gy = panelY + kHeaderH + kPanelPad;
|
||||||
|
|
||||||
|
_hoveredIndex = -1;
|
||||||
|
|
||||||
|
// ── Slot grid ─────────────────────────────────────────────────
|
||||||
|
for (int i = 0; i < maxSlots; i++)
|
||||||
|
{
|
||||||
|
int col = i % columns;
|
||||||
|
int row = i / columns;
|
||||||
|
float sx = gx + col * (kSlotSize + kSlotPad);
|
||||||
|
float sy = gy + row * (kSlotSize + kSlotPad);
|
||||||
|
Rect slotRect = new Rect(sx, sy, kSlotSize, kSlotSize);
|
||||||
|
|
||||||
|
bool hasItem = i < items.Count;
|
||||||
|
bool isHover = slotRect.Contains(mouse) && !_ctxOpen;
|
||||||
|
bool isSel = (i == _selectedIndex);
|
||||||
|
bool isEquipped = hasItem && items[i].isEquipped;
|
||||||
|
bool isWeapon = hasItem && items[i].IsWeapon;
|
||||||
|
|
||||||
|
if (isHover) _hoveredIndex = i;
|
||||||
|
|
||||||
|
// Left-click: select
|
||||||
|
if (isHover && Event.current.type == EventType.MouseDown
|
||||||
|
&& Event.current.button == 0 && hasItem)
|
||||||
|
{
|
||||||
|
_selectedIndex = i;
|
||||||
|
CloseContextMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Left double-click: equip weapon
|
||||||
|
if (isHover && Event.current.type == EventType.MouseDown
|
||||||
|
&& Event.current.button == 0 && Event.current.clickCount == 2 && isWeapon)
|
||||||
|
EquipItem(i);
|
||||||
|
|
||||||
|
// Right-click: open context menu
|
||||||
|
if (isHover && Event.current.type == EventType.MouseDown
|
||||||
|
&& Event.current.button == 1 && hasItem)
|
||||||
|
{
|
||||||
|
_selectedIndex = i;
|
||||||
|
OpenContextMenu(i, mouse);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slot colour
|
||||||
|
Color slotCol;
|
||||||
|
if (isEquipped) slotCol = colSlotEquipped;
|
||||||
|
else if (isSel) slotCol = colSlotSelected;
|
||||||
|
else if (isHover) slotCol = colSlotHover;
|
||||||
|
else if (hasItem) slotCol = colSlotFilled;
|
||||||
|
else slotCol = colSlotEmpty;
|
||||||
|
|
||||||
|
Color borderCol = isEquipped ? colWeaponAccent : (isSel ? colAccent : colBorder);
|
||||||
|
DrawBorderedBox(slotRect, slotCol, borderCol, (isSel || isEquipped) ? 2f : 1f);
|
||||||
|
|
||||||
|
if (!hasItem) continue;
|
||||||
|
|
||||||
|
InventoryEntry entry = items[i];
|
||||||
|
|
||||||
|
// Icon or letter placeholder
|
||||||
|
if (entry.icon != null)
|
||||||
|
{
|
||||||
|
float ip = 10f;
|
||||||
|
GUI.DrawTexture(new Rect(sx + ip, sy + ip, kSlotSize - ip * 2f, kSlotSize - 28f),
|
||||||
|
entry.icon.texture, ScaleMode.ScaleToFit, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Color lc = isEquipped ? colWeaponAccent : (isSel ? Color.white : colAccent);
|
||||||
|
GUI.Label(new Rect(sx, sy + 8f, kSlotSize, kSlotSize - 24f),
|
||||||
|
entry.DisplayName.Substring(0, 1).ToUpper(), MakeStyle(26, FontStyle.Bold, lc, TextAnchor.MiddleCenter));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Weapon tag
|
||||||
|
if (isWeapon)
|
||||||
|
{
|
||||||
|
Color tc = isEquipped ? colWeaponAccent : new Color(0.6f, 0.5f, 0.2f, 1f);
|
||||||
|
GUI.Label(new Rect(sx + 3f, sy + 2f, 40f, 12f),
|
||||||
|
isEquipped ? "EQUIP" : "WPN", MakeStyle(8, FontStyle.Bold, tc, TextAnchor.UpperLeft));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right-click hint on hovered slot
|
||||||
|
if (isHover)
|
||||||
|
{
|
||||||
|
GUIStyle hint = MakeStyle(8, FontStyle.Normal, new Color(0.6f, 0.6f, 0.6f, 0.9f), TextAnchor.LowerRight);
|
||||||
|
GUI.Label(new Rect(sx + 2f, sy + 2f, kSlotSize - 4f, kSlotSize - 4f), "RMB ▾", hint);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Item name
|
||||||
|
string dn = entry.DisplayName.Length > 10 ? entry.DisplayName.Substring(0, 9) + "…" : entry.DisplayName;
|
||||||
|
Color nc = isEquipped ? colWeaponAccent : (isSel ? Color.white : colText);
|
||||||
|
GUI.Label(new Rect(sx + 2f, sy + kSlotSize - 28f, kSlotSize - 4f, 16f),
|
||||||
|
dn, MakeStyle(9, FontStyle.Bold, nc, TextAnchor.LowerCenter));
|
||||||
|
|
||||||
|
// Count badge
|
||||||
|
if (entry.count > 1)
|
||||||
|
{
|
||||||
|
DrawRect(new Rect(sx + kSlotSize - 22f, sy + kSlotSize - 18f, 20f, 16f), new Color(0, 0, 0, 0.7f));
|
||||||
|
GUI.Label(new Rect(sx + kSlotSize - 22f, sy + kSlotSize - 18f, 19f, 16f),
|
||||||
|
$"x{entry.count}", MakeStyle(10, FontStyle.Bold, Color.white, TextAnchor.LowerRight));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Detail panel ──────────────────────────────────────────────
|
||||||
|
float detailY = gy + gridH + kPanelPad;
|
||||||
|
Rect detailRect = new Rect(panelX + kPanelPad, detailY, panelW - kPanelPad * 2f, kDetailPanelH - kPanelPad);
|
||||||
|
DrawBorderedBox(detailRect, new Color(0.08f, 0.08f, 0.08f, 1f), colBorder, 1f);
|
||||||
|
|
||||||
|
if (_selectedIndex < items.Count)
|
||||||
|
{
|
||||||
|
InventoryEntry sel = items[_selectedIndex];
|
||||||
|
float dy = detailRect.y + 8f;
|
||||||
|
float dx = detailRect.x + 10f;
|
||||||
|
float dw = detailRect.width - 20f;
|
||||||
|
|
||||||
|
GUI.Label(new Rect(dx, dy, dw, 16f), "SELECTED", MakeStyle(10, FontStyle.Normal, colDim, TextAnchor.UpperLeft));
|
||||||
|
GUI.Label(new Rect(dx, dy + 16f, dw, 22f), sel.DisplayName, MakeStyle(14, FontStyle.Bold, colText, TextAnchor.UpperLeft));
|
||||||
|
|
||||||
|
string desc = (sel.definition != null && sel.definition.description != "")
|
||||||
|
? sel.definition.description : (sel.IsWeapon ? "Weapon" : "Item");
|
||||||
|
GUI.Label(new Rect(dx, dy + 38f, dw * 0.65f, 30f), desc, MakeStyle(10, FontStyle.Normal, colDim, TextAnchor.UpperLeft));
|
||||||
|
|
||||||
|
if (sel.IsEquippable)
|
||||||
|
{
|
||||||
|
Color wc = sel.isEquipped ? colWeaponAccent : colDim;
|
||||||
|
string tag = sel.isEquipped ? "▶ EQUIPPED" : "right-click or [F] to equip";
|
||||||
|
GUI.Label(new Rect(dx, dy + 68f, dw, 18f), tag, MakeStyle(11, FontStyle.Bold, wc, TextAnchor.UpperLeft));
|
||||||
|
}
|
||||||
|
else if (sel.count > 1)
|
||||||
|
{
|
||||||
|
GUI.Label(new Rect(dx, dy + 68f, dw, 18f), $"Quantity: {sel.count}",
|
||||||
|
MakeStyle(11, FontStyle.Bold, colAccent, TextAnchor.UpperLeft));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equip / Unequip button bottom-right of detail panel
|
||||||
|
if (sel.IsEquippable)
|
||||||
|
{
|
||||||
|
Rect btnRect = new Rect(detailRect.x + detailRect.width - 110f,
|
||||||
|
detailRect.y + detailRect.height - 34f, 100f, 26f);
|
||||||
|
if (sel.isEquipped)
|
||||||
|
{
|
||||||
|
DrawBorderedBox(btnRect, new Color(0.4f, 0.15f, 0.05f, 1f), colWeaponAccent, 1f);
|
||||||
|
GUI.Label(btnRect, "[ F ] UNEQUIP", MakeStyle(11, FontStyle.Bold, Color.white, TextAnchor.MiddleCenter));
|
||||||
|
if (GUI.Button(btnRect, "", GUIStyle.none)) UnequipItem(_selectedIndex);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DrawBorderedBox(btnRect, colSlotEquipped, colWeaponAccent, 1f);
|
||||||
|
GUI.Label(btnRect, "[ F ] EQUIP", MakeStyle(11, FontStyle.Bold, Color.white, TextAnchor.MiddleCenter));
|
||||||
|
if (GUI.Button(btnRect, "", GUIStyle.none)) EquipItem(_selectedIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GUI.Label(detailRect, "No item selected", MakeStyle(11, FontStyle.Normal, colDim, TextAnchor.MiddleCenter));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hints
|
||||||
|
GUIStyle hintStyle = MakeStyle(10, FontStyle.Normal, colDim, TextAnchor.LowerRight);
|
||||||
|
GUI.Label(new Rect(panelX, panelY + panelH - 18f, panelW - 8f, 16f), "[ I ] close | RMB slot for options", hintStyle);
|
||||||
|
|
||||||
|
// ── Context menu (drawn last so it's on top) ──────────────────
|
||||||
|
if (_ctxOpen)
|
||||||
|
DrawContextMenu(mouse);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Context menu draw ────────────────────────────────────────────
|
||||||
|
Rect GetCtxRect()
|
||||||
|
{
|
||||||
|
float menuH = _ctxActions.Count * kCtxItemH + 8f;
|
||||||
|
return new Rect(_ctxPos.x, _ctxPos.y, kCtxWidth, menuH);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawContextMenu(Vector2 mouse)
|
||||||
|
{
|
||||||
|
float menuH = _ctxActions.Count * kCtxItemH + 8f;
|
||||||
|
Rect bgRect = new Rect(_ctxPos.x, _ctxPos.y, kCtxWidth, menuH);
|
||||||
|
|
||||||
|
// Shadow
|
||||||
|
DrawRect(new Rect(bgRect.x + 3f, bgRect.y + 3f, bgRect.width, bgRect.height),
|
||||||
|
new Color(0f, 0f, 0f, 0.45f));
|
||||||
|
|
||||||
|
// Background + border
|
||||||
|
DrawBorderedBox(bgRect, colCtxBg, colCtxBorder, 1.5f);
|
||||||
|
|
||||||
|
// Header strip — item name
|
||||||
|
if (_ctxItemIndex >= 0 && _ctxItemIndex < items.Count)
|
||||||
|
{
|
||||||
|
string title = items[_ctxItemIndex].DisplayName;
|
||||||
|
DrawRect(new Rect(bgRect.x + 1.5f, bgRect.y + 1.5f, bgRect.width - 3f, 20f),
|
||||||
|
new Color(0.15f, 0.22f, 0.15f, 1f));
|
||||||
|
GUI.Label(new Rect(bgRect.x + kCtxPadX, bgRect.y + 3f, kCtxWidth - kCtxPadX * 2f, 16f),
|
||||||
|
title.ToUpper(), MakeStyle(9, FontStyle.Bold, colWeaponAccent, TextAnchor.MiddleLeft));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Action rows
|
||||||
|
for (int i = 0; i < _ctxActions.Count; i++)
|
||||||
|
{
|
||||||
|
float rowY = bgRect.y + 22f + i * kCtxItemH;
|
||||||
|
Rect rowRect = new Rect(bgRect.x + 1.5f, rowY, bgRect.width - 3f, kCtxItemH);
|
||||||
|
bool hover = rowRect.Contains(mouse);
|
||||||
|
|
||||||
|
if (hover)
|
||||||
|
DrawRect(rowRect, colCtxHover);
|
||||||
|
|
||||||
|
Color textColor = _ctxActions[i].isDestructive
|
||||||
|
? (hover ? Color.white : colCtxDestructive)
|
||||||
|
: (hover ? Color.white : colCtxText);
|
||||||
|
|
||||||
|
GUI.Label(new Rect(bgRect.x + kCtxPadX, rowY + 2f, kCtxWidth - kCtxPadX * 2f, kCtxItemH - 4f),
|
||||||
|
_ctxActions[i].label, MakeStyle(12, FontStyle.Normal, textColor, TextAnchor.MiddleLeft));
|
||||||
|
|
||||||
|
// Separator line between items
|
||||||
|
if (i < _ctxActions.Count - 1)
|
||||||
|
DrawRect(new Rect(bgRect.x + 6f, rowY + kCtxItemH - 1f, bgRect.width - 12f, 1f),
|
||||||
|
new Color(1f, 1f, 1f, 0.06f));
|
||||||
|
|
||||||
|
// Click to fire action
|
||||||
|
if (hover && Event.current.type == EventType.MouseDown && Event.current.button == 0)
|
||||||
|
{
|
||||||
|
_ctxActions[i].callback?.Invoke();
|
||||||
|
CloseContextMenu();
|
||||||
|
Event.current.Use();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Style / Draw helpers ─────────────────────────────────────────
|
||||||
|
static GUIStyle MakeStyle(int size, FontStyle style, Color color, TextAnchor align)
|
||||||
|
{
|
||||||
|
var s = new GUIStyle();
|
||||||
|
s.fontSize = size;
|
||||||
|
s.fontStyle = style;
|
||||||
|
s.normal.textColor = color;
|
||||||
|
s.alignment = align;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DrawRect(Rect r, Color c)
|
||||||
|
{
|
||||||
|
Color prev = GUI.color;
|
||||||
|
GUI.color = c;
|
||||||
|
GUI.DrawTexture(r, Texture2D.whiteTexture);
|
||||||
|
GUI.color = prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DrawBorderedBox(Rect r, Color fill, Color border, float thickness)
|
||||||
|
{
|
||||||
|
DrawRect(r, border);
|
||||||
|
DrawRect(new Rect(r.x + thickness, r.y + thickness,
|
||||||
|
r.width - thickness * 2f,
|
||||||
|
r.height - thickness * 2f), fill);
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/Scripts/Inventory.cs.meta
Normal file
2
Assets/Scripts/Inventory.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 514ba78e0d4cf8b4787471c2db0c6976
|
||||||
28
Assets/Scripts/ItemDefinition.cs
Normal file
28
Assets/Scripts/ItemDefinition.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create via right-click → Create → OGG → Item Definition
|
||||||
|
/// Drag onto PickupItem components in the scene.
|
||||||
|
/// </summary>
|
||||||
|
[CreateAssetMenu(fileName = "New Item", menuName = "OGG/Item Definition")]
|
||||||
|
public class ItemDefinition : ScriptableObject
|
||||||
|
{
|
||||||
|
public enum ItemType { Misc, Consumable, Weapon }
|
||||||
|
|
||||||
|
[Header("Identity")]
|
||||||
|
public string itemName = "Unnamed Item";
|
||||||
|
public Sprite icon;
|
||||||
|
public ItemType type = ItemType.Misc;
|
||||||
|
|
||||||
|
[Header("Weapon (only if type == Weapon)")]
|
||||||
|
[Tooltip("The prefab that gets spawned in the weapon holder when this is equipped.")]
|
||||||
|
public GameObject weaponPrefab;
|
||||||
|
|
||||||
|
[Header("Equipment")]
|
||||||
|
[Tooltip("If true, this item can be equipped/unequipped from the inventory even if it isn't a weapon.")]
|
||||||
|
public bool isEquippable = false;
|
||||||
|
|
||||||
|
[Header("Flavour")]
|
||||||
|
[TextArea(2, 4)]
|
||||||
|
public string description = "";
|
||||||
|
}
|
||||||
2
Assets/Scripts/ItemDefinition.cs.meta
Normal file
2
Assets/Scripts/ItemDefinition.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b549159f64e1b164082e2e4ae4d28ac1
|
||||||
156
Assets/Scripts/PickupItem.cs
Normal file
156
Assets/Scripts/PickupItem.cs
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attach to any pickup object. Spins + bobs like MGS rations.
|
||||||
|
/// Assign an ItemDefinition asset for full weapon/equip support,
|
||||||
|
/// or just fill itemName for a simple non-weapon pickup.
|
||||||
|
/// </summary>
|
||||||
|
public class PickupItem : MonoBehaviour
|
||||||
|
{
|
||||||
|
[Header("Item")]
|
||||||
|
[Tooltip("Preferred: drag an ItemDefinition ScriptableObject here.")]
|
||||||
|
public ItemDefinition definition;
|
||||||
|
|
||||||
|
[Tooltip("Fallback name if no ItemDefinition is set.")]
|
||||||
|
public string itemName = "Item";
|
||||||
|
public Sprite itemIcon;
|
||||||
|
|
||||||
|
[Header("Spin & Bob")]
|
||||||
|
public float spinSpeed = 120f;
|
||||||
|
public float bobHeight = 0.18f;
|
||||||
|
public float bobSpeed = 2.2f;
|
||||||
|
|
||||||
|
[Header("Pickup")]
|
||||||
|
public float pickupRadius = 2.0f;
|
||||||
|
public KeyCode pickupKey = KeyCode.E;
|
||||||
|
|
||||||
|
[Header("FX")]
|
||||||
|
public GameObject pickupParticlesPrefab;
|
||||||
|
public AudioClip pickupSound;
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────
|
||||||
|
private Vector3 _startPos;
|
||||||
|
private Transform _playerTransform;
|
||||||
|
private Inventory _inventory;
|
||||||
|
private AudioSource _audioSource;
|
||||||
|
private bool _collected = false;
|
||||||
|
|
||||||
|
// Prompt display
|
||||||
|
private bool _inRange = false;
|
||||||
|
|
||||||
|
void Start()
|
||||||
|
{
|
||||||
|
_startPos = transform.position;
|
||||||
|
|
||||||
|
Player player = FindObjectOfType<Player>();
|
||||||
|
if (player != null)
|
||||||
|
{
|
||||||
|
_playerTransform = player.transform;
|
||||||
|
_inventory = player.GetComponent<Inventory>();
|
||||||
|
}
|
||||||
|
|
||||||
|
_audioSource = gameObject.AddComponent<AudioSource>();
|
||||||
|
_audioSource.spatialBlend = 1f;
|
||||||
|
_audioSource.playOnAwake = false;
|
||||||
|
|
||||||
|
// If we have a definition, use its name/icon
|
||||||
|
if (definition != null)
|
||||||
|
{
|
||||||
|
itemName = definition.itemName;
|
||||||
|
itemIcon = definition.icon;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Update()
|
||||||
|
{
|
||||||
|
if (_collected) return;
|
||||||
|
|
||||||
|
// ── Spin ──────────────────────────────
|
||||||
|
transform.Rotate(Vector3.up, spinSpeed * Time.deltaTime, Space.World);
|
||||||
|
|
||||||
|
// ── Bob ───────────────────────────────
|
||||||
|
float newY = _startPos.y + Mathf.Sin(Time.time * bobSpeed * Mathf.PI * 2f) * bobHeight;
|
||||||
|
transform.position = new Vector3(transform.position.x, newY, transform.position.z);
|
||||||
|
|
||||||
|
// ── Pickup check ─────────────────────
|
||||||
|
if (_playerTransform == null) return;
|
||||||
|
|
||||||
|
float dist = Vector3.Distance(transform.position, _playerTransform.position);
|
||||||
|
_inRange = dist <= pickupRadius;
|
||||||
|
|
||||||
|
if (_inRange && Input.GetKeyDown(pickupKey))
|
||||||
|
Collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnGUI()
|
||||||
|
{
|
||||||
|
if (_collected || !_inRange) return;
|
||||||
|
|
||||||
|
// World-to-screen prompt
|
||||||
|
Vector3 screenPos = Camera.main != null
|
||||||
|
? Camera.main.WorldToScreenPoint(_startPos + Vector3.up * (bobHeight + 0.4f))
|
||||||
|
: Vector3.zero;
|
||||||
|
|
||||||
|
if (screenPos.z < 0) return; // behind camera
|
||||||
|
|
||||||
|
float sx = screenPos.x;
|
||||||
|
float sy = Screen.height - screenPos.y; // flip y
|
||||||
|
|
||||||
|
string label = definition != null ? definition.itemName : itemName;
|
||||||
|
bool isWeapon = definition != null && definition.type == ItemDefinition.ItemType.Weapon;
|
||||||
|
string prompt = $"[{pickupKey}] Pick up {label}";
|
||||||
|
|
||||||
|
GUIStyle bg = new GUIStyle(GUI.skin.box);
|
||||||
|
bg.fontSize = 12;
|
||||||
|
bg.fontStyle = FontStyle.Bold;
|
||||||
|
bg.normal.textColor = isWeapon ? new Color(1f, 0.85f, 0.2f) : Color.white;
|
||||||
|
|
||||||
|
Vector2 size = bg.CalcSize(new GUIContent(prompt));
|
||||||
|
Rect bgRect = new Rect(sx - size.x * 0.5f - 6f, sy - size.y - 4f, size.x + 12f, size.y + 6f);
|
||||||
|
|
||||||
|
// Dark backing box
|
||||||
|
Color prev = GUI.color;
|
||||||
|
GUI.color = new Color(0, 0, 0, 0.65f);
|
||||||
|
GUI.DrawTexture(bgRect, Texture2D.whiteTexture);
|
||||||
|
GUI.color = prev;
|
||||||
|
|
||||||
|
GUI.Label(bgRect, " " + prompt + " ", bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnDrawGizmosSelected()
|
||||||
|
{
|
||||||
|
Gizmos.color = Color.yellow;
|
||||||
|
Gizmos.DrawWireSphere(transform.position, pickupRadius);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Collect()
|
||||||
|
{
|
||||||
|
_collected = true;
|
||||||
|
_inRange = false;
|
||||||
|
|
||||||
|
if (_inventory != null)
|
||||||
|
{
|
||||||
|
if (definition != null)
|
||||||
|
_inventory.AddItem(definition, autoEquip: true);
|
||||||
|
else
|
||||||
|
_inventory.AddItem(itemName, itemIcon);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogWarning("PickupItem: No Inventory found on player!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pickupParticlesPrefab != null)
|
||||||
|
Instantiate(pickupParticlesPrefab, transform.position, Quaternion.identity);
|
||||||
|
|
||||||
|
if (pickupSound != null)
|
||||||
|
{
|
||||||
|
_audioSource.PlayOneShot(pickupSound);
|
||||||
|
Destroy(gameObject, pickupSound.length);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Destroy(gameObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/Scripts/PickupItem.cs.meta
Normal file
2
Assets/Scripts/PickupItem.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 95709fa8fd6c75045b1ef8e4f505c152
|
||||||
@@ -1,6 +1,65 @@
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Core player stats: health and stamina.
|
||||||
|
/// Stamina drains while sprinting, regens when not.
|
||||||
|
/// If stamina hits zero the player is exhausted and can't sprint again until it fully refills.
|
||||||
|
/// </summary>
|
||||||
public class Player : MonoBehaviour
|
public class Player : MonoBehaviour
|
||||||
{
|
{
|
||||||
public float health;
|
[Header("Health")]
|
||||||
|
public float maxHealth = 100f;
|
||||||
|
public float health = 100f;
|
||||||
|
|
||||||
|
[Header("Stamina")]
|
||||||
|
public float maxStamina = 100f;
|
||||||
|
public float stamina = 100f;
|
||||||
|
public float staminaDrain = 22f; // per second while sprinting
|
||||||
|
public float staminaRegen = 12f; // per second while not sprinting
|
||||||
|
public float regenDelay = 1.2f; // seconds after stopping sprint before regen kicks in
|
||||||
|
|
||||||
|
// ─── State ───────────────────────────────────────────────────────
|
||||||
|
[HideInInspector] public bool isSprinting = false;
|
||||||
|
[HideInInspector] public bool isExhausted = false; // true until stamina fully refills
|
||||||
|
|
||||||
|
private float _regenTimer = 0f;
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
void Update()
|
||||||
|
{
|
||||||
|
if (isSprinting && !isExhausted)
|
||||||
|
{
|
||||||
|
stamina -= staminaDrain * Time.deltaTime;
|
||||||
|
_regenTimer = 0f;
|
||||||
|
|
||||||
|
if (stamina <= 0f)
|
||||||
|
{
|
||||||
|
stamina = 0f;
|
||||||
|
isExhausted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Count down regen delay
|
||||||
|
_regenTimer += Time.deltaTime;
|
||||||
|
|
||||||
|
if (_regenTimer >= regenDelay)
|
||||||
|
{
|
||||||
|
stamina = Mathf.MoveTowards(stamina, maxStamina, staminaRegen * Time.deltaTime);
|
||||||
|
|
||||||
|
// Clear exhaustion only once fully refilled
|
||||||
|
if (isExhausted && stamina >= maxStamina)
|
||||||
|
isExhausted = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
health = Mathf.Clamp(health, 0f, maxHealth);
|
||||||
|
stamina = Mathf.Clamp(stamina, 0f, maxStamina);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Helpers other scripts can call ──────────────────────────────
|
||||||
|
public bool CanSprint() => !isExhausted && stamina > 0f;
|
||||||
|
|
||||||
|
public float HealthFraction => health / maxHealth;
|
||||||
|
public float StaminaFraction => stamina / maxStamina;
|
||||||
}
|
}
|
||||||
|
|||||||
281
Assets/Scripts/PlayerHUD.cs
Normal file
281
Assets/Scripts/PlayerHUD.cs
Normal file
@@ -0,0 +1,281 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Draws the player health and stamina bars.
|
||||||
|
/// Attach to the Player GameObject alongside Player.cs
|
||||||
|
///
|
||||||
|
/// Deliberately ugly. This is a Cruelty Squad game.
|
||||||
|
/// </summary>
|
||||||
|
public class PlayerHUD : MonoBehaviour
|
||||||
|
{
|
||||||
|
[Header("Layout")]
|
||||||
|
public float barWidth = 220f;
|
||||||
|
public float barHeight = 16f;
|
||||||
|
public float barSpacing = 10f; // gap between health and stamina bars
|
||||||
|
public float edgePadX = 18f;
|
||||||
|
public float edgePadY = 18f;
|
||||||
|
public AnchorCorner anchor = AnchorCorner.BottomLeft;
|
||||||
|
|
||||||
|
public enum AnchorCorner { BottomLeft, BottomRight, TopLeft, TopRight }
|
||||||
|
|
||||||
|
[Header("Health Bar")]
|
||||||
|
public Color colHealthFull = new Color(0.15f, 0.80f, 0.25f, 1f);
|
||||||
|
public Color colHealthMid = new Color(0.85f, 0.75f, 0.05f, 1f);
|
||||||
|
public Color colHealthLow = new Color(0.90f, 0.12f, 0.12f, 1f);
|
||||||
|
public float healthMidThreshold = 0.5f;
|
||||||
|
public float healthLowThreshold = 0.25f;
|
||||||
|
|
||||||
|
[Header("Stamina Bar")]
|
||||||
|
public Color colStaminaNormal = new Color(0.20f, 0.55f, 0.95f, 1f);
|
||||||
|
public Color colStaminaRegen = new Color(0.20f, 0.55f, 0.95f, 0.45f); // dimmed while regenerating
|
||||||
|
public Color colStaminaExhaust = new Color(0.60f, 0.18f, 0.18f, 1f); // red flash when tapped out
|
||||||
|
|
||||||
|
[Header("Shared Style")]
|
||||||
|
public Color colBarBackground = new Color(0.06f, 0.06f, 0.06f, 0.90f);
|
||||||
|
public Color colBarBorder = new Color(0.28f, 0.28f, 0.28f, 1.00f);
|
||||||
|
public Color colLabel = new Color(0.80f, 0.80f, 0.80f, 1.00f);
|
||||||
|
public Color colLabelCritical = new Color(1.00f, 0.25f, 0.25f, 1.00f);
|
||||||
|
public float borderThickness = 1.5f;
|
||||||
|
|
||||||
|
[Header("Pulse")]
|
||||||
|
[Tooltip("The low-health bar pulses opacity when below the low threshold.")]
|
||||||
|
public float pulseSpeed = 3.5f;
|
||||||
|
|
||||||
|
[Header("Speedometer")]
|
||||||
|
public bool showSpeedometer = true;
|
||||||
|
public Color colSpeedo = new Color(0.20f, 0.95f, 0.40f, 1f);
|
||||||
|
public Color colSpeedoFast = new Color(0.95f, 0.80f, 0.10f, 1f);
|
||||||
|
public Color colSpeedoCritical = new Color(0.95f, 0.20f, 0.20f, 1f);
|
||||||
|
public float speedoFastThreshold = 60f;
|
||||||
|
public float speedoCritThreshold = 75f;
|
||||||
|
|
||||||
|
// ─── Private ─────────────────────────────────────────────────────
|
||||||
|
private Player _player;
|
||||||
|
private CharacterController _cc;
|
||||||
|
private Texture2D _white;
|
||||||
|
|
||||||
|
// Smooth display values so bars glide rather than snap
|
||||||
|
private float _displayHealth = 1f;
|
||||||
|
private float _displayStamina = 1f;
|
||||||
|
private const float kSmoothSpeed = 8f;
|
||||||
|
|
||||||
|
// Speed tracking
|
||||||
|
private Vector3 _lastPos;
|
||||||
|
private float _displaySpeed;
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
void Start()
|
||||||
|
{
|
||||||
|
_player = GetComponent<Player>();
|
||||||
|
_cc = GetComponent<CharacterController>();
|
||||||
|
_white = Texture2D.whiteTexture;
|
||||||
|
_lastPos = transform.position;
|
||||||
|
|
||||||
|
if (_player == null)
|
||||||
|
Debug.LogWarning("[PlayerHUD] No Player component found on this GameObject!");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Update()
|
||||||
|
{
|
||||||
|
if (_player == null) return;
|
||||||
|
|
||||||
|
// Smooth bar fill values towards actual values
|
||||||
|
_displayHealth = Mathf.Lerp(_displayHealth, _player.HealthFraction, Time.deltaTime * kSmoothSpeed);
|
||||||
|
_displayStamina = Mathf.Lerp(_displayStamina, _player.StaminaFraction, Time.deltaTime * kSmoothSpeed);
|
||||||
|
|
||||||
|
// Calculate speed from position delta (horizontal only)
|
||||||
|
Vector3 pos = transform.position;
|
||||||
|
float rawSpeed = new Vector3(pos.x - _lastPos.x, 0f, pos.z - _lastPos.z).magnitude / Time.deltaTime;
|
||||||
|
_displaySpeed = Mathf.Lerp(_displaySpeed, rawSpeed, Time.deltaTime * 10f);
|
||||||
|
_lastPos = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnGUI()
|
||||||
|
{
|
||||||
|
if (_player == null) return;
|
||||||
|
|
||||||
|
float totalH = barHeight * 2f + barSpacing + 20f; // label heights included
|
||||||
|
float totalW = barWidth;
|
||||||
|
|
||||||
|
float ox, oy;
|
||||||
|
switch (anchor)
|
||||||
|
{
|
||||||
|
case AnchorCorner.BottomLeft:
|
||||||
|
ox = edgePadX;
|
||||||
|
oy = Screen.height - totalH - edgePadY;
|
||||||
|
break;
|
||||||
|
case AnchorCorner.BottomRight:
|
||||||
|
ox = Screen.width - totalW - edgePadX;
|
||||||
|
oy = Screen.height - totalH - edgePadY;
|
||||||
|
break;
|
||||||
|
case AnchorCorner.TopLeft:
|
||||||
|
ox = edgePadX;
|
||||||
|
oy = edgePadY;
|
||||||
|
break;
|
||||||
|
default: // TopRight
|
||||||
|
ox = Screen.width - totalW - edgePadX;
|
||||||
|
oy = edgePadY;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
float cursor = oy;
|
||||||
|
|
||||||
|
// ── Health bar ───────────────────────────────────────────────
|
||||||
|
cursor = DrawStatBar(
|
||||||
|
ox, cursor,
|
||||||
|
label: "HEALTH",
|
||||||
|
value: _player.health,
|
||||||
|
maxValue: _player.maxHealth,
|
||||||
|
displayFraction: _displayHealth,
|
||||||
|
fillColor: GetHealthColor(),
|
||||||
|
pulse: _player.HealthFraction <= healthLowThreshold,
|
||||||
|
labelCritical: _player.HealthFraction <= healthLowThreshold
|
||||||
|
);
|
||||||
|
|
||||||
|
cursor += barSpacing;
|
||||||
|
|
||||||
|
// ── Stamina bar ──────────────────────────────────────────────
|
||||||
|
Color staminaColor;
|
||||||
|
if (_player.isExhausted)
|
||||||
|
staminaColor = colStaminaExhaust;
|
||||||
|
else if (!_player.isSprinting && _player.StaminaFraction < 1f)
|
||||||
|
staminaColor = colStaminaRegen;
|
||||||
|
else
|
||||||
|
staminaColor = colStaminaNormal;
|
||||||
|
|
||||||
|
string staminaLabel = _player.isExhausted ? "STAMINA [EXHAUSTED]" : "STAMINA";
|
||||||
|
|
||||||
|
DrawStatBar(
|
||||||
|
ox, cursor,
|
||||||
|
label: staminaLabel,
|
||||||
|
value: _player.stamina,
|
||||||
|
maxValue: _player.maxStamina,
|
||||||
|
displayFraction: _displayStamina,
|
||||||
|
fillColor: staminaColor,
|
||||||
|
pulse: _player.isExhausted,
|
||||||
|
labelCritical: _player.isExhausted
|
||||||
|
);
|
||||||
|
|
||||||
|
// ── Speedometer ──────────────────────────────────────────────
|
||||||
|
if (showSpeedometer && _cc != null)
|
||||||
|
DrawSpeedometer(ox, oy - 90f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawSpeedometer(float x, float y)
|
||||||
|
{
|
||||||
|
float speed = _displaySpeed;
|
||||||
|
|
||||||
|
Color col;
|
||||||
|
if (speed >= speedoCritThreshold) col = colSpeedoCritical;
|
||||||
|
else if (speed >= speedoFastThreshold) col = colSpeedoFast;
|
||||||
|
else col = colSpeedo;
|
||||||
|
|
||||||
|
float w = 130f;
|
||||||
|
float h = 70f;
|
||||||
|
|
||||||
|
// Dark backing box
|
||||||
|
DrawTex(new Rect(x - 6f, y - 6f, w + 12f, h + 12f), new Color(0f, 0f, 0f, 0.55f));
|
||||||
|
// Coloured left edge accent
|
||||||
|
DrawTex(new Rect(x - 6f, y - 6f, 3f, h + 12f), col);
|
||||||
|
|
||||||
|
// "SPEED" label
|
||||||
|
GUIStyle labelStyle = new GUIStyle();
|
||||||
|
labelStyle.fontSize = 10;
|
||||||
|
labelStyle.fontStyle = FontStyle.Bold;
|
||||||
|
labelStyle.normal.textColor = new Color(col.r, col.g, col.b, 0.75f);
|
||||||
|
GUI.Label(new Rect(x, y, w, 16f), "SPEED", labelStyle);
|
||||||
|
|
||||||
|
// Big number
|
||||||
|
GUIStyle numStyle = new GUIStyle();
|
||||||
|
numStyle.fontSize = 36;
|
||||||
|
numStyle.fontStyle = FontStyle.Bold;
|
||||||
|
numStyle.normal.textColor = col;
|
||||||
|
GUI.Label(new Rect(x, y + 14f, w, 42f), $"{speed:F1}", numStyle);
|
||||||
|
|
||||||
|
// Unit
|
||||||
|
GUIStyle unitStyle = new GUIStyle();
|
||||||
|
unitStyle.fontSize = 10;
|
||||||
|
unitStyle.fontStyle = FontStyle.Normal;
|
||||||
|
unitStyle.normal.textColor = new Color(col.r, col.g, col.b, 0.55f);
|
||||||
|
GUI.Label(new Rect(x, y + 54f, w, 16f), "units / sec", unitStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Bar renderer ─────────────────────────────────────────────────
|
||||||
|
/// <summary>Draws one labelled bar. Returns the Y position after the bar.</summary>
|
||||||
|
float DrawStatBar(
|
||||||
|
float x, float y,
|
||||||
|
string label,
|
||||||
|
float value, float maxValue,
|
||||||
|
float displayFraction,
|
||||||
|
Color fillColor,
|
||||||
|
bool pulse,
|
||||||
|
bool labelCritical)
|
||||||
|
{
|
||||||
|
float labelH = 14f;
|
||||||
|
float innerW = barWidth - borderThickness * 2f;
|
||||||
|
float innerH = barHeight - borderThickness * 2f;
|
||||||
|
|
||||||
|
// ── Label ────────────────────────────────────────────────────
|
||||||
|
GUIStyle labelStyle = new GUIStyle();
|
||||||
|
labelStyle.fontSize = 10;
|
||||||
|
labelStyle.fontStyle = FontStyle.Bold;
|
||||||
|
labelStyle.normal.textColor = labelCritical ? colLabelCritical : colLabel;
|
||||||
|
|
||||||
|
string valueStr = $"{Mathf.CeilToInt(value)} / {Mathf.CeilToInt(maxValue)}";
|
||||||
|
GUI.Label(new Rect(x, y, barWidth * 0.6f, labelH), label, labelStyle);
|
||||||
|
|
||||||
|
GUIStyle valStyle = new GUIStyle(labelStyle);
|
||||||
|
valStyle.alignment = TextAnchor.UpperRight;
|
||||||
|
valStyle.normal.textColor = labelCritical ? colLabelCritical : colLabel;
|
||||||
|
GUI.Label(new Rect(x, y, barWidth, labelH), valueStr, valStyle);
|
||||||
|
|
||||||
|
float barY = y + labelH + 2f;
|
||||||
|
|
||||||
|
// ── Background ───────────────────────────────────────────────
|
||||||
|
DrawTex(new Rect(x, barY, barWidth, barHeight), colBarBorder);
|
||||||
|
DrawTex(new Rect(x + borderThickness, barY + borderThickness, innerW, innerH), colBarBackground);
|
||||||
|
|
||||||
|
// ── Fill ─────────────────────────────────────────────────────
|
||||||
|
float fillW = Mathf.Max(0f, displayFraction * innerW);
|
||||||
|
|
||||||
|
// Pulse alpha on low health / exhaustion
|
||||||
|
Color drawColor = fillColor;
|
||||||
|
if (pulse)
|
||||||
|
{
|
||||||
|
float alpha = Mathf.Lerp(0.35f, 1f, (Mathf.Sin(Time.time * pulseSpeed) + 1f) * 0.5f);
|
||||||
|
drawColor = new Color(fillColor.r, fillColor.g, fillColor.b, fillColor.a * alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fillW > 0f)
|
||||||
|
DrawTex(new Rect(x + borderThickness, barY + borderThickness, fillW, innerH), drawColor);
|
||||||
|
|
||||||
|
// ── Segment tick marks (every 25%) ───────────────────────────
|
||||||
|
for (int i = 1; i < 4; i++)
|
||||||
|
{
|
||||||
|
float tickX = x + borderThickness + innerW * (i / 4f);
|
||||||
|
DrawTex(new Rect(tickX - 0.5f, barY + borderThickness, 1f, innerH),
|
||||||
|
new Color(0f, 0f, 0f, 0.45f));
|
||||||
|
}
|
||||||
|
|
||||||
|
return barY + barHeight; // bottom of bar
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Helpers ─────────────────────────────────────────────────────
|
||||||
|
Color GetHealthColor()
|
||||||
|
{
|
||||||
|
float f = _player.HealthFraction;
|
||||||
|
if (f <= healthLowThreshold)
|
||||||
|
return colHealthLow;
|
||||||
|
if (f <= healthMidThreshold)
|
||||||
|
return Color.Lerp(colHealthLow, colHealthMid, (f - healthLowThreshold) / (healthMidThreshold - healthLowThreshold));
|
||||||
|
return Color.Lerp(colHealthMid, colHealthFull, (f - healthMidThreshold) / (1f - healthMidThreshold));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawTex(Rect r, Color c)
|
||||||
|
{
|
||||||
|
Color prev = GUI.color;
|
||||||
|
GUI.color = c;
|
||||||
|
GUI.DrawTexture(r, _white);
|
||||||
|
GUI.color = prev;
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/Scripts/PlayerHUD.cs.meta
Normal file
2
Assets/Scripts/PlayerHUD.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1d84d3e6c1cf6794da6ff84203edb291
|
||||||
@@ -16,6 +16,8 @@ public class SimpleGun : MonoBehaviour
|
|||||||
public float recoilRecoverySpeed = 12f;
|
public float recoilRecoverySpeed = 12f;
|
||||||
|
|
||||||
[Header("Weapon Bob Settings")]
|
[Header("Weapon Bob Settings")]
|
||||||
|
[Tooltip("Set false if WeaponBob component is on the Camera — they'll double-stack otherwise.")]
|
||||||
|
public bool enableInternalBob = false;
|
||||||
public float bobFrequency = 10f;
|
public float bobFrequency = 10f;
|
||||||
public float bobHorizontalAmplitude = 0.05f;
|
public float bobHorizontalAmplitude = 0.05f;
|
||||||
public float bobVerticalAmplitude = 0.03f;
|
public float bobVerticalAmplitude = 0.03f;
|
||||||
@@ -48,6 +50,10 @@ public class SimpleGun : MonoBehaviour
|
|||||||
// Fire timing
|
// Fire timing
|
||||||
private float nextTimeToFire = 0f;
|
private float nextTimeToFire = 0f;
|
||||||
|
|
||||||
|
// Cached refs for inventory check
|
||||||
|
private Inventory _inventory;
|
||||||
|
private WeaponManager _weaponManager;
|
||||||
|
|
||||||
void Start()
|
void Start()
|
||||||
{
|
{
|
||||||
currentAmmo = maxAmmo;
|
currentAmmo = maxAmmo;
|
||||||
@@ -58,7 +64,12 @@ public class SimpleGun : MonoBehaviour
|
|||||||
fpsCam = GetComponentInParent<Camera>();
|
fpsCam = GetComponentInParent<Camera>();
|
||||||
|
|
||||||
playerController = GetComponentInParent<CharacterController>();
|
playerController = GetComponentInParent<CharacterController>();
|
||||||
originalLocalPos = transform.localPosition;
|
_inventory = GetComponentInParent<Inventory>();
|
||||||
|
_weaponManager = GetComponentInParent<WeaponManager>();
|
||||||
|
// originalLocalPos is driven by WeaponManager's offset — don't cache here
|
||||||
|
originalLocalPos = _weaponManager != null
|
||||||
|
? _weaponManager.weaponPositionOffset
|
||||||
|
: transform.localPosition;
|
||||||
|
|
||||||
|
|
||||||
// NUCLEAR OPTION: Remove ANY collider on this object or children
|
// NUCLEAR OPTION: Remove ANY collider on this object or children
|
||||||
@@ -93,6 +104,10 @@ public class SimpleGun : MonoBehaviour
|
|||||||
|
|
||||||
void Update()
|
void Update()
|
||||||
{
|
{
|
||||||
|
// Don't shoot while the inventory is open
|
||||||
|
bool inventoryOpen = _inventory != null && _inventory.IsOpen;
|
||||||
|
if (inventoryOpen) return;
|
||||||
|
|
||||||
// Shooting
|
// Shooting
|
||||||
if (isAutomatic ? InputData.leftMouseButton.down : InputData.leftMouseButton.pressed)
|
if (isAutomatic ? InputData.leftMouseButton.down : InputData.leftMouseButton.pressed)
|
||||||
{
|
{
|
||||||
@@ -107,11 +122,18 @@ public class SimpleGun : MonoBehaviour
|
|||||||
if (InputData.reload.pressed)
|
if (InputData.reload.pressed)
|
||||||
Reload();
|
Reload();
|
||||||
|
|
||||||
|
// Keep base position in sync with WeaponManager's live offset
|
||||||
|
if (_weaponManager != null)
|
||||||
|
originalLocalPos = _weaponManager.weaponPositionOffset;
|
||||||
|
|
||||||
// Recover from recoil
|
// Recover from recoil
|
||||||
recoilOffset = Vector3.Lerp(recoilOffset, Vector3.zero, Time.deltaTime * recoilRecoverySpeed);
|
recoilOffset = Vector3.Lerp(recoilOffset, Vector3.zero, Time.deltaTime * recoilRecoverySpeed);
|
||||||
|
|
||||||
// Weapon bob
|
// Weapon bob (only if internal bob is enabled — disable if WeaponBob is on the Camera)
|
||||||
UpdateBob();
|
if (enableInternalBob)
|
||||||
|
UpdateBob();
|
||||||
|
else
|
||||||
|
bobOffset = Vector3.zero;
|
||||||
|
|
||||||
// Apply combined position: original + recoil + bob
|
// Apply combined position: original + recoil + bob
|
||||||
transform.localPosition = originalLocalPos + recoilOffset + bobOffset;
|
transform.localPosition = originalLocalPos + recoilOffset + bobOffset;
|
||||||
|
|||||||
36
Assets/Scripts/StaminaBoostPickup.cs
Normal file
36
Assets/Scripts/StaminaBoostPickup.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add to a pickup GameObject alongside PickupItem.
|
||||||
|
/// When the item is collected, applies a one-time boost to the player's max stamina.
|
||||||
|
/// </summary>
|
||||||
|
[RequireComponent(typeof(PickupItem))]
|
||||||
|
public class StaminaBoostPickup : MonoBehaviour
|
||||||
|
{
|
||||||
|
[Tooltip("Max stamina will be set to this value when picked up.")]
|
||||||
|
public float newMaxStamina = 200f;
|
||||||
|
|
||||||
|
void Start()
|
||||||
|
{
|
||||||
|
// Hook into PickupItem's collect event via a simple poll
|
||||||
|
var pickup = GetComponent<PickupItem>();
|
||||||
|
// We override by watching PickupItem's collected state each frame
|
||||||
|
StartCoroutine(WatchForCollect(pickup));
|
||||||
|
}
|
||||||
|
|
||||||
|
System.Collections.IEnumerator WatchForCollect(PickupItem pickup)
|
||||||
|
{
|
||||||
|
// Wait until the pickup is destroyed (collected)
|
||||||
|
while (pickup != null)
|
||||||
|
yield return null;
|
||||||
|
|
||||||
|
// Find player and apply boost
|
||||||
|
Player p = FindObjectOfType<Player>();
|
||||||
|
if (p != null)
|
||||||
|
{
|
||||||
|
p.maxStamina = newMaxStamina;
|
||||||
|
p.stamina = newMaxStamina;
|
||||||
|
Debug.Log($"[StaminaBoost] Max stamina set to {newMaxStamina}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/Scripts/StaminaBoostPickup.cs.meta
Normal file
2
Assets/Scripts/StaminaBoostPickup.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1ce88c8ee6fd8a0469215a541f72f835
|
||||||
197
Assets/Scripts/WeaponManager.cs
Normal file
197
Assets/Scripts/WeaponManager.cs
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attach to the Player GameObject.
|
||||||
|
/// Manages all instantiated weapon instances and switching between them.
|
||||||
|
/// Also registers any weapon already in the scene (e.g. the starting gun).
|
||||||
|
/// </summary>
|
||||||
|
public class WeaponManager : MonoBehaviour
|
||||||
|
{
|
||||||
|
[Header("References")]
|
||||||
|
[Tooltip("The transform weapons are parented to — usually the Camera or a WeaponHolder child.")]
|
||||||
|
public Transform weaponHolder;
|
||||||
|
|
||||||
|
[Header("Weapon Position")]
|
||||||
|
[Tooltip("Local position offset applied to all equipped weapons.")]
|
||||||
|
public Vector3 weaponPositionOffset = new Vector3(0.2f, -0.25f, 0.45f);
|
||||||
|
[Tooltip("Local euler rotation offset applied to all equipped weapons.")]
|
||||||
|
public Vector3 weaponRotationOffset = new Vector3(0f, 0f, 0f);
|
||||||
|
[Tooltip("Scale applied to all equipped weapons.")]
|
||||||
|
public Vector3 weaponScale = new Vector3(1f, 1f, 1f);
|
||||||
|
|
||||||
|
[Header("Switching")]
|
||||||
|
public bool allowScrollSwitch = true;
|
||||||
|
public bool allowNumberKeys = true;
|
||||||
|
|
||||||
|
// ─── internal slot ───────────────────────────────────────────────
|
||||||
|
public class WeaponSlot
|
||||||
|
{
|
||||||
|
public string itemName;
|
||||||
|
public GameObject instance; // the live GO in weaponHolder
|
||||||
|
public ItemDefinition definition; // may be null for the starting gun
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<WeaponSlot> slots = new List<WeaponSlot>();
|
||||||
|
public int activeIndex { get; private set; } = -1;
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
void Start()
|
||||||
|
{
|
||||||
|
// Auto-find weapon holder if not set
|
||||||
|
if (weaponHolder == null)
|
||||||
|
{
|
||||||
|
Camera cam = GetComponentInChildren<Camera>();
|
||||||
|
weaponHolder = cam != null ? cam.transform : transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register any SimpleGun already present as children of the holder
|
||||||
|
SimpleGun[] existing = weaponHolder.GetComponentsInChildren<SimpleGun>(true);
|
||||||
|
foreach (SimpleGun gun in existing)
|
||||||
|
{
|
||||||
|
RegisterExistingGun(gun.gameObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Inventory _inventory;
|
||||||
|
|
||||||
|
void Update()
|
||||||
|
{
|
||||||
|
if (slots.Count == 0) return;
|
||||||
|
|
||||||
|
// Live-update active weapon transform so Inspector tweaks take effect immediately
|
||||||
|
if (activeIndex >= 0 && activeIndex < slots.Count)
|
||||||
|
{
|
||||||
|
var vm = slots[activeIndex].instance.GetComponent<WeaponViewmodel>();
|
||||||
|
if (vm != null)
|
||||||
|
vm.Apply();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Transform t = slots[activeIndex].instance.transform;
|
||||||
|
t.localPosition = weaponPositionOffset;
|
||||||
|
t.localRotation = Quaternion.Euler(weaponRotationOffset);
|
||||||
|
t.localScale = weaponScale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't switch weapons while the inventory UI is open
|
||||||
|
if (_inventory == null) _inventory = GetComponent<Inventory>();
|
||||||
|
if (_inventory != null && _inventory.IsOpen) return;
|
||||||
|
|
||||||
|
// Scroll wheel
|
||||||
|
if (allowScrollSwitch)
|
||||||
|
{
|
||||||
|
float scroll = Input.GetAxis("Mouse ScrollWheel");
|
||||||
|
if (scroll > 0f) CycleWeapon(1);
|
||||||
|
if (scroll < 0f) CycleWeapon(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Number keys 1–9
|
||||||
|
if (allowNumberKeys)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Mathf.Min(slots.Count, 9); i++)
|
||||||
|
{
|
||||||
|
if (Input.GetKeyDown(KeyCode.Alpha1 + i))
|
||||||
|
EquipByIndex(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Public API ──────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// <summary>Register a weapon that already exists in the scene (starting gun).</summary>
|
||||||
|
public void RegisterExistingGun(GameObject gunGO, ItemDefinition def = null)
|
||||||
|
{
|
||||||
|
string name = def != null ? def.itemName : gunGO.name;
|
||||||
|
|
||||||
|
// Don't double-register
|
||||||
|
if (slots.Exists(s => s.instance == gunGO)) return;
|
||||||
|
|
||||||
|
slots.Add(new WeaponSlot { itemName = name, instance = gunGO, definition = def });
|
||||||
|
int idx = slots.Count - 1;
|
||||||
|
|
||||||
|
// Always start inactive — player must pick up / equip from inventory
|
||||||
|
gunGO.SetActive(false);
|
||||||
|
|
||||||
|
Debug.Log($"[WeaponManager] Registered existing weapon: {name} at slot {idx} (inactive)");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Add a new weapon from a definition (called by Inventory on first equip).</summary>
|
||||||
|
public int AddWeapon(ItemDefinition def)
|
||||||
|
{
|
||||||
|
if (def == null || def.weaponPrefab == null)
|
||||||
|
{
|
||||||
|
Debug.LogWarning("[WeaponManager] AddWeapon: definition or prefab is null.");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Already registered?
|
||||||
|
int existing = slots.FindIndex(s => s.definition == def);
|
||||||
|
if (existing >= 0) return existing;
|
||||||
|
|
||||||
|
GameObject instance = Instantiate(def.weaponPrefab, weaponHolder);
|
||||||
|
var vm = instance.GetComponent<WeaponViewmodel>();
|
||||||
|
if (vm != null)
|
||||||
|
vm.Apply();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
instance.transform.localPosition = weaponPositionOffset;
|
||||||
|
instance.transform.localRotation = Quaternion.Euler(weaponRotationOffset);
|
||||||
|
instance.transform.localScale = weaponScale;
|
||||||
|
}
|
||||||
|
instance.SetActive(false);
|
||||||
|
|
||||||
|
// Strip colliders so they don't mess with the CharacterController
|
||||||
|
foreach (Collider col in instance.GetComponentsInChildren<Collider>())
|
||||||
|
if (!(col is CharacterController)) Destroy(col);
|
||||||
|
|
||||||
|
slots.Add(new WeaponSlot { itemName = def.itemName, instance = instance, definition = def });
|
||||||
|
int newIdx = slots.Count - 1;
|
||||||
|
Debug.Log($"[WeaponManager] Added weapon: {def.itemName} at slot {newIdx}");
|
||||||
|
return newIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Equip weapon by slot index.</summary>
|
||||||
|
public void EquipByIndex(int index)
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= slots.Count) return;
|
||||||
|
|
||||||
|
// Deactivate current
|
||||||
|
if (activeIndex >= 0 && activeIndex < slots.Count)
|
||||||
|
slots[activeIndex].instance.SetActive(false);
|
||||||
|
|
||||||
|
activeIndex = index;
|
||||||
|
slots[activeIndex].instance.SetActive(true);
|
||||||
|
Debug.Log($"[WeaponManager] Equipped: {slots[activeIndex].itemName}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Equip weapon by item name.</summary>
|
||||||
|
public void EquipByName(string name)
|
||||||
|
{
|
||||||
|
int idx = slots.FindIndex(s => s.itemName == name);
|
||||||
|
if (idx >= 0)
|
||||||
|
EquipByIndex(idx);
|
||||||
|
else
|
||||||
|
Debug.LogWarning($"[WeaponManager] No slot found for: {name}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Cycle forward (+1) or backward (-1).</summary>
|
||||||
|
public void CycleWeapon(int dir)
|
||||||
|
{
|
||||||
|
if (slots.Count == 0) return;
|
||||||
|
int next = (activeIndex + dir + slots.Count) % slots.Count;
|
||||||
|
EquipByIndex(next);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Deactivate the current weapon without switching to another.</summary>
|
||||||
|
public void Unequip()
|
||||||
|
{
|
||||||
|
if (activeIndex >= 0 && activeIndex < slots.Count)
|
||||||
|
slots[activeIndex].instance.SetActive(false);
|
||||||
|
activeIndex = -1;
|
||||||
|
Debug.Log("[WeaponManager] Unequipped all weapons.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ActiveWeaponName =>
|
||||||
|
(activeIndex >= 0 && activeIndex < slots.Count) ? slots[activeIndex].itemName : "";
|
||||||
|
}
|
||||||
2
Assets/Scripts/WeaponManager.cs.meta
Normal file
2
Assets/Scripts/WeaponManager.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 18715d388d5da7544b10c5d7a385cbee
|
||||||
29
Assets/Scripts/WeaponViewmodel.cs
Normal file
29
Assets/Scripts/WeaponViewmodel.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add to a weapon prefab to define how it sits in the player's hands.
|
||||||
|
/// WeaponManager reads these values when the weapon is equipped.
|
||||||
|
/// </summary>
|
||||||
|
public class WeaponViewmodel : MonoBehaviour
|
||||||
|
{
|
||||||
|
[Header("View Model Transform")]
|
||||||
|
public Vector3 positionOffset = new Vector3(0.2f, -0.25f, 0.45f);
|
||||||
|
public Vector3 rotationOffset = new Vector3(0f, 0f, 0f);
|
||||||
|
public Vector3 scale = new Vector3(1f, 1f, 1f);
|
||||||
|
|
||||||
|
// Called by WeaponManager to apply these values
|
||||||
|
public void Apply()
|
||||||
|
{
|
||||||
|
transform.localPosition = positionOffset;
|
||||||
|
transform.localRotation = Quaternion.Euler(rotationOffset);
|
||||||
|
transform.localScale = scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called by WeaponManager to sync back after gizmo editing
|
||||||
|
public void SyncFromTransform()
|
||||||
|
{
|
||||||
|
positionOffset = transform.localPosition;
|
||||||
|
rotationOffset = transform.localRotation.eulerAngles;
|
||||||
|
scale = transform.localScale;
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/Scripts/WeaponViewmodel.cs.meta
Normal file
2
Assets/Scripts/WeaponViewmodel.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 59433c687536a60418803799027ddcc3
|
||||||
353
DEV_NOTES.md
Normal file
353
DEV_NOTES.md
Normal file
@@ -0,0 +1,353 @@
|
|||||||
|
# OGG — Dev Notes & Systems Guide
|
||||||
|
|
||||||
|
> Boomer shooter. Cruelty Squad vibes. Jank is a feature.
|
||||||
|
> This doc covers all current systems.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Scene Hierarchy
|
||||||
|
|
||||||
|
```
|
||||||
|
Player [CharacterController, FirstPersonController, SimpleCrosshair]
|
||||||
|
│ [Player, PlayerHUD, Inventory, WeaponManager, BootsEffect]
|
||||||
|
└── Camera [Camera, AudioListener, CameraShake, WeaponBob]
|
||||||
|
```
|
||||||
|
|
||||||
|
Pickups are standalone GameObjects placed anywhere in the scene — **not** children of the player.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Scripts Overview
|
||||||
|
|
||||||
|
| Script | Lives On | What it does |
|
||||||
|
|---|---|---|
|
||||||
|
| `FirstPersonController` | Player | WASD move, mouse look, jump, sprint, cursor lock |
|
||||||
|
| `Player` | Player | Health + stamina stats, drain/regen logic, sprint gating |
|
||||||
|
| `PlayerHUD` | Player | Health bar, stamina bar, speedometer (IMGUI) |
|
||||||
|
| `SimpleGun` | Weapon prefabs | Shooting, ammo, recoil, muzzle flash, bullet decals |
|
||||||
|
| `CameraShake` | Camera | Singleton shake impulses, called by SimpleGun on fire |
|
||||||
|
| `WeaponBob` | Camera | Procedural weapon sway while moving |
|
||||||
|
| `WeaponViewmodel` | Weapon prefabs | Per-weapon position/rotation/scale in the player's hands |
|
||||||
|
| `SimpleCrosshair` | Player | Draws the crosshair via OnGUI |
|
||||||
|
| `EnemyHealth` | Enemy objects | Takes damage from SimpleGun raycasts, flashes red, dies |
|
||||||
|
| `EnemySpawner` | Any object | Spawns enemies at runtime |
|
||||||
|
| `HumanoidEnemy` | Enemy objects | Enemy AI / behaviour |
|
||||||
|
| `ItemDefinition` | ScriptableObject asset | Data definition for any item: name, icon, type, weapon prefab, equippable flag |
|
||||||
|
| `PickupItem` | Pickup GameObjects | Spin/bob animation, proximity prompt, E-to-collect |
|
||||||
|
| `Inventory` | Player | Item list, [I] HUD, right-click context menu, equip logic |
|
||||||
|
| `WeaponManager` | Player | Instantiates weapon prefabs, handles slot switching |
|
||||||
|
| `BootsEffect` | Player | Watches inventory for Bunny Hop Boots equipped state, applies stamina boost |
|
||||||
|
| `StaminaBoostPickup` | Pickup GameObjects | Legacy — superseded by BootsEffect. Left in for reference. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Player Stats — `Player` + `PlayerHUD`
|
||||||
|
|
||||||
|
`Player.cs` is the data container for all player stats. `PlayerHUD.cs` reads from it and draws the HUD.
|
||||||
|
|
||||||
|
**Health fields:**
|
||||||
|
- `maxHealth` / `health` — enemies subtract from `health` directly, no auto-regen
|
||||||
|
|
||||||
|
**Stamina fields:**
|
||||||
|
- `maxStamina` / `stamina` — depletes while sprinting
|
||||||
|
- `staminaDrain` — units lost per second while sprinting (default 22)
|
||||||
|
- `staminaRegen` — units gained per second when resting (default 12)
|
||||||
|
- `regenDelay` — seconds of non-sprinting before regen starts (default 1.2)
|
||||||
|
- When stamina hits 0 the player is **exhausted** — sprint locked out until stamina fully refills
|
||||||
|
|
||||||
|
**HUD Layout:**
|
||||||
|
```
|
||||||
|
[bottom-left corner]
|
||||||
|
SPEED
|
||||||
|
42.3
|
||||||
|
units / sec
|
||||||
|
|
||||||
|
HEALTH 85 / 100
|
||||||
|
[████████████░░░]
|
||||||
|
STAMINA 62 / 100
|
||||||
|
[██████░░░░░░░░░]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Speedometer:**
|
||||||
|
- Sits above the health/stamina bars, bottom-left
|
||||||
|
- Speed calculated from actual position delta per frame (horizontal only)
|
||||||
|
- Colour shifts: green → yellow → red as speed increases
|
||||||
|
- Thresholds configurable in Inspector (`speedoFastThreshold`, `speedoCritThreshold`)
|
||||||
|
- Can be toggled off via `showSpeedometer` bool
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Movement — `FirstPersonController`
|
||||||
|
|
||||||
|
Standard FPS controller.
|
||||||
|
|
||||||
|
**Inspector fields:**
|
||||||
|
- `walkSpeed` / `runSpeed` — intentionally high (50/80) due to CharacterController collision tuning
|
||||||
|
- `mouseSensitivity` — linear, no acceleration
|
||||||
|
- `playerCamera` — auto-found if empty, best to assign explicitly
|
||||||
|
|
||||||
|
**Controls:**
|
||||||
|
| Key | Action |
|
||||||
|
|---|---|
|
||||||
|
| W A S D / Arrows | Move |
|
||||||
|
| Left Shift | Sprint (drains stamina) |
|
||||||
|
| Space | Jump |
|
||||||
|
| Mouse | Look |
|
||||||
|
| Escape | Unlock cursor |
|
||||||
|
| Left Click (unlocked) | Re-lock cursor |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Shooting — `SimpleGun`
|
||||||
|
|
||||||
|
Raycast-based hitscan. No projectiles.
|
||||||
|
|
||||||
|
**Key fields:**
|
||||||
|
- `damage` — per shot
|
||||||
|
- `range` — raycast distance in units
|
||||||
|
- `fireRate` — shots per second
|
||||||
|
- `isAutomatic` — hold vs click
|
||||||
|
- `recoilKickback` / `recoilKickUp` — feel tuning
|
||||||
|
- `fpsCam` — auto-found if null
|
||||||
|
|
||||||
|
**Internals:**
|
||||||
|
- Muzzle flash: child Quad + Point Light, shown ~50ms per shot
|
||||||
|
- Bullet decals: pooled (30 max), oldest reused when full
|
||||||
|
- Spark particles: spawned and self-destroyed on impact
|
||||||
|
- Calls `CameraShake.Instance.Shake()` on every shot
|
||||||
|
- Shooting blocked when inventory is open
|
||||||
|
- Position driven by `WeaponManager.weaponPositionOffset` each frame so recoil applies on top correctly
|
||||||
|
|
||||||
|
**Ammo:** `currentAmmo` / `maxAmmo`. Press **R** to reload (instant).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Item Definitions — `ItemDefinition` (ScriptableObject)
|
||||||
|
|
||||||
|
**Create:** Right-click in Project → **Create → OGG → Item Definition**
|
||||||
|
|
||||||
|
| Field | Notes |
|
||||||
|
|---|---|
|
||||||
|
| `itemName` | Display name everywhere — must be unique |
|
||||||
|
| `icon` | Sprite shown in inventory slots |
|
||||||
|
| `type` | `Misc`, `Consumable`, or `Weapon` |
|
||||||
|
| `isEquippable` | Tick for non-weapon items that can be equipped (e.g. boots) — shows equip button in inventory |
|
||||||
|
| `weaponPrefab` | Weapon type only — prefab spawned in weapon holder when equipped |
|
||||||
|
| `description` | Shown in inventory detail panel |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Pickups — `PickupItem`
|
||||||
|
|
||||||
|
Attach to any GameObject to make it a spinning, bobbing pickup.
|
||||||
|
|
||||||
|
**Setup:**
|
||||||
|
1. Place a GameObject in the scene with any mesh
|
||||||
|
2. Add `PickupItem` component
|
||||||
|
3. Drag an `ItemDefinition` into the `Definition` slot
|
||||||
|
|
||||||
|
**Inspector fields:**
|
||||||
|
| Field | Notes |
|
||||||
|
|---|---|
|
||||||
|
| `definition` | The ItemDefinition asset |
|
||||||
|
| `spinSpeed` | Degrees/sec Y rotation (default 120) |
|
||||||
|
| `bobHeight` | Vertical travel in units (default 0.18) |
|
||||||
|
| `bobSpeed` | Bob cycles per second (default 2.2) |
|
||||||
|
| `pickupRadius` | Proximity trigger distance |
|
||||||
|
| `pickupKey` | Default E |
|
||||||
|
| `pickupParticlesPrefab` | Optional — spawned on collect |
|
||||||
|
| `pickupSound` | Optional — played on collect |
|
||||||
|
|
||||||
|
**On pickup:** Calls `Inventory.AddItem(definition, autoEquip: true)` — picking up a weapon automatically equips it.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Inventory — `Inventory`
|
||||||
|
|
||||||
|
Lives on the Player. Press **I** to open/close. Cursor unlocks when open.
|
||||||
|
|
||||||
|
**HUD Layout:**
|
||||||
|
```
|
||||||
|
┌──────────────────────────────┐
|
||||||
|
│ [ INVENTORY ] │ ← header, shows active weapon top-right
|
||||||
|
├──────────────────────────────┤
|
||||||
|
│ [ ] [ ] [ ] [ ] │
|
||||||
|
│ [ ] [ ] [ ] [ ] │ ← item grid (4×5 by default)
|
||||||
|
├──────────────────────────────┤
|
||||||
|
│ SELECTED: Item Name │ ← detail panel
|
||||||
|
│ Description │
|
||||||
|
│ ▶ EQUIPPED [ F ] UNEQUIP│ ← shown for weapons AND equippable items
|
||||||
|
└──────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**Right-click context menu** on any slot:
|
||||||
|
- **Equip** / **Unequip** — shown for weapons and items with `isEquippable = true`
|
||||||
|
- **Inspect** — selects the item in the detail panel
|
||||||
|
- **Drop** — removes from inventory
|
||||||
|
|
||||||
|
**Controls:**
|
||||||
|
| Input | Action |
|
||||||
|
|---|---|
|
||||||
|
| I | Toggle inventory open/close |
|
||||||
|
| Click slot | Select item |
|
||||||
|
| Double-click weapon slot | Equip it |
|
||||||
|
| Right-click slot | Open context menu |
|
||||||
|
| F | Equip selected item |
|
||||||
|
| Scroll Wheel (closed) | Cycle weapons |
|
||||||
|
| 1–9 (closed) | Equip weapon by slot number |
|
||||||
|
|
||||||
|
**Slot colours:**
|
||||||
|
- Dark grey = empty
|
||||||
|
- Dark green = has item
|
||||||
|
- Bright green = selected
|
||||||
|
- Orange = equipped
|
||||||
|
|
||||||
|
**Useful methods:**
|
||||||
|
```csharp
|
||||||
|
inventory.HasItem("Medkit") // bool
|
||||||
|
inventory.CountOf("Grenade") // int
|
||||||
|
inventory.RemoveItem("Medkit", 1) // bool
|
||||||
|
inventory.AddItem(definition) // add by ItemDefinition
|
||||||
|
inventory.AddItem("Raw Name", sprite) // add by name
|
||||||
|
inventory.IsOpen // bool — checked by gun + WeaponManager
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Weapon Manager — `WeaponManager`
|
||||||
|
|
||||||
|
Lives on the Player. Manages weapon instances and which is active.
|
||||||
|
|
||||||
|
**Setup:**
|
||||||
|
- Add `WeaponManager` to Player
|
||||||
|
- Set `Weapon Holder` to the Camera transform
|
||||||
|
- Any `SimpleGun` child of the holder is auto-registered but starts **inactive** — player must pick up and equip from inventory
|
||||||
|
|
||||||
|
**Weapon Position fields:**
|
||||||
|
- `weaponPositionOffset` / `weaponRotationOffset` / `weaponScale` — global fallback if the weapon has no `WeaponViewmodel` component
|
||||||
|
- Per-weapon positioning is preferred — add `WeaponViewmodel` to the weapon prefab instead
|
||||||
|
|
||||||
|
**Live positioning workflow (in Editor):**
|
||||||
|
1. Hit Play, equip the gun
|
||||||
|
2. On WeaponManager Inspector → click **"Select Active Weapon in Scene"**
|
||||||
|
3. Use gizmos in Scene view to position it
|
||||||
|
4. Click **"Sync Transform → Offset Fields"** — saves to the `WeaponViewmodel` on the prefab automatically
|
||||||
|
5. Stop Play — values persist
|
||||||
|
|
||||||
|
**Useful methods:**
|
||||||
|
```csharp
|
||||||
|
weaponManager.EquipByIndex(0)
|
||||||
|
weaponManager.EquipByName("Gun Splat")
|
||||||
|
weaponManager.CycleWeapon(1) // next
|
||||||
|
weaponManager.CycleWeapon(-1) // previous
|
||||||
|
weaponManager.ActiveWeaponName // string
|
||||||
|
weaponManager.Unequip() // holster all
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. WeaponViewmodel — `WeaponViewmodel`
|
||||||
|
|
||||||
|
Add to a **weapon prefab** to define how it sits in the player's hands. WeaponManager reads from it on equip instead of using the global offset.
|
||||||
|
|
||||||
|
**Fields:**
|
||||||
|
- `positionOffset` — local position relative to weapon holder
|
||||||
|
- `rotationOffset` — euler angles
|
||||||
|
- `scale` — local scale
|
||||||
|
|
||||||
|
Each weapon prefab gets its own values, so Gun Splat can sit differently from any future weapon.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Equippable Items — `BootsEffect`
|
||||||
|
|
||||||
|
**Add `BootsEffect` to the Player** for the Bunny Hop Boots to work.
|
||||||
|
|
||||||
|
Lives on the Player. Watches `Inventory.items` each frame for the boots' `isEquipped` state and adjusts `Player.maxStamina` accordingly.
|
||||||
|
|
||||||
|
| State | Effect |
|
||||||
|
|---|---|
|
||||||
|
| Boots equipped | `maxStamina` → 200, stamina topped up |
|
||||||
|
| Boots unequipped | `maxStamina` → 100, stamina clamped back down |
|
||||||
|
|
||||||
|
The stamina bar stretches/shrinks live to reflect the new max.
|
||||||
|
|
||||||
|
**Inspector fields:**
|
||||||
|
- `bootsItemName` — must match the ItemDefinition `itemName` exactly (default `"Bunny Hop Boots"`)
|
||||||
|
- `boostedMaxStamina` — default 200
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Setup Scripts (Editor Menu: OGG → Setup)
|
||||||
|
|
||||||
|
### Create Gun Splat Weapon
|
||||||
|
Creates:
|
||||||
|
- `Assets/Prefabs/Weapons/GunSplat.prefab` — held weapon with `SimpleGun`
|
||||||
|
- `Assets/Prefabs/Pickups/GunSplat_Pickup.prefab` — world pickup with spin/bob
|
||||||
|
- `Assets/Items/GunSplat_Item.asset` — `ItemDefinition` (type = Weapon)
|
||||||
|
|
||||||
|
### Create Bunny Hop Boots
|
||||||
|
Creates:
|
||||||
|
- `Assets/Items/BhopBoots_Item.asset` — `ItemDefinition` (type = Misc, isEquippable = true)
|
||||||
|
- `Assets/Prefabs/Pickups/BhopBoots_Pickup.prefab` — world pickup
|
||||||
|
|
||||||
|
**After running either script:** drag the pickup prefab into your scene.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Adding a New Weapon — Step by Step
|
||||||
|
|
||||||
|
1. Create an `ItemDefinition` asset — set `type = Weapon`, assign your gun prefab to `weaponPrefab`
|
||||||
|
2. Add `SimpleGun` to the gun prefab root
|
||||||
|
3. Add `WeaponViewmodel` to the gun prefab — set position/rotation/scale or dial it in with the live Editor workflow
|
||||||
|
4. Create a pickup: any mesh + `PickupItem` component + drag in the ItemDefinition
|
||||||
|
5. Place the pickup in the scene
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Adding a New Equippable Item
|
||||||
|
|
||||||
|
1. Create an `ItemDefinition` — set `type = Misc`, tick `isEquippable = true`
|
||||||
|
2. Write a component that lives on the Player, watches `inventory.items` for `entry.isEquipped`, and applies/removes the effect
|
||||||
|
3. Place a pickup in the scene referencing the definition
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Adding a Consumable
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
Inventory inv = GetComponent<Inventory>();
|
||||||
|
if (inv.HasItem("Medkit")) {
|
||||||
|
health += 50f;
|
||||||
|
inv.RemoveItem("Medkit");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Known Rough Edges
|
||||||
|
|
||||||
|
- **No weapon switch animation** — instant swap. Hook into `WeaponManager.EquipByIndex()` when ready.
|
||||||
|
- **No save system** — inventory is runtime only, nothing persists between sessions.
|
||||||
|
- **Starting gun** — any `SimpleGun` that's a child of the Camera at Start gets auto-registered but starts inactive. Player needs to find it as a pickup in the scene.
|
||||||
|
- **Inventory is IMGUI** — not Canvas. Performance fine at this scale. All data logic is decoupled from drawing so a Canvas swap later would be straightforward.
|
||||||
|
- **`SimpleGun` assumes it's always active** — non-SimpleGun weapons will slot-switch fine but need their own input handling to shoot.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Controls Reference
|
||||||
|
|
||||||
|
| Key | Action |
|
||||||
|
|---|---|
|
||||||
|
| W A S D | Move |
|
||||||
|
| Left Shift | Sprint |
|
||||||
|
| Space | Jump |
|
||||||
|
| Left Click | Shoot |
|
||||||
|
| Right Click | Context menu (inventory open) |
|
||||||
|
| R | Reload |
|
||||||
|
| E | Pick up nearby item |
|
||||||
|
| I | Open / close inventory |
|
||||||
|
| F | Equip selected item |
|
||||||
|
| Scroll Wheel | Cycle weapons |
|
||||||
|
| 1 – 9 | Equip weapon by slot |
|
||||||
|
| Escape | Unlock cursor |
|
||||||
Reference in New Issue
Block a user