Weapons
This commit is contained in:
parent
b4b5254950
commit
602b6f27ed
124
Assets/prefabs/Colt.prefab
Normal file
124
Assets/prefabs/Colt.prefab
Normal file
@ -0,0 +1,124 @@
|
||||
{
|
||||
"RootObject": {
|
||||
"__guid": "58539025-1035-4ff4-956c-cd4168ac898f",
|
||||
"Flags": 0,
|
||||
"Name": "colt",
|
||||
"Enabled": true,
|
||||
"Components": [
|
||||
{
|
||||
"__type": "SWB.Base.Weapon",
|
||||
"__guid": "30890fea-3650-49c6-9f60-00994a5cd464",
|
||||
"AimAnimData": {},
|
||||
"AimFOV": -1,
|
||||
"AimInFOVSpeed": 1,
|
||||
"AimOutFOVSpeed": 1,
|
||||
"AimPlayerFOV": -1,
|
||||
"AimSensitivity": 0.85,
|
||||
"AnimSpeed": 1,
|
||||
"BoltBack": false,
|
||||
"BoltBackAnim": "boltback",
|
||||
"BoltBackEjectDelay": 0,
|
||||
"BoltBackTime": 0,
|
||||
"BulletCocking": true,
|
||||
"ClassName": "Pistol",
|
||||
"CustomizeAnimData": {},
|
||||
"DeploySound": "sounds/swb/animations/swb_deploy.sound",
|
||||
"DisplayName": "Colt",
|
||||
"DrawAnim": "deploy",
|
||||
"DrawEmptyAnim": "",
|
||||
"DrawEmptyTime": -1,
|
||||
"DrawTime": 0.5,
|
||||
"FOV": 70,
|
||||
"HoldType": "Pistol",
|
||||
"Icon": "motivations/photo_2024-08-30_19-41-21.jpg",
|
||||
"Primary": {
|
||||
"_type": "component",
|
||||
"component_id": "71dbc9ea-6e68-4ebf-8bcd-d023111a935d",
|
||||
"go": "58539025-1035-4ff4-956c-cd4168ac898f",
|
||||
"component_type": "ShootInfo"
|
||||
},
|
||||
"ReloadAnim": "reload",
|
||||
"ReloadEmptyAnim": "reload_empty",
|
||||
"ReloadEmptyTime": -1,
|
||||
"ReloadTime": 1,
|
||||
"RunAnimData": {},
|
||||
"ScopeInfo": {
|
||||
"LensTexture": "/materials/swb/scopes/swb_lens_hunter.png",
|
||||
"ScopeTexture": "/materials/swb/scopes/swb_scope_hunter.png",
|
||||
"ScopeInDelay": 0.2,
|
||||
"FOV": 8,
|
||||
"AimSensitivity": 0.25
|
||||
},
|
||||
"Scoping": false,
|
||||
"ShellEjectDelay": 0,
|
||||
"ShellReloading": false,
|
||||
"ShellReloadingShootCancel": true,
|
||||
"ShellReloadInsertTime": 0,
|
||||
"ShellReloadStartTime": 0,
|
||||
"Slot": 1,
|
||||
"TuckRange": 30,
|
||||
"WorldModel": "weapons/swb/colt/w_colt.vmdl"
|
||||
},
|
||||
{
|
||||
"__type": "SWB.Base.ShootInfo",
|
||||
"__guid": "71dbc9ea-6e68-4ebf-8bcd-d023111a935d",
|
||||
"Ammo": 10,
|
||||
"AmmoType": "pistol",
|
||||
"BarrelSmokeParticle": "particles/swb/muzzle/barrel_smoke.vpcf",
|
||||
"BulletEjectParticle": "particles/swb/bullet_hulls/9mm_eject.vpcf",
|
||||
"Bullets": 1,
|
||||
"BulletSize": 0.1,
|
||||
"BulletTracerChance": 0.33,
|
||||
"ClipSize": 10,
|
||||
"Damage": 5,
|
||||
"DryShootSound": "sounds/swb/clip/swb_pistol.empty.sound",
|
||||
"FiringType": "semi",
|
||||
"Force": 0.1,
|
||||
"HitFlinch": 1.25,
|
||||
"InfiniteAmmo": "reserve",
|
||||
"MuzzleFlashParticle": "particles/swb/muzzle/flash.vpcf",
|
||||
"Recoil": 0,
|
||||
"RPM": 1000,
|
||||
"ScreenShake": {},
|
||||
"ShootAnim": "fire",
|
||||
"ShootEmptyAnim": "",
|
||||
"ShootSound": "sounds/swb/attachments/silencer/swb_pistol.silenced.fire.sound",
|
||||
"Spread": 0.02,
|
||||
"VMParticleScale": 1,
|
||||
"WMParticleScale": 2
|
||||
}
|
||||
],
|
||||
"Children": [],
|
||||
"__variables": [],
|
||||
"__properties": {
|
||||
"FixedUpdateFrequency": 50,
|
||||
"MaxFixedUpdates": 5,
|
||||
"NetworkFrequency": 30,
|
||||
"NetworkInterpolation": true,
|
||||
"PhysicsSubSteps": 1,
|
||||
"ThreadedAnimation": true,
|
||||
"TimeScale": 1,
|
||||
"UseFixedUpdate": true,
|
||||
"Metadata": {},
|
||||
"NavMesh": {
|
||||
"Enabled": false,
|
||||
"IncludeStaticBodies": true,
|
||||
"IncludeKeyframedBodies": true,
|
||||
"EditorAutoUpdate": true,
|
||||
"AgentHeight": 64,
|
||||
"AgentRadius": 16,
|
||||
"AgentStepSize": 18,
|
||||
"AgentMaxSlope": 40,
|
||||
"ExcludedBodies": "",
|
||||
"IncludedBodies": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"ShowInMenu": false,
|
||||
"MenuPath": null,
|
||||
"MenuIcon": null,
|
||||
"DontBreakAsTemplate": false,
|
||||
"ResourceVersion": 1,
|
||||
"__references": [],
|
||||
"__version": 1
|
||||
}
|
161
Assets/prefabs/ScarH.prefab
Normal file
161
Assets/prefabs/ScarH.prefab
Normal file
@ -0,0 +1,161 @@
|
||||
{
|
||||
"RootObject": {
|
||||
"__guid": "a3b668c3-acfb-4043-8f7c-93b7df84b5c8",
|
||||
"Flags": 0,
|
||||
"Name": "scarh",
|
||||
"Tags": "weapon",
|
||||
"Components": [
|
||||
{
|
||||
"__type": "SWB.Base.Weapon",
|
||||
"__guid": "4d44110c-f7a1-4168-a9f3-dbeaa77a630a",
|
||||
"AimAnimData": {
|
||||
"Angle": "-0.1,0.34,3.9",
|
||||
"Pos": "-2.163,0.36,0.233"
|
||||
},
|
||||
"AimFOV": 35,
|
||||
"AimInFOVSpeed": 1,
|
||||
"AimOutFOVSpeed": 1,
|
||||
"AimPlayerFOV": -1,
|
||||
"AimSensitivity": 0.85,
|
||||
"AnimSpeed": 1,
|
||||
"BoltBack": false,
|
||||
"BoltBackAnim": "boltback",
|
||||
"BoltBackEjectDelay": 0,
|
||||
"BoltBackTime": 0,
|
||||
"BulletCocking": true,
|
||||
"ClassName": "swb_scarh",
|
||||
"CustomizeAnimData": {
|
||||
"Angle": "-0.71,44.52,10",
|
||||
"Pos": "5.755,-1.2,1.592"
|
||||
},
|
||||
"DeploySound": "sounds/swb/animations/swb_deploy.sound",
|
||||
"DisplayName": "FN Scar H",
|
||||
"DrawAnim": "deploy",
|
||||
"DrawEmptyAnim": "",
|
||||
"DrawEmptyTime": -1,
|
||||
"DrawTime": 0.6,
|
||||
"FOV": 65,
|
||||
"HoldType": "Rifle",
|
||||
"Icon": "weapons/swb/scarh/scarh.png",
|
||||
"Primary": {
|
||||
"_type": "component",
|
||||
"component_id": "ff1c8c67-10c8-4767-925a-0d606a3845ff",
|
||||
"go": "a3b668c3-acfb-4043-8f7c-93b7df84b5c8",
|
||||
"component_type": "ShootInfo"
|
||||
},
|
||||
"ReloadAnim": "reload",
|
||||
"ReloadEmptyAnim": "reload_empty",
|
||||
"ReloadEmptyTime": -1,
|
||||
"ReloadTime": 4.38,
|
||||
"RunAnimData": {
|
||||
"Angle": "8.41,59.66,0",
|
||||
"Pos": "4.29,-0.86,-1.1"
|
||||
},
|
||||
"ScopeInfo": {
|
||||
"LensTexture": "/materials/swb/scopes/swb_lens_hunter.png",
|
||||
"ScopeTexture": "/materials/swb/scopes/swb_scope_hunter.png",
|
||||
"ScopeInDelay": 0.2,
|
||||
"FOV": 8,
|
||||
"AimSensitivity": 0.25
|
||||
},
|
||||
"Scoping": false,
|
||||
"ShellEjectDelay": 0,
|
||||
"ShellReloading": false,
|
||||
"ShellReloadingShootCancel": true,
|
||||
"ShellReloadInsertTime": 0,
|
||||
"ShellReloadStartTime": 0,
|
||||
"Slot": 5,
|
||||
"TuckRange": 30,
|
||||
"WorldModel": "weapons/swb/scarh/w_scarh.vmdl"
|
||||
},
|
||||
{
|
||||
"__type": "SWB.Base.ShootInfo",
|
||||
"__guid": "ff1c8c67-10c8-4767-925a-0d606a3845ff",
|
||||
"Ammo": 30,
|
||||
"AmmoType": "rifle",
|
||||
"BarrelSmokeParticle": "particles/swb/muzzle/barrel_smoke.vpcf",
|
||||
"BulletEjectParticle": "particles/swb/bullet_hulls/20mm_eject.vpcf",
|
||||
"Bullets": 1,
|
||||
"BulletSize": 2,
|
||||
"BulletTracerChance": 0.33,
|
||||
"BulletTracerParticle": "particles/swb/tracer/tracer.vpcf",
|
||||
"ClipSize": 30,
|
||||
"Damage": 25,
|
||||
"DryShootSound": "sounds/swb/clip/swb_rifle.empty.sound",
|
||||
"FiringType": "auto",
|
||||
"Force": 3,
|
||||
"HitFlinch": 2,
|
||||
"InfiniteAmmo": "reserve",
|
||||
"MuzzleFlashParticle": "particles/swb/muzzle/flash.vpcf",
|
||||
"Recoil": 0.35,
|
||||
"RPM": 600,
|
||||
"ScreenShake": {
|
||||
"Duration": 0.08,
|
||||
"Delay": 0.02,
|
||||
"Size": 0.2,
|
||||
"Rotation": 0.1
|
||||
},
|
||||
"ShootAimedAnim": "fireAimed",
|
||||
"ShootAnim": "fire",
|
||||
"ShootEmptyAnim": "",
|
||||
"ShootSound": "weapons/swb/scarh/sounds/scar.fire.sound",
|
||||
"Spread": 0.08,
|
||||
"VMParticleScale": 1.3,
|
||||
"WMParticleScale": 1
|
||||
},
|
||||
{
|
||||
"__type": "SWB.Demo.ReflexSightBG",
|
||||
"__guid": "0db16e29-30e1-463f-8812-7df647458e7c",
|
||||
"AimAnimData": {
|
||||
"Angle": "-1.47,0.44,3.9",
|
||||
"Pos": "-2.181,0.36,0.596"
|
||||
},
|
||||
"AimFOV": -1,
|
||||
"AimInFOVSpeed": -1,
|
||||
"AimOutFOVSpeed": -1,
|
||||
"ViewModelScale": "1,1,1",
|
||||
"WorldModelScale": "1,1,1"
|
||||
},
|
||||
{
|
||||
"__type": "SWB.Demo.RifleSilencerBG",
|
||||
"__guid": "6a2ee00f-5ba2-4b35-a74e-ceb5f3cda188",
|
||||
"EffectAttachmentOrBone": "muzzle_silenced",
|
||||
"ShootSound": "sounds/swb/attachments/silencer/swb_rifle.silenced.fire.sound",
|
||||
"ViewModelScale": "1,1,1",
|
||||
"WorldModelScale": "1,1,1"
|
||||
}
|
||||
],
|
||||
"Children": [],
|
||||
"__variables": [],
|
||||
"__properties": {
|
||||
"FixedUpdateFrequency": 50,
|
||||
"MaxFixedUpdates": 5,
|
||||
"NetworkFrequency": 30,
|
||||
"NetworkInterpolation": true,
|
||||
"PhysicsSubSteps": 1,
|
||||
"ThreadedAnimation": true,
|
||||
"TimeScale": 1,
|
||||
"UseFixedUpdate": true,
|
||||
"Metadata": {},
|
||||
"NavMesh": {
|
||||
"Enabled": false,
|
||||
"IncludeStaticBodies": true,
|
||||
"IncludeKeyframedBodies": true,
|
||||
"EditorAutoUpdate": true,
|
||||
"AgentHeight": 64,
|
||||
"AgentRadius": 16,
|
||||
"AgentStepSize": 18,
|
||||
"AgentMaxSlope": 40,
|
||||
"ExcludedBodies": "",
|
||||
"IncludedBodies": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"ShowInMenu": false,
|
||||
"MenuPath": null,
|
||||
"MenuIcon": null,
|
||||
"DontBreakAsTemplate": false,
|
||||
"ResourceVersion": 1,
|
||||
"__references": [],
|
||||
"__version": 1
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -23,6 +23,22 @@
|
||||
},
|
||||
"SpawnPoints": [],
|
||||
"StartServer": true
|
||||
},
|
||||
{
|
||||
"__type": "SWB.Base.WeaponRegistry",
|
||||
"__guid": "b4cb546a-e8ca-420d-b243-629a63341d2f",
|
||||
"WeaponPrefabs": [
|
||||
{
|
||||
"_type": "gameobject",
|
||||
"prefab": "prefabs/colt.prefab"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"__type": "SWB.Base.WeaponSettings",
|
||||
"__guid": "7a6906e5-8d3a-44b1-875e-bc0e0ba96a59",
|
||||
"AutoReload": false,
|
||||
"Customization": false
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -81,185 +97,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"__guid": "6ad70641-3c6c-4402-9c85-9a4969af4764",
|
||||
"Flags": 0,
|
||||
"Name": "Plane",
|
||||
"Scale": "5,5,5",
|
||||
"Components": [
|
||||
{
|
||||
"__type": "Sandbox.ModelRenderer",
|
||||
"__guid": "0b6a18bf-fdb8-4661-970e-ef635bfa9baa",
|
||||
"BodyGroups": 18446744073709551615,
|
||||
"MaterialOverride": "materials/default.vmat",
|
||||
"Model": "models/dev/plane.vmdl",
|
||||
"RenderOptions": {
|
||||
"GameLayer": true,
|
||||
"OverlayLayer": false,
|
||||
"BloomLayer": false,
|
||||
"AfterUILayer": false
|
||||
},
|
||||
"RenderType": "On",
|
||||
"Tint": "0.39546,0.51163,0.27128,1"
|
||||
},
|
||||
{
|
||||
"__type": "Sandbox.BoxCollider",
|
||||
"__guid": "0715cb55-1733-4f5e-8560-c288b8695631",
|
||||
"Center": "0,0,-5",
|
||||
"IsTrigger": false,
|
||||
"Scale": "100,100,10",
|
||||
"Static": false,
|
||||
"SurfaceVelocity": "0,0,0"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"__guid": "3c2490ef-54a0-49bb-8f13-490e40aa51d1",
|
||||
"Flags": 0,
|
||||
"Name": "Cube",
|
||||
"Position": "-18.53616,-147.7339,86.60748",
|
||||
"Rotation": "0.00000001819328,-0.00000000000000008235059,0.3052325,0.952278",
|
||||
"Scale": "0.5632889,0.5632889,0.5632889",
|
||||
"Enabled": true,
|
||||
"Components": [
|
||||
{
|
||||
"__type": "Sandbox.ModelRenderer",
|
||||
"__guid": "b9121ffa-617c-4ccc-a2aa-8acc98727590",
|
||||
"BodyGroups": 18446744073709551615,
|
||||
"MaterialOverride": "materials/default.vmat",
|
||||
"Model": "models/dev/box.vmdl",
|
||||
"RenderOptions": {
|
||||
"GameLayer": true,
|
||||
"OverlayLayer": false,
|
||||
"BloomLayer": false,
|
||||
"AfterUILayer": false
|
||||
},
|
||||
"RenderType": "On",
|
||||
"Tint": "1,0,0.93333,1"
|
||||
},
|
||||
{
|
||||
"__type": "Sandbox.BoxCollider",
|
||||
"__guid": "8bb3ebcf-1ec9-4b20-bf31-4aece0950008",
|
||||
"Center": "0,0,0",
|
||||
"IsTrigger": false,
|
||||
"Scale": "50,50,50",
|
||||
"Static": false,
|
||||
"SurfaceVelocity": "0,0,0"
|
||||
},
|
||||
{
|
||||
"__type": "Sandbox.Rigidbody",
|
||||
"__guid": "4aaa0334-6785-4716-9bae-869559ea6e10",
|
||||
"AngularDamping": 0,
|
||||
"Gravity": true,
|
||||
"LinearDamping": 0,
|
||||
"Locking": {},
|
||||
"MassCenterOverride": "0,0,0",
|
||||
"MassOverride": 0,
|
||||
"MotionEnabled": true,
|
||||
"OverrideMassCenter": false,
|
||||
"RigidbodyFlags": 0,
|
||||
"StartAsleep": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"__guid": "523e3e8f-a4ec-4ec1-af9a-d86ffc9c17e1",
|
||||
"Flags": 0,
|
||||
"Name": "Cube (1)",
|
||||
"Position": "40.81348,46.97572,14.40159",
|
||||
"Rotation": "0.00000001819328,-0.00000000000000008235059,0.3052325,0.952278",
|
||||
"Scale": "0.5632889,0.5632889,0.5632889",
|
||||
"Enabled": true,
|
||||
"Components": [
|
||||
{
|
||||
"__type": "Sandbox.ModelRenderer",
|
||||
"__guid": "9e8a546d-a41d-44b5-9906-22cea00e1066",
|
||||
"BodyGroups": 18446744073709551615,
|
||||
"MaterialOverride": "materials/default.vmat",
|
||||
"Model": "models/dev/box.vmdl",
|
||||
"RenderOptions": {
|
||||
"GameLayer": true,
|
||||
"OverlayLayer": false,
|
||||
"BloomLayer": false,
|
||||
"AfterUILayer": false
|
||||
},
|
||||
"RenderType": "On",
|
||||
"Tint": "1,0,0.93333,1"
|
||||
},
|
||||
{
|
||||
"__type": "Sandbox.BoxCollider",
|
||||
"__guid": "adf22580-7a25-4d7f-b1c3-3fb8fabf1612",
|
||||
"Center": "0,0,0",
|
||||
"IsTrigger": false,
|
||||
"Scale": "50,50,50",
|
||||
"Static": false,
|
||||
"SurfaceVelocity": "0,0,0"
|
||||
},
|
||||
{
|
||||
"__type": "Sandbox.Rigidbody",
|
||||
"__guid": "e53bfcb0-7f44-41d0-8d51-263bdc173a57",
|
||||
"AngularDamping": 0,
|
||||
"Gravity": true,
|
||||
"LinearDamping": 0,
|
||||
"Locking": {},
|
||||
"MassCenterOverride": "0,0,0",
|
||||
"MassOverride": 0,
|
||||
"MotionEnabled": true,
|
||||
"OverrideMassCenter": false,
|
||||
"RigidbodyFlags": 0,
|
||||
"StartAsleep": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"__guid": "5b483a09-bbf2-4949-98c7-a73b789d0ee7",
|
||||
"Flags": 0,
|
||||
"Name": "Cube (2)",
|
||||
"Position": "49.53706,34.08897,128.0143",
|
||||
"Rotation": "0.00000001819328,-0.00000000000000008235059,0.3052325,0.952278",
|
||||
"Scale": "0.5632889,0.5632889,0.5632889",
|
||||
"Enabled": true,
|
||||
"Components": [
|
||||
{
|
||||
"__type": "Sandbox.ModelRenderer",
|
||||
"__guid": "ac284ab8-cdb7-4ba6-92de-d9be36088b1b",
|
||||
"BodyGroups": 18446744073709551615,
|
||||
"MaterialOverride": "materials/default.vmat",
|
||||
"Model": "models/dev/box.vmdl",
|
||||
"RenderOptions": {
|
||||
"GameLayer": true,
|
||||
"OverlayLayer": false,
|
||||
"BloomLayer": false,
|
||||
"AfterUILayer": false
|
||||
},
|
||||
"RenderType": "On",
|
||||
"Tint": "1,0,0.93333,1"
|
||||
},
|
||||
{
|
||||
"__type": "Sandbox.BoxCollider",
|
||||
"__guid": "ca4eb284-231e-4c84-9bc4-7ba89962f307",
|
||||
"Center": "0,0,0",
|
||||
"IsTrigger": false,
|
||||
"Scale": "50,50,50",
|
||||
"Static": false,
|
||||
"SurfaceVelocity": "0,0,0"
|
||||
},
|
||||
{
|
||||
"__type": "Sandbox.Rigidbody",
|
||||
"__guid": "61ef15dd-3eb2-4f52-8b9a-315f477f79b7",
|
||||
"AngularDamping": 0,
|
||||
"Gravity": true,
|
||||
"LinearDamping": 0,
|
||||
"Locking": {},
|
||||
"MassCenterOverride": "0,0,0",
|
||||
"MassOverride": 0,
|
||||
"MotionEnabled": true,
|
||||
"OverrideMassCenter": false,
|
||||
"RigidbodyFlags": 0,
|
||||
"StartAsleep": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"__guid": "3ee1c9f4-07be-4e0b-8b23-67bee2d8ec8a",
|
||||
"Flags": 0,
|
||||
@ -352,6 +189,260 @@
|
||||
}
|
||||
],
|
||||
"Children": []
|
||||
},
|
||||
{
|
||||
"__guid": "a100c2ff-ab5a-42dd-8c8f-6681e93d0bed",
|
||||
"Flags": 0,
|
||||
"Name": "Props",
|
||||
"Enabled": true,
|
||||
"Children": [
|
||||
{
|
||||
"__guid": "05bf2234-dded-4460-9cf0-9fe560d5aef0",
|
||||
"Flags": 0,
|
||||
"Name": "marketstall",
|
||||
"Position": "416.919,201.6388,-218.2547",
|
||||
"Rotation": "0,0,0.7071067,0.7071068",
|
||||
"Scale": "3,3,3",
|
||||
"Enabled": true,
|
||||
"Components": [
|
||||
{
|
||||
"__type": "Sandbox.Prop",
|
||||
"__guid": "4a71d1a2-b037-4cf6-acce-33bdc73faf6f",
|
||||
"BodyGroups": 18446744073709551615,
|
||||
"Health": 0,
|
||||
"IsStatic": true,
|
||||
"Model": "models/3dmodelscc0/medievalpropspack/marketstall/marketstall.vmdl",
|
||||
"StartAsleep": false,
|
||||
"Tint": "1,1,1,1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"__guid": "06b026eb-cb28-4808-97c2-3a6db8294da6",
|
||||
"Flags": 0,
|
||||
"Name": "couch",
|
||||
"Position": "388.4759,324.1619,-246.5044",
|
||||
"Rotation": "0,0,0.7071067,0.7071068",
|
||||
"Enabled": true,
|
||||
"Components": [
|
||||
{
|
||||
"__type": "Sandbox.Prop",
|
||||
"__guid": "cb993131-4f9c-4a03-aa64-896183340491",
|
||||
"BodyGroups": 18446744073709551615,
|
||||
"Health": 5,
|
||||
"IsStatic": true,
|
||||
"Model": "models/props/couch/couch.vmdl",
|
||||
"StartAsleep": false,
|
||||
"Tint": "1,1,1,1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"__guid": "523e3e8f-a4ec-4ec1-af9a-d86ffc9c17e1",
|
||||
"Flags": 0,
|
||||
"Name": "Cube (1)",
|
||||
"Position": "40.81348,46.97572,14.40159",
|
||||
"Rotation": "0.00000001819328,-0.00000000000000008235059,0.3052325,0.952278",
|
||||
"Scale": "0.5632889,0.5632889,0.5632889",
|
||||
"Enabled": true,
|
||||
"Components": [
|
||||
{
|
||||
"__type": "Sandbox.ModelRenderer",
|
||||
"__guid": "9e8a546d-a41d-44b5-9906-22cea00e1066",
|
||||
"BodyGroups": 18446744073709551615,
|
||||
"MaterialOverride": "materials/default.vmat",
|
||||
"Model": "models/dev/box.vmdl",
|
||||
"RenderOptions": {
|
||||
"GameLayer": true,
|
||||
"OverlayLayer": false,
|
||||
"BloomLayer": false,
|
||||
"AfterUILayer": false
|
||||
},
|
||||
"RenderType": "On",
|
||||
"Tint": "1,0,0.93333,1"
|
||||
},
|
||||
{
|
||||
"__type": "Sandbox.BoxCollider",
|
||||
"__guid": "adf22580-7a25-4d7f-b1c3-3fb8fabf1612",
|
||||
"Center": "0,0,0",
|
||||
"IsTrigger": false,
|
||||
"Scale": "50,50,50",
|
||||
"Static": false,
|
||||
"SurfaceVelocity": "0,0,0"
|
||||
},
|
||||
{
|
||||
"__type": "Sandbox.Rigidbody",
|
||||
"__guid": "e53bfcb0-7f44-41d0-8d51-263bdc173a57",
|
||||
"AngularDamping": 0,
|
||||
"Gravity": true,
|
||||
"LinearDamping": 0,
|
||||
"Locking": {},
|
||||
"MassCenterOverride": "0,0,0",
|
||||
"MassOverride": 0,
|
||||
"MotionEnabled": true,
|
||||
"OverrideMassCenter": false,
|
||||
"RigidbodyFlags": 0,
|
||||
"StartAsleep": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"__guid": "5b483a09-bbf2-4949-98c7-a73b789d0ee7",
|
||||
"Flags": 0,
|
||||
"Name": "Cube (2)",
|
||||
"Position": "49.53706,34.08897,128.0143",
|
||||
"Rotation": "0.00000001819328,-0.00000000000000008235059,0.3052325,0.952278",
|
||||
"Scale": "0.5632889,0.5632889,0.5632889",
|
||||
"Enabled": true,
|
||||
"Components": [
|
||||
{
|
||||
"__type": "Sandbox.ModelRenderer",
|
||||
"__guid": "ac284ab8-cdb7-4ba6-92de-d9be36088b1b",
|
||||
"BodyGroups": 18446744073709551615,
|
||||
"MaterialOverride": "materials/default.vmat",
|
||||
"Model": "models/dev/box.vmdl",
|
||||
"RenderOptions": {
|
||||
"GameLayer": true,
|
||||
"OverlayLayer": false,
|
||||
"BloomLayer": false,
|
||||
"AfterUILayer": false
|
||||
},
|
||||
"RenderType": "On",
|
||||
"Tint": "1,0,0.93333,1"
|
||||
},
|
||||
{
|
||||
"__type": "Sandbox.BoxCollider",
|
||||
"__guid": "ca4eb284-231e-4c84-9bc4-7ba89962f307",
|
||||
"Center": "0,0,0",
|
||||
"IsTrigger": false,
|
||||
"Scale": "50,50,50",
|
||||
"Static": false,
|
||||
"SurfaceVelocity": "0,0,0"
|
||||
},
|
||||
{
|
||||
"__type": "Sandbox.Rigidbody",
|
||||
"__guid": "61ef15dd-3eb2-4f52-8b9a-315f477f79b7",
|
||||
"AngularDamping": 0,
|
||||
"Gravity": true,
|
||||
"LinearDamping": 0,
|
||||
"Locking": {},
|
||||
"MassCenterOverride": "0,0,0",
|
||||
"MassOverride": 0,
|
||||
"MotionEnabled": true,
|
||||
"OverrideMassCenter": false,
|
||||
"RigidbodyFlags": 0,
|
||||
"StartAsleep": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"__guid": "3c2490ef-54a0-49bb-8f13-490e40aa51d1",
|
||||
"Flags": 0,
|
||||
"Name": "Cube",
|
||||
"Position": "-18.53616,-147.7339,86.60748",
|
||||
"Rotation": "0.00000001819328,-0.00000000000000008235059,0.3052325,0.952278",
|
||||
"Scale": "0.5632889,0.5632889,0.5632889",
|
||||
"Enabled": true,
|
||||
"Components": [
|
||||
{
|
||||
"__type": "Sandbox.ModelRenderer",
|
||||
"__guid": "b9121ffa-617c-4ccc-a2aa-8acc98727590",
|
||||
"BodyGroups": 18446744073709551615,
|
||||
"MaterialOverride": "materials/default.vmat",
|
||||
"Model": "models/dev/box.vmdl",
|
||||
"RenderOptions": {
|
||||
"GameLayer": true,
|
||||
"OverlayLayer": false,
|
||||
"BloomLayer": false,
|
||||
"AfterUILayer": false
|
||||
},
|
||||
"RenderType": "On",
|
||||
"Tint": "1,0,0.93333,1"
|
||||
},
|
||||
{
|
||||
"__type": "Sandbox.BoxCollider",
|
||||
"__guid": "8bb3ebcf-1ec9-4b20-bf31-4aece0950008",
|
||||
"Center": "0,0,0",
|
||||
"IsTrigger": false,
|
||||
"Scale": "50,50,50",
|
||||
"Static": false,
|
||||
"SurfaceVelocity": "0,0,0"
|
||||
},
|
||||
{
|
||||
"__type": "Sandbox.Rigidbody",
|
||||
"__guid": "4aaa0334-6785-4716-9bae-869559ea6e10",
|
||||
"AngularDamping": 0,
|
||||
"Gravity": true,
|
||||
"LinearDamping": 0,
|
||||
"Locking": {},
|
||||
"MassCenterOverride": "0,0,0",
|
||||
"MassOverride": 0,
|
||||
"MotionEnabled": true,
|
||||
"OverrideMassCenter": false,
|
||||
"RigidbodyFlags": 0,
|
||||
"StartAsleep": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"__guid": "9b58c908-7d46-4c7c-95b8-d43a0b374d6e",
|
||||
"Flags": 0,
|
||||
"Name": "sboxspongehouse",
|
||||
"Position": "-859.0681,986.7744,-244.0724",
|
||||
"Enabled": true,
|
||||
"Components": [
|
||||
{
|
||||
"__type": "Sandbox.Prop",
|
||||
"__guid": "7d1d85b7-df0a-42c5-b77a-9e47e802119c",
|
||||
"BodyGroups": 18446744073709551615,
|
||||
"Health": 0,
|
||||
"IsStatic": true,
|
||||
"Model": "spongebobshouse/sboxspongehouse.vmdl",
|
||||
"StartAsleep": false,
|
||||
"Tint": "1,1,1,1"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"__guid": "9d92bb31-0aae-4bd5-ba98-a4943b011fe6",
|
||||
"Flags": 0,
|
||||
"Name": "ceramicarch_m",
|
||||
"Position": "-246.0864,1310.813,-157.7126",
|
||||
"Scale": "1.520212,1.520212,1.520212",
|
||||
"Enabled": true,
|
||||
"Components": [
|
||||
{
|
||||
"__type": "Sandbox.Prop",
|
||||
"__guid": "2514ccb2-a612-4fb5-869b-658a9b9319a5",
|
||||
"BodyGroups": 18446744073709551615,
|
||||
"Health": 0,
|
||||
"IsStatic": true,
|
||||
"Model": "models/ceramicarch/ceramicarch_m.vmdl",
|
||||
"StartAsleep": false,
|
||||
"Tint": "1,1,1,1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"__guid": "cacdea24-9b76-4081-839a-5d377a34012f",
|
||||
"Flags": 0,
|
||||
"Name": "street_tree01",
|
||||
"Position": "-609.5272,760.2201,-247.2903",
|
||||
"Enabled": true,
|
||||
"Components": [
|
||||
{
|
||||
"__type": "Sandbox.Prop",
|
||||
"__guid": "e1c15a0e-34f9-43f8-b7a8-3ee4f658196c",
|
||||
"BodyGroups": 18446744073709551615,
|
||||
"Health": 0,
|
||||
"IsStatic": true,
|
||||
"Model": "models/tree/tree01/street_tree01.vmdl",
|
||||
"StartAsleep": false,
|
||||
"Tint": "1,1,1,1"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"SceneProperties": {
|
||||
@ -382,6 +473,12 @@
|
||||
"ResourceVersion": 2,
|
||||
"Title": "minimal",
|
||||
"Description": null,
|
||||
"__references": [],
|
||||
"__references": [
|
||||
"facepunch.couch#33708",
|
||||
"progressgames.ceramicarch_m#54712",
|
||||
"spongeis.spongehouse#65599",
|
||||
"tiowevolve.street_tree_01#15733",
|
||||
"titanovsky.marketstall#53840"
|
||||
],
|
||||
"__version": 2
|
||||
}
|
88
Code/Inventory.cs
Normal file
88
Code/Inventory.cs
Normal file
@ -0,0 +1,88 @@
|
||||
using SWB.Shared;
|
||||
|
||||
public class Inventory : Component, IInventory
|
||||
{
|
||||
[Sync] public NetList<GameObject> Items { get; set; } = new();
|
||||
[Sync] public new GameObject Active { get; set; }
|
||||
|
||||
Kal player;
|
||||
|
||||
protected override void OnAwake()
|
||||
{
|
||||
player = Components.Get<Kal>();
|
||||
}
|
||||
|
||||
public void Add( GameObject gameObject, bool makeActive = false )
|
||||
{
|
||||
if ( !Has( gameObject ) )
|
||||
{
|
||||
Items.Add( gameObject );
|
||||
}
|
||||
|
||||
if ( makeActive )
|
||||
SetActive( gameObject );
|
||||
else
|
||||
{
|
||||
if ( gameObject.Components.TryGet<IInventoryItem>( out var item ) )
|
||||
{
|
||||
item.OnCarryStop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public GameObject AddClone( GameObject gamePrefab, bool makeActive = true )
|
||||
{
|
||||
var gameObject = gamePrefab.Clone( player.GameObject, player.WorldPosition, player.WorldRotation, Vector3.One );
|
||||
gameObject.Name = gamePrefab.Name;
|
||||
gameObject.NetworkSpawn( player.Network.Owner );
|
||||
|
||||
Add( gameObject, makeActive );
|
||||
return gameObject;
|
||||
}
|
||||
|
||||
public bool Has( GameObject gameObject )
|
||||
{
|
||||
return Items.Contains( gameObject );
|
||||
}
|
||||
|
||||
public void SetActive( GameObject gameObject )
|
||||
{
|
||||
if ( !Has( gameObject ) || Active == gameObject ) return;
|
||||
|
||||
if ( Active is not null && Active.Components.TryGet<IInventoryItem>( out var oldActive ) )
|
||||
{
|
||||
if ( !oldActive.CanCarryStop() ) return;
|
||||
oldActive.OnCarryStop();
|
||||
}
|
||||
|
||||
if ( gameObject.Components.TryGet<IInventoryItem>( out var newActive, FindMode.EverythingInSelf ) )
|
||||
{
|
||||
newActive.OnCarryStart();
|
||||
}
|
||||
|
||||
Active = gameObject;
|
||||
}
|
||||
|
||||
public void SetActive( string name )
|
||||
{
|
||||
foreach ( var item in Items )
|
||||
{
|
||||
if ( item.Name == name )
|
||||
{
|
||||
SetActive( item );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
foreach ( var item in Items )
|
||||
{
|
||||
item.Destroy();
|
||||
}
|
||||
|
||||
Items.Clear();
|
||||
Active = null;
|
||||
}
|
||||
}
|
88
Code/Kal.cs
88
Code/Kal.cs
@ -2,17 +2,52 @@ using System;
|
||||
using Sandbox.Citizen;
|
||||
using Sandbox.Network;
|
||||
using ShrimpleCharacterController;
|
||||
using SWB.Base;
|
||||
using SWB.Shared;
|
||||
using DamageInfo = SWB.Shared.DamageInfo;
|
||||
|
||||
public sealed class Kal : Component
|
||||
public sealed class Kal : Component, IPlayerBase
|
||||
{
|
||||
[RequireComponent] private ShrimpleCharacterController.ShrimpleCharacterController Controller { get; set; }
|
||||
[RequireComponent] private AnimationHelper AnimationHelper { get; set; }
|
||||
[RequireComponent] private RagdollController RagdollController { get; set; }
|
||||
public SkinnedModelRenderer Renderer { get; set; }
|
||||
public ShrimpleCharacterController.ShrimpleCharacterController CharacterController { get; set; }
|
||||
public AnimationHelper AnimationHelper { get; set; }
|
||||
private RagdollController RagdollController { get; set; }
|
||||
public SkinnedModelRenderer BodyRenderer { get; set; }
|
||||
[Property] public GameObject Body { get; set; }
|
||||
[Property] public GameObject CameraPivot { get; set; }
|
||||
public GameObject Camera { get; set; }
|
||||
|
||||
public CameraComponent Camera { get; set; }
|
||||
public IInventory Inventory { get; set; }
|
||||
public bool IsAlive { get; set; } = true;
|
||||
[Property] public int MaxHealth { get; set; }
|
||||
public int Health { get; set; }
|
||||
public int Kills { get; set; }
|
||||
public int Deaths { get; set; }
|
||||
[Sync] public NetDictionary<string, int> Ammo { get; set; } = new();
|
||||
|
||||
public int AmmoCount( string ammoType )
|
||||
{
|
||||
if ( Ammo.TryGetValue( ammoType, out var amount ) )
|
||||
{
|
||||
return amount;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int TakeAmmo( string type, int amount )
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void TakeDamage( DamageInfo info )
|
||||
{
|
||||
Health -= (int)info.Damage;
|
||||
}
|
||||
|
||||
public void ShakeScreen( ScreenShake screenShake )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
[Property] [Range(1f, 200f, 1f)] public float CamOffsetX { get; set; }
|
||||
[Property] [Range(1f, 200f, 1f)] public float CamOffsetZ { get; set; }
|
||||
[Property] [Range(50f, 200f, 10f)] public float WalkSpeed { get; set; } = 100f;
|
||||
@ -26,18 +61,31 @@ public sealed class Kal : Component
|
||||
{
|
||||
base.OnStart();
|
||||
|
||||
Controller = Components.Get<ShrimpleCharacterController.ShrimpleCharacterController>();
|
||||
CharacterController = Components.Get<ShrimpleCharacterController.ShrimpleCharacterController>();
|
||||
AnimationHelper = Components.Get<AnimationHelper>();
|
||||
RagdollController = Components.Get<RagdollController>();
|
||||
|
||||
if ( !Network.IsOwner ) return;
|
||||
|
||||
Renderer = Components.Get<SkinnedModelRenderer>(FindMode.EverythingInSelfAndDescendants);
|
||||
Camera = Scene.Camera.GameObject;
|
||||
|
||||
Camera.SetParent(GameObject);
|
||||
BodyRenderer = Components.Get<SkinnedModelRenderer>(FindMode.EverythingInSelfAndDescendants);
|
||||
Camera = Scene.Camera;
|
||||
|
||||
Camera.GameObject.SetParent(GameObject);
|
||||
var cameraComponent = Camera.Components.Create<CameraComponent>();
|
||||
|
||||
cameraComponent.ZFar = 32768f;
|
||||
|
||||
var weaponRegistery = Scene.Components.GetInChildren<WeaponRegistry>();
|
||||
var weaponGO = weaponRegistery.Get( "Pistol" );
|
||||
var weapon = weaponGO.Components.Get<Weapon>( true );
|
||||
weaponGO.SetParent(GameObject);
|
||||
// weaponGO.Enabled = true;
|
||||
|
||||
Inventory = Components.Create<Inventory>();
|
||||
Inventory.Add(weaponGO);
|
||||
|
||||
Inventory.SetActive("Pistol");
|
||||
Log.Info(Inventory.Items[0].Name);
|
||||
}
|
||||
|
||||
protected override void OnFixedUpdate()
|
||||
@ -53,13 +101,13 @@ public sealed class Kal : Component
|
||||
var wishSpeed = isDucking ? DuckSpeed :
|
||||
isRunning ? RunSpeed : WalkSpeed;
|
||||
|
||||
Controller.WishVelocity = wishDirection * wishSpeed;
|
||||
CharacterController.WishVelocity = wishDirection * wishSpeed;
|
||||
|
||||
Controller.Move();
|
||||
CharacterController.Move();
|
||||
|
||||
if ( Input.Pressed( "Jump" ) && Controller.IsOnGround )
|
||||
if ( Input.Pressed( "Jump" ) && CharacterController.IsOnGround )
|
||||
{
|
||||
Controller.Punch( Vector3.Up * JumpStrength );
|
||||
CharacterController.Punch( Vector3.Up * JumpStrength );
|
||||
AnimationHelper.TriggerJump();
|
||||
}
|
||||
|
||||
@ -68,9 +116,9 @@ public sealed class Kal : Component
|
||||
AnimationHelper.DuckLevel = isDucking ? 1f : 0f;
|
||||
}
|
||||
|
||||
AnimationHelper.WithWishVelocity(Controller.WishVelocity);
|
||||
AnimationHelper.WithVelocity(Controller.Velocity);
|
||||
AnimationHelper.IsGrounded = Controller.IsOnGround;
|
||||
AnimationHelper.WithWishVelocity(CharacterController.WishVelocity);
|
||||
AnimationHelper.WithVelocity(CharacterController.Velocity);
|
||||
AnimationHelper.IsGrounded = CharacterController.IsOnGround;
|
||||
}
|
||||
|
||||
protected override void OnUpdate()
|
||||
@ -102,7 +150,7 @@ public sealed class Kal : Component
|
||||
|
||||
float rotateDifference = Body.WorldRotation.Distance(targetAngle);
|
||||
|
||||
if(rotateDifference > 30f || Controller.Velocity.Length > 10f)
|
||||
if(rotateDifference > 30f || CharacterController.Velocity.Length > 10f)
|
||||
{
|
||||
Body.WorldRotation = Rotation.Lerp(Body.WorldRotation, targetAngle, Time.Delta * 2f);
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ public sealed class RagdollController : Component
|
||||
[Group("Config"), Order(0), Property] public bool isLocked { get; set; }
|
||||
|
||||
[Sync]
|
||||
public bool Enabled
|
||||
public new bool Enabled
|
||||
{
|
||||
get => bodyPhysics.Enabled;
|
||||
private set
|
||||
|
71
Code/swb_base/BulletBase.HitScan.cs
Normal file
71
Code/swb_base/BulletBase.HitScan.cs
Normal file
@ -0,0 +1,71 @@
|
||||
using SWB.Shared;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace SWB.Base;
|
||||
|
||||
public class HitScanBullet : IBulletBase
|
||||
{
|
||||
public void Shoot( Weapon weapon, ShootInfo shootInfo, Vector3 spreadOffset )
|
||||
{
|
||||
if ( !weapon.IsValid ) return;
|
||||
|
||||
var player = weapon.Owner;
|
||||
if ( player is null ) return;
|
||||
|
||||
var forward = player.Camera.WorldRotation.Forward + spreadOffset;
|
||||
forward = forward.Normal;
|
||||
var endPos = player.Camera.WorldPosition + forward * 999999;
|
||||
var bulletTr = weapon.TraceBullet( player.Camera.WorldPosition, endPos );
|
||||
var hitObj = bulletTr.GameObject;
|
||||
|
||||
if ( SurfaceUtil.IsSkybox( bulletTr.Surface ) || bulletTr.HitPosition == Vector3.Zero ) return;
|
||||
|
||||
// Impact
|
||||
weapon.CreateBulletImpact( bulletTr );
|
||||
|
||||
// Tracer
|
||||
if ( shootInfo.BulletTracerParticle is not null )
|
||||
{
|
||||
var random = new Random();
|
||||
var randVal = random.NextDouble();
|
||||
|
||||
if ( randVal < shootInfo.BulletTracerChance )
|
||||
TracerEffects( weapon, shootInfo, bulletTr.HitPosition );
|
||||
}
|
||||
|
||||
// Damage
|
||||
if ( !weapon.IsProxy && hitObj is not null && hitObj.Tags.Has( TagsHelper.Player ) )
|
||||
{
|
||||
var target = hitObj.Components.GetInAncestorsOrSelf<IPlayerBase>();
|
||||
if ( !target.IsAlive ) return;
|
||||
|
||||
var hitTags = Array.Empty<string>();
|
||||
|
||||
if ( bulletTr.Hitbox is not null )
|
||||
hitTags = bulletTr.Hitbox.Tags.TryGetAll().ToArray();
|
||||
|
||||
var dmgInfo = Shared.DamageInfo.FromBullet( weapon.Owner.Id, weapon.ClassName, shootInfo.Damage, bulletTr.HitPosition, forward * 100 * shootInfo.Force, hitTags );
|
||||
target?.TakeDamage( dmgInfo );
|
||||
}
|
||||
}
|
||||
|
||||
public Vector3 GetRandomSpread( float spread )
|
||||
{
|
||||
return (Vector3.Random + Vector3.Random + Vector3.Random + Vector3.Random) * spread * 0.1f;
|
||||
}
|
||||
|
||||
void TracerEffects( Weapon weapon, ShootInfo shootInfo, Vector3 endPos )
|
||||
{
|
||||
var scale = shootInfo.WMParticleScale;
|
||||
var muzzleTransform = weapon.GetMuzzleTransform();
|
||||
|
||||
if ( !muzzleTransform.HasValue ) return;
|
||||
|
||||
SceneParticles particles = new( weapon.Scene.SceneWorld, shootInfo.BulletTracerParticle );
|
||||
particles?.SetControlPoint( 1, muzzleTransform.Value );
|
||||
particles?.SetControlPoint( 2, endPos );
|
||||
particles?.SetNamedValue( "scale", scale );
|
||||
particles?.PlayUntilFinished( TaskSource.Create() );
|
||||
}
|
||||
}
|
8
Code/swb_base/BulletBase.cs
Normal file
8
Code/swb_base/BulletBase.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace SWB.Base;
|
||||
|
||||
public interface IBulletBase
|
||||
{
|
||||
public void Shoot( Weapon weapon, ShootInfo shootInfo, Vector3 spreadOffset );
|
||||
|
||||
public Vector3 GetRandomSpread( float spread );
|
||||
}
|
24
Code/swb_base/Commands.cs
Normal file
24
Code/swb_base/Commands.cs
Normal file
@ -0,0 +1,24 @@
|
||||
// using SWB.Player;
|
||||
//
|
||||
// namespace SWB.Base;
|
||||
//
|
||||
// public class Commands
|
||||
// {
|
||||
// [ConCmd( "swb_host_customization", Help = "Enable the weapon customization menu (Q)" )]
|
||||
// public static void SetWeaponCustomization( int enable )
|
||||
// {
|
||||
// var player = PlayerBase.GetLocal();
|
||||
// if ( !player.IsHost ) return;
|
||||
//
|
||||
// WeaponSettings.Instance.Customization = enable != 0;
|
||||
// }
|
||||
//
|
||||
// [ConCmd( "swb_host_autoreload", Help = "Reload weapons automatically while shooting if clip is empty" )]
|
||||
// public static void SetWeaponAutoReload( int enable )
|
||||
// {
|
||||
// var player = PlayerBase.GetLocal();
|
||||
// if ( !player.IsHost ) return;
|
||||
//
|
||||
// WeaponSettings.Instance.AutoReload = enable != 0;
|
||||
// }
|
||||
// }
|
17
Code/swb_base/Weapon.Attachments.cs
Normal file
17
Code/swb_base/Weapon.Attachments.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using SWB.Base.Attachments;
|
||||
|
||||
namespace SWB.Base;
|
||||
|
||||
public partial class Weapon
|
||||
{
|
||||
public Attachment GetActiveAttachmentForCategory( AttachmentCategory category )
|
||||
{
|
||||
foreach ( var attachment in Attachments )
|
||||
{
|
||||
if ( attachment.Category == category && attachment.Equipped )
|
||||
return attachment;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
53
Code/swb_base/Weapon.Extra.cs
Normal file
53
Code/swb_base/Weapon.Extra.cs
Normal file
@ -0,0 +1,53 @@
|
||||
namespace SWB.Base;
|
||||
|
||||
public partial class Weapon
|
||||
{
|
||||
// Burst Fire
|
||||
public void ResetBurstFireCount( ShootInfo shootInfo, string inputButton )
|
||||
{
|
||||
if ( shootInfo is null || shootInfo.FiringType != FiringType.burst ) return;
|
||||
|
||||
if ( Input.Released( inputButton ) )
|
||||
{
|
||||
burstCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Barrel heat
|
||||
public void BarrelHeatCheck()
|
||||
{
|
||||
if ( TimeSincePrimaryShoot > 3 && TimeSinceSecondaryShoot > 0 )
|
||||
{
|
||||
barrelHeat = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Tucking
|
||||
public virtual float GetTuckDist()
|
||||
{
|
||||
if ( TuckRange == -1 )
|
||||
return -1;
|
||||
|
||||
if ( !Owner.IsValid ) return -1;
|
||||
|
||||
var pos = Owner.Camera.GameObject.WorldPosition;
|
||||
var forward = Owner.Camera.GameObject.WorldRotation.Forward;
|
||||
var trace = TraceBullet( Owner.Camera.GameObject.WorldPosition, pos + forward * TuckRange );
|
||||
|
||||
if ( !trace.Hit )
|
||||
return -1;
|
||||
|
||||
return trace.Distance;
|
||||
}
|
||||
|
||||
// public bool ShouldTuck()
|
||||
// {
|
||||
// return GetTuckDist() != -1;
|
||||
// }
|
||||
//
|
||||
// public bool ShouldTuck( out float dist )
|
||||
// {
|
||||
// dist = GetTuckDist();
|
||||
// return dist != -1;
|
||||
// }
|
||||
}
|
129
Code/swb_base/Weapon.Getters.cs
Normal file
129
Code/swb_base/Weapon.Getters.cs
Normal file
@ -0,0 +1,129 @@
|
||||
using SWB.Base.Attachments;
|
||||
|
||||
namespace SWB.Base;
|
||||
|
||||
public partial class Weapon
|
||||
{
|
||||
public virtual SkinnedModelRenderer GetEffectRenderer()
|
||||
{
|
||||
SkinnedModelRenderer effectModel = WorldModelRenderer;
|
||||
|
||||
return effectModel;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the info on where to show the muzzle effect
|
||||
/// </summary>
|
||||
public virtual Transform? GetMuzzleTransform()
|
||||
{
|
||||
var activeAttachment = GetActiveAttachmentForCategory( AttachmentCategory.Muzzle );
|
||||
var effectRenderer = GetEffectRenderer();
|
||||
var effectAttachment = "muzzle";
|
||||
|
||||
if ( activeAttachment is not null )
|
||||
{
|
||||
effectAttachment = activeAttachment.EffectAttachmentOrBone;
|
||||
Transform? effectBoneTransform = null;
|
||||
|
||||
// Custom models will not use attachments but bones instead to position effects
|
||||
if (activeAttachment.WorldModelRenderer is not null )
|
||||
{
|
||||
effectBoneTransform = activeAttachment.WorldModelRenderer.SceneModel.GetBoneWorldTransform( effectAttachment );
|
||||
}
|
||||
|
||||
if ( effectBoneTransform.HasValue )
|
||||
return effectBoneTransform.Value;
|
||||
}
|
||||
|
||||
return effectRenderer?.GetAttachment( effectAttachment );
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the correct shoot animation
|
||||
/// </summary>
|
||||
/// <param name="shootInfo">Info used for the current attack</param>
|
||||
/// <returns></returns>
|
||||
public virtual string GetShootAnimation( ShootInfo shootInfo )
|
||||
{
|
||||
if ( IsAiming && (!string.IsNullOrEmpty( shootInfo.ShootAimedAnim )) )
|
||||
{
|
||||
return shootInfo.ShootAimedAnim;
|
||||
}
|
||||
else if ( shootInfo.Ammo == 0 && !string.IsNullOrEmpty( shootInfo.ShootEmptyAnim ) )
|
||||
{
|
||||
return shootInfo.ShootEmptyAnim;
|
||||
}
|
||||
|
||||
return shootInfo.ShootAnim;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If there is usable ammo left
|
||||
/// </summary>
|
||||
public bool HasAmmo()
|
||||
{
|
||||
if ( Primary.InfiniteAmmo == InfiniteAmmoType.clip )
|
||||
return true;
|
||||
|
||||
if ( Primary.ClipSize == -1 )
|
||||
{
|
||||
return Owner.AmmoCount( Primary.AmmoType ) > 0;
|
||||
}
|
||||
|
||||
if ( Primary.Ammo == 0 )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public ShootInfo GetShootInfo( bool isPrimary )
|
||||
{
|
||||
return isPrimary ? Primary : Secondary;
|
||||
}
|
||||
|
||||
public bool IsShooting()
|
||||
{
|
||||
if ( Secondary is null )
|
||||
return GetRealRPM( Primary.RPM ) > TimeSincePrimaryShoot;
|
||||
|
||||
return GetRealRPM( Primary.RPM ) > TimeSincePrimaryShoot || GetRealRPM( Secondary.RPM ) > TimeSinceSecondaryShoot;
|
||||
}
|
||||
|
||||
public static float GetRealRPM( int rpm )
|
||||
{
|
||||
return 60f / rpm;
|
||||
}
|
||||
|
||||
public virtual float GetRealSpread( float baseSpread = -1 )
|
||||
{
|
||||
if ( !Owner.IsValid() ) return 0;
|
||||
|
||||
float spread = baseSpread != -1 ? baseSpread : Primary.Spread;
|
||||
float floatMod = 1f;
|
||||
|
||||
// Aiming
|
||||
if ( IsAiming && Primary.Bullets == 1 )
|
||||
floatMod /= 4;
|
||||
|
||||
// if ( !Owner.IsOnGround )
|
||||
// {
|
||||
// // Jumping
|
||||
// floatMod += 0.75f;
|
||||
// }
|
||||
// else if ( Owner.Velocity.Length > 100 )
|
||||
// {
|
||||
// // Moving
|
||||
// floatMod += 0.25f;
|
||||
// }
|
||||
|
||||
return spread * floatMod;
|
||||
}
|
||||
|
||||
public virtual Angles GetRecoilAngles( ShootInfo shootInfo )
|
||||
{
|
||||
var recoilX = IsAiming ? -shootInfo.Recoil * 0.4f : -shootInfo.Recoil;
|
||||
var recoilY = Game.Random.NextFloat( -0.2f, 0.2f ) * recoilX;
|
||||
var recoilAngles = new Angles( recoilX, recoilY, 0 );
|
||||
return recoilAngles;
|
||||
}
|
||||
}
|
125
Code/swb_base/Weapon.Reload.cs
Normal file
125
Code/swb_base/Weapon.Reload.cs
Normal file
@ -0,0 +1,125 @@
|
||||
namespace SWB.Base;
|
||||
|
||||
public partial class Weapon
|
||||
{
|
||||
public virtual void Reload()
|
||||
{
|
||||
if ( IsReloading || InBoltBack || IsShooting() )
|
||||
return;
|
||||
|
||||
var maxClipSize = BulletCocking ? Primary.ClipSize + 1 : Primary.ClipSize;
|
||||
|
||||
if ( Primary.Ammo >= maxClipSize || Primary.ClipSize == -1 )
|
||||
return;
|
||||
|
||||
var isEmptyReload = ReloadEmptyTime > 0 && Primary.Ammo == 0;
|
||||
TimeSinceReload = -(isEmptyReload ? ReloadEmptyTime : ReloadTime);
|
||||
|
||||
if ( Owner.AmmoCount( Primary.AmmoType ) <= 0 && Primary.InfiniteAmmo != InfiniteAmmoType.reserve )
|
||||
return;
|
||||
|
||||
if ( IsScoping )
|
||||
OnScopeEnd();
|
||||
|
||||
IsReloading = true;
|
||||
|
||||
// Anim
|
||||
var reloadAnim = ReloadAnim;
|
||||
if ( isEmptyReload && !string.IsNullOrEmpty( ReloadEmptyAnim ) )
|
||||
{
|
||||
reloadAnim = ReloadEmptyAnim;
|
||||
}
|
||||
|
||||
WorldModelRenderer?.Set( reloadAnim, true );
|
||||
|
||||
// Player anim
|
||||
HandleReloadEffects();
|
||||
|
||||
//Boltback
|
||||
if ( !isEmptyReload && Primary.Ammo == 0 && BoltBack )
|
||||
{
|
||||
TimeSinceReload -= BoltBackTime;
|
||||
AsyncBoltBack( ReloadTime );
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void OnReloadFinish()
|
||||
{
|
||||
IsReloading = false;
|
||||
var maxClipSize = BulletCocking && Primary.Ammo > 0 ? Primary.ClipSize + 1 : Primary.ClipSize;
|
||||
|
||||
if ( Primary.InfiniteAmmo == InfiniteAmmoType.reserve )
|
||||
{
|
||||
Primary.Ammo = maxClipSize;
|
||||
return;
|
||||
}
|
||||
|
||||
var ammo = Owner.TakeAmmo( Primary.AmmoType, maxClipSize - Primary.Ammo );
|
||||
|
||||
if ( ammo == 0 )
|
||||
return;
|
||||
|
||||
Primary.Ammo += ammo;
|
||||
}
|
||||
|
||||
public virtual void CancelShellReload()
|
||||
{
|
||||
IsReloading = false;
|
||||
WorldModelRenderer.Set( ReloadAnim, false );
|
||||
}
|
||||
|
||||
public virtual void OnShellReload()
|
||||
{
|
||||
ReloadTime = ShellReloadStartTime + ShellReloadInsertTime;
|
||||
Reload();
|
||||
}
|
||||
|
||||
public virtual void OnShellReloadFinish()
|
||||
{
|
||||
IsReloading = false;
|
||||
|
||||
var hasInfiniteReserve = Primary.InfiniteAmmo == InfiniteAmmoType.reserve;
|
||||
var ammo = hasInfiniteReserve ? 1 : Owner.TakeAmmo( Primary.AmmoType, 1 );
|
||||
|
||||
Primary.Ammo += 1;
|
||||
|
||||
if ( ammo != 0 && Primary.Ammo < Primary.ClipSize )
|
||||
{
|
||||
ReloadTime = ShellReloadInsertTime;
|
||||
Reload();
|
||||
}
|
||||
else
|
||||
{
|
||||
CancelShellReload();
|
||||
}
|
||||
}
|
||||
|
||||
async void AsyncBoltBack( float boltBackDelay )
|
||||
{
|
||||
InBoltBack = true;
|
||||
|
||||
// Start boltback
|
||||
await GameTask.DelaySeconds( boltBackDelay );
|
||||
if ( !IsValid ) return;
|
||||
if ( !IsProxy )
|
||||
WorldModelRenderer?.Set( BoltBackAnim, true );
|
||||
|
||||
// Eject shell
|
||||
await GameTask.DelaySeconds( BoltBackEjectDelay );
|
||||
if ( !IsValid ) return;
|
||||
var scale = Primary.WMParticleScale;
|
||||
CreateParticle( Primary.BulletEjectParticle, "ejection_point", scale );
|
||||
|
||||
// Finished
|
||||
await GameTask.DelaySeconds( BoltBackTime - BoltBackEjectDelay );
|
||||
if ( !IsValid ) return;
|
||||
InBoltBack = false;
|
||||
}
|
||||
|
||||
[Broadcast]
|
||||
public virtual void HandleReloadEffects()
|
||||
{
|
||||
// Player
|
||||
Owner?.BodyRenderer?.Set( "b_reload", true );
|
||||
}
|
||||
}
|
27
Code/swb_base/Weapon.Scoping.cs
Normal file
27
Code/swb_base/Weapon.Scoping.cs
Normal file
@ -0,0 +1,27 @@
|
||||
namespace SWB.Base;
|
||||
|
||||
public partial class Weapon
|
||||
{
|
||||
public async void OnScopeStart()
|
||||
{
|
||||
await GameTask.DelaySeconds( ScopeInfo.ScopeInDelay );
|
||||
if ( !IsAiming || IsScoping || IsReloading ) return;
|
||||
|
||||
IsScoping = true;
|
||||
// ViewModelHandler.ShouldDraw = false;
|
||||
|
||||
if ( ScopeInfo.ScopeInSound is not null )
|
||||
PlaySound( ScopeInfo.ScopeInSound.ResourceId );
|
||||
}
|
||||
|
||||
public void OnScopeEnd()
|
||||
{
|
||||
if ( !IsScoping ) return;
|
||||
|
||||
IsScoping = false;
|
||||
// ViewModelHandler.ShouldDraw = true;
|
||||
|
||||
if ( ScopeInfo.ScopeOutSound is not null )
|
||||
PlaySound( ScopeInfo.ScopeOutSound.ResourceId );
|
||||
}
|
||||
}
|
293
Code/swb_base/Weapon.Shoot.cs
Normal file
293
Code/swb_base/Weapon.Shoot.cs
Normal file
@ -0,0 +1,293 @@
|
||||
using SWB.Shared;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SWB.Base;
|
||||
|
||||
public partial class Weapon
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if the weapon can do the provided attack
|
||||
/// </summary>
|
||||
/// <param name="shootInfo">Attack information</param>
|
||||
/// <param name="lastAttackTime">Time since this attack</param>
|
||||
/// <param name="inputButton">The input button for this attack</param>
|
||||
/// <returns></returns>
|
||||
public virtual bool CanShoot( ShootInfo shootInfo, TimeSince lastAttackTime, string inputButton )
|
||||
{
|
||||
if ( (IsReloading && !ShellReloading) || (IsReloading && ShellReloading && !ShellReloadingShootCancel) || InBoltBack ) return false;
|
||||
if ( !Owner.IsValid() || !Input.Down( inputButton ) ) return false; // || (Secondary is null) shootInfo is null ||
|
||||
if ( !HasAmmo() )
|
||||
{
|
||||
if ( Input.Pressed( inputButton ) )
|
||||
{
|
||||
// Check for auto reloading
|
||||
if ( Settings.AutoReload && lastAttackTime > GetRealRPM( shootInfo.RPM ) )
|
||||
{
|
||||
TimeSincePrimaryShoot = 999;
|
||||
TimeSinceSecondaryShoot = 999;
|
||||
|
||||
if ( ShellReloading )
|
||||
OnShellReload();
|
||||
else
|
||||
Reload();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Dry fire
|
||||
if ( shootInfo.DryShootSound is not null )
|
||||
PlaySound( shootInfo.DryShootSound.ResourceId );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( shootInfo.FiringType == FiringType.semi && !Input.Pressed( inputButton ) ) return false;
|
||||
if ( shootInfo.FiringType == FiringType.burst )
|
||||
{
|
||||
if ( burstCount > 2 ) return false;
|
||||
|
||||
if ( Input.Down( inputButton ) && lastAttackTime > GetRealRPM( shootInfo.RPM ) )
|
||||
{
|
||||
burstCount++;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
if ( shootInfo.RPM <= 0 ) return true;
|
||||
|
||||
return lastAttackTime > GetRealRPM( shootInfo.RPM );
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if weapon can do the primary attack
|
||||
/// </summary>
|
||||
public virtual bool CanPrimaryShoot()
|
||||
{
|
||||
return CanShoot( Primary, TimeSincePrimaryShoot, InputButtonHelper.PrimaryAttack );
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if weapon can do the secondary attack
|
||||
/// </summary>
|
||||
public virtual bool CanSecondaryShoot()
|
||||
{
|
||||
return false; // CanShoot( Secondary, TimeSinceSecondaryShoot, InputButtonHelper.SecondaryAttack );
|
||||
}
|
||||
|
||||
public virtual void Shoot( ShootInfo shootInfo, bool isPrimary )
|
||||
{
|
||||
Log.Info(shootInfo);
|
||||
// Ammo
|
||||
shootInfo.Ammo -= 1;
|
||||
|
||||
// Animations
|
||||
var shootAnim = GetShootAnimation( shootInfo );
|
||||
if ( !string.IsNullOrEmpty( shootAnim ) )
|
||||
WorldModelRenderer.Set( shootAnim, true );
|
||||
|
||||
// Sound
|
||||
if ( shootInfo.ShootSound is not null )
|
||||
PlaySound( shootInfo.ShootSound.ResourceId );
|
||||
|
||||
// Particles
|
||||
HandleShootEffects( isPrimary );
|
||||
|
||||
// Barrel smoke
|
||||
barrelHeat += 1;
|
||||
|
||||
// Recoil
|
||||
// Owner.EyeAnglesOffset += GetRecoilAngles( shootInfo );
|
||||
|
||||
// Screenshake
|
||||
// if ( shootInfo.ScreenShake is not null )
|
||||
// Owner.ShakeScreen( shootInfo.ScreenShake );
|
||||
|
||||
// UI
|
||||
BroadcastUIEvent( "shoot", GetRealRPM( shootInfo.RPM ) );
|
||||
|
||||
// Bullet
|
||||
for ( int i = 0; i < shootInfo.Bullets; i++ )
|
||||
{
|
||||
var realSpread = IsScoping ? 0 : GetRealSpread( shootInfo.Spread );
|
||||
var spreadOffset = shootInfo.BulletType.GetRandomSpread( realSpread );
|
||||
ShootBullet( isPrimary, spreadOffset );
|
||||
}
|
||||
}
|
||||
|
||||
[Broadcast]
|
||||
public virtual void ShootBullet( bool isPrimary, Vector3 spreadOffset )
|
||||
{
|
||||
if ( !IsValid ) return;
|
||||
var shootInfo = GetShootInfo( isPrimary );
|
||||
shootInfo?.BulletType?.Shoot( this, shootInfo, spreadOffset );
|
||||
}
|
||||
|
||||
/// <summary> A single bullet trace from start to end with a certain radius.</summary>
|
||||
public virtual SceneTraceResult TraceBullet( Vector3 start, Vector3 end, float radius = 2.0f )
|
||||
{
|
||||
var startsInWater = SurfaceUtil.IsPointWater( start );
|
||||
List<string> withoutTags = new() { TagsHelper.Trigger, TagsHelper.PlayerClip, TagsHelper.PassBullets, TagsHelper.ViewModel };
|
||||
|
||||
if ( startsInWater )
|
||||
withoutTags.Add( TagsHelper.Water );
|
||||
|
||||
var tr = Scene.Trace.Ray( start, end )
|
||||
.UseHitboxes()
|
||||
.WithoutTags( withoutTags.ToArray() )
|
||||
.Size( radius )
|
||||
.IgnoreGameObjectHierarchy( Owner.GameObject )
|
||||
.Run();
|
||||
|
||||
// Log.Info( tr.GameObject );
|
||||
|
||||
return tr;
|
||||
}
|
||||
|
||||
[Broadcast]
|
||||
public virtual void HandleShootEffects( bool isPrimary )
|
||||
{
|
||||
if ( !IsValid || Owner is null ) return;
|
||||
|
||||
// Player
|
||||
Owner.BodyRenderer.Set( "b_attack", true );
|
||||
|
||||
// Weapon
|
||||
var shootInfo = GetShootInfo( isPrimary );
|
||||
if ( shootInfo is null ) return;
|
||||
|
||||
var scale = shootInfo.WMParticleScale;
|
||||
var muzzleTransform = GetMuzzleTransform();
|
||||
|
||||
// Bullet eject
|
||||
if ( shootInfo.BulletEjectParticle is not null )
|
||||
{
|
||||
if ( !BoltBack )
|
||||
{
|
||||
if ( !ShellReloading || (ShellReloading && ShellEjectDelay == 0) )
|
||||
{
|
||||
CreateParticle( shootInfo.BulletEjectParticle, "ejection_point", scale );
|
||||
}
|
||||
else
|
||||
{
|
||||
var delayedEject = async () =>
|
||||
{
|
||||
await GameTask.DelaySeconds( ShellEjectDelay );
|
||||
if ( !IsValid ) return;
|
||||
CreateParticle( shootInfo.BulletEjectParticle, "ejection_point", scale );
|
||||
};
|
||||
delayedEject();
|
||||
}
|
||||
}
|
||||
else if ( shootInfo.Ammo > 0 )
|
||||
{
|
||||
AsyncBoltBack( GetRealRPM( shootInfo.RPM ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( !muzzleTransform.HasValue ) return;
|
||||
|
||||
// Muzzle flash
|
||||
if ( shootInfo.MuzzleFlashParticle is not null )
|
||||
CreateParticle( shootInfo.MuzzleFlashParticle, muzzleTransform.Value, scale, ( particles ) => ParticleToMuzzlePos( particles ) );
|
||||
|
||||
// Barrel smoke
|
||||
if ( !IsProxy && shootInfo.BarrelSmokeParticle is not null && barrelHeat >= shootInfo.ClipSize * 0.75 )
|
||||
CreateParticle( shootInfo.BarrelSmokeParticle, muzzleTransform.Value, shootInfo.VMParticleScale, ( particles ) => ParticleToMuzzlePos( particles ) );
|
||||
}
|
||||
|
||||
void ParticleToMuzzlePos( SceneParticles particles )
|
||||
{
|
||||
var transform = GetMuzzleTransform();
|
||||
|
||||
if ( transform.HasValue )
|
||||
{
|
||||
// Apply velocity to prevent muzzle shift when moving fast
|
||||
particles?.SetControlPoint( 0, transform.Value.Position ); //+ Owner.Velocity * 0.03f
|
||||
particles?.SetControlPoint( 0, transform.Value.Rotation );
|
||||
}
|
||||
else
|
||||
{
|
||||
particles?.Delete();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Create a bullet impact effect</summary>
|
||||
public virtual void CreateBulletImpact( SceneTraceResult tr )
|
||||
{
|
||||
// Sound
|
||||
tr.Surface.PlayCollisionSound( tr.HitPosition );
|
||||
|
||||
// Particles
|
||||
if ( tr.Surface.ImpactEffects.Bullet is not null )
|
||||
{
|
||||
var effectPath = Game.Random.FromList( tr.Surface.ImpactEffects.Bullet, "particles/impact.generic.smokepuff.vpcf" );
|
||||
|
||||
if ( effectPath is not null )
|
||||
{
|
||||
// Surface def for flesh has wrong blood particle linked
|
||||
if ( effectPath.Contains( "impact.flesh" ) )
|
||||
{
|
||||
effectPath = "particles/impact.flesh.bloodpuff.vpcf";
|
||||
}
|
||||
else if ( effectPath.Contains( "impact.wood" ) )
|
||||
{
|
||||
effectPath = "particles/impact.generic.smokepuff.vpcf";
|
||||
}
|
||||
|
||||
var p = new SceneParticles( Scene.SceneWorld, effectPath );
|
||||
p.SetControlPoint( 0, tr.HitPosition );
|
||||
p.SetControlPoint( 0, Rotation.LookAt( tr.Normal ) );
|
||||
p.PlayUntilFinished( TaskSource.Create() );
|
||||
}
|
||||
}
|
||||
|
||||
// Decal
|
||||
if ( tr.Surface.ImpactEffects.BulletDecal is not null )
|
||||
{
|
||||
var decalPath = Game.Random.FromList( tr.Surface.ImpactEffects.BulletDecal, "decals/bullethole.decal" );
|
||||
|
||||
if ( ResourceLibrary.TryGet<DecalDefinition>( decalPath, out var decalDef ) )
|
||||
{
|
||||
var decalEntry = Game.Random.FromList( decalDef.Decals );
|
||||
|
||||
var gameObject = Scene.CreateObject();
|
||||
//gameObject.SetParent( tr.GameObject, false );
|
||||
gameObject.WorldPosition = tr.HitPosition;
|
||||
gameObject.WorldRotation = Rotation.LookAt( -tr.Normal );
|
||||
|
||||
var decalRenderer = gameObject.Components.Create<DecalRenderer>();
|
||||
decalRenderer.Material = decalEntry.Material;
|
||||
decalRenderer.Size = new( decalEntry.Height.GetValue(), decalEntry.Height.GetValue(), decalEntry.Depth.GetValue() );
|
||||
gameObject.DestroyAsync( 30f );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Create a weapon particle</summary>
|
||||
public virtual void CreateParticle( ParticleSystem particle, string attachment, float scale, Action<SceneParticles> OnFrame = null )
|
||||
{
|
||||
var effectRenderer = GetEffectRenderer();
|
||||
|
||||
if ( effectRenderer is null || effectRenderer.SceneModel is null ) return;
|
||||
|
||||
var transform = effectRenderer.SceneModel.GetAttachment( attachment );
|
||||
|
||||
if ( !transform.HasValue ) return;
|
||||
|
||||
CreateParticle( particle, transform.Value, scale, OnFrame );
|
||||
}
|
||||
|
||||
public virtual void CreateParticle( ParticleSystem particle, Transform transform, float scale, Action<SceneParticles> OnFrame = null )
|
||||
{
|
||||
SceneParticles particles = new( Scene.SceneWorld, particle );
|
||||
particles?.SetControlPoint( 0, transform.Position );
|
||||
particles?.SetControlPoint( 0, transform.Rotation );
|
||||
particles?.SetNamedValue( "scale", scale );
|
||||
|
||||
particles?.PlayUntilFinished( Task, OnFrame );
|
||||
}
|
||||
}
|
50
Code/swb_base/Weapon.UI.cs
Normal file
50
Code/swb_base/Weapon.UI.cs
Normal file
@ -0,0 +1,50 @@
|
||||
using SWB.Base.UI;
|
||||
|
||||
namespace SWB.Base;
|
||||
|
||||
public partial class Weapon
|
||||
{
|
||||
public ScreenPanel ScreenPanel { get; set; }
|
||||
public PanelComponent RootPanel { get; set; }
|
||||
|
||||
private CustomizationMenu customizationMenu;
|
||||
|
||||
/// <summary>Override this if you want custom UI elements</summary>
|
||||
public virtual void CreateUI()
|
||||
{
|
||||
ScreenPanel = Components.Create<ScreenPanel>();
|
||||
ScreenPanel.Opacity = 1;
|
||||
ScreenPanel.ZIndex = 1;
|
||||
|
||||
var rootPanel = Components.Create<RootWeaponDisplay>();
|
||||
rootPanel.Weapon = this;
|
||||
RootPanel = rootPanel;
|
||||
}
|
||||
|
||||
public virtual void DestroyUI()
|
||||
{
|
||||
ScreenPanel?.Destroy();
|
||||
RootPanel?.Destroy();
|
||||
}
|
||||
|
||||
void BroadcastUIEvent( string name, object value )
|
||||
{
|
||||
if ( RootPanel is null ) return;
|
||||
|
||||
foreach ( var panel in RootPanel.Panel.Children )
|
||||
{
|
||||
panel.CreateEvent( name, value );
|
||||
}
|
||||
}
|
||||
|
||||
void OpenCustomizationMenu()
|
||||
{
|
||||
customizationMenu = new CustomizationMenu( this );
|
||||
RootPanel.Panel.AddChild( customizationMenu );
|
||||
}
|
||||
|
||||
void CloseCustomizationMenu()
|
||||
{
|
||||
customizationMenu?.Delete( true );
|
||||
}
|
||||
}
|
164
Code/swb_base/Weapon.Var.cs
Normal file
164
Code/swb_base/Weapon.Var.cs
Normal file
@ -0,0 +1,164 @@
|
||||
using Sandbox.Citizen;
|
||||
|
||||
namespace SWB.Base;
|
||||
|
||||
public partial class Weapon
|
||||
{
|
||||
/// <summary>Thirdperson Model</summary>
|
||||
[Property, Group( "Models" )] public Model WorldModel { get; set; }
|
||||
|
||||
|
||||
/// <summary>Unique name that identifies the weapon</summary>
|
||||
[Property, Group( "General" )] public string ClassName { get; set; }
|
||||
|
||||
[Property, Group( "General" )] public string DisplayName { get; set; }
|
||||
|
||||
[Property, Group( "General" ), ImageAssetPath] public string Icon { get; set; }
|
||||
|
||||
/// <summary>How the player holds the weapon in thirdperson</summary>
|
||||
[Property, Group( "General" )] public AnimationHelper.HoldTypes HoldType { get; set; } = AnimationHelper.HoldTypes.Pistol;
|
||||
|
||||
/// <summary>Mouse sensitivity while aiming (lower is slower, 0 to disable)</summary>
|
||||
[Property, Group( "General" )] public float AimSensitivity { get; set; } = 0.85f;
|
||||
|
||||
/// <summary>Can bullets be cocked in the barrel? (clip ammo + 1)</summary>
|
||||
[Property, Group( "General" )] public bool BulletCocking { get; set; } = true;
|
||||
|
||||
/// <summary>Range that tucking should be enabled (-1 to disable tucking)</summary>
|
||||
[Property, Group( "General" )] public float TuckRange { get; set; } = 30f;
|
||||
|
||||
[Property, Group( "General" )] public int Slot { get; set; } = 0;
|
||||
|
||||
/// <summary>Firing sound when clip is empty</summary>
|
||||
[Property, Group( "Sounds" )] public SoundEvent DeploySound { get; set; }
|
||||
|
||||
|
||||
/// <summary>Default weapon field of view</summary>
|
||||
[Property, Group( "FOV" )] public float FOV { get; set; } = 70f;
|
||||
|
||||
/// <summary>Weapon FOV while aiming (-1 to use default weapon fov)</summary>
|
||||
[Property, Group( "FOV" )] public float AimFOV { get; set; } = -1f;
|
||||
|
||||
/// <summary>Player FOV while aiming (-1 to use default player fov)</summary>
|
||||
[Property, Group( "FOV" )] public float AimPlayerFOV { get; set; } = -1f;
|
||||
|
||||
/// <summary>FOV aim in speed</summary>
|
||||
[Property, Group( "FOV" ), Title( "Aim in FOV speed" )] public float AimInFOVSpeed { get; set; } = 1f;
|
||||
|
||||
/// <summary>FOV aim out speed</summary>
|
||||
[Property, Group( "FOV" ), Title( "Aim out FOV speed" )] public float AimOutFOVSpeed { get; set; } = 1f;
|
||||
|
||||
|
||||
/// <summary>Procedural animation speed (lower is slower)</summary>
|
||||
[Property, Group( "Animations" )] public float AnimSpeed { get; set; } = 1;
|
||||
|
||||
/// <summary>Offset used for setting the weapon to its aim position</summary>
|
||||
[Property, Group( "Animations" ), Title( "Aim Offset (swb_editor_offsets)" )] public AngPos AimAnimData { get; set; }
|
||||
|
||||
/// <summary>Offset used for setting the weapon to its run position</summary>
|
||||
[Property, Group( "Animations" ), Title( "Run Offset (swb_editor_offsets)" )] public AngPos RunAnimData { get; set; }
|
||||
|
||||
/// <summary>Offset used for setting the weapon to its run position</summary>
|
||||
[Property, Group( "Animations" ), Title( "Customizing Offset (swb_editor_offsets)" )] public AngPos CustomizeAnimData { get; set; }
|
||||
|
||||
/// <summary>Duration of the reload animation</summary>
|
||||
[Property, Group( "Animations" )] public float ReloadTime { get; set; } = 1f;
|
||||
|
||||
/// <summary>Reloading animation</summary>
|
||||
[Property, Group( "Animations" )] public string ReloadAnim { get; set; } = "reload";
|
||||
|
||||
/// <summary>Duration of the empty reload animation (-1 to disable)</summary>
|
||||
[Property, Group( "Animations" )] public float ReloadEmptyTime { get; set; } = -1f;
|
||||
|
||||
/// <summary>Reloading animation when clip is empty</summary>
|
||||
[Property, Group( "Animations" )] public string ReloadEmptyAnim { get; set; } = "reload_empty";
|
||||
|
||||
/// <summary>Duration of the draw animation</summary>
|
||||
[Property, Group( "Animations" )] public float DrawTime { get; set; } = 0.5f;
|
||||
|
||||
/// <summary>Draw animation</summary>
|
||||
[Property, Group( "Animations" )] public string DrawAnim { get; set; } = "deploy";
|
||||
|
||||
/// <summary>Duration of the empty draw animation (-1 to disable)</summary>
|
||||
[Property, Group( "Animations" )] public float DrawEmptyTime { get; set; } = -1f;
|
||||
|
||||
/// <summary>Draw animation when there is no ammo</summary>
|
||||
[Property, Group( "Animations" )] public string DrawEmptyAnim { get; set; } = "";
|
||||
|
||||
|
||||
/// <summary>Is the weapon reloading shells instead of a magazine?</summary>
|
||||
[Property, Group( "Shell Reloading" )] public bool ShellReloading { get; set; } = false;
|
||||
|
||||
/// <summary>Can the weapon shoot while reloading to cancel the reload?</summary>
|
||||
[Property, Group( "Shell Reloading" )] public bool ShellReloadingShootCancel { get; set; } = true;
|
||||
|
||||
/// <summary>Delay in fire animation to eject the shell</summary>
|
||||
[Property, Group( "Shell Reloading" )] public float ShellEjectDelay { get; set; } = 0;
|
||||
|
||||
/// <summary>Duration of the shell reload start animation (animation is set with ReloadAnim)</summary>
|
||||
[Property, Group( "Shell Reloading" )] public float ShellReloadStartTime { get; set; } = 0;
|
||||
|
||||
/// <summary>Duration of the shell reload insert animation (animation is set in animgraph)</summary>
|
||||
[Property, Group( "Shell Reloading" )] public float ShellReloadInsertTime { get; set; } = 0;
|
||||
|
||||
|
||||
/// <summary>Is this a bolt action weapon?</summary>
|
||||
[Property, Group( "Bolt Action Reloading" ), Title( "Bolt Action" )] public bool BoltBack { get; set; } = false;
|
||||
|
||||
/// <summary>Duration of the boltback animation</summary>
|
||||
[Property, Group( "Bolt Action Reloading" )] public float BoltBackTime { get; set; } = 0f;
|
||||
|
||||
/// <summary>Boltback animation</summary>
|
||||
[Property, Group( "Bolt Action Reloading" )] public string BoltBackAnim { get; set; } = "boltback";
|
||||
|
||||
/// <summary>Bullet eject delay during the boltback animation (-1 to disable)</summary>
|
||||
[Property, Group( "Bolt Action Reloading" )] public float BoltBackEjectDelay { get; set; } = 0f;
|
||||
|
||||
/// <summary>Enable scoping, renders a 2D scope on ADS</summary>
|
||||
[Property, Group( "Scoping" )] public bool Scoping { get; set; } = false;
|
||||
|
||||
/// <summary>Scope Information</summary>
|
||||
[Property, Group( "Scoping" )] public ScopeInfo ScopeInfo { get; set; } = new();
|
||||
|
||||
/// <summary>Primary attack data</summary>
|
||||
[Property, Group( "Firing" ), Title( "Primary ShootInfo (component)" )] public ShootInfo Primary { get; set; } = new();
|
||||
|
||||
/// <summary>Secondary attack data (setting this will disable weapon aiming)</summary>
|
||||
[Property, Group( "Firing" ), Title( "Secondary ShootInfo (component)" )] public ShootInfo Secondary { get; set; }
|
||||
|
||||
|
||||
/// <summary>Time since the last primary attack</summary>
|
||||
public TimeSince TimeSincePrimaryShoot { get; set; }
|
||||
|
||||
/// <summary>Time since the last secondary attack</summary>
|
||||
public TimeSince TimeSinceSecondaryShoot { get; set; }
|
||||
|
||||
/// <summary>Time since deployment</summary>
|
||||
public TimeSince TimeSinceDeployed { get; set; }
|
||||
|
||||
/// <summary>Time since the last reload</summary>
|
||||
public TimeSince TimeSinceReload { get; set; }
|
||||
|
||||
public bool IsCustomizing { get; set; }
|
||||
|
||||
/// <summary>If the weapon is being reloaded</summary>
|
||||
[Sync] public bool IsReloading { get; set; }
|
||||
|
||||
/// <summary>If the weapon is being aimed</summary>
|
||||
[Sync] public bool IsAiming { get; set; }
|
||||
|
||||
/// <summary>If the weapon is being scoped</summary>
|
||||
[Sync] public bool IsScoping { get; set; }
|
||||
|
||||
/// <summary>If the weapon is being bolt back reloaded</summary>
|
||||
[Sync] public bool InBoltBack { get; set; }
|
||||
|
||||
public StatsModifier InitialPrimaryStats { get; private set; }
|
||||
public StatsModifier InitialSecondaryStats { get; private set; }
|
||||
|
||||
public bool IsDeploying => TimeSinceDeployed < 0;
|
||||
|
||||
// Private
|
||||
int burstCount = 0;
|
||||
int barrelHeat = 0;
|
||||
}
|
225
Code/swb_base/Weapon.cs
Normal file
225
Code/swb_base/Weapon.cs
Normal file
@ -0,0 +1,225 @@
|
||||
using SWB.Base.Attachments;
|
||||
using SWB.Shared;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace SWB.Base;
|
||||
|
||||
[Group( "SWB" )]
|
||||
[Title( "Weapon" )]
|
||||
public partial class Weapon : Component, IInventoryItem
|
||||
{
|
||||
public IPlayerBase Owner { get; private set; }
|
||||
public SkinnedModelRenderer WorldModelRenderer { get; private set; }
|
||||
public WeaponSettings Settings { get; private set; }
|
||||
public List<Attachment> Attachments = new();
|
||||
|
||||
protected override void OnAwake()
|
||||
{
|
||||
Tags.Add( TagsHelper.Weapon );
|
||||
|
||||
Attachments = Components.GetAll<Attachment>( FindMode.EverythingInSelf ).OrderBy( att => att.Name ).ToList();
|
||||
Settings = WeaponSettings.Instance;
|
||||
InitialPrimaryStats = StatsModifier.FromShootInfo( Primary );
|
||||
InitialSecondaryStats = StatsModifier.FromShootInfo( Primary );
|
||||
}
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnEnabled()
|
||||
{
|
||||
if ( IsProxy ) return;
|
||||
CreateUI();
|
||||
}
|
||||
|
||||
protected override void OnDisabled()
|
||||
{
|
||||
if ( IsProxy ) return;
|
||||
|
||||
IsReloading = false;
|
||||
IsScoping = false;
|
||||
IsAiming = false;
|
||||
IsCustomizing = false;
|
||||
|
||||
DestroyUI();
|
||||
}
|
||||
|
||||
[Broadcast]
|
||||
public void OnCarryStart()
|
||||
{
|
||||
if ( !IsValid ) return;
|
||||
GameObject.Enabled = true;
|
||||
}
|
||||
|
||||
[Broadcast]
|
||||
public void OnCarryStop()
|
||||
{
|
||||
if ( !IsValid ) return;
|
||||
GameObject.Enabled = false;
|
||||
}
|
||||
|
||||
public bool CanCarryStop()
|
||||
{
|
||||
return TimeSinceDeployed > 0;
|
||||
}
|
||||
|
||||
public void OnDeploy()
|
||||
{
|
||||
var delay = 0f;
|
||||
|
||||
if ( Primary.Ammo == 0 && !string.IsNullOrEmpty( DrawEmptyAnim ) )
|
||||
{
|
||||
delay = DrawEmptyTime;
|
||||
}
|
||||
else if ( !string.IsNullOrEmpty( DrawAnim ) )
|
||||
{
|
||||
delay = DrawTime;
|
||||
}
|
||||
|
||||
TimeSinceDeployed = -delay;
|
||||
|
||||
// Sound
|
||||
if ( DeploySound is not null )
|
||||
PlaySound( DeploySound.ResourceId );
|
||||
|
||||
// Start drawing
|
||||
|
||||
// Boltback
|
||||
if ( InBoltBack )
|
||||
AsyncBoltBack( delay );
|
||||
}
|
||||
|
||||
protected override void OnStart()
|
||||
{
|
||||
Owner = Components.GetInAncestors<IPlayerBase>();
|
||||
CreateModels();
|
||||
|
||||
// Attachments (load for clients joining late)
|
||||
if ( IsProxy )
|
||||
{
|
||||
// Log.Info( "Checking -> " + Network.Owner.DisplayName + "'s " + DisplayName + " for attachments" );
|
||||
Attachments.ForEach( att =>
|
||||
{
|
||||
// Log.Info( "[" + att.Name + "] equipped ->" + att.Equipped );
|
||||
if ( att is not null && att.Equipped )
|
||||
att.Equip();
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
if ( Owner is null ) return;
|
||||
|
||||
// UpdateModels();
|
||||
Owner.AnimationHelper.HoldType = HoldType;
|
||||
|
||||
if ( !IsProxy )
|
||||
{
|
||||
// if ( IsDeploying ) return;
|
||||
|
||||
// Customization
|
||||
if ( WeaponSettings.Instance.Customization && !IsScoping && !IsAiming && Input.Pressed( InputButtonHelper.Menu ) && Attachments.Count > 0 )
|
||||
{
|
||||
if ( !IsCustomizing )
|
||||
OpenCustomizationMenu();
|
||||
else
|
||||
CloseCustomizationMenu();
|
||||
|
||||
IsCustomizing = !IsCustomizing;
|
||||
}
|
||||
|
||||
// Don't cancel reload when customizing
|
||||
if ( IsCustomizing && !IsReloading ) return;
|
||||
|
||||
if ( Scoping )
|
||||
{
|
||||
if ( IsAiming && !IsScoping )
|
||||
OnScopeStart();
|
||||
else if ( !IsAiming && IsScoping )
|
||||
OnScopeEnd();
|
||||
}
|
||||
|
||||
ResetBurstFireCount( Primary, InputButtonHelper.PrimaryAttack );
|
||||
ResetBurstFireCount( Secondary, InputButtonHelper.SecondaryAttack );
|
||||
BarrelHeatCheck();
|
||||
|
||||
// var shouldTuck = ShouldTuck();
|
||||
|
||||
if ( CanPrimaryShoot() )
|
||||
{
|
||||
if ( IsReloading && ShellReloading && ShellReloadingShootCancel )
|
||||
CancelShellReload();
|
||||
|
||||
TimeSincePrimaryShoot = 0;
|
||||
Shoot( Primary, true );
|
||||
}
|
||||
else if ( CanSecondaryShoot())
|
||||
{
|
||||
TimeSinceSecondaryShoot = 0;
|
||||
Shoot( Secondary, false );
|
||||
}
|
||||
else if ( Input.Down( InputButtonHelper.Reload ) )
|
||||
{
|
||||
if ( ShellReloading )
|
||||
OnShellReload();
|
||||
else
|
||||
Reload();
|
||||
}
|
||||
|
||||
if ( IsReloading && TimeSinceReload >= 0 )
|
||||
{
|
||||
if ( ShellReloading )
|
||||
OnShellReloadFinish();
|
||||
else
|
||||
OnReloadFinish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CreateModels()
|
||||
{
|
||||
WorldModelRenderer = Components.Create<SkinnedModelRenderer>();
|
||||
WorldModelRenderer.Model = WorldModel;
|
||||
WorldModelRenderer.AnimationGraph = WorldModel.AnimGraph;
|
||||
WorldModelRenderer.CreateBoneObjects = true;
|
||||
|
||||
ModelUtil.ParentToBone( GameObject, Owner.BodyRenderer, "hold_R" );
|
||||
Network.ClearInterpolation();
|
||||
Log.Info("START WEAPON");
|
||||
}
|
||||
|
||||
// Temp fix until https://github.com/Facepunch/sbox-issues/issues/5247 is fixed
|
||||
// void ResetViewModelAnimations()
|
||||
// {
|
||||
// ViewModelRenderer?.Set( Primary.ShootAnim, false );
|
||||
// ViewModelRenderer?.Set( Primary.ShootEmptyAnim, false );
|
||||
// ViewModelRenderer?.Set( Primary.ShootAimedAnim, false );
|
||||
//
|
||||
// if ( Secondary is not null )
|
||||
// {
|
||||
// ViewModelRenderer?.Set( Secondary.ShootAnim, false );
|
||||
// ViewModelRenderer?.Set( Secondary.ShootEmptyAnim, false );
|
||||
// ViewModelRenderer?.Set( Secondary.ShootAimedAnim, false );
|
||||
// }
|
||||
//
|
||||
// ViewModelRenderer?.Set( ReloadAnim, false );
|
||||
// ViewModelRenderer?.Set( ReloadEmptyAnim, false );
|
||||
// ViewModelRenderer?.Set( DrawAnim, false );
|
||||
// ViewModelRenderer?.Set( DrawEmptyAnim, false );
|
||||
// }
|
||||
|
||||
[Broadcast]
|
||||
void PlaySound( int resourceID )
|
||||
{
|
||||
if ( !IsValid ) return;
|
||||
|
||||
var sound = ResourceLibrary.Get<SoundEvent>( resourceID );
|
||||
if ( sound is null ) return;
|
||||
|
||||
Sound.Play( sound, WorldPosition );
|
||||
|
||||
}
|
||||
}
|
55
Code/swb_base/WeaponRegistry.cs
Normal file
55
Code/swb_base/WeaponRegistry.cs
Normal file
@ -0,0 +1,55 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SWB.Base;
|
||||
|
||||
/*
|
||||
* Attach this component somewhere in the root of your scene.
|
||||
* Register all weapons that you want to use in here
|
||||
*/
|
||||
|
||||
[Group( "SWB" )]
|
||||
[Title( "Weapon Registry" )]
|
||||
public class WeaponRegistry : Component
|
||||
{
|
||||
[Property] public List<PrefabScene> WeaponPrefabs { get; set; } = new();
|
||||
public Dictionary<string, GameObject> Weapons { get; set; } = new();
|
||||
|
||||
static public WeaponRegistry Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
return Game.ActiveScene.Components.GetInChildren<WeaponRegistry>();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnAwake()
|
||||
{
|
||||
WeaponPrefabs.ForEach( weaponPrefab =>
|
||||
{
|
||||
var weaponGO = weaponPrefab.Clone();
|
||||
weaponGO.SetParent( this.GameObject );
|
||||
weaponGO.Enabled = false;
|
||||
|
||||
var weapon = weaponGO.Components.Get<Weapon>( true );
|
||||
Weapons.TryAdd( weapon.ClassName, weaponGO );
|
||||
|
||||
weaponGO.Name = weapon.ClassName;
|
||||
} );
|
||||
}
|
||||
|
||||
public GameObject Get( string className )
|
||||
{
|
||||
if ( className is null ) return null;
|
||||
|
||||
Weapons.TryGetValue( className, out var weaponGO );
|
||||
return weaponGO;
|
||||
}
|
||||
|
||||
public Weapon GetWeapon( string className )
|
||||
{
|
||||
var weaponGO = Get( className );
|
||||
if ( weaponGO is null ) return null;
|
||||
|
||||
return weaponGO.Components.Get<Weapon>( true );
|
||||
}
|
||||
}
|
30
Code/swb_base/WeaponSettings.cs
Normal file
30
Code/swb_base/WeaponSettings.cs
Normal file
@ -0,0 +1,30 @@
|
||||
namespace SWB.Base;
|
||||
|
||||
/*
|
||||
* Attach this component somewhere in the root of your scene.
|
||||
* Gives control over weapon settings (host only)
|
||||
*/
|
||||
|
||||
[Group( "SWB" )]
|
||||
[Title( "Weapon Settings" )]
|
||||
public class WeaponSettings : Component
|
||||
{
|
||||
/// <summary>Enable the weapon customization menu (Q)</summary>
|
||||
[HostSync, Property] public bool Customization { get; set; } = true;
|
||||
|
||||
/// <summary>Reload weapons automatically when trying to shoot if clip is empty</summary>
|
||||
[HostSync, Property] public bool AutoReload { get; set; } = true;
|
||||
|
||||
protected override void OnAwake()
|
||||
{
|
||||
GameObject.NetworkMode = NetworkMode.Object;
|
||||
}
|
||||
|
||||
static public WeaponSettings Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
return Game.ActiveScene.Components.GetInChildren<WeaponSettings>();
|
||||
}
|
||||
}
|
||||
}
|
243
Code/swb_base/attachments/Attachment.cs
Normal file
243
Code/swb_base/attachments/Attachment.cs
Normal file
@ -0,0 +1,243 @@
|
||||
using SWB.Shared;
|
||||
using System;
|
||||
|
||||
namespace SWB.Base.Attachments;
|
||||
|
||||
public enum AttachmentCategory
|
||||
{
|
||||
Barrel,
|
||||
Sight,
|
||||
Grip,
|
||||
Rail,
|
||||
Magazine,
|
||||
Muzzle,
|
||||
Stock,
|
||||
Other,
|
||||
Special,
|
||||
Tactical,
|
||||
Laser,
|
||||
None,
|
||||
}
|
||||
|
||||
/*
|
||||
* Attachment base that allows for weapon bone parenting as well as bodygroup changes simultaneously
|
||||
*/
|
||||
|
||||
[Group( "SWB Attachments" )]
|
||||
public abstract class Attachment : Component, IComparable<Attachment>
|
||||
{
|
||||
/// <summary>Display name (needs to be unique)</summary>
|
||||
public virtual string Name => "";
|
||||
|
||||
/// <summary>Display description</summary>
|
||||
public virtual string Description => "";
|
||||
|
||||
/// <summary>Only 1 active attachment per category, this is also used to determine what effect attachment to overide</summary>
|
||||
public virtual AttachmentCategory Category => AttachmentCategory.None;
|
||||
|
||||
/// <summary>List of positive attributes</summary>
|
||||
public virtual string[] Positives => Array.Empty<string>();
|
||||
|
||||
/// <summary>List of negative attributes</summary>
|
||||
public virtual string[] Negatives => Array.Empty<string>();
|
||||
|
||||
/// <summary>Weapon stats changer</summary>
|
||||
public virtual StatsModifier StatsModifier { get; set; }
|
||||
|
||||
/// <summary>Path to an image that represent the attachment on the HUD</summary>
|
||||
public virtual string IconPath => "";
|
||||
|
||||
/// <summary>Path to the attachment model</summary>
|
||||
public virtual string ModelPath => "";
|
||||
|
||||
/// <summary>Name of the model attachment used for new effect origins</summary>
|
||||
public virtual string EffectAttachmentOrBone { get; set; } = "";
|
||||
|
||||
/// <summary>Hide this attachment in menus</summary>
|
||||
public virtual bool Hide { get; set; } = false;
|
||||
|
||||
/// <summary>Depends on another attachment (e.g. rail/mount)</summary>
|
||||
[Property] public Attachment RequiresAttachment { get; set; }
|
||||
|
||||
/// <summary>Name of the bone you want to attach the model to</summary>
|
||||
[Property, Group( "Model Parenting" )] public virtual string Bone { get; set; }
|
||||
|
||||
/// <summary>Viewmodel scale</summary>
|
||||
[Property, Group( "Model Parenting" )] public virtual Vector3 ViewModelScale { get; set; } = Vector3.One;
|
||||
|
||||
/// <summary>Worldmodel scale</summary>
|
||||
[Property, Group( "Model Parenting" )] public virtual Vector3 WorldModelScale { get; set; } = Vector3.One;
|
||||
|
||||
/// <summary>The name of the body group</summary>
|
||||
[Property, Group( "BodyGroup" )] public virtual string BodyGroup { get; set; }
|
||||
|
||||
/// <summary>The name of the body group choice</summary>
|
||||
[Property, Group( "BodyGroup" )] public virtual int BodyGroupChoice { get; set; } = 0;
|
||||
|
||||
/// <summary>The default target body group value</summary>
|
||||
[Property, Group( "BodyGroup" )] public virtual int BodyGroupDefault { get; set; } = 0;
|
||||
|
||||
/// <summary>If already equipped</summary>
|
||||
[Sync] public bool Equipped { get; private set; }
|
||||
|
||||
public Weapon Weapon { get; private set; }
|
||||
public SkinnedModelRenderer ViewModelRenderer { get; private set; }
|
||||
public SkinnedModelRenderer WorldModelRenderer { get; private set; }
|
||||
|
||||
private int equipTries = 0;
|
||||
private bool equippedOnClient = false;
|
||||
|
||||
protected override void OnAwake()
|
||||
{
|
||||
Weapon = Components.Get<Weapon>();
|
||||
}
|
||||
|
||||
private void SetBodyGroup( int choice )
|
||||
{
|
||||
if ( string.IsNullOrEmpty( BodyGroup ) ) return;
|
||||
|
||||
Weapon.WorldModelRenderer.SetBodyGroup( BodyGroup, choice );
|
||||
}
|
||||
|
||||
private void CreateModel( bool isViewModel = false )
|
||||
{
|
||||
if ( string.IsNullOrEmpty( ModelPath ) || string.IsNullOrEmpty( Bone ) ) return;
|
||||
|
||||
var attachmentGo = new GameObject( true, "Attachment" );
|
||||
attachmentGo.Tags.Add( TagsHelper.Attachment );
|
||||
|
||||
var attachmentRenderer = attachmentGo.Components.Create<SkinnedModelRenderer>();
|
||||
attachmentRenderer.Model = Model.Load( ModelPath );
|
||||
attachmentRenderer.Enabled = true;
|
||||
|
||||
|
||||
attachmentRenderer.WorldScale = WorldModelScale;
|
||||
WorldModelRenderer = attachmentRenderer;
|
||||
ModelUtil.ParentToBone( attachmentGo, Weapon.WorldModelRenderer, Bone );
|
||||
}
|
||||
|
||||
private void CreateModels()
|
||||
{
|
||||
if ( !IsProxy )
|
||||
CreateModel( true );
|
||||
|
||||
CreateModel();
|
||||
}
|
||||
|
||||
/// <summary>Equips the attachment for everyone</summary>
|
||||
[Broadcast]
|
||||
public virtual void EquipBroadCast()
|
||||
{
|
||||
if ( !IsValid ) return;
|
||||
Equip();
|
||||
}
|
||||
|
||||
/// <summary>Equips the attachment</summary>
|
||||
public virtual void Equip()
|
||||
{
|
||||
// Log.Info( "Trying to equip -> " + Name + ", info -> equippedOnClient: " + equippedOnClient + " equipTries: " + equipTries );
|
||||
if ( equippedOnClient || !IsValid || Weapon is null ) return;
|
||||
if ( !IsProxy || Weapon.WorldModelRenderer is null )
|
||||
{
|
||||
if ( equipTries > 10 ) return;
|
||||
equipTries += 1;
|
||||
|
||||
async void retry()
|
||||
{
|
||||
await GameTask.Delay( 1 );
|
||||
Equip();
|
||||
}
|
||||
retry();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure there is no other active attachment in same category
|
||||
foreach ( var att in Weapon.Attachments )
|
||||
{
|
||||
if ( att.Category == Category && att.Equipped )
|
||||
{
|
||||
att.Unequip();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
equippedOnClient = true;
|
||||
|
||||
if ( !IsProxy )
|
||||
Equipped = true;
|
||||
|
||||
// Equip dependent attachment
|
||||
RequiresAttachment?.Equip();
|
||||
|
||||
// BodyGroup
|
||||
SetBodyGroup( BodyGroupChoice );
|
||||
|
||||
// Models
|
||||
CreateModels();
|
||||
|
||||
// Stats
|
||||
StatsModifier?.Apply( Weapon );
|
||||
|
||||
if ( !IsProxy )
|
||||
CreateHudElements();
|
||||
|
||||
OnEquip();
|
||||
}
|
||||
|
||||
/// <summary>Unequips the attachment for everyone</summary>
|
||||
[Broadcast]
|
||||
public virtual void UnEquipBroadCast()
|
||||
{
|
||||
if ( !IsValid ) return;
|
||||
Unequip();
|
||||
}
|
||||
|
||||
/// <summary>Unequips the attachment</summary>
|
||||
public virtual void Unequip()
|
||||
{
|
||||
if ( !equippedOnClient ) return;
|
||||
equippedOnClient = false;
|
||||
|
||||
if ( !IsProxy )
|
||||
Equipped = false;
|
||||
|
||||
// Unequip dependent attachment
|
||||
RequiresAttachment?.Unequip();
|
||||
|
||||
// BodyGroup
|
||||
SetBodyGroup( BodyGroupDefault );
|
||||
|
||||
// Model
|
||||
WorldModelRenderer?.GameObject.Destroy();
|
||||
|
||||
// Stats
|
||||
StatsModifier?.Remove( Weapon );
|
||||
|
||||
if ( !IsProxy )
|
||||
DestroyHudElements();
|
||||
|
||||
OnUnequip();
|
||||
}
|
||||
|
||||
/// <summary>Gets called after the attachment is equipped</summary>
|
||||
public abstract void OnEquip();
|
||||
|
||||
/// <summary>Gets called after the attachment is unequipped</summary>
|
||||
public abstract void OnUnequip();
|
||||
|
||||
/// <summary>Gets called when the weapon is creating its HUD elements</summary>
|
||||
public virtual void CreateHudElements() { }
|
||||
|
||||
/// <summary>Gets called when the weapon is destroying its HUD elements</summary>
|
||||
public virtual void DestroyHudElements() { }
|
||||
|
||||
public int CompareTo( Attachment obj )
|
||||
{
|
||||
if ( obj == null )
|
||||
return 1;
|
||||
|
||||
else
|
||||
return Name.CompareTo( obj.Name );
|
||||
}
|
||||
}
|
119
Code/swb_base/attachments/types/LaserAttachment.cs
Normal file
119
Code/swb_base/attachments/types/LaserAttachment.cs
Normal file
@ -0,0 +1,119 @@
|
||||
using SWB.Shared;
|
||||
|
||||
namespace SWB.Base.Attachments;
|
||||
|
||||
public abstract class LaserAttachment : Attachment
|
||||
{
|
||||
public override string Name => "Laser";
|
||||
public override AttachmentCategory Category => AttachmentCategory.Laser;
|
||||
public override string Description => "Aids target acquisition by projecting a beam onto the target that provides a visual reference point.";
|
||||
|
||||
// Not easily possible atm, attachments and bones lag behind.
|
||||
// Waiting for: https://github.com/Facepunch/sbox-issues/issues/5200
|
||||
public override bool Hide => true;
|
||||
|
||||
public override string[] Positives => new string[]
|
||||
{
|
||||
"Increases accuracy by 5%"
|
||||
};
|
||||
|
||||
public override string[] Negatives => new string[]
|
||||
{
|
||||
"Visibible to enemies"
|
||||
};
|
||||
|
||||
public override StatsModifier StatsModifier { get; set; } = new()
|
||||
{
|
||||
Spread = -0.05f,
|
||||
};
|
||||
|
||||
/// <summary>New muzzle flash effect point</summary>
|
||||
[Property, Group( "Laser" )] public override string EffectAttachmentOrBone { get; set; } = "laser_start";
|
||||
|
||||
/// <summary>Laser beam particle</summary>
|
||||
[Property, Group( "Laser" )] public virtual ParticleSystem BeamParticle { get; set; } = ParticleSystem.Load( "particles/swb/laser/laser_small.vpcf" );
|
||||
|
||||
/// <summary>Laser dot particle</summary>
|
||||
[Property, Group( "Laser" )] public virtual ParticleSystem DotParticle { get; set; } = ParticleSystem.Load( "particles/swb/laser/laser_dot.vpcf" );
|
||||
|
||||
[Property, Group( "Laser" )] public virtual Color Color { get; set; } = Color.Red;
|
||||
|
||||
SceneParticles beamParticles;
|
||||
SceneParticles dotParticles;
|
||||
|
||||
public override void OnEquip()
|
||||
{
|
||||
CreateParticles();
|
||||
}
|
||||
|
||||
public override void OnUnequip()
|
||||
{
|
||||
DestroyParticles();
|
||||
}
|
||||
|
||||
private void CreateParticles()
|
||||
{
|
||||
DestroyParticles();
|
||||
|
||||
beamParticles = new( Weapon.Scene.SceneWorld, BeamParticle );
|
||||
beamParticles.Tags.Add( TagsHelper.ViewModel );
|
||||
//beamParticles.
|
||||
//beamParticles?.SetControlPoint( 1, muzzleTransform.Value );
|
||||
//beamParticles?.SetControlPoint( 2, endPos );
|
||||
beamParticles?.SetNamedValue( "color", Color );
|
||||
// beamParticles?.SetControlPoint( 3, Color );
|
||||
beamParticles?.PlayUntilFinished( TaskSource.Create(), ( particles ) =>
|
||||
{
|
||||
//var startAttachment = Weapon.ViewModelRenderer.SceneModel.GetAttachment( EffectAttachmentOrBone );
|
||||
//var startPos = startAttachment.Value.Position;
|
||||
//var endPos = startAttachment.Value.Position + startAttachment.Value.Rotation.Forward * 9999;
|
||||
|
||||
//var tr = Scene.Trace.Ray( startPos, endPos )
|
||||
//.UseHitboxes()
|
||||
//.WithoutTags( "trigger" )
|
||||
//.Size( 1.0f )
|
||||
//.IgnoreGameObjectHierarchy( Weapon.Owner.GameObject )
|
||||
//.Run();
|
||||
|
||||
//var testStartPos = Weapon.GetMuzzleTransform();
|
||||
|
||||
////particles?.SetControlPoint( 0, startPos );
|
||||
//particles?.SetControlPoint( 0, startPos );
|
||||
//particles?.SetControlPoint( 1, tr.EndPosition );
|
||||
} );
|
||||
|
||||
//laserParticle = Particles.Create( Particle );
|
||||
//laserParticle?.SetPosition( 3, Color );
|
||||
|
||||
//laserDotParticle = Particles.Create( DotParticle );
|
||||
//laserDotParticle?.SetPosition( 1, Color );
|
||||
}
|
||||
|
||||
private void DestroyParticles()
|
||||
{
|
||||
beamParticles?.Delete();
|
||||
beamParticles = null;
|
||||
|
||||
dotParticles?.Delete();
|
||||
dotParticles = null;
|
||||
}
|
||||
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
if ( !Equipped ) return;
|
||||
|
||||
var startAttachment = Weapon.WorldModelRenderer.SceneModel.GetAttachment( EffectAttachmentOrBone );
|
||||
var startPos = startAttachment.Value.Position;
|
||||
var endPos = startAttachment.Value.Position + startAttachment.Value.Rotation.Forward * 9999;
|
||||
|
||||
var tr = Scene.Trace.Ray( startPos, endPos )
|
||||
.UseHitboxes()
|
||||
.WithoutTags( TagsHelper.Trigger )
|
||||
.Size( 1.0f )
|
||||
.IgnoreGameObjectHierarchy( Weapon.Owner.GameObject )
|
||||
.Run();
|
||||
|
||||
beamParticles?.SetControlPoint( 0, startPos );
|
||||
beamParticles?.SetControlPoint( 1, tr.EndPosition );
|
||||
}
|
||||
}
|
17
Code/swb_base/attachments/types/RailAttachment.cs
Normal file
17
Code/swb_base/attachments/types/RailAttachment.cs
Normal file
@ -0,0 +1,17 @@
|
||||
namespace SWB.Base.Attachments;
|
||||
|
||||
public abstract class RailAttachment : Attachment
|
||||
{
|
||||
public override string Name => "Rail";
|
||||
public override AttachmentCategory Category => AttachmentCategory.Rail;
|
||||
public override string Description => "Used by other attachments to be able to attach to the weapon.";
|
||||
public override bool Hide { get; set; } = true;
|
||||
|
||||
public override void OnEquip()
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnUnequip()
|
||||
{
|
||||
}
|
||||
}
|
57
Code/swb_base/attachments/types/Scope2DAttachment.cs
Normal file
57
Code/swb_base/attachments/types/Scope2DAttachment.cs
Normal file
@ -0,0 +1,57 @@
|
||||
using SWB.Base.UI;
|
||||
|
||||
namespace SWB.Base.Attachments;
|
||||
|
||||
public abstract class Scope2DAttachment : Attachment
|
||||
{
|
||||
public override string Name => "2D Scope";
|
||||
public override AttachmentCategory Category => AttachmentCategory.Sight;
|
||||
public override string Description => "A high magnification scope that is utilized for firing at long ranges.";
|
||||
public override string[] Positives => new string[]
|
||||
{
|
||||
"x12 magnification",
|
||||
"100% accurate while scoped in"
|
||||
};
|
||||
|
||||
public override string[] Negatives => new string[]
|
||||
{
|
||||
};
|
||||
|
||||
/// <summary>The new aim position offset</summary>
|
||||
[Property, Group( "Scope" )] public AngPos AimAnimData { get; set; }
|
||||
AngPos oldAimAnimData;
|
||||
|
||||
/// <summary>Scope Information</summary>
|
||||
[Property, Group( "Scope" )] public virtual ScopeInfo ScopeInfo { get; set; } = new();
|
||||
ScopeInfo oldScopeInfo = new();
|
||||
|
||||
SniperScope sniperScope;
|
||||
|
||||
public override void OnEquip()
|
||||
{
|
||||
oldAimAnimData = Weapon.AimAnimData;
|
||||
oldScopeInfo = Weapon.ScopeInfo;
|
||||
|
||||
Weapon.Scoping = true;
|
||||
Weapon.AimAnimData = AimAnimData;
|
||||
Weapon.ScopeInfo = ScopeInfo;
|
||||
}
|
||||
|
||||
public override void OnUnequip()
|
||||
{
|
||||
Weapon.Scoping = false;
|
||||
Weapon.AimAnimData = oldAimAnimData;
|
||||
Weapon.ScopeInfo = oldScopeInfo;
|
||||
}
|
||||
|
||||
public override void CreateHudElements()
|
||||
{
|
||||
sniperScope = new SniperScope( Weapon, Weapon.ScopeInfo.LensTexture, Weapon.ScopeInfo.ScopeTexture );
|
||||
Weapon.RootPanel.Panel.AddChild( sniperScope );
|
||||
}
|
||||
|
||||
public override void DestroyHudElements()
|
||||
{
|
||||
sniperScope?.Delete();
|
||||
}
|
||||
}
|
75
Code/swb_base/attachments/types/SightAttachment.cs
Normal file
75
Code/swb_base/attachments/types/SightAttachment.cs
Normal file
@ -0,0 +1,75 @@
|
||||
namespace SWB.Base.Attachments;
|
||||
|
||||
public abstract class SightAttachment : Attachment
|
||||
{
|
||||
public override string Name => "Sight";
|
||||
public override AttachmentCategory Category => AttachmentCategory.Sight;
|
||||
public override string Description => "An optical sight that allows the user to look through a partially reflecting glass element and see an illuminated projection of an aiming point or some other image superimposed on the field of view.";
|
||||
public override string[] Positives => new string[]
|
||||
{
|
||||
"Precision sight picture",
|
||||
"Increases accuracy by 5%"
|
||||
};
|
||||
|
||||
public override string[] Negatives => new string[]
|
||||
{
|
||||
};
|
||||
|
||||
public override StatsModifier StatsModifier { get; set; } = new()
|
||||
{
|
||||
Spread = -0.05f,
|
||||
};
|
||||
|
||||
|
||||
/// <summary>The new aim position offset</summary>
|
||||
[Property, Group( "Sight" )] public AngPos AimAnimData { get; set; }
|
||||
AngPos oldAimAnimData;
|
||||
|
||||
/// <summary>Weapon FOV while aiming (-1 to use default)</summary>
|
||||
[Property, Group( "Sight" )] public virtual float AimFOV { get; set; } = -1f;
|
||||
float oldAimFOV;
|
||||
|
||||
/// <summary>Player FOV while aiming (-1 to use default)</summary>
|
||||
[Property, Group( "Sight" )] public virtual float AimPlayerFOV { get; set; } = -1f;
|
||||
float oldAimPlayerFOV;
|
||||
|
||||
/// <summary>FOV aim in speed (-1 to use default)</summary>
|
||||
[Property, Group( "Sight" ), Title( "Aim in FOV speed" )] public virtual float AimInFOVSpeed { get; set; } = -1f;
|
||||
float oldAimInFOVSpeed;
|
||||
|
||||
/// <summary>FOV aim out speed (-1 to use default)</summary>
|
||||
[Property, Group( "Sight" ), Title( "Aim out FOV speed" )] public virtual float AimOutFOVSpeed { get; set; } = -1f;
|
||||
private float oldAimOutFOVSpeed;
|
||||
|
||||
/// <summary>Mouse sensitivity while aiming (-1 to use default)</summary>
|
||||
[Property, Group( "Sight" )] public virtual float AimSensitivity { get; set; } = -1;
|
||||
float oldAimSensitivity;
|
||||
|
||||
public override void OnEquip()
|
||||
{
|
||||
oldAimAnimData = Weapon.AimAnimData;
|
||||
oldAimFOV = Weapon.AimFOV;
|
||||
oldAimPlayerFOV = Weapon.AimPlayerFOV;
|
||||
oldAimInFOVSpeed = Weapon.AimInFOVSpeed;
|
||||
oldAimOutFOVSpeed = Weapon.AimOutFOVSpeed;
|
||||
oldAimSensitivity = Weapon.AimSensitivity;
|
||||
|
||||
Weapon.AimAnimData = AimAnimData;
|
||||
|
||||
if ( AimFOV > -1 ) Weapon.AimFOV = AimFOV;
|
||||
if ( AimPlayerFOV > -1 ) Weapon.AimPlayerFOV = AimPlayerFOV;
|
||||
if ( AimInFOVSpeed > -1 ) Weapon.AimInFOVSpeed = AimInFOVSpeed;
|
||||
if ( AimOutFOVSpeed > -1 ) Weapon.AimOutFOVSpeed = AimOutFOVSpeed;
|
||||
if ( AimSensitivity > -1 ) Weapon.AimSensitivity = AimSensitivity;
|
||||
}
|
||||
|
||||
public override void OnUnequip()
|
||||
{
|
||||
Weapon.AimAnimData = oldAimAnimData;
|
||||
Weapon.AimFOV = oldAimFOV;
|
||||
Weapon.AimPlayerFOV = oldAimPlayerFOV;
|
||||
Weapon.AimInFOVSpeed = oldAimInFOVSpeed;
|
||||
Weapon.AimOutFOVSpeed = oldAimOutFOVSpeed;
|
||||
Weapon.AimSensitivity = oldAimSensitivity;
|
||||
}
|
||||
}
|
52
Code/swb_base/attachments/types/SilencerAttachment.cs
Normal file
52
Code/swb_base/attachments/types/SilencerAttachment.cs
Normal file
@ -0,0 +1,52 @@
|
||||
namespace SWB.Base.Attachments;
|
||||
|
||||
public abstract class SilencerAttachment : Attachment
|
||||
{
|
||||
public override string Name => "Silencer";
|
||||
public override AttachmentCategory Category => AttachmentCategory.Muzzle;
|
||||
public override string Description => "Reduces the acoustic intensity of the muzzle report and the recoil when a gun is discharged by modulating the speed and pressure of the propellant gas from the muzzle.";
|
||||
public override string[] Positives => new string[]
|
||||
{
|
||||
"Reduce sound",
|
||||
"Reduce muzzle flash",
|
||||
"Increases accuracy by 5%"
|
||||
};
|
||||
|
||||
public override string[] Negatives => new string[]
|
||||
{
|
||||
};
|
||||
|
||||
public override StatsModifier StatsModifier { get; set; } = new()
|
||||
{
|
||||
Spread = -0.05f,
|
||||
};
|
||||
|
||||
/// <summary>New muzzle flash effect point</summary>
|
||||
[Property, Group( "Silencer" )] public override string EffectAttachmentOrBone { get; set; } = "muzzle_silenced";
|
||||
|
||||
/// <summary>New particle used for the muzzle flash</summary>
|
||||
[Property, Group( "Silencer" )] public virtual ParticleSystem MuzzleFlashParticle { get; set; }
|
||||
ParticleSystem oldMuzzleFlashParticle;
|
||||
|
||||
/// <summary>New sound used for firing</summary>
|
||||
[Property, Group( "Silencer" )] public virtual SoundEvent ShootSound { get; set; }
|
||||
SoundEvent oldShootSound;
|
||||
|
||||
public override void OnEquip()
|
||||
{
|
||||
oldMuzzleFlashParticle = Weapon.Primary.MuzzleFlashParticle;
|
||||
oldShootSound = Weapon.Primary.ShootSound;
|
||||
|
||||
if ( MuzzleFlashParticle is not null )
|
||||
Weapon.Primary.MuzzleFlashParticle = MuzzleFlashParticle;
|
||||
|
||||
if ( ShootSound is not null )
|
||||
Weapon.Primary.ShootSound = ShootSound;
|
||||
}
|
||||
|
||||
public override void OnUnequip()
|
||||
{
|
||||
Weapon.Primary.MuzzleFlashParticle = oldMuzzleFlashParticle;
|
||||
Weapon.Primary.ShootSound = oldShootSound;
|
||||
}
|
||||
}
|
36
Code/swb_base/extensions/GameObjectExtensions.cs
Normal file
36
Code/swb_base/extensions/GameObjectExtensions.cs
Normal file
@ -0,0 +1,36 @@
|
||||
namespace SWB.Base;
|
||||
|
||||
public sealed class TimedDestroyComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// How long until we destroy the GameObject.
|
||||
/// </summary>
|
||||
[Property] public float Time { get; set; } = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// The real time until we destroy the GameObject.
|
||||
/// </summary>
|
||||
[Property, ReadOnly] TimeUntil TimeUntilDestroy { get; set; } = 0;
|
||||
|
||||
protected override void OnStart()
|
||||
{
|
||||
TimeUntilDestroy = Time;
|
||||
}
|
||||
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
if ( TimeUntilDestroy )
|
||||
{
|
||||
GameObject.Destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class GameObjectExtensions
|
||||
{
|
||||
public static void DestroyAsync( this GameObject self, float seconds = 1.0f )
|
||||
{
|
||||
var component = self.Components.Create<TimedDestroyComponent>();
|
||||
component.Time = seconds;
|
||||
}
|
||||
}
|
29
Code/swb_base/extensions/ParticleExtensions.cs
Normal file
29
Code/swb_base/extensions/ParticleExtensions.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SWB.Base;
|
||||
|
||||
public static class ParticleExtensions
|
||||
{
|
||||
public static async void PlayUntilFinished( this SceneParticles particles, TaskSource source, Action<SceneParticles> OnFrame = null )
|
||||
{
|
||||
try
|
||||
{
|
||||
while ( particles.IsValid() && !particles.Finished )
|
||||
{
|
||||
await source.Frame();
|
||||
|
||||
if ( OnFrame is not null )
|
||||
OnFrame( particles );
|
||||
|
||||
particles?.Simulate( Time.Delta );
|
||||
}
|
||||
}
|
||||
catch ( TaskCanceledException )
|
||||
{
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
particles.Delete();
|
||||
}
|
||||
}
|
13
Code/swb_base/extensions/RandomExtensions.cs
Normal file
13
Code/swb_base/extensions/RandomExtensions.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using System;
|
||||
|
||||
namespace SWB.Base;
|
||||
public static class RandomExtensions
|
||||
{
|
||||
public static float NextFloat(
|
||||
this Random random,
|
||||
float minValue,
|
||||
float maxValue )
|
||||
{
|
||||
return random.Float() * (maxValue - minValue) + minValue;
|
||||
}
|
||||
}
|
62
Code/swb_base/structures/AngPos.cs
Normal file
62
Code/swb_base/structures/AngPos.cs
Normal file
@ -0,0 +1,62 @@
|
||||
namespace SWB.Base;
|
||||
|
||||
public struct AngPos
|
||||
{
|
||||
[KeyProperty] public Angles Angle { get; set; } = Angles.Zero;
|
||||
[KeyProperty] public Vector3 Pos { get; set; } = Vector3.Zero;
|
||||
|
||||
public static readonly AngPos Zero = new();
|
||||
|
||||
public AngPos()
|
||||
{
|
||||
}
|
||||
|
||||
public AngPos( Angles angle, Vector3 pos )
|
||||
{
|
||||
Angle = angle;
|
||||
Pos = pos;
|
||||
}
|
||||
|
||||
public bool Equals( AngPos angPos )
|
||||
{
|
||||
return Angle == angPos.Angle && Pos == angPos.Pos;
|
||||
}
|
||||
|
||||
public static AngPos operator +( AngPos x, AngPos y )
|
||||
{
|
||||
return new AngPos( x.Angle + y.Angle, x.Pos + y.Pos );
|
||||
}
|
||||
|
||||
public static AngPos operator -( AngPos x, AngPos y )
|
||||
{
|
||||
return new AngPos( x.Angle - y.Angle, x.Pos - y.Pos );
|
||||
}
|
||||
|
||||
public static AngPos operator -( AngPos x )
|
||||
{
|
||||
return new AngPos( x.Angle * -1, -x.Pos );
|
||||
}
|
||||
|
||||
public static bool operator ==( AngPos x, AngPos y )
|
||||
{
|
||||
return x.Equals( y );
|
||||
}
|
||||
|
||||
public static bool operator !=( AngPos x, AngPos y )
|
||||
{
|
||||
return !x.Equals( y );
|
||||
}
|
||||
|
||||
public override bool Equals( object obj )
|
||||
{
|
||||
if ( obj is AngPos angPos )
|
||||
return Equals( angPos );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Angle.GetHashCode() + Pos.GetHashCode();
|
||||
}
|
||||
}
|
25
Code/swb_base/structures/ScopeInfo.cs
Normal file
25
Code/swb_base/structures/ScopeInfo.cs
Normal file
@ -0,0 +1,25 @@
|
||||
namespace SWB.Base;
|
||||
|
||||
public class ScopeInfo
|
||||
{
|
||||
/// <summary>2D lens texture</summary>
|
||||
[Property, ImageAssetPathAttribute] public string LensTexture { get; set; } = "/materials/swb/scopes/swb_lens_hunter.png";
|
||||
|
||||
/// <summary>2D scope texture</summary>
|
||||
[Property, ImageAssetPathAttribute] public string ScopeTexture { get; set; } = "/materials/swb/scopes/swb_scope_hunter.png";
|
||||
|
||||
/// <summary>Delay between ADS and scoping in ms</summary>
|
||||
[Property] public float ScopeInDelay { get; set; } = 0.2f;
|
||||
|
||||
/// <summary>Sound that plays when scoping starts</summary>
|
||||
[Property] public SoundEvent ScopeInSound { get; set; }
|
||||
|
||||
/// <summary>Sound that plays when scoping ends</summary>
|
||||
[Property] public SoundEvent ScopeOutSound { get; set; }
|
||||
|
||||
/// <summary>Player FOV while scoping</summary>
|
||||
[Property] public float FOV { get; set; } = 8f;
|
||||
|
||||
/// <summary>Mouse sensitivity while scoping (lower is slower, 0 to use AimSensitivity while scoped)</summary>
|
||||
[Property] public float AimSensitivity { get; set; } = 0.25f;
|
||||
}
|
117
Code/swb_base/structures/ShootInfo.cs
Normal file
117
Code/swb_base/structures/ShootInfo.cs
Normal file
@ -0,0 +1,117 @@
|
||||
using SWB.Shared;
|
||||
|
||||
namespace SWB.Base;
|
||||
|
||||
public enum FiringType
|
||||
{
|
||||
/// <summary>Single fire</summary>
|
||||
semi,
|
||||
/// <summary>Automatic fire</summary>
|
||||
auto,
|
||||
/// <summary>3-Burst fire</summary>
|
||||
burst
|
||||
}
|
||||
|
||||
public enum InfiniteAmmoType
|
||||
{
|
||||
/// <summary>No infinite ammo</summary>
|
||||
disabled = 0,
|
||||
/// <summary>Infinite clip ammo, no need to reload</summary>
|
||||
clip = 1,
|
||||
/// <summary>Infinite reserve ammo, can always reload</summary>
|
||||
reserve = 2
|
||||
}
|
||||
|
||||
[Group( "SWB" )]
|
||||
[Title( "ShootInfo" )]
|
||||
public class ShootInfo : Component
|
||||
{
|
||||
/// <summary>Type of ammo</summary>
|
||||
[Property, Group( "Ammo" )] public string AmmoType { get; set; } = "pistol";
|
||||
|
||||
/// <summary>Amount of ammo in the clip</summary>
|
||||
[Property, Group( "Ammo" ), Sync] public int Ammo { get; set; } = 10;
|
||||
|
||||
/// <summary>Size of the clip</summary>
|
||||
[Property, Group( "Ammo" )] public int ClipSize { get; set; } = 10;
|
||||
|
||||
/// <summary>If the weapon should have infinite ammo</summary>
|
||||
[Property, Group( "Ammo" )] public InfiniteAmmoType InfiniteAmmo { get; set; } = InfiniteAmmoType.disabled;
|
||||
|
||||
// Shooting //
|
||||
|
||||
/// <summary>Amount of bullets per shot</summary>
|
||||
[Property, Group( "Bullets" )] public int Bullets { get; set; } = 1;
|
||||
|
||||
/// <summary>Bullet size</summary>
|
||||
[Property, Group( "Bullets" )] public float BulletSize { get; set; } = 0.1f;
|
||||
|
||||
/// <summary>Bullet type (Hitscan/Physical)</summary>
|
||||
public IBulletBase BulletType { get; set; } = new HitScanBullet();
|
||||
|
||||
/// <summary>Chance the BulletTracerParticle is created (0-1)</summary>
|
||||
[Property, Group( "Bullets" )] public float BulletTracerChance { get; set; } = 0.33f;
|
||||
|
||||
/// <summary>Damage per bullet</summary>
|
||||
[Property, Group( "Bullets" )] public float Damage { get; set; } = 5;
|
||||
|
||||
/// <summary>Bullet impact force</summary>
|
||||
[Property, Group( "Bullets" )] public float Force { get; set; } = 0.1f;
|
||||
|
||||
/// <summary>Bullet hit flinch</summary>
|
||||
[Property, Group( "Bullets" )] public float HitFlinch { get; set; } = 1.25f;
|
||||
|
||||
/// <summary>Weapon spread</summary>
|
||||
[Property, Group( "Bullets" )] public float Spread { get; set; } = 0.1f;
|
||||
|
||||
/// <summary>Weapon recoil</summary>
|
||||
[Property, Group( "Bullets" )] public float Recoil { get; set; } = 0.1f;
|
||||
|
||||
/// <summary>Rate Per Minute, firing speed (higher is faster)</summary>
|
||||
[Property, Group( "Bullets" )] public int RPM { get; set; } = 200;
|
||||
|
||||
/// <summary>Screenshake per shot</summary>
|
||||
[Property, Group( "Bullets" )] public ScreenShake ScreenShake { get; set; }
|
||||
|
||||
/// <summary>Weapon firing type</summary>
|
||||
[Property, Group( "Bullets" )] public FiringType FiringType { get; set; } = FiringType.semi;
|
||||
|
||||
// Animations //
|
||||
|
||||
/// <summary>Animation used for shooting</summary>
|
||||
[Property, Group( "Animations" )] public string ShootAnim { get; set; } = "fire";
|
||||
|
||||
/// <summary>Animation used for shooting the last bullet</summary>
|
||||
[Property, Group( "Animations" )] public string ShootEmptyAnim { get; set; } = "";
|
||||
|
||||
/// <summary>Animation used for shooting while aiming</summary>
|
||||
[Property, Group( "Animations" )] public string ShootAimedAnim { get; set; }
|
||||
|
||||
// Sounds //
|
||||
|
||||
/// <summary>Firing sound when clip is empty</summary>
|
||||
[Property, Group( "Sounds" )] public SoundEvent DryShootSound { get; set; }
|
||||
|
||||
/// <summary>Firing sound</summary>
|
||||
[Property, Group( "Sounds" )] public SoundEvent ShootSound { get; set; }
|
||||
|
||||
// Particles //
|
||||
|
||||
/// <summary> View Model particle scale</summary>
|
||||
[Property, Title( "View Model Scale" ), Group( "Particles" )] public float VMParticleScale { get; set; } = 1f;
|
||||
|
||||
/// <summary> World Model particle scale</summary>
|
||||
[Property, Title( "World Model Scale" ), Group( "Particles" )] public float WMParticleScale { get; set; } = 1f;
|
||||
|
||||
/// <summary>Particle used for bullet ejection</summary>
|
||||
[Property, Group( "Particles" )] public ParticleSystem BulletEjectParticle { get; set; }
|
||||
|
||||
/// <summary>Particle used for the muzzle flash</summary>
|
||||
[Property, Group( "Particles" )] public ParticleSystem MuzzleFlashParticle { get; set; }
|
||||
|
||||
/// <summary>Particle used for the barrel smoke</summary>
|
||||
[Property, Group( "Particles" )] public ParticleSystem BarrelSmokeParticle { get; set; }
|
||||
|
||||
/// <summary>Particle used for the barrel smoke</summary>
|
||||
[Property, Group( "Particles" )] public ParticleSystem BulletTracerParticle { get; set; }
|
||||
}
|
76
Code/swb_base/structures/StatsModifier.cs
Normal file
76
Code/swb_base/structures/StatsModifier.cs
Normal file
@ -0,0 +1,76 @@
|
||||
namespace SWB.Base;
|
||||
|
||||
public class StatsModifier
|
||||
{
|
||||
public float Damage { get; set; }
|
||||
public float Recoil { get; set; }
|
||||
public float Spread { get; set; }
|
||||
public float RPM { get; set; }
|
||||
public float Force { get; set; }
|
||||
|
||||
// After phys bullets have been recreated
|
||||
//public float BulletVelocity { get; set; } = 0;
|
||||
|
||||
public static readonly StatsModifier Zero = new();
|
||||
|
||||
private bool applied;
|
||||
|
||||
public static StatsModifier FromShootInfo( ShootInfo shootInfo )
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Damage = shootInfo.Damage,
|
||||
Recoil = shootInfo.Recoil,
|
||||
Spread = shootInfo.Spread,
|
||||
RPM = shootInfo.RPM,
|
||||
Force = shootInfo.Force,
|
||||
};
|
||||
}
|
||||
|
||||
public void Apply( Weapon weapon, bool onPrimary = true )
|
||||
{
|
||||
if ( applied ) return;
|
||||
|
||||
if ( onPrimary )
|
||||
Apply( weapon.Primary, weapon.InitialPrimaryStats );
|
||||
else
|
||||
Apply( weapon.Secondary, weapon.InitialSecondaryStats );
|
||||
|
||||
applied = true;
|
||||
}
|
||||
|
||||
private void Apply( ShootInfo shootInfo, StatsModifier initialStats )
|
||||
{
|
||||
if ( shootInfo is null || initialStats is null ) return;
|
||||
|
||||
shootInfo.Damage += initialStats.Damage * Damage;
|
||||
shootInfo.Recoil += initialStats.Recoil * Recoil;
|
||||
shootInfo.Spread += initialStats.Spread * Spread;
|
||||
shootInfo.RPM += (int)(initialStats.RPM * RPM);
|
||||
|
||||
//weapon.BulletVelocityMod += BulletVelocity;
|
||||
}
|
||||
|
||||
public void Remove( Weapon weapon, bool onPrimary = true )
|
||||
{
|
||||
if ( !applied ) return;
|
||||
|
||||
if ( onPrimary )
|
||||
Remove( weapon.Primary, weapon.InitialPrimaryStats );
|
||||
else
|
||||
Remove( weapon.Secondary, weapon.InitialSecondaryStats );
|
||||
|
||||
//weapon.BulletVelocityMod -= BulletVelocity;
|
||||
applied = false;
|
||||
}
|
||||
|
||||
private void Remove( ShootInfo shootInfo, StatsModifier initialStats )
|
||||
{
|
||||
if ( shootInfo is null || initialStats is null ) return;
|
||||
|
||||
shootInfo.Damage -= initialStats.Damage * Damage;
|
||||
shootInfo.Recoil -= initialStats.Recoil * Recoil;
|
||||
shootInfo.Spread -= initialStats.Spread * Spread;
|
||||
shootInfo.RPM -= (int)(initialStats.RPM * RPM);
|
||||
}
|
||||
}
|
146
Code/swb_base/ui/Crosshair.cs
Normal file
146
Code/swb_base/ui/Crosshair.cs
Normal file
@ -0,0 +1,146 @@
|
||||
using Sandbox.UI;
|
||||
using SWB.Shared;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SWB.Base.UI;
|
||||
|
||||
public class Crosshair : Panel
|
||||
{
|
||||
IPlayerBase player => weapon.Owner;
|
||||
Weapon weapon;
|
||||
|
||||
Panel centerDot;
|
||||
Panel leftBar;
|
||||
Panel rightBar;
|
||||
Panel topBar;
|
||||
Panel bottomBar;
|
||||
|
||||
int spreadOffset = 400;
|
||||
int sprintOffset = 100;
|
||||
int fireOffset = 50;
|
||||
|
||||
bool wasAiming = false;
|
||||
|
||||
public Crosshair( Weapon weapon )
|
||||
{
|
||||
this.weapon = weapon;
|
||||
StyleSheet.Load( "/swb_base/ui/Crosshair.cs.scss" );
|
||||
|
||||
centerDot = Add.Panel( "centerDot" );
|
||||
leftBar = Add.Panel( "leftBar" );
|
||||
rightBar = Add.Panel( "rightBar" );
|
||||
topBar = Add.Panel( "topBar" );
|
||||
bottomBar = Add.Panel( "bottomBar" );
|
||||
|
||||
leftBar.AddClass( "sharedBarStyling" );
|
||||
rightBar.AddClass( "sharedBarStyling" );
|
||||
topBar.AddClass( "sharedBarStyling" );
|
||||
bottomBar.AddClass( "sharedBarStyling" );
|
||||
}
|
||||
|
||||
private void UpdateCrosshair()
|
||||
{
|
||||
centerDot.Style.Dirty();
|
||||
leftBar.Style.Dirty();
|
||||
rightBar.Style.Dirty();
|
||||
topBar.Style.Dirty();
|
||||
bottomBar.Style.Dirty();
|
||||
}
|
||||
|
||||
private void RestoreBarPositions()
|
||||
{
|
||||
leftBar.Style.Left = -16;
|
||||
rightBar.Style.Left = 5;
|
||||
topBar.Style.Top = -16;
|
||||
bottomBar.Style.Top = 5;
|
||||
}
|
||||
|
||||
private void RestoreCrosshairOpacity()
|
||||
{
|
||||
centerDot.Style.Opacity = 1;
|
||||
leftBar.Style.Opacity = 1;
|
||||
rightBar.Style.Opacity = 1;
|
||||
topBar.Style.Opacity = 1;
|
||||
bottomBar.Style.Opacity = 1;
|
||||
}
|
||||
|
||||
private void HideBarLines()
|
||||
{
|
||||
leftBar.Style.Opacity = 0;
|
||||
rightBar.Style.Opacity = 0;
|
||||
topBar.Style.Opacity = 0;
|
||||
bottomBar.Style.Opacity = 0;
|
||||
}
|
||||
|
||||
public override void Tick()
|
||||
{
|
||||
bool isValidWeapon = weapon is not null;
|
||||
var shouldHide = !isValidWeapon || weapon.IsScoping || weapon.IsCustomizing;
|
||||
|
||||
SetClass( "hideCrosshair", shouldHide );
|
||||
|
||||
//var hideCrosshairDot = shouldHide /*|| !weapon.UISettings.ShowCrosshairDot*/;
|
||||
//centerDot.SetClass( "hideCrosshair", hideCrosshairDot );
|
||||
|
||||
//var hideCrosshairLines = shouldHide /*|| !weapon.UISettings.ShowCrosshairLines*/;
|
||||
//leftBar.SetClass( "hideCrosshair", hideCrosshairLines );
|
||||
//rightBar.SetClass( "hideCrosshair", hideCrosshairLines );
|
||||
//topBar.SetClass( "hideCrosshair", hideCrosshairLines );
|
||||
//bottomBar.SetClass( "hideCrosshair", hideCrosshairLines );
|
||||
|
||||
if ( shouldHide ) return;
|
||||
|
||||
// Crosshair spread offset
|
||||
var screenOffset = spreadOffset * weapon.GetRealSpread();
|
||||
leftBar.Style.MarginLeft = -screenOffset;
|
||||
rightBar.Style.MarginLeft = screenOffset;
|
||||
topBar.Style.MarginTop = -screenOffset;
|
||||
bottomBar.Style.MarginTop = screenOffset;
|
||||
|
||||
// Sprint spread offsets
|
||||
if (weapon.IsReloading || weapon.IsDeploying || (weapon.InBoltBack && !weapon.IsAiming) )
|
||||
{
|
||||
leftBar.Style.Left = -sprintOffset;
|
||||
rightBar.Style.Left = sprintOffset - 5;
|
||||
topBar.Style.Top = -sprintOffset;
|
||||
bottomBar.Style.Top = sprintOffset - 5;
|
||||
|
||||
HideBarLines();
|
||||
}
|
||||
else if ( weapon.IsAiming )
|
||||
{
|
||||
wasAiming = true;
|
||||
|
||||
|
||||
// centerDot.Style.Opacity = 0;
|
||||
// HideBarLines();
|
||||
}
|
||||
else if ( leftBar.Style.Left == -sprintOffset || wasAiming )
|
||||
{
|
||||
wasAiming = false;
|
||||
RestoreBarPositions();
|
||||
RestoreCrosshairOpacity();
|
||||
}
|
||||
|
||||
UpdateCrosshair();
|
||||
}
|
||||
|
||||
[PanelEvent( "shoot" )]
|
||||
public void ShootEvent( float fireDelay )
|
||||
{
|
||||
// Fire spread offsets
|
||||
leftBar.Style.Left = -fireOffset;
|
||||
rightBar.Style.Left = fireOffset - 5;
|
||||
topBar.Style.Top = -fireOffset;
|
||||
bottomBar.Style.Top = fireOffset - 5;
|
||||
|
||||
_ = FireDelay( fireDelay / 2 );
|
||||
}
|
||||
|
||||
private async Task FireDelay( float delay )
|
||||
{
|
||||
await GameTask.DelaySeconds( delay );
|
||||
RestoreBarPositions();
|
||||
RestoreCrosshairOpacity();
|
||||
}
|
||||
}
|
51
Code/swb_base/ui/Crosshair.cs.scss
Normal file
51
Code/swb_base/ui/Crosshair.cs.scss
Normal file
@ -0,0 +1,51 @@
|
||||
Crosshair {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
z-index: -1;
|
||||
|
||||
.centerDot {
|
||||
background-color: white;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
width: 2px;
|
||||
height: 2px;
|
||||
box-shadow: 0px 0px 1px 1px rgba(0,0,0,0.4);
|
||||
transition: all 0.1s ease-out 0s;
|
||||
}
|
||||
|
||||
.leftBar {
|
||||
left: -16px;
|
||||
width: 12px;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
.rightBar {
|
||||
left: 5px;
|
||||
width: 12px;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
.topBar {
|
||||
top: -16px;
|
||||
width: 1px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.bottomBar {
|
||||
top: 5px;
|
||||
width: 1px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.sharedBarStyling {
|
||||
position: absolute;
|
||||
background-color: white;
|
||||
box-shadow: 0px 0px 1px 1px rgba(0,0,0,0.4);
|
||||
transition: all 0.1s ease-out 0s;
|
||||
}
|
||||
|
||||
&.hideCrosshair {
|
||||
display: none;
|
||||
}
|
||||
}
|
186
Code/swb_base/ui/CustomizationMenu.cs
Normal file
186
Code/swb_base/ui/CustomizationMenu.cs
Normal file
@ -0,0 +1,186 @@
|
||||
using Sandbox.UI;
|
||||
using Sandbox.UI.Construct;
|
||||
using SWB.Base.Attachments;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SWB.Base.UI;
|
||||
|
||||
public class CustomizationMenu : Panel
|
||||
{
|
||||
Weapon weapon;
|
||||
|
||||
Panel categoryWrapper;
|
||||
Panel attachmentWrapper;
|
||||
Panel descriptionWrapper;
|
||||
Panel activeCategoryP;
|
||||
Panel activeAttachmentP;
|
||||
|
||||
Attachment selectedAttachment;
|
||||
Attachment hoveredAttachment;
|
||||
|
||||
Dictionary<AttachmentCategory, List<Attachment>> attachmentsPerCategory = new();
|
||||
|
||||
public CustomizationMenu( Weapon weapon )
|
||||
{
|
||||
this.weapon = weapon;
|
||||
FillAttachmentsPerCategory();
|
||||
StyleSheet.Load( "/swb_base/ui/CustomizationMenu.cs.scss" );
|
||||
|
||||
categoryWrapper = Add.Panel( "categoryWrapper" );
|
||||
categoryWrapper.Add.Label( weapon.DisplayName, "weaponName" );
|
||||
attachmentWrapper = Add.Panel( "attachmentWrapper" );
|
||||
descriptionWrapper = Add.Panel( "descriptionWrapper" );
|
||||
|
||||
CreateCategoryPanels();
|
||||
}
|
||||
|
||||
void FillAttachmentsPerCategory()
|
||||
{
|
||||
weapon.Attachments.ForEach( attachment =>
|
||||
{
|
||||
if ( attachment.Hide ) return;
|
||||
if ( attachmentsPerCategory.TryGetValue( attachment.Category, out var attachments ) )
|
||||
attachments.Add( attachment );
|
||||
else
|
||||
attachmentsPerCategory.Add( attachment.Category, new List<Attachment>() { attachment } );
|
||||
} );
|
||||
}
|
||||
|
||||
void CreateCategoryPanels()
|
||||
{
|
||||
foreach ( var entry in attachmentsPerCategory )
|
||||
{
|
||||
var categoryP = categoryWrapper.Add.Panel( "category" );
|
||||
categoryP.Add.Label( entry.Key.ToString(), "name" );
|
||||
categoryP.Add.Label( "", "attName" );
|
||||
|
||||
var catActiveAttachP = categoryP.Add.Panel( "activeAttachment" );
|
||||
catActiveAttachP.Add.Label( "", "name" );
|
||||
|
||||
var iconWrapperP = catActiveAttachP.Add.Panel( "iconWrapper" );
|
||||
iconWrapperP.Add.Image( "", "icon" );
|
||||
|
||||
var activeCatAttachment = weapon.GetActiveAttachmentForCategory( entry.Key );
|
||||
if ( activeCatAttachment is not null )
|
||||
SetAttachmentOnCategoryPanel( categoryP, activeCatAttachment );
|
||||
|
||||
|
||||
categoryP.AddEventListener( "onmousedown", () =>
|
||||
{
|
||||
if ( activeCategoryP != categoryP )
|
||||
{
|
||||
PlaySound( "swb_click" );
|
||||
activeCategoryP?.SetClass( "active", false );
|
||||
categoryP.SetClass( "active", true );
|
||||
activeCategoryP = categoryP;
|
||||
selectedAttachment = null;
|
||||
descriptionWrapper.DeleteChildren();
|
||||
CreateAttachmentPanels( entry.Key );
|
||||
}
|
||||
} );
|
||||
|
||||
categoryP.AddEventListener( "onmouseover", () =>
|
||||
{
|
||||
PlaySound( "ui.button.over" );
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
void SetAttachmentOnCategoryPanel( Panel categoryP, Attachment attachment )
|
||||
{
|
||||
categoryP.SetClass( "hasAttachment", true );
|
||||
|
||||
var attachP = categoryP.GetChild( 2 );
|
||||
var nameL = (Label)attachP.GetChild( 0 );
|
||||
var iconWrapper = attachP.GetChild( 1 );
|
||||
var iconP = (Image)iconWrapper.GetChild( 0 );
|
||||
|
||||
nameL.Text = attachment.Name;
|
||||
iconP.SetTexture( attachment.IconPath );
|
||||
}
|
||||
|
||||
void CreateAttachmentPanels( AttachmentCategory category )
|
||||
{
|
||||
attachmentWrapper.DeleteChildren();
|
||||
activeAttachmentP = null;
|
||||
|
||||
if ( !attachmentsPerCategory.TryGetValue( category, out var attachments ) ) return;
|
||||
|
||||
var activeAttachment = weapon.GetActiveAttachmentForCategory( category );
|
||||
|
||||
attachments.ForEach( attachment =>
|
||||
{
|
||||
var attachmentP = attachmentWrapper.Add.Panel( "attachment" );
|
||||
attachmentP.Add.Label( attachment.Name, "name" );
|
||||
var iconWrapperP = attachmentP.Add.Panel( "iconWrapper" );
|
||||
iconWrapperP.Add.Image( attachment.IconPath, "icon" );
|
||||
|
||||
var isActiveAttachment = attachment == activeAttachment;
|
||||
attachmentP.SetClass( "active", isActiveAttachment );
|
||||
|
||||
if ( isActiveAttachment )
|
||||
{
|
||||
activeAttachmentP = attachmentP;
|
||||
selectedAttachment = attachment;
|
||||
CreateDescriptionPanel( attachment );
|
||||
}
|
||||
|
||||
attachmentP.AddEventListener( "onmousedown", () =>
|
||||
{
|
||||
activeAttachmentP?.SetClass( "active", false );
|
||||
|
||||
if ( activeAttachmentP != attachmentP )
|
||||
{
|
||||
PlaySound( "swb_equip" );
|
||||
attachment.EquipBroadCast();
|
||||
attachmentP.SetClass( "active", true );
|
||||
activeAttachmentP = attachmentP;
|
||||
selectedAttachment = attachment;
|
||||
SetAttachmentOnCategoryPanel( activeCategoryP, attachment );
|
||||
}
|
||||
else
|
||||
{
|
||||
PlaySound( "swb_unequip" );
|
||||
attachment.UnEquipBroadCast();
|
||||
activeCategoryP.SetClass( "hasAttachment", false );
|
||||
activeAttachmentP = null;
|
||||
selectedAttachment = null;
|
||||
}
|
||||
} );
|
||||
|
||||
attachmentP.AddEventListener( "onmouseover", () =>
|
||||
{
|
||||
hoveredAttachment = attachment;
|
||||
PlaySound( "ui.button.over" );
|
||||
CreateDescriptionPanel( attachment );
|
||||
} );
|
||||
|
||||
attachmentP.AddEventListener( "onmouseout", () =>
|
||||
{
|
||||
if ( hoveredAttachment != attachment ) return;
|
||||
if ( selectedAttachment is not null )
|
||||
CreateDescriptionPanel( this.selectedAttachment );
|
||||
else
|
||||
descriptionWrapper.DeleteChildren();
|
||||
} );
|
||||
} );
|
||||
}
|
||||
|
||||
private void CreateDescriptionPanel( Attachment attach )
|
||||
{
|
||||
descriptionWrapper.DeleteChildren();
|
||||
descriptionWrapper.Add.Label( attach.Description, "description" );
|
||||
|
||||
var posWrapper = descriptionWrapper.Add.Panel( "posWrapper" );
|
||||
foreach ( var pos in attach.Positives )
|
||||
{
|
||||
posWrapper.Add.Label( "> " + pos, "label" );
|
||||
}
|
||||
|
||||
var negWrapper = descriptionWrapper.Add.Panel( "negWrapper" );
|
||||
foreach ( var neg in attach.Negatives )
|
||||
{
|
||||
negWrapper.Add.Label( "> " + neg, "label" );
|
||||
}
|
||||
}
|
||||
}
|
303
Code/swb_base/ui/CustomizationMenu.cs.scss
Normal file
303
Code/swb_base/ui/CustomizationMenu.cs.scss
Normal file
@ -0,0 +1,303 @@
|
||||
$item-width: 280px;
|
||||
$item-bg-full-dark: rgb(33, 33, 33);
|
||||
$item-bg-extra-dark: rgba(33, 33, 33,0.95);
|
||||
$item-bg-dark: rgba(33, 33, 33,0.8);
|
||||
|
||||
$col-green: #2ecc71;
|
||||
$col-green-trans: #2ecc7196;
|
||||
$col-red: #e84118;
|
||||
|
||||
CustomizationMenu {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
left: 60px;
|
||||
pointer-events: all;
|
||||
align-items: flex-start; // Enables columns with diff heights
|
||||
transition: opacity 0.2s ease-out 0s;
|
||||
text-shadow: 0px 1px 1px rgba(0,0,0,0.3);
|
||||
gap: 4px;
|
||||
|
||||
&:intro {
|
||||
opacity: 0.01;
|
||||
}
|
||||
|
||||
.categoryWrapper {
|
||||
flex-direction: column;
|
||||
|
||||
.weaponName {
|
||||
min-width: $item-width;
|
||||
align-items: center;
|
||||
color: #eccc68;
|
||||
text-shadow: 1px 1px 1px rgba(0,0,0,0.5);
|
||||
font-weight: 500;
|
||||
font-size: 28px;
|
||||
font-family: FONTSPRING DEMO - Integral CF;
|
||||
padding: 0 12px 2px 12px;
|
||||
border-radius: 2px;
|
||||
min-height: 60px;
|
||||
background-color: rgba( #2222, 0.75 );
|
||||
backdrop-filter: blur( 16px );
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.category {
|
||||
margin-bottom: 4px;
|
||||
width: $item-width;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
align-items: center;
|
||||
min-height: 44px;
|
||||
transition: all 0.1s ease-out 0s;
|
||||
background-color: rgba( #2222, 0.5 );
|
||||
backdrop-filter: blur( 16px );
|
||||
|
||||
&.active {
|
||||
border-left: 3px solid $col-green;
|
||||
border-bottom-left-radius: 0;
|
||||
border-top-left-radius: 0;
|
||||
|
||||
.name {
|
||||
color: $col-green;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
|
||||
.name {
|
||||
color: $col-green;
|
||||
}
|
||||
|
||||
&.hasAttachment {
|
||||
.name {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.activeAttachment {
|
||||
.name {
|
||||
color: $col-green;
|
||||
transform: scale(1.04);
|
||||
}
|
||||
|
||||
.iconWrapper {
|
||||
filter: drop-shadow(0px 0px 6px $col-green);
|
||||
|
||||
.icon {
|
||||
height: 60px;
|
||||
width: 60px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.hasAttachment {
|
||||
min-height: 60px;
|
||||
overflow: hidden; // too long attach name
|
||||
|
||||
&.active {
|
||||
.name {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.activeAttachment {
|
||||
.name {
|
||||
color: $col-green;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 16px;
|
||||
margin-bottom: auto;
|
||||
margin-top: 4px;
|
||||
color: rgb(200,200,200);
|
||||
}
|
||||
|
||||
.activeAttachment {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
color: white;
|
||||
font-family: Poppins;
|
||||
font-size: 22px;
|
||||
margin-left: 10px;
|
||||
border-radius: 2px;
|
||||
transition: font-size 0.05s ease;
|
||||
pointer-events: none;
|
||||
text-shadow: 1px 1px 1px rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
.activeAttachment {
|
||||
position: absolute;
|
||||
width: $item-width;
|
||||
height: 60px;
|
||||
border-radius: 2px;
|
||||
opacity: 0;
|
||||
transition: all 0.1s ease-out 0s;
|
||||
pointer-events: none;
|
||||
|
||||
.name {
|
||||
color: white;
|
||||
font-family: Poppins;
|
||||
font-size: 20px;
|
||||
margin: 28px 0 0 10px;
|
||||
transition: all 0.1s ease-out 0s;
|
||||
}
|
||||
|
||||
.iconWrapper {
|
||||
height: 60px;
|
||||
width: 60px;
|
||||
border-radius: 2px;
|
||||
margin: 0 6px 0 auto;
|
||||
//margin-right: -1px;
|
||||
filter: drop-shadow(0px 0px 6px #eccc68);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
transition: all 0.1s ease-out 0s;
|
||||
|
||||
.icon {
|
||||
height: 56px;
|
||||
width: 56px;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
transition: all 0.1s ease-out 0s;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.attachmentWrapper {
|
||||
flex-direction: column;
|
||||
|
||||
.attachment {
|
||||
margin-bottom: 4px;
|
||||
width: 260px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
align-items: center;
|
||||
min-height: 40px;
|
||||
transition: all 0.1s ease-out 0s;
|
||||
background-color: rgba( #2222, 0.5 );
|
||||
backdrop-filter: blur( 16px );
|
||||
|
||||
&:hover {
|
||||
.name {
|
||||
color: $col-green;
|
||||
}
|
||||
|
||||
.iconWrapper {
|
||||
filter: drop-shadow(0px 0px 6px $col-green);
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
min-height: 46px;
|
||||
border-left: 3px solid $col-green;
|
||||
border-bottom-left-radius: 0;
|
||||
border-top-left-radius: 0;
|
||||
|
||||
.name {
|
||||
color: $col-green;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.iconWrapper {
|
||||
filter: drop-shadow(0px 0px 6px $col-green);
|
||||
min-height: 46px;
|
||||
width: 62px;
|
||||
|
||||
.icon {
|
||||
height: 46px;
|
||||
width: 46px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
color: white;
|
||||
font-family: Poppins;
|
||||
font-size: 18px;
|
||||
padding-top: 2px;
|
||||
margin-left: 10px;
|
||||
border-radius: 2px;
|
||||
transition: font-size 0.05s ease;
|
||||
pointer-events: none;
|
||||
text-shadow: 1px 1px 1px rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
.iconWrapper {
|
||||
// height: 100% -> broken somehow
|
||||
min-height: 40px;
|
||||
width: 52px;
|
||||
border-radius: 2px;
|
||||
margin-left: auto;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
transition: all 0.1s ease-out 0s;
|
||||
pointer-events: none;
|
||||
filter: drop-shadow(0px 0px 1px white);
|
||||
|
||||
.icon {
|
||||
height: 42px;
|
||||
width: 42px;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
transition: all 0.1s ease-out 0s;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.descriptionWrapper {
|
||||
position: relative;
|
||||
width: 400px;
|
||||
margin-left: 4px;
|
||||
flex-direction: column;
|
||||
border-radius: 2px;
|
||||
padding: 10px;
|
||||
font-family: Poppins;
|
||||
background-color: rgba( #2222, 0.5 );
|
||||
backdrop-filter: blur( 16px );
|
||||
text-shadow: 1px 1px 1px rgba(0,0,0,0.5);
|
||||
font-size: 16px;
|
||||
|
||||
&:empty {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.description {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.posWrapper {
|
||||
margin-top: 10px;
|
||||
padding-left: 4px;
|
||||
flex-direction: column;
|
||||
border-left: 2px solid $col-green;
|
||||
|
||||
.label {
|
||||
color: $col-green;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.negWrapper {
|
||||
margin-top: 10px;
|
||||
padding-left: 4px;
|
||||
flex-direction: column;
|
||||
border-left: 2px solid $col-red;
|
||||
|
||||
&:empty {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.label {
|
||||
color: $col-red;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
28
Code/swb_base/ui/RootWeaponDisplay.cs
Normal file
28
Code/swb_base/ui/RootWeaponDisplay.cs
Normal file
@ -0,0 +1,28 @@
|
||||
using Sandbox.UI;
|
||||
|
||||
namespace SWB.Base.UI;
|
||||
|
||||
public class RootWeaponDisplay : PanelComponent
|
||||
{
|
||||
public Weapon Weapon { get; set; }
|
||||
|
||||
protected override void OnStart()
|
||||
{
|
||||
if ( IsProxy )
|
||||
{
|
||||
Enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
Panel.StyleSheet.Load( "/swb_base/ui/RootWeaponDisplay.cs.scss" );
|
||||
|
||||
var crosshair = new Crosshair( Weapon );
|
||||
Panel.AddChild( crosshair );
|
||||
|
||||
if ( Weapon.Scoping )
|
||||
{
|
||||
var sniperScope = new SniperScope( Weapon, Weapon.ScopeInfo.LensTexture, Weapon.ScopeInfo.ScopeTexture );
|
||||
Panel.AddChild( sniperScope );
|
||||
}
|
||||
}
|
||||
}
|
5
Code/swb_base/ui/RootWeaponDisplay.cs.scss
Normal file
5
Code/swb_base/ui/RootWeaponDisplay.cs.scss
Normal file
@ -0,0 +1,5 @@
|
||||
RootWeaponDisplay {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
84
Code/swb_base/ui/SniperScope.cs
Normal file
84
Code/swb_base/ui/SniperScope.cs
Normal file
@ -0,0 +1,84 @@
|
||||
using Sandbox.UI;
|
||||
using Sandbox.UI.Construct;
|
||||
using SWB.Shared;
|
||||
using System;
|
||||
|
||||
namespace SWB.Base.UI;
|
||||
|
||||
public class SniperScope : Panel
|
||||
{
|
||||
IPlayerBase player => weapon.Owner;
|
||||
Weapon weapon;
|
||||
|
||||
Panel lensWrapper;
|
||||
Panel scope;
|
||||
|
||||
float lensRotation;
|
||||
|
||||
public SniperScope( Weapon weapon, string lensTexture, string scopeTexture )
|
||||
{
|
||||
this.weapon = weapon;
|
||||
StyleSheet.Load( "/swb_base/ui/SniperScope.cs.scss" );
|
||||
AddClass( "hide" );
|
||||
|
||||
if ( scopeTexture != null )
|
||||
Add.Panel( "leftBar" );
|
||||
|
||||
lensWrapper = Add.Panel( "lensWrapper" );
|
||||
lensWrapper.Add.Image( lensTexture, "lens" );
|
||||
|
||||
if ( scopeTexture != null )
|
||||
{
|
||||
scope = lensWrapper.Add.Image( scopeTexture, "scope" );
|
||||
|
||||
Add.Panel( "rightBar" );
|
||||
Add.Panel( "topBar" );
|
||||
Add.Panel( "bottomBar" );
|
||||
}
|
||||
}
|
||||
|
||||
public override void Tick()
|
||||
{
|
||||
if ( weapon is null ) return;
|
||||
|
||||
// Scope size
|
||||
var scopeSize = Screen.Height * ScaleFromScreen;
|
||||
lensWrapper.Style.Width = Length.Pixels( scopeSize );
|
||||
|
||||
// Show when zooming
|
||||
SetClass( "hide", !weapon.IsScoping );
|
||||
|
||||
// Check if ADS & firing
|
||||
if ( weapon.IsAiming && weapon.TimeSincePrimaryShoot < 0.1f )
|
||||
return;
|
||||
|
||||
// Movement impact
|
||||
var velocityJump = 0.02f;
|
||||
var velocityMove = 0.005f;
|
||||
var lensBob = 0f;
|
||||
|
||||
if ( velocityJump != 0 )
|
||||
{
|
||||
lensBob += velocityJump;
|
||||
}
|
||||
else if ( velocityMove != 0 )
|
||||
{
|
||||
lensBob += MathF.Sin( RealTime.Now * 17f ) * velocityMove;
|
||||
}
|
||||
|
||||
Style.MarginTop = Length.Percent( velocityJump + lensBob );
|
||||
|
||||
if ( scope == null ) return;
|
||||
|
||||
// Rotation impact
|
||||
var rightVector = player.Camera.WorldRotation.Right * 0f;
|
||||
var targetRotation = (rightVector.y + rightVector.x) * 0.015f;
|
||||
var rotateTransform = new PanelTransform();
|
||||
lensRotation = MathUtil.FILerp( lensRotation, targetRotation, 20 );
|
||||
rotateTransform.AddRotation( 0, 0, lensRotation );
|
||||
scope.Style.Transform = rotateTransform;
|
||||
|
||||
// Movement blur
|
||||
scope.Style.FilterBlur = Math.Abs( lensRotation * 2 + velocityJump + lensBob );
|
||||
}
|
||||
}
|
49
Code/swb_base/ui/SniperScope.cs.scss
Normal file
49
Code/swb_base/ui/SniperScope.cs.scss
Normal file
@ -0,0 +1,49 @@
|
||||
SniperScope {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: visible;
|
||||
|
||||
&.hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.lensWrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.lens {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.scope {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.leftBar,
|
||||
.rightBar {
|
||||
background-color: black;
|
||||
height: 100%;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.topBar {
|
||||
position: absolute;
|
||||
background-color: black;
|
||||
top: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.bottomBar {
|
||||
position: absolute;
|
||||
background-color: black;
|
||||
bottom: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
52
Code/swb_base/util/MathUtil.cs
Normal file
52
Code/swb_base/util/MathUtil.cs
Normal file
@ -0,0 +1,52 @@
|
||||
using System;
|
||||
|
||||
/*
|
||||
* Utility class to handle framerate independent + useful calculations
|
||||
*/
|
||||
|
||||
namespace SWB.Base;
|
||||
|
||||
class MathUtil
|
||||
{
|
||||
public static float FILerp( float fromF, float toF, float amount )
|
||||
{
|
||||
return fromF.LerpTo( toF, amount * RealTime.Delta );
|
||||
}
|
||||
|
||||
public static Vector3 FILerp( Vector3 fromVec, Vector3 toVec, float amount )
|
||||
{
|
||||
return fromVec.LerpTo( toVec, amount * RealTime.Delta );
|
||||
}
|
||||
|
||||
public static Angles FILerp( Angles fromAng, Angles toAng, float amount )
|
||||
{
|
||||
return Angles.Lerp( fromAng, toAng, amount * RealTime.Delta );
|
||||
}
|
||||
|
||||
public static Vector3 RelativeAdd( Vector3 vec1, Vector3 vec2, Rotation rot )
|
||||
{
|
||||
vec1 += vec2.x * rot.Right;
|
||||
vec1 += vec2.y * rot.Up;
|
||||
vec1 += vec2.z * rot.Forward;
|
||||
|
||||
return vec1;
|
||||
}
|
||||
|
||||
|
||||
// Helpful bezier function. Use this if you gotta: https://www.desmos.com/calculator/cahqdxeshd
|
||||
public static float BezierY( float f, float a, float b, float c )
|
||||
{
|
||||
f *= 3.2258f;
|
||||
return MathF.Pow( (1.0f - f), 2.0f ) * a + 2.0f * (1.0f - f) * f * b + MathF.Pow( f, 2.0f ) * c;
|
||||
}
|
||||
|
||||
public static Vector3 ToVector3( Angles angles )
|
||||
{
|
||||
return new Vector3( angles.pitch, angles.yaw, angles.roll );
|
||||
}
|
||||
|
||||
public static Angles ToAngles( Vector3 vector )
|
||||
{
|
||||
return new Angles( vector.x, vector.y, vector.z );
|
||||
}
|
||||
}
|
22
Code/swb_base/util/ModelUtil.cs
Normal file
22
Code/swb_base/util/ModelUtil.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using System.Linq;
|
||||
|
||||
namespace SWB.Base;
|
||||
|
||||
public class ModelUtil
|
||||
{
|
||||
public static void ParentToBone( GameObject gameObject, SkinnedModelRenderer target, string bone )
|
||||
{
|
||||
var targetBone = target.Model.Bones.AllBones.FirstOrDefault( b => b.Name == bone );
|
||||
if ( targetBone is null )
|
||||
{
|
||||
Log.Error( $"Could not find bone '{bone}' on {target}" );
|
||||
return;
|
||||
}
|
||||
|
||||
var holdBoneGo = target.GetBoneObject( targetBone );
|
||||
Log.Info(holdBoneGo);
|
||||
gameObject.SetParent( holdBoneGo );
|
||||
gameObject.WorldPosition = holdBoneGo.WorldPosition;
|
||||
gameObject.WorldRotation = holdBoneGo.WorldRotation;
|
||||
}
|
||||
}
|
47
Code/swb_base/util/SurfaceUtil.cs
Normal file
47
Code/swb_base/util/SurfaceUtil.cs
Normal file
@ -0,0 +1,47 @@
|
||||
using SWB.Shared;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/*
|
||||
* Util class for checking surface properties
|
||||
*/
|
||||
|
||||
namespace SWB.Base;
|
||||
|
||||
public static class SurfaceUtil
|
||||
{
|
||||
public static List<string> PenetratableSurfaces = new()
|
||||
{
|
||||
"water",
|
||||
"glass",
|
||||
"glass.pane"
|
||||
};
|
||||
|
||||
public static List<string> RicochetSurfaces = new()
|
||||
{
|
||||
"wip",
|
||||
};
|
||||
|
||||
public static bool CanPenetrate( Surface surface )
|
||||
{
|
||||
return PenetratableSurfaces.Contains( surface.ResourceName );
|
||||
}
|
||||
|
||||
public static bool CanRicochet( Surface surface )
|
||||
{
|
||||
return RicochetSurfaces.Contains( surface.ResourceName );
|
||||
}
|
||||
|
||||
public static bool IsPointWater( Vector3 pos )
|
||||
{
|
||||
var tr = Game.SceneTrace.Ray( pos, pos + Vector3.Forward )
|
||||
.WithTag( TagsHelper.Water )
|
||||
.Run();
|
||||
|
||||
return tr.Hit;
|
||||
}
|
||||
|
||||
public static bool IsSkybox( Surface surface )
|
||||
{
|
||||
return surface.HasTag( TagsHelper.World ) && !surface.HasTag( TagsHelper.Solid );
|
||||
}
|
||||
}
|
16
Code/swb_base/util/TableUtil.cs
Normal file
16
Code/swb_base/util/TableUtil.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SWB.Base;
|
||||
|
||||
class TableUtil
|
||||
{
|
||||
public static T GetRandom<T>( List<T> list )
|
||||
{
|
||||
if ( list.Count == 0 ) return default;
|
||||
|
||||
var random = new Random();
|
||||
var randI = random.Next( list.Count );
|
||||
return list[randI];
|
||||
}
|
||||
}
|
27
Code/swb_shared/DamageInfo.cs
Normal file
27
Code/swb_shared/DamageInfo.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace SWB.Shared;
|
||||
|
||||
public class DamageInfo
|
||||
{
|
||||
public Guid AttackerId { get; set; }
|
||||
public string Inflictor { get; set; }
|
||||
public float Damage { get; set; }
|
||||
public Vector3 Origin { get; set; }
|
||||
public Vector3 Force { get; set; }
|
||||
public string[] Tags { get; set; }
|
||||
|
||||
public static DamageInfo FromBullet( Guid attackerId, string inflictor, float damage, Vector3 origin, Vector3 force, string[] tags )
|
||||
{
|
||||
return new()
|
||||
{
|
||||
AttackerId = attackerId,
|
||||
Inflictor = inflictor,
|
||||
Damage = damage,
|
||||
Origin = origin,
|
||||
Force = force,
|
||||
Tags = new[] { "bullet" }.Concat( tags ).ToArray(),
|
||||
};
|
||||
}
|
||||
}
|
35
Code/swb_shared/IInventory.cs
Normal file
35
Code/swb_shared/IInventory.cs
Normal file
@ -0,0 +1,35 @@
|
||||
namespace SWB.Shared;
|
||||
|
||||
public interface IInventory
|
||||
{
|
||||
public NetList<GameObject> Items { get; set; }
|
||||
public GameObject Active { get; set; }
|
||||
|
||||
public void Add( GameObject gameObject, bool makeActive = false );
|
||||
public GameObject AddClone( GameObject gamePrefab, bool makeActive = true );
|
||||
public bool Has( GameObject gameObject );
|
||||
public void SetActive( GameObject gameObject );
|
||||
public void SetActive( string name );
|
||||
public void Clear();
|
||||
}
|
||||
|
||||
public interface IInventoryItem : IValid
|
||||
{
|
||||
/// <summary>Inventory slot</summary>
|
||||
public int Slot { get; set; }
|
||||
|
||||
/// <summary>Image that represent the item on the HUD</summary>
|
||||
public string Icon { get; set; }
|
||||
|
||||
/// <summary>Name that represent the item on the HUD</summary>
|
||||
public string DisplayName { get; set; }
|
||||
|
||||
/// <summary>Called on the GameObject that will be the new active one (Broadcast for networked gameObjects!)</summary>
|
||||
public void OnCarryStart();
|
||||
|
||||
/// <summary>Called when the GameObject stops being the active one (Broadcast for networked gameObjects!)</summary>
|
||||
public void OnCarryStop();
|
||||
|
||||
/// <summary>Can the GameObject be switched out</summary>
|
||||
public bool CanCarryStop();
|
||||
}
|
52
Code/swb_shared/IPlayerBase.cs
Normal file
52
Code/swb_shared/IPlayerBase.cs
Normal file
@ -0,0 +1,52 @@
|
||||
using Sandbox.Citizen;
|
||||
using System;
|
||||
|
||||
namespace SWB.Shared;
|
||||
|
||||
public interface IPlayerBase : IValid
|
||||
{
|
||||
// public CameraComponent ViewModelCamera { get; set; }
|
||||
public CameraComponent Camera { get; set; }
|
||||
public GameObject Body { get; set; }
|
||||
public SkinnedModelRenderer BodyRenderer { get; set; }
|
||||
public ShrimpleCharacterController.ShrimpleCharacterController CharacterController { get; set; }
|
||||
public AnimationHelper AnimationHelper { get; set; }
|
||||
public GameObject GameObject { get; }
|
||||
public IInventory Inventory { get; set; }
|
||||
public bool IsAlive { get; }
|
||||
public int MaxHealth { get; set; }
|
||||
public int Health { get; set; }
|
||||
public int Kills { get; set; }
|
||||
public int Deaths { get; set; }
|
||||
public Guid Id { get; }
|
||||
|
||||
// /// <summary>Input sensitivity modifier</summary>
|
||||
// public float InputSensitivity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Called when the weapon wants to know how much ammo is available
|
||||
/// </summary>
|
||||
/// <param name="type">The type of ammo</param>
|
||||
/// <returns>How much ammo is available</returns>
|
||||
public int AmmoCount( string type );
|
||||
|
||||
/// <summary>
|
||||
/// Called when the weapon is trying to take ammo.
|
||||
/// </summary>
|
||||
/// <param name="type">The type of ammo</param>
|
||||
/// <param name="amount">The amount of ammo requested</param>
|
||||
/// <returns>How much ammo was actually taken</returns>
|
||||
public int TakeAmmo( string type, int amount );
|
||||
|
||||
/// <summary>
|
||||
/// Called when the player takes damage
|
||||
/// </summary>
|
||||
/// <param name="info">Information about the damage</param>
|
||||
public void TakeDamage( DamageInfo info );
|
||||
|
||||
/// <summary>
|
||||
/// Shakes the camera
|
||||
/// </summary>
|
||||
/// <param name="screenShake">Information about the shake</param>
|
||||
public void ShakeScreen( ScreenShake screenShake );
|
||||
}
|
36
Code/swb_shared/InputButtonHelper.cs
Normal file
36
Code/swb_shared/InputButtonHelper.cs
Normal file
@ -0,0 +1,36 @@
|
||||
namespace SWB.Shared;
|
||||
|
||||
public partial class InputButtonHelper
|
||||
{
|
||||
public static string Forward => "Forward";
|
||||
public static string Backward => "Backward";
|
||||
public static string Left => "Left";
|
||||
public static string Right => "Right";
|
||||
public static string Jump => "Jump";
|
||||
public static string Run => "Run";
|
||||
public static string Walk => "Walk";
|
||||
public static string Duck => "Duck";
|
||||
public static string PrimaryAttack => "attack1";
|
||||
public static string SecondaryAttack => "attack2";
|
||||
public static string Reload => "reload";
|
||||
public static string Use => "use";
|
||||
public static string Slot1 => "Slot1";
|
||||
public static string Slot2 => "Slot2";
|
||||
public static string Slot3 => "Slot3";
|
||||
public static string Slot4 => "Slot4";
|
||||
public static string Slot5 => "Slot5";
|
||||
public static string Slot6 => "Slot6";
|
||||
public static string Slot7 => "Slot7";
|
||||
public static string Slot8 => "Slot8";
|
||||
public static string Slot9 => "Slot9";
|
||||
public static string Slot0 => "Slot0";
|
||||
public static string SlotPrev => "SlotPrev";
|
||||
public static string SlotNext => "SlotNext";
|
||||
public static string View => "View";
|
||||
public static string Voice => "Voice";
|
||||
public static string Drop => "Drop";
|
||||
public static string Flashlight => "Flashlight";
|
||||
public static string Score => "Score";
|
||||
public static string Menu => "Menu";
|
||||
public static string Chat => "Chat";
|
||||
}
|
16
Code/swb_shared/ScreenShake.cs
Normal file
16
Code/swb_shared/ScreenShake.cs
Normal file
@ -0,0 +1,16 @@
|
||||
namespace SWB.Shared;
|
||||
|
||||
public class ScreenShake
|
||||
{
|
||||
/// <summary>Duration (s)</summary>
|
||||
[KeyProperty] public float Duration { get; set; } = 0f;
|
||||
|
||||
/// <summary>Delay between shakes (s)</summary>
|
||||
[KeyProperty] public float Delay { get; set; } = 0f;
|
||||
|
||||
/// <summary>Screen disposition amount</summary>
|
||||
[KeyProperty] public float Size { get; set; } = 0f;
|
||||
|
||||
/// <summary>Screen rotation amount</summary>
|
||||
[KeyProperty] public float Rotation { get; set; } = 0f;
|
||||
}
|
15
Code/swb_shared/TagsHelper.cs
Normal file
15
Code/swb_shared/TagsHelper.cs
Normal file
@ -0,0 +1,15 @@
|
||||
namespace SWB.Shared;
|
||||
|
||||
public partial class TagsHelper
|
||||
{
|
||||
public static string Player => "player";
|
||||
public static string Trigger => "trigger";
|
||||
public static string Weapon => "weapon";
|
||||
public static string ViewModel => "viewmodel";
|
||||
public static string PlayerClip => "playerclip";
|
||||
public static string PassBullets => "passbullets";
|
||||
public static string Water => "water";
|
||||
public static string World => "world";
|
||||
public static string Solid => "solid";
|
||||
public static string Attachment => "attachment";
|
||||
}
|
@ -1,8 +1,11 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/AddReferences/RecentPaths/=C_003A_005CUsers_005Chamit_005Fba31xcg_005CDocuments_005Cs_0026box_0020projects_005Ckakozuzo_005F2_005CLibraries_005Cfish_002Escc_005Ccode_005CAssembly_002Ecs/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/AddReferences/RecentPaths/=C_003A_005CUsers_005Chamit_005Fba31xcg_005CDocuments_005Cs_0026box_0020projects_005Ckakozuzo_005F2_005CLibraries_005Cfish_002Escc_005Cscc_002Esbproj/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AComponentList_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fda27136869dc28c32bc5fb3ea4841e055f0e290e45d4bde9530ad27d38be0db_003FComponentList_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AInputActionAttribute_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae28b94f898844b4bf21b8441ca0a34b66e00_003F4d_003F40860ce8_003FInputActionAttribute_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AInput_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fcdc4720139e44c50ab0f14122fb7a0f2237a00_003Fe3_003Fe2d38eef_003FInput_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIValid_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa8e4635c89e1456aac179819973df53b68400_003F14_003F50c24b3d_003FIValid_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ANetworking_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F81ecae4f6275493893a36d9c54b5776d233200_003F73_003F8bf7d187_003FNetworking_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASkinnedModelRenderer_002EParameters_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fed128fe9d254c953a46da1ac97997e29107b59c8f1369a7bff15f3d718978_003FSkinnedModelRenderer_002EParameters_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AVoid_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fb34296d7545f4a54bda041aecea156b4b1d878_003F_005Fe67e6_003FVoid_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
</wpf:ResourceDictionary>
|
Loading…
x
Reference in New Issue
Block a user