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
|
||||
%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
|
||||
Material:
|
||||
serializedVersion: 8
|
||||
@@ -30,8 +43,22 @@ Material:
|
||||
m_Texture: {fileID: -5390149515647955136, guid: 24bb6e32322f940e59304941b7849582, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_Lightmaps:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_LightmapsInd:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_ShadowMasks:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Ints: []
|
||||
m_Floats:
|
||||
- _AlphaClip: 0
|
||||
- _AlphaToMask: 0
|
||||
- _BUILTIN_AlphaClip: 0
|
||||
- _BUILTIN_Blend: 0
|
||||
- _BUILTIN_CullMode: 2
|
||||
@@ -43,6 +70,19 @@ Material:
|
||||
- _BUILTIN_ZTest: 4
|
||||
- _BUILTIN_ZWrite: 1
|
||||
- _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
|
||||
- baseColorTexture_texCoord: 0
|
||||
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
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents: []
|
||||
m_RemovedGameObjects: []
|
||||
m_RemovedGameObjects:
|
||||
- {fileID: 2029899304289424255, guid: 33de4b621d70a49aab5df775f2b826ef, type: 3}
|
||||
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}
|
||||
--- !u!1001 &197010634
|
||||
PrefabInstance:
|
||||
@@ -6459,52 +6472,6 @@ MeshCollider:
|
||||
m_Convex: 0
|
||||
m_CookingOptions: 30
|
||||
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
|
||||
GameObject:
|
||||
m_CorrespondingSourceObject: {fileID: -8098169881513260187, guid: 3df8bb31de452e64fa85a1265898d698, type: 3}
|
||||
@@ -6532,52 +6499,6 @@ MeshCollider:
|
||||
m_Convex: 0
|
||||
m_CookingOptions: 30
|
||||
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
|
||||
GameObject:
|
||||
m_CorrespondingSourceObject: {fileID: -8220620780705863596, guid: 9e848a421785b4212abcb7cf4ae082f3, type: 3}
|
||||
@@ -6732,6 +6653,33 @@ MonoBehaviour:
|
||||
m_ShadowLayerMask: 1
|
||||
m_RenderingLayers: 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
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -6758,7 +6706,7 @@ PrefabInstance:
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3447742079981357586, guid: 681011e79237b4409ad76bc628bdf5b3, type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: 365.609
|
||||
value: 363.888
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3447742079981357586, guid: 681011e79237b4409ad76bc628bdf5b3, type: 3}
|
||||
propertyPath: m_LocalPosition.y
|
||||
@@ -6766,7 +6714,7 @@ PrefabInstance:
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3447742079981357586, guid: 681011e79237b4409ad76bc628bdf5b3, type: 3}
|
||||
propertyPath: m_LocalPosition.z
|
||||
value: 156.609
|
||||
value: 157.267
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3447742079981357586, guid: 681011e79237b4409ad76bc628bdf5b3, type: 3}
|
||||
propertyPath: m_LocalRotation.w
|
||||
@@ -6799,7 +6747,10 @@ PrefabInstance:
|
||||
m_RemovedComponents: []
|
||||
m_RemovedGameObjects: []
|
||||
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}
|
||||
--- !u!1001 &806276174
|
||||
PrefabInstance:
|
||||
@@ -6919,6 +6870,66 @@ MeshCollider:
|
||||
m_Convex: 0
|
||||
m_CookingOptions: 30
|
||||
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
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -7125,6 +7136,60 @@ MeshCollider:
|
||||
m_Convex: 0
|
||||
m_CookingOptions: 30
|
||||
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
|
||||
GameObject:
|
||||
m_CorrespondingSourceObject: {fileID: -8098169881513260187, guid: 3c8a5d44028bd40d3abe5e22cafcb901, type: 3}
|
||||
@@ -7569,11 +7634,11 @@ PrefabInstance:
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3447742079981357586, guid: 4f15ea45bfb5e4f11bc2bce50e39fc10, type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: 304.18
|
||||
value: -186547
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3447742079981357586, guid: 4f15ea45bfb5e4f11bc2bce50e39fc10, type: 3}
|
||||
propertyPath: m_LocalPosition.y
|
||||
value: -146.63
|
||||
value: -74119
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3447742079981357586, guid: 4f15ea45bfb5e4f11bc2bce50e39fc10, type: 3}
|
||||
propertyPath: m_LocalPosition.z
|
||||
@@ -8155,52 +8220,6 @@ PrefabInstance:
|
||||
insertIndex: -1
|
||||
addedObject: {fileID: 277384082}
|
||||
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
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -8277,52 +8296,115 @@ PrefabInstance:
|
||||
insertIndex: -1
|
||||
addedObject: {fileID: 990201665}
|
||||
m_SourcePrefab: {fileID: 3150474306388093854, guid: 1e096925873fc492b854ee214231ab86, type: 3}
|
||||
--- !u!1 &2073272903
|
||||
--- !u!1 &2039163524 stripped
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_CorrespondingSourceObject: {fileID: 6882903375964528458, guid: 33de4b621d70a49aab5df775f2b826ef, type: 3}
|
||||
m_PrefabInstance: {fileID: 110786869}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
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
|
||||
--- !u!114 &2039163533
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2073272903}
|
||||
m_GameObject: {fileID: 2039163524}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 180c6606991a4b64f812f9713907fd0c, type: 3}
|
||||
m_Script: {fileID: 11500000, guid: 514ba78e0d4cf8b4787471c2db0c6976, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::EnemySpawner
|
||||
enemyType: 1
|
||||
healthOverride: 0
|
||||
--- !u!4 &2073272905
|
||||
Transform:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::Inventory
|
||||
items: []
|
||||
toggleKey: 105
|
||||
equipKey: 102
|
||||
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_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2073272903}
|
||||
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}
|
||||
m_GameObject: {fileID: 2039163524}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 18715d388d5da7544b10c5d7a385cbee, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::WeaponManager
|
||||
weaponHolder: {fileID: 0}
|
||||
weaponPositionOffset: {x: 0.2, y: -0.25, z: 0.45}
|
||||
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
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -8445,6 +8527,33 @@ MeshCollider:
|
||||
m_Convex: 0
|
||||
m_CookingOptions: 30
|
||||
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
|
||||
SceneRoots:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -8454,10 +8563,6 @@ SceneRoots:
|
||||
- {fileID: 1719075356}
|
||||
- {fileID: 1410308539}
|
||||
- {fileID: 343869061}
|
||||
- {fileID: 2073272905}
|
||||
- {fileID: 487503084}
|
||||
- {fileID: 606146707}
|
||||
- {fileID: 1882479421}
|
||||
- {fileID: 1360218911}
|
||||
- {fileID: 271705868}
|
||||
- {fileID: 1022368674}
|
||||
@@ -8478,3 +8583,5 @@ SceneRoots:
|
||||
- {fileID: 1716730849}
|
||||
- {fileID: 2026185005}
|
||||
- {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
|
||||
{
|
||||
[Header("Movement Settings")]
|
||||
public float walkSpeed = 50f; // Boosted from 8f to overcome collision issues
|
||||
public float runSpeed = 80f; // Boosted from 14f
|
||||
public float walkSpeed = 50f;
|
||||
public float runSpeed = 80f;
|
||||
public float jumpHeight = 2.5f;
|
||||
public float gravity = -20f;
|
||||
public float gravity = -20f;
|
||||
|
||||
[Header("Mouse Look Settings")]
|
||||
public float mouseSensitivity = 3f;
|
||||
public float maxLookAngle = 90f;
|
||||
public float maxLookAngle = 90f;
|
||||
|
||||
[Header("References")]
|
||||
public Camera playerCamera;
|
||||
|
||||
// Private variables
|
||||
private CharacterController controller;
|
||||
private Vector3 velocity;
|
||||
private bool isGrounded;
|
||||
private float xRotation = 0f;
|
||||
private Inventory inventory;
|
||||
private Player player;
|
||||
private Vector3 velocity;
|
||||
private bool isGrounded;
|
||||
private float xRotation = 0f;
|
||||
|
||||
void Start()
|
||||
{
|
||||
Debug.Log("Starting game");
|
||||
|
||||
// FORCE NORMAL TIME (in case something external changed it)
|
||||
Time.timeScale = 1f;
|
||||
|
||||
controller = GetComponent<CharacterController>();
|
||||
|
||||
if (controller == null)
|
||||
{
|
||||
Debug.LogError("FirstPersonController: No CharacterController found!");
|
||||
@@ -40,85 +38,58 @@ public class FirstPersonController : MonoBehaviour
|
||||
if (playerCamera == null)
|
||||
playerCamera = GetComponentInChildren<Camera>();
|
||||
|
||||
inventory = GetComponent<Inventory>();
|
||||
player = GetComponent<Player>();
|
||||
|
||||
Cursor.lockState = CursorLockMode.Locked;
|
||||
Cursor.visible = false;
|
||||
Cursor.visible = false;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if( controller == null )
|
||||
return;
|
||||
if (controller == null) return;
|
||||
|
||||
isGrounded = controller.isGrounded;
|
||||
|
||||
if (isGrounded && velocity.y < 0)
|
||||
velocity.y = -2f;
|
||||
|
||||
// ─── Movement via direct KeyCode ───
|
||||
float moveX = 0f;
|
||||
float moveZ = 0f;
|
||||
|
||||
if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow)) moveZ += 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}");
|
||||
}
|
||||
float moveX = 0f, moveZ = 0f;
|
||||
if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow)) moveZ += 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;
|
||||
|
||||
Vector3 move = transform.right * moveX + transform.forward * moveZ;
|
||||
if (move.magnitude > 1f) move.Normalize();
|
||||
|
||||
if (move.magnitude > 1f)
|
||||
move.Normalize();
|
||||
bool wantSprint = Input.GetKey(KeyCode.LeftShift) && move.magnitude > 0f;
|
||||
bool isSprinting = wantSprint && (player == null || player.CanSprint());
|
||||
if (player != null) player.isSprinting = isSprinting;
|
||||
|
||||
float currentSpeed = Input.GetKey(KeyCode.LeftShift) ? runSpeed : walkSpeed;
|
||||
|
||||
Vector3 posBefore = transform.position;
|
||||
float currentSpeed = isSprinting ? runSpeed : walkSpeed;
|
||||
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)
|
||||
velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
|
||||
|
||||
// Gravity
|
||||
velocity.y += gravity * Time.deltaTime;
|
||||
controller.Move(velocity * Time.deltaTime);
|
||||
controller.Move(new Vector3(0f, velocity.y, 0f) * Time.deltaTime);
|
||||
|
||||
// Mouse look
|
||||
HandleMouseLook();
|
||||
bool inventoryOpen = inventory != null && inventory.IsOpen;
|
||||
if (!inventoryOpen)
|
||||
HandleMouseLook();
|
||||
|
||||
// Escape to unlock cursor
|
||||
if (Input.GetKeyDown(KeyCode.Escape))
|
||||
{
|
||||
Cursor.lockState = CursorLockMode.None;
|
||||
Cursor.visible = true;
|
||||
Cursor.visible = true;
|
||||
}
|
||||
|
||||
// Click to re-lock cursor
|
||||
if (Input.GetMouseButtonDown(0) && Cursor.lockState == CursorLockMode.None)
|
||||
if (Input.GetMouseButtonDown(0) && Cursor.lockState == CursorLockMode.None && !inventoryOpen)
|
||||
{
|
||||
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;
|
||||
|
||||
xRotation -= mouseY;
|
||||
xRotation = Mathf.Clamp(xRotation, -maxLookAngle, maxLookAngle);
|
||||
xRotation = Mathf.Clamp(xRotation, -maxLookAngle, maxLookAngle);
|
||||
playerCamera.transform.localRotation = Quaternion.Euler(xRotation, 0f, 0f);
|
||||
|
||||
transform.Rotate(Vector3.up * mouseX);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ public static class InputData
|
||||
new MouseButton( Defines.Input.kLeftMouseButton),
|
||||
new KeyboardButton( KeyCode.LeftControl ),
|
||||
} ) );
|
||||
reload = AddButton( new KeyboardButton( KeyCode.Space ) );
|
||||
reload = AddButton( new KeyboardButton( KeyCode.R ) );
|
||||
jump = AddButton( new KeyboardButton( KeyCode.Space ) );
|
||||
|
||||
horizontalMovement = AddAxis( new MultiAxis(
|
||||
|
||||
@@ -187,8 +187,13 @@ public class HumanoidEnemy : MonoBehaviour
|
||||
// Punch animation
|
||||
StartCoroutine(PunchAnimation());
|
||||
|
||||
// TODO: Hook into player health system when you have one
|
||||
// player.GetComponent<PlayerHealth>()?.TakeDamage(attackDamage);
|
||||
// Damage the player via Player.health
|
||||
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;
|
||||
|
||||
/// <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 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;
|
||||
|
||||
[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 bobHorizontalAmplitude = 0.05f;
|
||||
public float bobVerticalAmplitude = 0.03f;
|
||||
@@ -48,6 +50,10 @@ public class SimpleGun : MonoBehaviour
|
||||
// Fire timing
|
||||
private float nextTimeToFire = 0f;
|
||||
|
||||
// Cached refs for inventory check
|
||||
private Inventory _inventory;
|
||||
private WeaponManager _weaponManager;
|
||||
|
||||
void Start()
|
||||
{
|
||||
currentAmmo = maxAmmo;
|
||||
@@ -58,7 +64,12 @@ public class SimpleGun : MonoBehaviour
|
||||
fpsCam = GetComponentInParent<Camera>();
|
||||
|
||||
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
|
||||
@@ -93,6 +104,10 @@ public class SimpleGun : MonoBehaviour
|
||||
|
||||
void Update()
|
||||
{
|
||||
// Don't shoot while the inventory is open
|
||||
bool inventoryOpen = _inventory != null && _inventory.IsOpen;
|
||||
if (inventoryOpen) return;
|
||||
|
||||
// Shooting
|
||||
if (isAutomatic ? InputData.leftMouseButton.down : InputData.leftMouseButton.pressed)
|
||||
{
|
||||
@@ -107,11 +122,18 @@ public class SimpleGun : MonoBehaviour
|
||||
if (InputData.reload.pressed)
|
||||
Reload();
|
||||
|
||||
// Keep base position in sync with WeaponManager's live offset
|
||||
if (_weaponManager != null)
|
||||
originalLocalPos = _weaponManager.weaponPositionOffset;
|
||||
|
||||
// Recover from recoil
|
||||
recoilOffset = Vector3.Lerp(recoilOffset, Vector3.zero, Time.deltaTime * recoilRecoverySpeed);
|
||||
|
||||
// Weapon bob
|
||||
UpdateBob();
|
||||
// Weapon bob (only if internal bob is enabled — disable if WeaponBob is on the Camera)
|
||||
if (enableInternalBob)
|
||||
UpdateBob();
|
||||
else
|
||||
bobOffset = Vector3.zero;
|
||||
|
||||
// Apply combined position: original + recoil + bob
|
||||
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