diff --git a/Assets/prefabs/Colt.prefab b/Assets/prefabs/Colt.prefab new file mode 100644 index 0000000..8c89b55 --- /dev/null +++ b/Assets/prefabs/Colt.prefab @@ -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 +} \ No newline at end of file diff --git a/Assets/prefabs/ScarH.prefab b/Assets/prefabs/ScarH.prefab new file mode 100644 index 0000000..12550c5 --- /dev/null +++ b/Assets/prefabs/ScarH.prefab @@ -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 +} \ No newline at end of file diff --git a/Assets/prefabs/player.prefab b/Assets/prefabs/player.prefab index 6ddace0..07a6355 100644 --- a/Assets/prefabs/player.prefab +++ b/Assets/prefabs/player.prefab @@ -26,7 +26,7 @@ "MaxGroundAngle": 60, "MaxUnstuckTries": 20, "PseudoStepsEnabled": true, - "PushEnabled": false, + "PushEnabled": true, "PushTagsWeight": { "player": 1 }, @@ -36,7 +36,7 @@ "StepsEnabled": true, "StepTolerance": 1, "TraceHeight": 72, - "TraceWidth": 16, + "TraceWidth": 20, "UnstuckEnabled": true, "UseSceneGravity": true, "WallTolerance": 1 @@ -56,6 +56,7 @@ "CamOffsetZ": 61, "DuckSpeed": 50, "JumpStrength": 350, + "MaxHealth": 0, "RunSpeed": 300, "WalkSpeed": 100 }, @@ -73,6 +74,26 @@ "component_type": "SkinnedModelRenderer" } }, + { + "__type": "FootSteps", + "__guid": "f6ee40f5-5541-4a85-bc06-259b451d0891", + "Source": { + "_type": "component", + "component_id": "4fd8c36d-180b-41af-8439-6b975ca1c7b3", + "go": "b99ceffc-a173-4a6e-a239-6d3c1612c09f", + "component_type": "SkinnedModelRenderer" + } + }, + { + "__type": "PlayerDresser", + "__guid": "cc66fd84-f698-4cf4-a1ac-e038245e37fd", + "BodyRenderer": { + "_type": "component", + "component_id": "4fd8c36d-180b-41af-8439-6b975ca1c7b3", + "go": "b99ceffc-a173-4a6e-a239-6d3c1612c09f", + "component_type": "SkinnedModelRenderer" + } + }, { "__type": "RagdollController", "__guid": "5433b29f-ed75-4581-acfc-d99dda4ff64b", @@ -88,29 +109,8 @@ "go": "b99ceffc-a173-4a6e-a239-6d3c1612c09f", "component_type": "SkinnedModelRenderer" }, - "impaledPhysicsBodyIndex": 0, "isLocked": false }, - { - "__type": "FootSteps", - "__guid": "f6ee40f5-5541-4a85-bc06-259b451d0891", - "Source": { - "_type": "component", - "component_id": "4fd8c36d-180b-41af-8439-6b975ca1c7b3", - "go": "b99ceffc-a173-4a6e-a239-6d3c1612c09f", - "component_type": "SkinnedModelRenderer" - } - }, - { - "__type": "PlayerDresser", - "__guid": "cc66fd84-f698-4cf4-a1ac-e038245e37fd", - "BodyRenderer": { - "_type": "component", - "component_id": "4fd8c36d-180b-41af-8439-6b975ca1c7b3", - "go": "b99ceffc-a173-4a6e-a239-6d3c1612c09f", - "component_type": "SkinnedModelRenderer" - } - }, { "__type": "Sandbox.ModelPhysics", "__guid": "262154b8-438c-451e-b2c7-4d23d5c73120", @@ -138,7 +138,7 @@ "__type": "Sandbox.SkinnedModelRenderer", "__guid": "4fd8c36d-180b-41af-8439-6b975ca1c7b3", "BodyGroups": 341, - "CreateBoneObjects": false, + "CreateBoneObjects": true, "Model": "models/citizen/citizen.vmdl", "Morphs": {}, "Parameters": { @@ -162,113 +162,114 @@ "Children": [ { "__guid": "55f02b37-0d7a-485b-9ff9-577329327b72", - "Flags": 0, + "Flags": 4, "Name": "pelvis", - "Position": "1.09978,0.02634115,30.81247", - "Rotation": "-0.4635624,-0.5211884,-0.5340351,0.4777857", + "Position": "0.9498934,0.2170715,30.67575", + "Rotation": "-0.4681589,-0.514821,-0.5301685,0.4844668", "Enabled": true, "Children": [ { "__guid": "e6791e79-6d2f-4889-938a-469e36e7a394", - "Flags": 0, + "Flags": 4, "Name": "spine_0", - "Position": "3.921841,-0.002455711,-0.000120163", - "Rotation": "-0.0005211234,0.004492462,-0.02736789,-0.9996151", + "Position": "3.921829,-0.002458572,-0.0001277924", + "Rotation": "-0.0005621016,0.00272873,-0.03208181,-0.9994812", "Enabled": true, "Children": [ { "__guid": "62632f4c-5251-47ee-99e9-5eca787ebd13", - "Flags": 0, + "Flags": 4, "Name": "spine_1", - "Position": "5.610458,0.000005722046,0.00003051758", - "Rotation": "-0.0001792312,0.007572293,-0.04617235,-0.9989036", + "Position": "5.610458,0,0.00002670288", + "Rotation": "-0.0002571642,0.004780471,-0.0541603,-0.9985197", "Enabled": true, "Children": [ { "__guid": "9efd1d11-91e3-43b0-8203-122dddd4da0c", - "Flags": 0, + "Flags": 4, "Name": "spine_2", - "Position": "5.610497,0.00001525879,-0.000007629395", + "Position": "5.610504,0.00001335144,-0.00001144409", + "Rotation": "-0.0003281981,0.001365393,-0.01217191,-0.9999217", "Enabled": true, "Children": [ { "__guid": "7b3a4b1c-3851-444c-9b10-c59019d02a34", - "Flags": 0, + "Flags": 4, "Name": "neck_0", - "Position": "5.820332,-0.09347343,-0.003276825", - "Rotation": "0.00003476441,-0.0002155006,0.009900302,-0.9999442", + "Position": "5.820351,-0.09348869,-0.003257751", + "Rotation": "0.00004458427,-0.0002156198,0.006331474,-0.9999733", "Enabled": true, "Children": [ { "__guid": "a660f880-88bb-4972-bb01-5a6a2e2d2a84", - "Flags": 0, + "Flags": 4, "Name": "head", - "Position": "4.511506,-0.0004174262,-0.001046275", - "Rotation": "0.4999392,-0.006922714,0.01199041,-0.8659359", + "Position": "4.511509,-0.0007648468,-0.0007514954", + "Rotation": "-0.0000004172325,0,0.01174361,-0.9999189", "Enabled": true, "Children": [ { "__guid": "ae84828d-d4dc-461b-bbf5-55906713444d", - "Flags": 0, + "Flags": 4, "Name": "ear_R", - "Position": "5.622315,-0.4147104,-5.930751", - "Rotation": "0.04622595,-0.6934852,0.1379034,-0.7056088", + "Position": "5.622513,-0.4153175,-5.930378", + "Rotation": "0.04622601,-0.6934857,0.1379035,-0.7056092", "Enabled": true }, { "__guid": "9f92ced9-922e-41be-ad20-28a016d36589", - "Flags": 0, + "Flags": 4, "Name": "ear_L", - "Position": "5.62321,-0.4153741,5.927209", - "Rotation": "0.705609,-0.1379605,0.6934716,-0.04625562", + "Position": "5.622925,-0.415432,5.927505", + "Rotation": "0.7056087,-0.1379606,0.6934718,-0.04625592", "Enabled": true }, { "__guid": "46ddc438-4d04-414e-8c83-da033150c6c5", - "Flags": 0, + "Flags": 4, "Name": "face_lid_lower_R", - "Position": "6.909532,5.141103,-3.235655", - "Rotation": "-0.1292284,0.9817931,-0.1379476,-0.01812655", + "Position": "6.91008,5.140553,-3.235741", + "Rotation": "-0.1292286,0.981794,-0.1379475,-0.01812777", "Enabled": true }, { "__guid": "34031d1b-74e3-4053-b701-3e66ec71138b", - "Flags": 0, + "Flags": 4, "Name": "face_lid_lower_L", - "Position": "6.90997,5.140628,3.233467", - "Rotation": "-0.1292284,0.9817871,0.1379907,0.01812656", + "Position": "6.910255,5.140377,3.233379", + "Rotation": "-0.1292285,0.9817878,0.1379906,0.01812781", "Enabled": true }, { "__guid": "58f01ec1-49fe-4a7b-82ba-5eed960160b6", - "Flags": 0, + "Flags": 4, "Name": "face_lid_upper_L", - "Position": "6.909989,5.140629,3.233452", - "Rotation": "0.9817871,0.1292284,0.01812819,-0.1379904", + "Position": "6.91029,5.140385,3.233345", + "Rotation": "0.9817877,0.1292284,0.01812828,-0.1379905", "Enabled": true }, { "__guid": "0bf32bb9-e057-419e-a942-c23ea0582433", - "Flags": 0, + "Flags": 4, "Name": "face_lid_upper_R", - "Position": "6.909554,5.141101,-3.235678", - "Rotation": "0.9817932,0.1292284,-0.01812825,0.1379473", + "Position": "6.91008,5.140553,-3.235741", + "Rotation": "0.9817939,0.1292285,-0.01812819,0.1379474", "Enabled": true }, { "__guid": "18358843-3c89-48fd-8be0-5fa0b7955c1f", - "Flags": 0, + "Flags": 4, "Name": "eye_L", - "Position": "6.911271,5.140766,3.23198", - "Rotation": "0.7092671,0.7048279,0.004742622,-0.004704632", + "Position": "6.911369,5.140463,3.232056", + "Rotation": "0.7070774,0.7070562,-0.0000001192093,-0.0000006472692", "Enabled": true }, { "__guid": "0ca9dfc6-8ca1-41f9-9d24-f753227f780d", - "Flags": 0, + "Flags": 4, "Name": "eye_R", - "Position": "6.910652,5.141091,-3.237001", - "Rotation": "0.7092671,0.7048283,0.004742473,-0.004704908", + "Position": "6.911068,5.140486,-3.236935", + "Rotation": "0.7070774,0.7070563,-0.0000002086163,-0.0000009257346", "Enabled": true } ] @@ -277,90 +278,90 @@ }, { "__guid": "dd94729b-774e-416d-b6c9-d70e663a7549", - "Flags": 0, + "Flags": 4, "Name": "clavicle_L", - "Position": "4.038403,0.2752094,1.103661", - "Rotation": "-0.08429766,0.6549308,0.08405611,-0.7462444", + "Position": "4.038406,0.2751923,1.10368", + "Rotation": "-0.08322743,0.6583217,0.08369048,-0.7434165", "Enabled": true, "Children": [ { "__guid": "d26a150a-98eb-418d-966c-53a3e640bfa9", - "Flags": 0, + "Flags": 4, "Name": "arm_upper_L", - "Position": "6.218311,0.00006532669,-0.001342773", - "Rotation": "-0.06809062,0.6623875,-0.01330963,-0.7459265", + "Position": "6.218311,0.00004720688,-0.001358032", + "Rotation": "-0.06634165,0.6711065,-0.01204745,-0.7382727", "Enabled": true, "Children": [ { "__guid": "7e697fb9-910c-4642-ac8d-4607ac28caed", - "Flags": 0, + "Flags": 4, "Name": "arm_upper_L_twist1", - "Position": "5.020269,-0.0002432466,-0.002967119", - "Rotation": "0.009102732,-0.00000002980232,0.00000004470348,-0.9999471", + "Position": "5.020214,-0.0004844666,-0.003196716", + "Rotation": "0.008961678,0.00000001490116,-0.00000001490116,-0.9999482", "Enabled": true }, { "__guid": "6efdeca4-2f83-42fe-8f96-b2712ec06baf", - "Flags": 0, + "Flags": 4, "Name": "arm_lower_L", - "Position": "10.04386,0.0002688169,-0.001078844", - "Rotation": "0.0000002831221,-0.0000001192093,-0.1624508,-0.9866987", + "Position": "10.04381,-0.0001144409,-0.001579285", + "Rotation": "0.0000003874302,-0.00000005960464,-0.1214664,-0.9925776", "Enabled": true, "Children": [ { "__guid": "ba1a0dca-a8c0-4181-ab02-44b9e7fb259b", - "Flags": 0, + "Flags": 4, "Name": "arm_lower_L_twist0", - "Position": "-0.001255035,-0.0001220703,-0.00227356", - "Rotation": "-0.0000001341105,-0.0000003427267,0.00000001490116,-0.9999745", + "Position": "-0.001251221,-0.0002689362,-0.002170563", + "Rotation": "-0.0000001043081,-0.0000003278255,0.00000004470348,-0.9999753", "Enabled": true }, { "__guid": "79070cab-d818-4c61-ad7d-daf4e8177a65", - "Flags": 0, + "Flags": 4, "Name": "hand_L", - "Position": "7.705186,0.0006004572,-0.001422405", - "Rotation": "0.02671701,-0.04907528,0.04178061,-0.9975375", + "Position": "7.705168,-0.000005722046,-0.001974106", + "Rotation": "0.0267171,-0.04907531,0.04178061,-0.9975384", "Enabled": true, "Children": [ { "__guid": "a1b8756b-e6e3-4cac-a998-748f1c9ff1f5", - "Flags": 0, + "Flags": 4, "Name": "hold_L", - "Position": "5.358815,0.6225458,3.392119", - "Rotation": "-0.695914,-0.1704008,-0.3928553,0.5764221", + "Position": "5.359367,0.6213875,3.391457", + "Rotation": "0.6959142,0.1704009,0.3928553,-0.5764222", "Enabled": true }, { "__guid": "7fd1f7c4-d1c3-436e-b8d5-a8fe9c55de6d", - "Flags": 0, + "Flags": 4, "Name": "hand_R_to_L_ikrule", - "Position": "-7.81682,-3.155989,21.13516", - "Rotation": "-0.140897,0.3437678,-0.003125086,-0.928382", + "Position": "-6.664104,-3.735872,20.39766", + "Rotation": "-0.1744881,0.308711,-0.002506047,-0.9349738", "Enabled": true }, { "__guid": "95097fea-f7a1-40cb-acc9-7f56044b1243", - "Flags": 0, + "Flags": 4, "Name": "finger_thumb_0_L", - "Position": "1.126226,1.648307,0.8637016", - "Rotation": "-0.5683933,-0.1216716,-0.4116745,-0.7018403", + "Position": "1.12656,1.647884,0.8638954", + "Rotation": "-0.5683939,-0.1216718,-0.411675,-0.7018412", "Enabled": true, "Children": [ { "__guid": "cc2772c4-9801-4b63-b85b-1549777e767a", - "Flags": 0, + "Flags": 4, "Name": "finger_thumb_1_L", - "Position": "1.904869,-0.001191139,-0.004262924", - "Rotation": "-0.00000001024455,0.1627173,-0.00000002793968,-0.9866276", + "Position": "1.90469,-0.001134872,-0.004007339", + "Rotation": "-0.000000005587935,0.1627174,-0.00000002235174,-0.9866279", "Enabled": true, "Children": [ { "__guid": "ef0b2fb4-a3c3-4ad2-9f39-a7b6983eba24", - "Flags": 0, + "Flags": 4, "Name": "finger_thumb_2_L", - "Position": "2.133833,-0.001554489,-0.004840851", - "Rotation": "-0.00000001490116,0.08416325,-0.00000004842877,-0.9963972", + "Position": "2.133718,-0.001477242,-0.004419327", + "Rotation": "-0.00000001490116,0.0841634,-0.00000006332994,-0.9963977", "Enabled": true } ] @@ -369,34 +370,34 @@ }, { "__guid": "07b60cdd-5110-4cee-96dd-5106b51c1fd9", - "Flags": 0, + "Flags": 4, "Name": "finger_index_meta_L", - "Position": "2.073688,1.204044,0.3748571", - "Rotation": "-0.06033978,-0.04579458,-0.1048825,-0.9915605", + "Position": "2.073862,1.2036,0.3747482", + "Rotation": "-0.06033975,-0.04579456,-0.1048825,-0.9915607", "Enabled": true, "Children": [ { "__guid": "836eff99-8079-4fc6-a0fa-276e306ae544", - "Flags": 0, + "Flags": 4, "Name": "finger_index_0_L", - "Position": "3.066636,0.0002753139,-0.003266573", - "Rotation": "0.003366634,0.2652252,0.06858285,-0.9616922", + "Position": "3.066603,-0.0003318787,-0.003740311", + "Rotation": "0.003366575,0.2652252,0.06858283,-0.9616925", "Enabled": true, "Children": [ { "__guid": "c0b06cd5-7ca6-4d93-831a-4a6841bca328", - "Flags": 0, + "Flags": 4, "Name": "finger_index_1_L", - "Position": "2.280267,0.0002042651,-0.002904043", - "Rotation": "0,0.2334992,-0.00000008940697,-0.972301", + "Position": "2.280365,-0.0008735657,-0.003376007", + "Rotation": "-0.00000002980232,0.2334994,-0.00000005960464,-0.9723014", "Enabled": true, "Children": [ { "__guid": "250e6957-c1d3-4c72-aee1-c337f79511b0", - "Flags": 0, + "Flags": 4, "Name": "finger_index_2_L", - "Position": "1.590233,0.0002466887,-0.002176881", - "Rotation": "-0.00000005960464,0.0137254,-0.0000003874302,-0.9998416", + "Position": "1.590473,-0.001146317,-0.002437592", + "Rotation": "-0.00000004470348,0.01372541,-0.0000003874302,-0.9998423", "Enabled": true } ] @@ -407,34 +408,34 @@ }, { "__guid": "9186496c-cad2-4b94-b5d0-47f2ee6a92d8", - "Flags": 0, + "Flags": 4, "Name": "finger_middle_meta_L", - "Position": "2.099168,-0.1590744,0.2834409", - "Rotation": "-0.008934826,-0.03483182,0.01933663,-0.9991316", + "Position": "2.099216,-0.1595154,0.2831421", + "Rotation": "-0.008934811,-0.03483181,0.01933673,-0.9991317", "Enabled": true, "Children": [ { "__guid": "50923b14-9231-4087-8d73-a340a8a61028", - "Flags": 0, + "Flags": 4, "Name": "finger_middle_0_L", - "Position": "2.997435,-0.00002804399,-0.003062844", - "Rotation": "-0.0008631647,0.2334588,-0.001294941,-0.9723202", + "Position": "2.997475,-0.0006752014,-0.003637314", + "Rotation": "-0.0008631647,0.2334588,-0.001295015,-0.9723204", "Enabled": true, "Children": [ { "__guid": "731dc303-c26e-4eb5-a263-13238cfd353d", - "Flags": 0, + "Flags": 4, "Name": "finger_middle_1_L", - "Position": "2.557826,0.0001758784,-0.002626687", - "Rotation": "-0.00000008940697,0.2322481,-0.0000002086163,-0.9726022", + "Position": "2.557945,-0.0009613037,-0.003372192", + "Rotation": "-0.0000001192093,0.2322482,-0.0000002086163,-0.9726026", "Enabled": true, "Children": [ { "__guid": "c5954938-4384-4036-8b7e-f9e163f89603", - "Flags": 0, + "Flags": 4, "Name": "finger_middle_2_L", - "Position": "1.67947,0.0001565814,-0.001923561", - "Rotation": "-0.0000001341105,0.04821065,-0.0000002682209,-0.9987773", + "Position": "1.679678,-0.001264572,-0.00245285", + "Rotation": "-0.00000008940697,0.04821068,-0.0000002682209,-0.9987776", "Enabled": true } ] @@ -445,34 +446,34 @@ }, { "__guid": "7ae4b8db-9914-4634-9b2d-96c8ee88536e", - "Flags": 0, + "Flags": 4, "Name": "finger_ring_meta_L", - "Position": "2.10349,-1.444458,0.2211144", - "Rotation": "0.06374946,0.02917722,0.13065,-0.9889115", + "Position": "2.103422,-1.444895,0.2206383", + "Rotation": "0.06374948,0.02917722,0.1306502,-0.9889116", "Enabled": true, "Children": [ { "__guid": "6837225a-451f-40f8-8594-b5c63104c45a", - "Flags": 0, + "Flags": 4, "Name": "finger_ring_0_L", - "Position": "2.822251,-0.0002965927,-0.002760768", - "Rotation": "-0.004488692,0.1855496,-0.05619583,-0.9809707", + "Position": "2.822359,-0.0009937286,-0.00349617", + "Rotation": "-0.004488662,0.1855496,-0.0561959,-0.9809709", "Enabled": true, "Children": [ { "__guid": "15bb7037-51de-48d4-b0e9-711193db9d36", - "Flags": 0, + "Flags": 4, "Name": "finger_ring_1_L", - "Position": "2.194641,-0.00004752725,-0.002517208", - "Rotation": "0.00004316866,0.2565899,0.00004312396,-0.9664629", + "Position": "2.194767,-0.001087189,-0.003385544", + "Rotation": "0.00004313886,0.2565899,0.00004309416,-0.966463", "Enabled": true, "Children": [ { "__guid": "45867c49-3f20-4c44-87b8-7616f0e28ae8", - "Flags": 0, + "Flags": 4, "Name": "finger_ring_2_L", - "Position": "1.532103,-0.00003477372,-0.001665652", - "Rotation": "0.0000001788139,-0.05036834,0.0000002384186,0.9986643", + "Position": "1.532309,-0.001394272,-0.00234127", + "Rotation": "0.0000001788139,-0.05036835,0.0000001788139,0.998665", "Enabled": true } ] @@ -485,28 +486,28 @@ }, { "__guid": "ce073101-82d3-4a55-858f-df5608f6f8a1", - "Flags": 0, + "Flags": 4, "Name": "arm_lower_L_twist1", - "Position": "3.850731,0.0001845956,-0.003923535", - "Rotation": "0.01740356,-0.00000001490116,0.00000004470348,-0.9998308", + "Position": "3.850601,-0.0003395081,-0.004226685", + "Rotation": "0.01740371,0.00000001490116,0,-0.9998307", "Enabled": true }, { "__guid": "855af04e-f003-477d-abd8-8e4b9c7b7222", - "Flags": 0, + "Flags": 4, "Name": "arm_elbow_helper_L", - "Position": "-0.001174927,-0.003246307,-0.002754211", - "Rotation": "0.002399921,0.00216841,0.7619089,-0.6476718", + "Position": "0.00050354,0.008630753,-0.002735138", + "Rotation": "0.002234429,0.002276609,0.7474117,-0.6643494", "Enabled": true } ] }, { "__guid": "254f75ef-591b-48ab-977b-8f05045341c5", - "Flags": 0, + "Flags": 4, "Name": "arm_upper_L_twist0", - "Position": "-0.002178192,-0.0006027222,-0.003330231", - "Rotation": "0.05913498,0.00000001490116,0,-0.9982385", + "Position": "-0.002223969,-0.0006904602,-0.003311157", + "Rotation": "0.05821951,-0.00000004470348,0.00000002980232,-0.9982922", "Enabled": true } ] @@ -515,92 +516,92 @@ }, { "__guid": "c3c4078b-1077-4888-bc65-2aeda8abd4a1", - "Flags": 0, + "Flags": 4, "Name": "clavicle_R", - "Position": "4.037277,0.2748718,-1.108452", - "Rotation": "0.08442839,-0.655288,0.08409488,-0.7459114", + "Position": "4.037285,0.2748642,-1.108433", + "Rotation": "0.08335777,-0.6586776,0.08373019,-0.743082", "Enabled": true, "Children": [ { "__guid": "15c9abaf-ac95-4941-81a9-cd94d872e53e", - "Flags": 0, + "Flags": 4, "Name": "arm_upper_R", - "Position": "6.218316,0.0003953613,-0.00002598763", - "Rotation": "0.06907114,-0.6644883,-0.008399844,-0.744036", + "Position": "6.218296,0.00008368492,-0.000003814697", + "Rotation": "0.06760296,-0.6713889,-0.007281214,-0.7379629", "Enabled": true, "Children": [ { "__guid": "9c96d6fd-271a-4a7b-a036-6db96dd70497", - "Flags": 0, + "Flags": 4, "Name": "arm_lower_R", - "Position": "10.04377,0.0008434057,-0.0004107952", - "Rotation": "0.0000002086163,-0.00000002980232,-0.1624506,-0.9866967", + "Position": "10.04372,0.0002269745,-0.0008583069", + "Rotation": "0.0000002682209,-0.00000005960464,-0.1214664,-0.9925756", "Enabled": true, "Children": [ { "__guid": "b6383140-1e6d-4b46-a1b1-5ed35a1fadd5", - "Flags": 0, + "Flags": 4, "Name": "arm_lower_R_twist0", - "Position": "-0.001403809,0.0006489754,-0.0008583069", - "Rotation": "0,0.0000001490116,-0.00000005960464,-0.9999706", + "Position": "-0.001461029,0.0004501343,-0.0009346008", + "Rotation": "-0.0000001043081,0.0000001192093,-0.00000002980232,-0.9999704", "Enabled": true, "Children": [ { "__guid": "cfb6423d-393d-449a-bd98-470519e4bd81", - "Flags": 0, + "Flags": 4, "Name": "arm_elbow_helper_R", - "Position": "-0.001998901,-0.002149105,-0.0009536743", - "Rotation": "0.001526296,0.001149135,0.7618573,-0.647737", + "Position": "-0.0003585815,0.009586334,-0.001064301", + "Rotation": "0.001489639,0.001306791,0.7473603,-0.6644111", "Enabled": true } ] }, { "__guid": "a860ea26-f03d-48f9-9039-daf136730a1a", - "Flags": 0, + "Flags": 4, "Name": "hand_R", - "Position": "7.70503,0.001723886,-0.0003260374", - "Rotation": "-0.02671666,0.04907504,0.04178038,-0.9975337", + "Position": "7.704939,0.0006952286,-0.0009613037", + "Rotation": "-0.02671681,0.04907507,0.04178044,-0.9975334", "Enabled": true, "Children": [ { "__guid": "98a9377b-d445-40a7-ae54-25fca52d42c1", - "Flags": 0, + "Flags": 4, "Name": "hold_R", - "Position": "5.359564,0.6235515,-3.394599", - "Rotation": "0.5764175,-0.3928525,-0.1704,-0.6959082", + "Position": "5.359365,0.6226668,-3.395111", + "Rotation": "0.5764186,-0.3928533,-0.1704003,-0.6959097", "Enabled": true }, { "__guid": "6de045f3-a8e5-491b-94ab-f61b6b1aa4ed", - "Flags": 0, + "Flags": 4, "Name": "hand_L_to_R_ikrule", - "Position": "-7.899995,-3.201715,-21.11883", - "Rotation": "0.1409045,-0.3452918,0.00001519918,-0.9278128", + "Position": "-6.532598,-3.95226,-20.4858", + "Rotation": "0.1706123,-0.3072287,0.001134008,-0.9361715", "Enabled": true }, { "__guid": "ccb0ff52-c63f-43c2-9907-7318b9c5166f", - "Flags": 0, + "Flags": 4, "Name": "finger_thumb_0_R", - "Position": "1.126014,1.649442,-0.8676212", - "Rotation": "-0.5683893,-0.1216713,0.411671,0.7018345", + "Position": "1.126326,1.648983,-0.867506", + "Rotation": "-0.5683905,-0.1216715,0.4116719,0.7018359", "Enabled": true, "Children": [ { "__guid": "8505adbd-ae44-4f6c-9898-dbeecee80322", - "Flags": 0, + "Flags": 4, "Name": "finger_thumb_1_R", - "Position": "1.904552,0.000449568,0.0002350211", - "Rotation": "0.00000001303852,0.1627161,-0.00000008195639,0.9866157", + "Position": "1.904339,0.0004377365,-0.0005264282", + "Rotation": "0.00000001117587,0.1627162,-0.00000008940697,0.9866164", "Enabled": true, "Children": [ { "__guid": "48a98f90-49f0-44a5-b01f-1bddad0d1132", - "Flags": 0, + "Flags": 4, "Name": "finger_thumb_2_R", - "Position": "2.133166,0.0006179959,0.0002120137", - "Rotation": "0.00000001490116,0.08416235,-0.00000006332994,0.9963783", + "Position": "2.133007,0.0006222725,-0.001390457", + "Rotation": "0.00000001117587,0.08416254,-0.00000008009374,0.9963803", "Enabled": true } ] @@ -609,34 +610,34 @@ }, { "__guid": "8e12d685-e2e7-409f-8d07-7162f53e18d6", - "Flags": 0, + "Flags": 4, "Name": "finger_index_meta_R", - "Position": "2.073404,1.205412,-0.3782896", - "Rotation": "0.06033978,0.04579416,-0.1048823,-0.9915539", + "Position": "2.073565,1.204744,-0.3784142", + "Rotation": "0.06033975,0.04579416,-0.1048823,-0.9915534", "Enabled": true, "Children": [ { "__guid": "8bfd5924-6872-43f9-a225-7002c3c60d6f", - "Flags": 0, + "Flags": 4, "Name": "finger_index_0_R", - "Position": "3.066302,0.002848506,-0.00003266335", - "Rotation": "-0.003366441,-0.2651789,0.06858253,-0.9616932", + "Position": "3.066177,0.001466513,-0.0006980896", + "Rotation": "-0.00336647,-0.2651788,0.06858253,-0.9616928", "Enabled": true, "Children": [ { "__guid": "5be460fd-9370-4863-b5de-82ff3070ae60", - "Flags": 0, + "Flags": 4, "Name": "finger_index_1_R", - "Position": "2.279522,0.002900422,-0.001230642", - "Rotation": "-0.0000001490116,-0.2334951,0.0000001788139,-0.9722833", + "Position": "2.27968,0.001564503,-0.002193451", + "Rotation": "0.00000002980232,-0.2334953,0.0000003129244,-0.972284", "Enabled": true, "Children": [ { "__guid": "215cd42e-5881-4300-8d82-74b3e573bcbb", - "Flags": 0, + "Flags": 4, "Name": "finger_index_2_R", - "Position": "1.589059,0.003142416,-0.003608108", - "Rotation": "-0.00000007450581,-0.01372504,0.0000004172325,-0.9998136", + "Position": "1.589418,0.001976013,-0.004562378", + "Rotation": "-0.0000001639128,-0.01372503,0.0000003576279,-0.9998149", "Enabled": true } ] @@ -647,34 +648,34 @@ }, { "__guid": "590202fa-d1f2-4d0a-9c4e-63cad36df0ec", - "Flags": 0, + "Flags": 4, "Name": "finger_middle_meta_R", - "Position": "2.099049,-0.1576577,-0.2863335", - "Rotation": "0.008934096,0.03483143,0.01933622,-0.9991246", + "Position": "2.098953,-0.1583447,-0.2866611", + "Rotation": "0.008934066,0.03483152,0.01933625,-0.9991243", "Enabled": true, "Children": [ { "__guid": "5b7b506d-b804-48e3-8390-a37e4aee254d", - "Flags": 0, + "Flags": 4, "Name": "finger_middle_0_R", - "Position": "2.996975,0.002461806,-0.0001957417", - "Rotation": "0.0008631945,-0.2334556,-0.001294434,-0.9723077", + "Position": "2.997021,0.0009996891,-0.0007915497", + "Rotation": "0.0008631945,-0.2334558,-0.001294464,-0.9723083", "Enabled": true, "Children": [ { "__guid": "3a2205b5-6ea2-414c-a342-5e11746a7f92", - "Flags": 0, + "Flags": 4, "Name": "finger_middle_1_R", - "Position": "2.556989,0.002898887,-0.001322389", - "Rotation": "-0.00000002980232,-0.2322431,0.0000002384186,-0.9725818", + "Position": "2.557173,0.001381397,-0.002202988", + "Rotation": "-0.00000002980232,-0.2322431,0.0000002682209,-0.9725822", "Enabled": true, "Children": [ { "__guid": "a1465f74-c03d-4c07-9562-5fb1e9f5266c", - "Flags": 0, + "Flags": 4, "Name": "finger_middle_2_R", - "Position": "1.6782,0.002957284,-0.003669441", - "Rotation": "-0.0000001639128,-0.04820885,0.000000320375,-0.9987424", + "Position": "1.678555,0.001659393,-0.004465103", + "Rotation": "-0.0000001490116,-0.04820894,0.0000002682209,-0.9987438", "Enabled": true } ] @@ -685,34 +686,34 @@ }, { "__guid": "1fc09b08-cbeb-438b-8b80-8b1e5d07c5d5", - "Flags": 0, + "Flags": 4, "Name": "finger_ring_meta_R", - "Position": "2.103528,-1.44299,-0.2234823", - "Rotation": "-0.06374952,-0.02917719,0.1306494,-0.9889046", + "Position": "2.103191,-1.443681,-0.2240009", + "Rotation": "-0.06374952,-0.02917714,0.1306495,-0.9889044", "Enabled": true, "Children": [ { "__guid": "629acca2-ec4d-49b2-9826-1d36149c4afc", - "Flags": 0, + "Flags": 4, "Name": "finger_ring_0_R", - "Position": "2.821856,0.001830548,-0.0005825162", - "Rotation": "0.004488915,-0.1855044,-0.05619501,-0.9809677", + "Position": "2.821962,0.0005807877,-0.001054764", + "Rotation": "0.004488856,-0.1855043,-0.05619496,-0.9809674", "Enabled": true, "Children": [ { "__guid": "64c370c9-18ee-4737-a78d-4b765073f922", - "Flags": 0, + "Flags": 4, "Name": "finger_ring_1_R", - "Position": "2.193966,0.002415007,-0.001737997", - "Rotation": "-0.00004330277,-0.2565853,0.00004337728,-0.9664457", + "Position": "2.19412,0.001083851,-0.002325058", + "Rotation": "-0.00004324317,-0.2565852,0.00004341453,-0.9664454", "Enabled": true, "Children": [ { "__guid": "0662b27e-6b7f-42b2-9ad9-070074c10bf2", - "Flags": 0, + "Flags": 4, "Name": "finger_ring_2_R", - "Position": "1.531064,0.002551214,-0.004439533", - "Rotation": "-0.0000001788139,-0.05036671,0.0000002086163,-0.9986366", + "Position": "1.53142,0.001292229,-0.004867554", + "Rotation": "-0.0000001788139,-0.05036679,0.0000002756715,-0.9986374", "Enabled": true } ] @@ -725,28 +726,28 @@ }, { "__guid": "f5cb25ae-c6a0-4e26-becd-9b5a5915faac", - "Flags": 0, + "Flags": 4, "Name": "arm_lower_R_twist1", - "Position": "3.850192,0.001937687,-0.001579106", - "Rotation": "-0.01740326,0.00000005960464,0,-0.9998282", + "Position": "3.85017,0.001062393,-0.001922607", + "Rotation": "-0.01740347,-0.00000002980232,0.00000002980232,-0.9998287", "Enabled": true } ] }, { "__guid": "05ad9f39-b8f6-4dce-af85-d6451bb4886f", - "Flags": 0, + "Flags": 4, "Name": "arm_upper_R_twist1", - "Position": "5.020006,0.0005286336,-0.001421452", - "Rotation": "-0.009256572,0.00000002980232,-0.00000002980232,-0.9999448", + "Position": "5.019962,0.0001468658,-0.001689911", + "Rotation": "-0.009134978,0,0.00000002980232,-0.999946", "Enabled": true }, { "__guid": "859eff5e-e24e-4094-adc6-193925b607ef", - "Flags": 0, + "Flags": 4, "Name": "arm_upper_R_twist0", - "Position": "-0.002349854,0.00006580353,-0.001583099", - "Rotation": "-0.06013197,-0.00000002980232,-0.00000002980232,-0.9981781", + "Position": "-0.002380371,-0.00003528595,-0.001647949", + "Rotation": "-0.0593434,-0.00000005960464,-0.00000005960464,-0.9982253", "Enabled": true } ] @@ -755,10 +756,10 @@ }, { "__guid": "b3a3a804-a9dd-4e63-b317-6dc98ef9d8a4", - "Flags": 0, + "Flags": 4, "Name": "neck_clothing", - "Position": "5.820335,-0.09347534,-0.003288269", - "Rotation": "0.0000089854,-0.0002160072,0.01479772,-0.9998878", + "Position": "5.820351,-0.09348488,-0.00327301", + "Rotation": "0.00001524389,-0.0002190322,0.01049453,-0.9999422", "Enabled": true } ] @@ -769,166 +770,166 @@ }, { "__guid": "c7082eb0-7445-455f-bea9-8c19435161a2", - "Flags": 0, + "Flags": 4, "Name": "leg_upper_R", - "Position": "-0.2881527,-0.4264507,-4.427382", - "Rotation": "0.02125055,0.9980782,-0.05431545,0.02094065", + "Position": "-0.288168,-0.4264555,-4.427376", + "Rotation": "0.04860693,0.9969949,-0.0541029,0.02667363", "Enabled": true, "Children": [ { "__guid": "b32f3291-da30-407d-be75-11bb1f67ae52", - "Flags": 0, + "Flags": 4, "Name": "leg_lower_R", - "Position": "14.47042,-0.00000333786,0.000003814697", - "Rotation": "0.004711121,-0.00006237626,0.178974,-0.9838427", + "Position": "14.47041,-0.000002384186,0.000001907349", + "Rotation": "0.006699666,-0.0001024902,0.207234,-0.9782685", "Enabled": true, "Children": [ { "__guid": "482b21bc-f0ce-409d-823e-b8d6d2269469", - "Flags": 0, + "Flags": 4, "Name": "ankle_R", - "Position": "12.51555,-0.000001907349,0.000001907349", - "Rotation": "-0.03533651,0.009895742,-0.4673119,-0.8833309", + "Position": "12.51555,-0.000002622604,0.000001907349", + "Rotation": "-0.03726895,0.006852925,-0.4778947,-0.8775996", "Enabled": true, "Children": [ { "__guid": "96edbe5f-7adf-4361-a639-6c8fe09bc4ea", - "Flags": 0, + "Flags": 4, "Name": "ball_R", - "Position": "4.429122,-0.0000140667,0.000002384186", - "Rotation": "0.0000002272427,-0.000000461936,-0.3905396,-0.9205871", + "Position": "4.429122,-0.00001358986,-0.0000004768372", + "Rotation": "0.0000002868474,-0.0000005215406,-0.3905389,-0.9205851", "Enabled": true } ] }, { "__guid": "523f8614-bc95-4442-abb0-e1ae05fb1654", - "Flags": 0, + "Flags": 4, "Name": "leg_lower_R_twist0", - "Rotation": "0.00000005960464,0.0000001192093,-0.00000008940697,-1", + "Rotation": "0.00000005960464,0.0000001490116,-0.0000001341105,-0.9999999", "Enabled": true }, { "__guid": "1cb339a4-fa36-4cad-bd3d-542c1fa102f1", - "Flags": 0, + "Flags": 4, "Name": "leg_lower_R_twist1", - "Position": "6.25779,-0.00001907349,-0.000004768372", - "Rotation": "-0.02398723,0,0.00000001490116,-0.9997125", + "Position": "6.257796,-0.00001811981,-0.000004768372", + "Rotation": "-0.02546191,0,-0.00000001490116,-0.9996758", "Enabled": true }, { "__guid": "4d69aeee-db99-4768-b4bb-1cf7d03259c2", - "Flags": 0, + "Flags": 4, "Name": "leg_knee_helper_R", - "Position": "-0.007190704,0.05285455,0.0002584457", - "Rotation": "-0.001560916,0.001812547,-0.7461287,-0.6657979", + "Position": "-0.01046753,0.06085324,0.0004358292", + "Rotation": "-0.002286247,0.002573729,-0.7560278,-0.6545307", "Enabled": true } ] }, { "__guid": "bbe8f2e8-e480-4ab3-9dc0-20f8fa200059", - "Flags": 0, + "Flags": 4, "Name": "leg_upper_R_twist1", - "Position": "7.235819,-0.00001811981,-0.000003814697", - "Rotation": "0.005437598,0,0,-0.9999852", + "Position": "7.235819,-0.00001955032,-0.000003814697", + "Rotation": "0.005421802,0,0,-0.9999854", "Enabled": true }, { "__guid": "37ec1971-f2bf-4e61-bcbb-5e385e417200", - "Flags": 0, + "Flags": 4, "Name": "leg_upper_R_twist0", - "Rotation": "0.03533095,-0.00000002980232,-0.00000002980232,-0.9993757", + "Rotation": "0.03523198,0,0,-0.9993791", "Enabled": true }, { "__guid": "d46108e1-4cc8-4a5d-a4fc-11efb1ce3820", - "Flags": 0, + "Flags": 4, "Name": "leg_glute_helper_R", - "Rotation": "-0.0000002980232,-0.0000003241003,0.7070966,-0.7071172", + "Rotation": "-0.0000003278255,-0.0000002011657,0.707096,-0.7071167", "Enabled": true } ] }, { "__guid": "afc7ee64-84d0-478b-b845-dbffc38db7dc", - "Flags": 0, + "Flags": 4, "Name": "leg_upper_L", - "Position": "-0.2881393,-0.4264393,4.427385", - "Rotation": "-0.007433116,0.9984711,0.05426106,0.007487625", + "Position": "-0.2881508,-0.4264402,4.427393", + "Rotation": "0.01967284,0.9982586,0.05401273,0.01323268", "Enabled": true, "Children": [ { "__guid": "69d94113-1d92-4570-a461-9b8f626ac512", - "Flags": 0, + "Flags": 4, "Name": "leg_lower_L", - "Position": "14.47038,0.000004768372,0.000008583069", - "Rotation": "0.004010305,-0.00003439188,0.1198097,-0.9927889", + "Position": "14.47036,0.00001049042,0.000004768372", + "Rotation": "0.006715566,-0.00007283688,0.1493678,-0.9887589", "Enabled": true, "Children": [ { "__guid": "66e60c1d-f2bd-4378-b9b6-3fdd3fc5d9fe", - "Flags": 0, + "Flags": 4, "Name": "ankle_L", - "Position": "12.51555,-0.000001430511,0.000002861023", - "Rotation": "0.02723786,-0.008950412,-0.4382423,-0.8983996", + "Position": "12.51554,-0.0000004768372,-0.0000004768372", + "Rotation": "0.02488759,-0.01157352,-0.4505113,-0.8923488", "Enabled": true, "Children": [ { "__guid": "862b07fd-7c61-474d-a24b-03bf3b697f20", - "Flags": 0, + "Flags": 4, "Name": "ball_L", - "Position": "4.429139,-0.000002622604,-0.000003814697", - "Rotation": "-0.00000008568168,0.0000003129244,0.3907111,0.9205113", + "Position": "4.429137,-0.000005245209,-0.00000667572", + "Rotation": "-0.0000001192093,0.0000003278255,0.39071,0.9205089", "Enabled": true } ] }, { "__guid": "1b62659b-781f-49ab-a38c-bc4918c2f243", - "Flags": 0, + "Flags": 4, "Name": "leg_lower_L_twist0", - "Rotation": "-0.00000008940697,-0.0000002980232,-0.00000004470348,-0.9999992", + "Rotation": "-0.0000001192093,-0.0000002831221,-0.00000005960464,-0.9999982", "Enabled": true }, { "__guid": "f215b579-d99b-4a73-a82b-41c25c97acae", - "Flags": 0, + "Flags": 4, "Name": "leg_lower_L_twist1", - "Position": "6.257804,-0.00002193451,0.000001907349", - "Rotation": "0.01818442,0.00000002980232,0.00000002980232,-0.9998348", + "Position": "6.2578,-0.00002384186,0.000003814697", + "Rotation": "0.01672888,0.00000002980232,0.00000002980232,-0.9998602", "Enabled": true }, { "__guid": "bcb4f9d6-b22f-43f6-8bee-be2026635707", - "Flags": 0, + "Flags": 4, "Name": "leg_knee_helper_L", - "Position": "-0.00188446,0.03569415,0.0001401901", - "Rotation": "0.001566295,-0.001615524,0.7499412,0.6615008", + "Position": "-0.004272461,0.04432109,0.0003070831", + "Rotation": "0.002554005,-0.002648711,0.7602049,0.6496727", "Enabled": true } ] }, { "__guid": "872c631e-66d0-491e-a5d3-18703c10bb82", - "Flags": 0, + "Flags": 4, "Name": "leg_upper_L_twist1", - "Position": "7.235796,-0.00002574921,0", - "Rotation": "-0.0054297,-0.00000001490116,-0.00000004470348,-0.9999853", + "Position": "7.235794,-0.00002479553,-0.000001907349", + "Rotation": "-0.005405545,0.00000001490116,0.00000004470348,-0.9999855", "Enabled": true }, { "__guid": "ab710057-1eb5-4c83-a428-d79899eec4df", - "Flags": 0, + "Flags": 4, "Name": "leg_upper_L_twist0", - "Rotation": "-0.03528263,0,0.00000002980232,-0.9993776", + "Rotation": "-0.03512859,-0.00000001490116,0.00000002980232,-0.9993828", "Enabled": true }, { "__guid": "1319a98f-6a9e-4434-b355-6311d621c89c", - "Flags": 0, + "Flags": 4, "Name": "leg_glute_helper_L", - "Rotation": "-0.0000002682209,-0.0000002249144,0.7070962,-0.7071172", + "Rotation": "-0.0000002682209,-0.0000002533197,0.7070959,-0.707117", "Enabled": true } ] @@ -937,14 +938,14 @@ }, { "__guid": "119ce4a2-be0f-4dea-9d7e-7ca2e5bf2834", - "Flags": 0, + "Flags": 4, "Name": "root_IK", "Position": "1.180664,0.000006914139,0.00001496077", "Enabled": true, "Children": [ { "__guid": "5f6e553c-71cf-47d8-a2a8-085fa016a462", - "Flags": 0, + "Flags": 4, "Name": "hand_R_IK_target", "Position": "-2.719624,-11.35815,33.26634", "Rotation": "0.4582616,0.3513036,-0.6075084,0.5454503", @@ -952,7 +953,7 @@ }, { "__guid": "f4dd759e-5fb0-4065-b438-39d2233d982f", - "Flags": 0, + "Flags": 4, "Name": "hand_L_IK_target", "Position": "-2.719516,11.35806,33.26635", "Rotation": "0.5454503,0.6075078,-0.3513034,0.4582625", @@ -960,14 +961,14 @@ }, { "__guid": "5f8c54a4-c53b-4523-a10e-ded075d50d2c", - "Flags": 0, + "Flags": 4, "Name": "aim_matrix_02a", "Position": "4.638925,-0.0000008343591,9.8425", "Enabled": true, "Children": [ { "__guid": "f67b6696-c5d8-454c-adf1-ca932644a8c4", - "Flags": 0, + "Flags": 4, "Name": "aim_matrix_02b", "Position": "3.937002,-0.0000004822496,-0.0000009536743", "Enabled": true @@ -976,14 +977,14 @@ }, { "__guid": "68eac2de-d762-42ad-aec1-2e6ddac68b31", - "Flags": 0, + "Flags": 4, "Name": "aim_matrix_01", "Position": "4.638925,-0.0000008343591,11.811", "Enabled": true }, { "__guid": "0c2813a3-57fd-49c5-8133-fc4ea6b5eb0d", - "Flags": 0, + "Flags": 4, "Name": "foot_R_IK_target", "Position": "-0.773421,-4.402194,3.807818", "Rotation": "0.672557,0.2183617,-0.3318372,0.6243953", @@ -991,7 +992,7 @@ }, { "__guid": "fa33beab-3dce-4e46-84c5-80c8fff6815b", - "Flags": 0, + "Flags": 4, "Name": "foot_L_IK_target", "Position": "-0.769591,4.402877,3.80613", "Rotation": "0.6243098,0.3319235,-0.2184912,0.6725516", diff --git a/Assets/scenes/minimal.scene b/Assets/scenes/minimal.scene index ff37dd1..d898186 100644 --- a/Assets/scenes/minimal.scene +++ b/Assets/scenes/minimal.scene @@ -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 } \ No newline at end of file diff --git a/Code/Inventory.cs b/Code/Inventory.cs new file mode 100644 index 0000000..2ecd8a8 --- /dev/null +++ b/Code/Inventory.cs @@ -0,0 +1,88 @@ +using SWB.Shared; + +public class Inventory : Component, IInventory +{ + [Sync] public NetList Items { get; set; } = new(); + [Sync] public new GameObject Active { get; set; } + + Kal player; + + protected override void OnAwake() + { + player = Components.Get(); + } + + public void Add( GameObject gameObject, bool makeActive = false ) + { + if ( !Has( gameObject ) ) + { + Items.Add( gameObject ); + } + + if ( makeActive ) + SetActive( gameObject ); + else + { + if ( gameObject.Components.TryGet( 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( out var oldActive ) ) + { + if ( !oldActive.CanCarryStop() ) return; + oldActive.OnCarryStop(); + } + + if ( gameObject.Components.TryGet( 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; + } +} diff --git a/Code/Kal.cs b/Code/Kal.cs index dee6008..3e29f5c 100644 --- a/Code/Kal.cs +++ b/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 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(); + CharacterController = Components.Get(); AnimationHelper = Components.Get(); RagdollController = Components.Get(); if ( !Network.IsOwner ) return; - - Renderer = Components.Get(FindMode.EverythingInSelfAndDescendants); - Camera = Scene.Camera.GameObject; - Camera.SetParent(GameObject); + BodyRenderer = Components.Get(FindMode.EverythingInSelfAndDescendants); + Camera = Scene.Camera; + + Camera.GameObject.SetParent(GameObject); var cameraComponent = Camera.Components.Create(); + cameraComponent.ZFar = 32768f; + + var weaponRegistery = Scene.Components.GetInChildren(); + var weaponGO = weaponRegistery.Get( "Pistol" ); + var weapon = weaponGO.Components.Get( true ); + weaponGO.SetParent(GameObject); + // weaponGO.Enabled = true; + + Inventory = Components.Create(); + 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); } diff --git a/Code/RagdollController.cs b/Code/RagdollController.cs index 7097015..7c8332d 100644 --- a/Code/RagdollController.cs +++ b/Code/RagdollController.cs @@ -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 diff --git a/Code/swb_base/BulletBase.HitScan.cs b/Code/swb_base/BulletBase.HitScan.cs new file mode 100644 index 0000000..04e08ae --- /dev/null +++ b/Code/swb_base/BulletBase.HitScan.cs @@ -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(); + if ( !target.IsAlive ) return; + + var hitTags = Array.Empty(); + + 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() ); + } +} diff --git a/Code/swb_base/BulletBase.cs b/Code/swb_base/BulletBase.cs new file mode 100644 index 0000000..537fdf4 --- /dev/null +++ b/Code/swb_base/BulletBase.cs @@ -0,0 +1,8 @@ +namespace SWB.Base; + +public interface IBulletBase +{ + public void Shoot( Weapon weapon, ShootInfo shootInfo, Vector3 spreadOffset ); + + public Vector3 GetRandomSpread( float spread ); +} diff --git a/Code/swb_base/Commands.cs b/Code/swb_base/Commands.cs new file mode 100644 index 0000000..7df5145 --- /dev/null +++ b/Code/swb_base/Commands.cs @@ -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; +// } +// } diff --git a/Code/swb_base/Weapon.Attachments.cs b/Code/swb_base/Weapon.Attachments.cs new file mode 100644 index 0000000..f120e0d --- /dev/null +++ b/Code/swb_base/Weapon.Attachments.cs @@ -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; + } +} diff --git a/Code/swb_base/Weapon.Extra.cs b/Code/swb_base/Weapon.Extra.cs new file mode 100644 index 0000000..5e1b6ac --- /dev/null +++ b/Code/swb_base/Weapon.Extra.cs @@ -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; + // } +} diff --git a/Code/swb_base/Weapon.Getters.cs b/Code/swb_base/Weapon.Getters.cs new file mode 100644 index 0000000..42026cb --- /dev/null +++ b/Code/swb_base/Weapon.Getters.cs @@ -0,0 +1,129 @@ +using SWB.Base.Attachments; + +namespace SWB.Base; + +public partial class Weapon +{ + public virtual SkinnedModelRenderer GetEffectRenderer() + { + SkinnedModelRenderer effectModel = WorldModelRenderer; + + return effectModel; + } + + /// + /// Gets the info on where to show the muzzle effect + /// + 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 ); + } + + /// + /// Gets the correct shoot animation + /// + /// Info used for the current attack + /// + 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; + } + + /// + /// If there is usable ammo left + /// + 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; + } +} diff --git a/Code/swb_base/Weapon.Reload.cs b/Code/swb_base/Weapon.Reload.cs new file mode 100644 index 0000000..5caf26d --- /dev/null +++ b/Code/swb_base/Weapon.Reload.cs @@ -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 ); + } +} diff --git a/Code/swb_base/Weapon.Scoping.cs b/Code/swb_base/Weapon.Scoping.cs new file mode 100644 index 0000000..c37ed3f --- /dev/null +++ b/Code/swb_base/Weapon.Scoping.cs @@ -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 ); + } +} diff --git a/Code/swb_base/Weapon.Shoot.cs b/Code/swb_base/Weapon.Shoot.cs new file mode 100644 index 0000000..37df679 --- /dev/null +++ b/Code/swb_base/Weapon.Shoot.cs @@ -0,0 +1,293 @@ +using SWB.Shared; +using System; +using System.Collections.Generic; + +namespace SWB.Base; + +public partial class Weapon +{ + /// + /// Checks if the weapon can do the provided attack + /// + /// Attack information + /// Time since this attack + /// The input button for this attack + /// + 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 ); + } + + /// + /// Checks if weapon can do the primary attack + /// + public virtual bool CanPrimaryShoot() + { + return CanShoot( Primary, TimeSincePrimaryShoot, InputButtonHelper.PrimaryAttack ); + } + + /// + /// Checks if weapon can do the secondary attack + /// + 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 ); + } + + /// A single bullet trace from start to end with a certain radius. + public virtual SceneTraceResult TraceBullet( Vector3 start, Vector3 end, float radius = 2.0f ) + { + var startsInWater = SurfaceUtil.IsPointWater( start ); + List 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(); + } + } + + /// Create a bullet impact effect + 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( 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.Material = decalEntry.Material; + decalRenderer.Size = new( decalEntry.Height.GetValue(), decalEntry.Height.GetValue(), decalEntry.Depth.GetValue() ); + gameObject.DestroyAsync( 30f ); + } + } + } + + /// Create a weapon particle + public virtual void CreateParticle( ParticleSystem particle, string attachment, float scale, Action 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 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 ); + } +} diff --git a/Code/swb_base/Weapon.UI.cs b/Code/swb_base/Weapon.UI.cs new file mode 100644 index 0000000..5677c62 --- /dev/null +++ b/Code/swb_base/Weapon.UI.cs @@ -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; + + /// Override this if you want custom UI elements + public virtual void CreateUI() + { + ScreenPanel = Components.Create(); + ScreenPanel.Opacity = 1; + ScreenPanel.ZIndex = 1; + + var rootPanel = Components.Create(); + 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 ); + } +} diff --git a/Code/swb_base/Weapon.Var.cs b/Code/swb_base/Weapon.Var.cs new file mode 100644 index 0000000..6228e8d --- /dev/null +++ b/Code/swb_base/Weapon.Var.cs @@ -0,0 +1,164 @@ +using Sandbox.Citizen; + +namespace SWB.Base; + +public partial class Weapon +{ + /// Thirdperson Model + [Property, Group( "Models" )] public Model WorldModel { get; set; } + + + /// Unique name that identifies the weapon + [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; } + + /// How the player holds the weapon in thirdperson + [Property, Group( "General" )] public AnimationHelper.HoldTypes HoldType { get; set; } = AnimationHelper.HoldTypes.Pistol; + + /// Mouse sensitivity while aiming (lower is slower, 0 to disable) + [Property, Group( "General" )] public float AimSensitivity { get; set; } = 0.85f; + + /// Can bullets be cocked in the barrel? (clip ammo + 1) + [Property, Group( "General" )] public bool BulletCocking { get; set; } = true; + + /// Range that tucking should be enabled (-1 to disable tucking) + [Property, Group( "General" )] public float TuckRange { get; set; } = 30f; + + [Property, Group( "General" )] public int Slot { get; set; } = 0; + + /// Firing sound when clip is empty + [Property, Group( "Sounds" )] public SoundEvent DeploySound { get; set; } + + + /// Default weapon field of view + [Property, Group( "FOV" )] public float FOV { get; set; } = 70f; + + /// Weapon FOV while aiming (-1 to use default weapon fov) + [Property, Group( "FOV" )] public float AimFOV { get; set; } = -1f; + + /// Player FOV while aiming (-1 to use default player fov) + [Property, Group( "FOV" )] public float AimPlayerFOV { get; set; } = -1f; + + /// FOV aim in speed + [Property, Group( "FOV" ), Title( "Aim in FOV speed" )] public float AimInFOVSpeed { get; set; } = 1f; + + /// FOV aim out speed + [Property, Group( "FOV" ), Title( "Aim out FOV speed" )] public float AimOutFOVSpeed { get; set; } = 1f; + + + /// Procedural animation speed (lower is slower) + [Property, Group( "Animations" )] public float AnimSpeed { get; set; } = 1; + + /// Offset used for setting the weapon to its aim position + [Property, Group( "Animations" ), Title( "Aim Offset (swb_editor_offsets)" )] public AngPos AimAnimData { get; set; } + + /// Offset used for setting the weapon to its run position + [Property, Group( "Animations" ), Title( "Run Offset (swb_editor_offsets)" )] public AngPos RunAnimData { get; set; } + + /// Offset used for setting the weapon to its run position + [Property, Group( "Animations" ), Title( "Customizing Offset (swb_editor_offsets)" )] public AngPos CustomizeAnimData { get; set; } + + /// Duration of the reload animation + [Property, Group( "Animations" )] public float ReloadTime { get; set; } = 1f; + + /// Reloading animation + [Property, Group( "Animations" )] public string ReloadAnim { get; set; } = "reload"; + + /// Duration of the empty reload animation (-1 to disable) + [Property, Group( "Animations" )] public float ReloadEmptyTime { get; set; } = -1f; + + /// Reloading animation when clip is empty + [Property, Group( "Animations" )] public string ReloadEmptyAnim { get; set; } = "reload_empty"; + + /// Duration of the draw animation + [Property, Group( "Animations" )] public float DrawTime { get; set; } = 0.5f; + + /// Draw animation + [Property, Group( "Animations" )] public string DrawAnim { get; set; } = "deploy"; + + /// Duration of the empty draw animation (-1 to disable) + [Property, Group( "Animations" )] public float DrawEmptyTime { get; set; } = -1f; + + /// Draw animation when there is no ammo + [Property, Group( "Animations" )] public string DrawEmptyAnim { get; set; } = ""; + + + /// Is the weapon reloading shells instead of a magazine? + [Property, Group( "Shell Reloading" )] public bool ShellReloading { get; set; } = false; + + /// Can the weapon shoot while reloading to cancel the reload? + [Property, Group( "Shell Reloading" )] public bool ShellReloadingShootCancel { get; set; } = true; + + /// Delay in fire animation to eject the shell + [Property, Group( "Shell Reloading" )] public float ShellEjectDelay { get; set; } = 0; + + /// Duration of the shell reload start animation (animation is set with ReloadAnim) + [Property, Group( "Shell Reloading" )] public float ShellReloadStartTime { get; set; } = 0; + + /// Duration of the shell reload insert animation (animation is set in animgraph) + [Property, Group( "Shell Reloading" )] public float ShellReloadInsertTime { get; set; } = 0; + + + /// Is this a bolt action weapon? + [Property, Group( "Bolt Action Reloading" ), Title( "Bolt Action" )] public bool BoltBack { get; set; } = false; + + /// Duration of the boltback animation + [Property, Group( "Bolt Action Reloading" )] public float BoltBackTime { get; set; } = 0f; + + /// Boltback animation + [Property, Group( "Bolt Action Reloading" )] public string BoltBackAnim { get; set; } = "boltback"; + + /// Bullet eject delay during the boltback animation (-1 to disable) + [Property, Group( "Bolt Action Reloading" )] public float BoltBackEjectDelay { get; set; } = 0f; + + /// Enable scoping, renders a 2D scope on ADS + [Property, Group( "Scoping" )] public bool Scoping { get; set; } = false; + + /// Scope Information + [Property, Group( "Scoping" )] public ScopeInfo ScopeInfo { get; set; } = new(); + + /// Primary attack data + [Property, Group( "Firing" ), Title( "Primary ShootInfo (component)" )] public ShootInfo Primary { get; set; } = new(); + + /// Secondary attack data (setting this will disable weapon aiming) + [Property, Group( "Firing" ), Title( "Secondary ShootInfo (component)" )] public ShootInfo Secondary { get; set; } + + + /// Time since the last primary attack + public TimeSince TimeSincePrimaryShoot { get; set; } + + /// Time since the last secondary attack + public TimeSince TimeSinceSecondaryShoot { get; set; } + + /// Time since deployment + public TimeSince TimeSinceDeployed { get; set; } + + /// Time since the last reload + public TimeSince TimeSinceReload { get; set; } + + public bool IsCustomizing { get; set; } + + /// If the weapon is being reloaded + [Sync] public bool IsReloading { get; set; } + + /// If the weapon is being aimed + [Sync] public bool IsAiming { get; set; } + + /// If the weapon is being scoped + [Sync] public bool IsScoping { get; set; } + + /// If the weapon is being bolt back reloaded + [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; +} diff --git a/Code/swb_base/Weapon.cs b/Code/swb_base/Weapon.cs new file mode 100644 index 0000000..7a9f7f9 --- /dev/null +++ b/Code/swb_base/Weapon.cs @@ -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 Attachments = new(); + + protected override void OnAwake() + { + Tags.Add( TagsHelper.Weapon ); + + Attachments = Components.GetAll( 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(); + 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(); + 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( resourceID ); + if ( sound is null ) return; + + Sound.Play( sound, WorldPosition ); + + } +} diff --git a/Code/swb_base/WeaponRegistry.cs b/Code/swb_base/WeaponRegistry.cs new file mode 100644 index 0000000..faaaea6 --- /dev/null +++ b/Code/swb_base/WeaponRegistry.cs @@ -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 WeaponPrefabs { get; set; } = new(); + public Dictionary Weapons { get; set; } = new(); + + static public WeaponRegistry Instance + { + get + { + return Game.ActiveScene.Components.GetInChildren(); + } + } + + protected override void OnAwake() + { + WeaponPrefabs.ForEach( weaponPrefab => + { + var weaponGO = weaponPrefab.Clone(); + weaponGO.SetParent( this.GameObject ); + weaponGO.Enabled = false; + + var weapon = weaponGO.Components.Get( 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( true ); + } +} diff --git a/Code/swb_base/WeaponSettings.cs b/Code/swb_base/WeaponSettings.cs new file mode 100644 index 0000000..0fdefa2 --- /dev/null +++ b/Code/swb_base/WeaponSettings.cs @@ -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 +{ + /// Enable the weapon customization menu (Q) + [HostSync, Property] public bool Customization { get; set; } = true; + + /// Reload weapons automatically when trying to shoot if clip is empty + [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(); + } + } +} diff --git a/Code/swb_base/attachments/Attachment.cs b/Code/swb_base/attachments/Attachment.cs new file mode 100644 index 0000000..c14265c --- /dev/null +++ b/Code/swb_base/attachments/Attachment.cs @@ -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 +{ + /// Display name (needs to be unique) + public virtual string Name => ""; + + /// Display description + public virtual string Description => ""; + + /// Only 1 active attachment per category, this is also used to determine what effect attachment to overide + public virtual AttachmentCategory Category => AttachmentCategory.None; + + /// List of positive attributes + public virtual string[] Positives => Array.Empty(); + + /// List of negative attributes + public virtual string[] Negatives => Array.Empty(); + + /// Weapon stats changer + public virtual StatsModifier StatsModifier { get; set; } + + /// Path to an image that represent the attachment on the HUD + public virtual string IconPath => ""; + + /// Path to the attachment model + public virtual string ModelPath => ""; + + /// Name of the model attachment used for new effect origins + public virtual string EffectAttachmentOrBone { get; set; } = ""; + + /// Hide this attachment in menus + public virtual bool Hide { get; set; } = false; + + /// Depends on another attachment (e.g. rail/mount) + [Property] public Attachment RequiresAttachment { get; set; } + + /// Name of the bone you want to attach the model to + [Property, Group( "Model Parenting" )] public virtual string Bone { get; set; } + + /// Viewmodel scale + [Property, Group( "Model Parenting" )] public virtual Vector3 ViewModelScale { get; set; } = Vector3.One; + + /// Worldmodel scale + [Property, Group( "Model Parenting" )] public virtual Vector3 WorldModelScale { get; set; } = Vector3.One; + + /// The name of the body group + [Property, Group( "BodyGroup" )] public virtual string BodyGroup { get; set; } + + /// The name of the body group choice + [Property, Group( "BodyGroup" )] public virtual int BodyGroupChoice { get; set; } = 0; + + /// The default target body group value + [Property, Group( "BodyGroup" )] public virtual int BodyGroupDefault { get; set; } = 0; + + /// If already equipped + [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(); + } + + 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(); + 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(); + } + + /// Equips the attachment for everyone + [Broadcast] + public virtual void EquipBroadCast() + { + if ( !IsValid ) return; + Equip(); + } + + /// Equips the attachment + 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(); + } + + /// Unequips the attachment for everyone + [Broadcast] + public virtual void UnEquipBroadCast() + { + if ( !IsValid ) return; + Unequip(); + } + + /// Unequips the attachment + 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(); + } + + /// Gets called after the attachment is equipped + public abstract void OnEquip(); + + /// Gets called after the attachment is unequipped + public abstract void OnUnequip(); + + /// Gets called when the weapon is creating its HUD elements + public virtual void CreateHudElements() { } + + /// Gets called when the weapon is destroying its HUD elements + public virtual void DestroyHudElements() { } + + public int CompareTo( Attachment obj ) + { + if ( obj == null ) + return 1; + + else + return Name.CompareTo( obj.Name ); + } +} diff --git a/Code/swb_base/attachments/types/LaserAttachment.cs b/Code/swb_base/attachments/types/LaserAttachment.cs new file mode 100644 index 0000000..f366aaa --- /dev/null +++ b/Code/swb_base/attachments/types/LaserAttachment.cs @@ -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, + }; + + /// New muzzle flash effect point + [Property, Group( "Laser" )] public override string EffectAttachmentOrBone { get; set; } = "laser_start"; + + /// Laser beam particle + [Property, Group( "Laser" )] public virtual ParticleSystem BeamParticle { get; set; } = ParticleSystem.Load( "particles/swb/laser/laser_small.vpcf" ); + + /// Laser dot particle + [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 ); + } +} diff --git a/Code/swb_base/attachments/types/RailAttachment.cs b/Code/swb_base/attachments/types/RailAttachment.cs new file mode 100644 index 0000000..1129784 --- /dev/null +++ b/Code/swb_base/attachments/types/RailAttachment.cs @@ -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() + { + } +} diff --git a/Code/swb_base/attachments/types/Scope2DAttachment.cs b/Code/swb_base/attachments/types/Scope2DAttachment.cs new file mode 100644 index 0000000..2086538 --- /dev/null +++ b/Code/swb_base/attachments/types/Scope2DAttachment.cs @@ -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[] + { + }; + + /// The new aim position offset + [Property, Group( "Scope" )] public AngPos AimAnimData { get; set; } + AngPos oldAimAnimData; + + /// Scope Information + [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(); + } +} diff --git a/Code/swb_base/attachments/types/SightAttachment.cs b/Code/swb_base/attachments/types/SightAttachment.cs new file mode 100644 index 0000000..8be3b77 --- /dev/null +++ b/Code/swb_base/attachments/types/SightAttachment.cs @@ -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, + }; + + + /// The new aim position offset + [Property, Group( "Sight" )] public AngPos AimAnimData { get; set; } + AngPos oldAimAnimData; + + /// Weapon FOV while aiming (-1 to use default) + [Property, Group( "Sight" )] public virtual float AimFOV { get; set; } = -1f; + float oldAimFOV; + + /// Player FOV while aiming (-1 to use default) + [Property, Group( "Sight" )] public virtual float AimPlayerFOV { get; set; } = -1f; + float oldAimPlayerFOV; + + /// FOV aim in speed (-1 to use default) + [Property, Group( "Sight" ), Title( "Aim in FOV speed" )] public virtual float AimInFOVSpeed { get; set; } = -1f; + float oldAimInFOVSpeed; + + /// FOV aim out speed (-1 to use default) + [Property, Group( "Sight" ), Title( "Aim out FOV speed" )] public virtual float AimOutFOVSpeed { get; set; } = -1f; + private float oldAimOutFOVSpeed; + + /// Mouse sensitivity while aiming (-1 to use default) + [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; + } +} diff --git a/Code/swb_base/attachments/types/SilencerAttachment.cs b/Code/swb_base/attachments/types/SilencerAttachment.cs new file mode 100644 index 0000000..4da6a0e --- /dev/null +++ b/Code/swb_base/attachments/types/SilencerAttachment.cs @@ -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, + }; + + /// New muzzle flash effect point + [Property, Group( "Silencer" )] public override string EffectAttachmentOrBone { get; set; } = "muzzle_silenced"; + + /// New particle used for the muzzle flash + [Property, Group( "Silencer" )] public virtual ParticleSystem MuzzleFlashParticle { get; set; } + ParticleSystem oldMuzzleFlashParticle; + + /// New sound used for firing + [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; + } +} diff --git a/Code/swb_base/extensions/GameObjectExtensions.cs b/Code/swb_base/extensions/GameObjectExtensions.cs new file mode 100644 index 0000000..dc5a5bc --- /dev/null +++ b/Code/swb_base/extensions/GameObjectExtensions.cs @@ -0,0 +1,36 @@ +namespace SWB.Base; + +public sealed class TimedDestroyComponent : Component +{ + /// + /// How long until we destroy the GameObject. + /// + [Property] public float Time { get; set; } = 1f; + + /// + /// The real time until we destroy the GameObject. + /// + [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(); + component.Time = seconds; + } +} diff --git a/Code/swb_base/extensions/ParticleExtensions.cs b/Code/swb_base/extensions/ParticleExtensions.cs new file mode 100644 index 0000000..30f99e6 --- /dev/null +++ b/Code/swb_base/extensions/ParticleExtensions.cs @@ -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 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(); + } +} diff --git a/Code/swb_base/extensions/RandomExtensions.cs b/Code/swb_base/extensions/RandomExtensions.cs new file mode 100644 index 0000000..4b9fe7e --- /dev/null +++ b/Code/swb_base/extensions/RandomExtensions.cs @@ -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; + } +} diff --git a/Code/swb_base/structures/AngPos.cs b/Code/swb_base/structures/AngPos.cs new file mode 100644 index 0000000..e4e2803 --- /dev/null +++ b/Code/swb_base/structures/AngPos.cs @@ -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(); + } +} diff --git a/Code/swb_base/structures/ScopeInfo.cs b/Code/swb_base/structures/ScopeInfo.cs new file mode 100644 index 0000000..97d9bd1 --- /dev/null +++ b/Code/swb_base/structures/ScopeInfo.cs @@ -0,0 +1,25 @@ +namespace SWB.Base; + +public class ScopeInfo +{ + /// 2D lens texture + [Property, ImageAssetPathAttribute] public string LensTexture { get; set; } = "/materials/swb/scopes/swb_lens_hunter.png"; + + /// 2D scope texture + [Property, ImageAssetPathAttribute] public string ScopeTexture { get; set; } = "/materials/swb/scopes/swb_scope_hunter.png"; + + /// Delay between ADS and scoping in ms + [Property] public float ScopeInDelay { get; set; } = 0.2f; + + /// Sound that plays when scoping starts + [Property] public SoundEvent ScopeInSound { get; set; } + + /// Sound that plays when scoping ends + [Property] public SoundEvent ScopeOutSound { get; set; } + + /// Player FOV while scoping + [Property] public float FOV { get; set; } = 8f; + + /// Mouse sensitivity while scoping (lower is slower, 0 to use AimSensitivity while scoped) + [Property] public float AimSensitivity { get; set; } = 0.25f; +} diff --git a/Code/swb_base/structures/ShootInfo.cs b/Code/swb_base/structures/ShootInfo.cs new file mode 100644 index 0000000..d0adeb2 --- /dev/null +++ b/Code/swb_base/structures/ShootInfo.cs @@ -0,0 +1,117 @@ +using SWB.Shared; + +namespace SWB.Base; + +public enum FiringType +{ + /// Single fire + semi, + /// Automatic fire + auto, + /// 3-Burst fire + burst +} + +public enum InfiniteAmmoType +{ + /// No infinite ammo + disabled = 0, + /// Infinite clip ammo, no need to reload + clip = 1, + /// Infinite reserve ammo, can always reload + reserve = 2 +} + +[Group( "SWB" )] +[Title( "ShootInfo" )] +public class ShootInfo : Component +{ + /// Type of ammo + [Property, Group( "Ammo" )] public string AmmoType { get; set; } = "pistol"; + + /// Amount of ammo in the clip + [Property, Group( "Ammo" ), Sync] public int Ammo { get; set; } = 10; + + /// Size of the clip + [Property, Group( "Ammo" )] public int ClipSize { get; set; } = 10; + + /// If the weapon should have infinite ammo + [Property, Group( "Ammo" )] public InfiniteAmmoType InfiniteAmmo { get; set; } = InfiniteAmmoType.disabled; + + // Shooting // + + /// Amount of bullets per shot + [Property, Group( "Bullets" )] public int Bullets { get; set; } = 1; + + /// Bullet size + [Property, Group( "Bullets" )] public float BulletSize { get; set; } = 0.1f; + + /// Bullet type (Hitscan/Physical) + public IBulletBase BulletType { get; set; } = new HitScanBullet(); + + /// Chance the BulletTracerParticle is created (0-1) + [Property, Group( "Bullets" )] public float BulletTracerChance { get; set; } = 0.33f; + + /// Damage per bullet + [Property, Group( "Bullets" )] public float Damage { get; set; } = 5; + + /// Bullet impact force + [Property, Group( "Bullets" )] public float Force { get; set; } = 0.1f; + + /// Bullet hit flinch + [Property, Group( "Bullets" )] public float HitFlinch { get; set; } = 1.25f; + + /// Weapon spread + [Property, Group( "Bullets" )] public float Spread { get; set; } = 0.1f; + + /// Weapon recoil + [Property, Group( "Bullets" )] public float Recoil { get; set; } = 0.1f; + + /// Rate Per Minute, firing speed (higher is faster) + [Property, Group( "Bullets" )] public int RPM { get; set; } = 200; + + /// Screenshake per shot + [Property, Group( "Bullets" )] public ScreenShake ScreenShake { get; set; } + + /// Weapon firing type + [Property, Group( "Bullets" )] public FiringType FiringType { get; set; } = FiringType.semi; + + // Animations // + + /// Animation used for shooting + [Property, Group( "Animations" )] public string ShootAnim { get; set; } = "fire"; + + /// Animation used for shooting the last bullet + [Property, Group( "Animations" )] public string ShootEmptyAnim { get; set; } = ""; + + /// Animation used for shooting while aiming + [Property, Group( "Animations" )] public string ShootAimedAnim { get; set; } + + // Sounds // + + /// Firing sound when clip is empty + [Property, Group( "Sounds" )] public SoundEvent DryShootSound { get; set; } + + /// Firing sound + [Property, Group( "Sounds" )] public SoundEvent ShootSound { get; set; } + + // Particles // + + /// View Model particle scale + [Property, Title( "View Model Scale" ), Group( "Particles" )] public float VMParticleScale { get; set; } = 1f; + + /// World Model particle scale + [Property, Title( "World Model Scale" ), Group( "Particles" )] public float WMParticleScale { get; set; } = 1f; + + /// Particle used for bullet ejection + [Property, Group( "Particles" )] public ParticleSystem BulletEjectParticle { get; set; } + + /// Particle used for the muzzle flash + [Property, Group( "Particles" )] public ParticleSystem MuzzleFlashParticle { get; set; } + + /// Particle used for the barrel smoke + [Property, Group( "Particles" )] public ParticleSystem BarrelSmokeParticle { get; set; } + + /// Particle used for the barrel smoke + [Property, Group( "Particles" )] public ParticleSystem BulletTracerParticle { get; set; } +} diff --git a/Code/swb_base/structures/StatsModifier.cs b/Code/swb_base/structures/StatsModifier.cs new file mode 100644 index 0000000..9917bbf --- /dev/null +++ b/Code/swb_base/structures/StatsModifier.cs @@ -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); + } +} diff --git a/Code/swb_base/ui/Crosshair.cs b/Code/swb_base/ui/Crosshair.cs new file mode 100644 index 0000000..478dcbb --- /dev/null +++ b/Code/swb_base/ui/Crosshair.cs @@ -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(); + } +} diff --git a/Code/swb_base/ui/Crosshair.cs.scss b/Code/swb_base/ui/Crosshair.cs.scss new file mode 100644 index 0000000..2cb02ae --- /dev/null +++ b/Code/swb_base/ui/Crosshair.cs.scss @@ -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; + } +} diff --git a/Code/swb_base/ui/CustomizationMenu.cs b/Code/swb_base/ui/CustomizationMenu.cs new file mode 100644 index 0000000..8841cb5 --- /dev/null +++ b/Code/swb_base/ui/CustomizationMenu.cs @@ -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> 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 } ); + } ); + } + + 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" ); + } + } +} diff --git a/Code/swb_base/ui/CustomizationMenu.cs.scss b/Code/swb_base/ui/CustomizationMenu.cs.scss new file mode 100644 index 0000000..9b2cdfc --- /dev/null +++ b/Code/swb_base/ui/CustomizationMenu.cs.scss @@ -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; + } + } + } +} diff --git a/Code/swb_base/ui/RootWeaponDisplay.cs b/Code/swb_base/ui/RootWeaponDisplay.cs new file mode 100644 index 0000000..f9a6fed --- /dev/null +++ b/Code/swb_base/ui/RootWeaponDisplay.cs @@ -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 ); + } + } +} diff --git a/Code/swb_base/ui/RootWeaponDisplay.cs.scss b/Code/swb_base/ui/RootWeaponDisplay.cs.scss new file mode 100644 index 0000000..181d273 --- /dev/null +++ b/Code/swb_base/ui/RootWeaponDisplay.cs.scss @@ -0,0 +1,5 @@ +RootWeaponDisplay { + position: absolute; + width: 100%; + height: 100%; +} diff --git a/Code/swb_base/ui/SniperScope.cs b/Code/swb_base/ui/SniperScope.cs new file mode 100644 index 0000000..1ca4bef --- /dev/null +++ b/Code/swb_base/ui/SniperScope.cs @@ -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 ); + } +} diff --git a/Code/swb_base/ui/SniperScope.cs.scss b/Code/swb_base/ui/SniperScope.cs.scss new file mode 100644 index 0000000..6fc1951 --- /dev/null +++ b/Code/swb_base/ui/SniperScope.cs.scss @@ -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%; + } +} diff --git a/Code/swb_base/util/MathUtil.cs b/Code/swb_base/util/MathUtil.cs new file mode 100644 index 0000000..1c234e3 --- /dev/null +++ b/Code/swb_base/util/MathUtil.cs @@ -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 ); + } +} diff --git a/Code/swb_base/util/ModelUtil.cs b/Code/swb_base/util/ModelUtil.cs new file mode 100644 index 0000000..f74dabe --- /dev/null +++ b/Code/swb_base/util/ModelUtil.cs @@ -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; + } +} diff --git a/Code/swb_base/util/SurfaceUtil.cs b/Code/swb_base/util/SurfaceUtil.cs new file mode 100644 index 0000000..2ad10db --- /dev/null +++ b/Code/swb_base/util/SurfaceUtil.cs @@ -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 PenetratableSurfaces = new() + { + "water", + "glass", + "glass.pane" + }; + + public static List 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 ); + } +} diff --git a/Code/swb_base/util/TableUtil.cs b/Code/swb_base/util/TableUtil.cs new file mode 100644 index 0000000..ccef285 --- /dev/null +++ b/Code/swb_base/util/TableUtil.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; + +namespace SWB.Base; + +class TableUtil +{ + public static T GetRandom( List list ) + { + if ( list.Count == 0 ) return default; + + var random = new Random(); + var randI = random.Next( list.Count ); + return list[randI]; + } +} diff --git a/Code/swb_shared/DamageInfo.cs b/Code/swb_shared/DamageInfo.cs new file mode 100644 index 0000000..60d2588 --- /dev/null +++ b/Code/swb_shared/DamageInfo.cs @@ -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(), + }; + } +} diff --git a/Code/swb_shared/IInventory.cs b/Code/swb_shared/IInventory.cs new file mode 100644 index 0000000..5bf0dc3 --- /dev/null +++ b/Code/swb_shared/IInventory.cs @@ -0,0 +1,35 @@ +namespace SWB.Shared; + +public interface IInventory +{ + public NetList 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 +{ + /// Inventory slot + public int Slot { get; set; } + + /// Image that represent the item on the HUD + public string Icon { get; set; } + + /// Name that represent the item on the HUD + public string DisplayName { get; set; } + + /// Called on the GameObject that will be the new active one (Broadcast for networked gameObjects!) + public void OnCarryStart(); + + /// Called when the GameObject stops being the active one (Broadcast for networked gameObjects!) + public void OnCarryStop(); + + /// Can the GameObject be switched out + public bool CanCarryStop(); +} diff --git a/Code/swb_shared/IPlayerBase.cs b/Code/swb_shared/IPlayerBase.cs new file mode 100644 index 0000000..165c8cd --- /dev/null +++ b/Code/swb_shared/IPlayerBase.cs @@ -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; } + + // /// Input sensitivity modifier + // public float InputSensitivity { get; set; } + + /// + /// Called when the weapon wants to know how much ammo is available + /// + /// The type of ammo + /// How much ammo is available + public int AmmoCount( string type ); + + /// + /// Called when the weapon is trying to take ammo. + /// + /// The type of ammo + /// The amount of ammo requested + /// How much ammo was actually taken + public int TakeAmmo( string type, int amount ); + + /// + /// Called when the player takes damage + /// + /// Information about the damage + public void TakeDamage( DamageInfo info ); + + /// + /// Shakes the camera + /// + /// Information about the shake + public void ShakeScreen( ScreenShake screenShake ); +} diff --git a/Code/swb_shared/InputButtonHelper.cs b/Code/swb_shared/InputButtonHelper.cs new file mode 100644 index 0000000..ef62e4d --- /dev/null +++ b/Code/swb_shared/InputButtonHelper.cs @@ -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"; +} diff --git a/Code/swb_shared/ScreenShake.cs b/Code/swb_shared/ScreenShake.cs new file mode 100644 index 0000000..10a52d6 --- /dev/null +++ b/Code/swb_shared/ScreenShake.cs @@ -0,0 +1,16 @@ +namespace SWB.Shared; + +public class ScreenShake +{ + /// Duration (s) + [KeyProperty] public float Duration { get; set; } = 0f; + + /// Delay between shakes (s) + [KeyProperty] public float Delay { get; set; } = 0f; + + /// Screen disposition amount + [KeyProperty] public float Size { get; set; } = 0f; + + /// Screen rotation amount + [KeyProperty] public float Rotation { get; set; } = 0f; +} diff --git a/Code/swb_shared/TagsHelper.cs b/Code/swb_shared/TagsHelper.cs new file mode 100644 index 0000000..f82783f --- /dev/null +++ b/Code/swb_shared/TagsHelper.cs @@ -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"; +} diff --git a/kakozuzo_2.sln.DotSettings.user b/kakozuzo_2.sln.DotSettings.user index ca5de4c..aa7380a 100644 --- a/kakozuzo_2.sln.DotSettings.user +++ b/kakozuzo_2.sln.DotSettings.user @@ -1,8 +1,11 @@  True True + ForceIncluded ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded + ForceIncluded \ No newline at end of file