diff --git a/godot/assets/Shotgun.fbx.import b/godot/assets/Shotgun.fbx.import new file mode 100644 index 0000000..f88116d --- /dev/null +++ b/godot/assets/Shotgun.fbx.import @@ -0,0 +1,46 @@ +[remap] + +importer="scene" +importer_version=1 +type="PackedScene" +uid="uid://dnh13kxeqbxuu" +path="res://.godot/imported/Shotgun.fbx-c2b7fc9bafc73d2492e9aaff879f9e24.scn" + +[deps] + +source_file="res://assets/Shotgun.fbx" +dest_files=["res://.godot/imported/Shotgun.fbx-c2b7fc9bafc73d2492e9aaff879f9e24.scn"] + +[params] + +nodes/root_type="" +nodes/root_name="" +nodes/root_script=null +mesh_library/use_node_names_as_mesh_names=false +array_mesh/deduplicate_surfaces=true +nodes/apply_root_scale=true +nodes/root_scale=1.0 +nodes/import_as_skeleton_bones=false +nodes/use_name_suffixes=true +nodes/use_node_type_suffixes=true +meshes/ensure_tangents=true +meshes/generate_lods=true +meshes/create_shadow_meshes=true +meshes/light_baking=1 +meshes/lightmap_texel_size=0.2 +meshes/force_disable_compression=false +skins/use_named_skins=true +animation/import=true +animation/fps=30 +animation/trimming=true +animation/remove_immutable_tracks=true +animation/import_rest_as_RESET=false +import_script/path="" +materials/extract=0 +materials/extract_format=0 +materials/extract_path="" +_subresources={} +fbx/importer=0 +fbx/allow_geometry_helper_nodes=false +fbx/embedded_image_handling=1 +fbx/naming_version=2 diff --git a/godot/mesh/shotgun.tscn b/godot/mesh/shotgun.tscn new file mode 100644 index 0000000..ea93123 --- /dev/null +++ b/godot/mesh/shotgun.tscn @@ -0,0 +1,12 @@ +[gd_scene format=3 uid="uid://b8nwd3323kwlc"] + +[ext_resource type="PackedScene" uid="uid://dnh13kxeqbxuu" path="res://assets/Shotgun.fbx" id="1_wwlim"] +[ext_resource type="Texture2D" uid="uid://bis6yrsc7uo1p" path="res://raw_assets/Soldier/Shotgun.png" id="2_cseg6"] + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_cseg6"] +albedo_texture = ExtResource("2_cseg6") + +[node name="Shotgun" unique_id=1412770732 instance=ExtResource("1_wwlim")] + +[node name="Shotgun" parent="." index="0" unique_id=596235308] +surface_material_override/0 = SubResource("StandardMaterial3D_cseg6") diff --git a/godot/project.godot b/godot/project.godot index c8e2dd8..04811ae 100644 --- a/godot/project.godot +++ b/godot/project.godot @@ -104,6 +104,16 @@ open_chat={ "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194309,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) ] } +weapon_1={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":49,"key_label":0,"unicode":49,"location":0,"echo":false,"script":null) +] +} +weapon_2={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":50,"key_label":0,"unicode":50,"location":0,"echo":false,"script":null) +] +} [physics] diff --git a/godot/raw_assets/Soldier/Shotgun.blend b/godot/raw_assets/Soldier/Shotgun.blend index 7fac9ea..068dcde 100644 Binary files a/godot/raw_assets/Soldier/Shotgun.blend and b/godot/raw_assets/Soldier/Shotgun.blend differ diff --git a/godot/raw_assets/Soldier/Shotgun.blend1 b/godot/raw_assets/Soldier/Shotgun.blend1 index e0b0f8c..7fac9ea 100644 Binary files a/godot/raw_assets/Soldier/Shotgun.blend1 and b/godot/raw_assets/Soldier/Shotgun.blend1 differ diff --git a/godot/raw_assets/Soldier/Shotgun.png.import b/godot/raw_assets/Soldier/Shotgun.png.import new file mode 100644 index 0000000..d63b077 --- /dev/null +++ b/godot/raw_assets/Soldier/Shotgun.png.import @@ -0,0 +1,41 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bis6yrsc7uo1p" +path.s3tc="res://.godot/imported/Shotgun.png-6f3d985edecc9116b957d4c0485a368d.s3tc.ctex" +metadata={ +"imported_formats": ["s3tc_bptc"], +"vram_texture": true +} + +[deps] + +source_file="res://raw_assets/Soldier/Shotgun.png" +dest_files=["res://.godot/imported/Shotgun.png-6f3d985edecc9116b957d4c0485a368d.s3tc.ctex"] + +[params] + +compress/mode=2 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/godot/scenes/player/shotgun.tscn b/godot/scenes/player/shotgun.tscn new file mode 100644 index 0000000..871ed14 --- /dev/null +++ b/godot/scenes/player/shotgun.tscn @@ -0,0 +1,20 @@ +[gd_scene format=3 uid="uid://deyfeiqyi0wmj"] + +[ext_resource type="PackedScene" uid="uid://ctslratcr8h6v" path="res://scenes/player/raygun_beam.tscn" id="1_0xpw2"] +[ext_resource type="PackedScene" uid="uid://b8nwd3323kwlc" path="res://mesh/shotgun.tscn" id="3_0xpw2"] +[ext_resource type="AudioStream" uid="uid://bq553wxrelpdr" path="res://raw_assets/sfx/LaserWithRecoveryEditedCompressed.mp3" id="3_8oj30"] + +[node name="shotgun" type="Raygun" unique_id=573018429 node_paths=PackedStringArray("sound_emitter", "bullet_source")] +sound_emitter = NodePath("sound_emitter") +bullet_prefab = ExtResource("1_0xpw2") +bullet_source = NodePath("bullet_source") +cooldown = 1.5 + +[node name="sound_emitter" type="AudioStreamPlayer3D" parent="." unique_id=1572422640] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.8420726, 0) +stream = ExtResource("3_8oj30") + +[node name="bullet_source" type="Node3D" parent="." unique_id=211401237] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.36108458, -1.490972) + +[node name="Shotgun" parent="." unique_id=1412770732 instance=ExtResource("3_0xpw2")] diff --git a/godot/scenes/player/soldier_m.tscn b/godot/scenes/player/soldier_m.tscn index c289320..56882ad 100644 --- a/godot/scenes/player/soldier_m.tscn +++ b/godot/scenes/player/soldier_m.tscn @@ -3,6 +3,7 @@ [ext_resource type="PackedScene" uid="uid://cw7duhhjebypt" path="res://scenes/player/raygun.tscn" id="1_a50o4"] [ext_resource type="PackedScene" uid="uid://ncxcmgdit2j1" path="res://mesh/soldier_m.tscn" id="1_sxefq"] [ext_resource type="PackedScene" uid="uid://ci8r4lgijtalh" path="res://scenes/player/ball_weapon.tscn" id="2_jbny0"] +[ext_resource type="PackedScene" uid="uid://deyfeiqyi0wmj" path="res://scenes/player/shotgun.tscn" id="2_kknoo"] [sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_a50o4"] animation = &"Soldier|StrafeLeft" @@ -49,19 +50,20 @@ hand_attachment = NodePath("bone_attachment_3d/hand") animation_tree = NodePath("animation_tree") skeleton = NodePath("SoldierM/Soldier/Skeleton3D") raygun = ExtResource("1_a50o4") +shotgun = ExtResource("2_kknoo") ball = ExtResource("2_jbny0") look_at = NodePath("look_at_root") hand_remover = NodePath("SoldierM/Soldier/Skeleton3D/player_hand_remover") [node name="SoldierM" parent="." unique_id=1868587353 instance=ExtResource("1_sxefq")] -[node name="Skeleton3D" parent="SoldierM/Soldier" parent_id_path=PackedInt32Array(1868587353, 803584260) index="0" unique_id=1574145981] +[node name="Skeleton3D" parent="SoldierM/Soldier" parent_id_path=PackedInt32Array(1868587353, 1101404322) index="0" unique_id=1074852808] bones/0/position = Vector3(0, 0, 0.00071418716) bones/1/position = Vector3(0, 0, -0.007) -bones/1/rotation = Quaternion(0.6942035, 0, 0, 0.7197788) +bones/1/rotation = Quaternion(0.69334954, 0, 0, 0.7206015) bones/1/scale = Vector3(1, 1, 1) bones/2/position = Vector3(0.002, 3.469447e-18, -1.4901118e-10) -bones/2/rotation = Quaternion(0.9459847, 2.1362002e-08, 2.3277545e-08, -0.3242114) +bones/2/rotation = Quaternion(0.9452424, 2.1415062e-08, 2.3228734e-08, -0.32636914) bones/2/scale = Vector3(1, 1, 1) bones/3/position = Vector3(0, 0.0069999984, 5.169879e-25) bones/3/scale = Vector3(1, 0.99999994, 1) @@ -69,7 +71,7 @@ bones/4/position = Vector3(-2.39752e-11, 0.010049875, -1.4156722e-10) bones/4/rotation = Quaternion(0.73982674, -2.6881349e-08, 7.0005948e-09, 0.67279744) bones/4/scale = Vector3(1, 0.99999994, 0.99999994) bones/6/position = Vector3(-0.002, 3.469447e-18, -1.4901205e-10) -bones/6/rotation = Quaternion(0.9995959, 1.81219e-08, 1.8705832e-08, 0.02842787) +bones/6/rotation = Quaternion(0.99965096, 1.8159414e-08, 1.8669416e-08, 0.026421284) bones/6/scale = Vector3(1, 1, 1) bones/7/position = Vector3(0, 0.0069999984, -5.169879e-25) bones/7/rotation = Quaternion(-0.21973656, 4.264305e-08, 9.604991e-09, 0.9755593) @@ -78,33 +80,33 @@ bones/8/position = Vector3(-2.3975525e-11, 0.010049875, -4.6138777e-11) bones/8/rotation = Quaternion(0.86758894, -7.867721e-09, 3.036851e-08, 0.4972821) bones/8/scale = Vector3(1, 0.99999994, 0.99999994) bones/10/position = Vector3(0, 0.009, 0) -bones/10/rotation = Quaternion(-0.1543622, -0.13157132, 0.01979417, 0.97901464) +bones/10/rotation = Quaternion(-0.15285766, -0.13164644, 0.019584488, 0.97924477) bones/10/scale = Vector3(1, 1, 1) bones/11/position = Vector3(6.8186457e-17, 0.008, 0.001) -bones/11/rotation = Quaternion(0.058568474, 0.00011758354, -0.016027864, 0.9981548) +bones/11/rotation = Quaternion(0.06447923, 0.00012945535, -0.017645404, 0.99776304) bones/11/scale = Vector3(1, 1, 1) bones/12/position = Vector3(-2.2285185e-16, 0.0035, 0.0015000002) bones/12/rotation = Quaternion(-0.70710677, 0, 0, 0.70710677) bones/12/scale = Vector3(1, 1, 1) bones/14/position = Vector3(0.005, 0.005, -1.7336413e-21) -bones/14/rotation = Quaternion(0.8948624, 0.033474486, 0.43199828, -0.10713685) +bones/14/rotation = Quaternion(0.8961431, 0.033815596, 0.4309325, -0.10040586) bones/14/scale = Vector3(1, 0.99999994, 1) bones/15/position = Vector3(-1.3005262e-15, 0.0050000004, -6.938894e-18) -bones/15/rotation = Quaternion(1.0353366e-08, -6.775671e-09, 0.9306435, 0.3659275) +bones/15/rotation = Quaternion(1.0371065e-08, -6.748545e-09, 0.93159807, 0.36349007) bones/15/scale = Vector3(1, 1, 1) bones/16/position = Vector3(7.632945e-11, 0.007071071, 6.938894e-18) -bones/16/rotation = Quaternion(0.0061545833, -0.0946999, -0.18472542, 0.9781977) +bones/16/rotation = Quaternion(0.0037297043, -0.10077756, -0.18467139, 0.9776127) bones/16/scale = Vector3(0.99999994, 0.9999999, 1) bones/18/position = Vector3(-0.005, 0.005, -1.7303326e-21) -bones/18/rotation = Quaternion(0.6569365, 0.16839267, -0.13031884, -0.72325325) +bones/18/rotation = Quaternion(0.6575139, 0.16474941, -0.13285781, -0.7231058) bones/18/scale = Vector3(1, 0.99999994, 1) bones/19/position = Vector3(-1.3005262e-15, 0.0050000004, -6.938894e-18) -bones/19/rotation = Quaternion(-2.1264423e-08, -3.5540214e-08, -0.2685776, 0.9632581) +bones/19/rotation = Quaternion(-2.1580865e-08, -3.5348958e-08, -0.27716652, 0.960822) bones/19/scale = Vector3(1, 1, 1) bones/20/position = Vector3(-2.2286711e-10, 0.007071072, 0) -bones/20/rotation = Quaternion(-0.11203431, -0.4710411, -0.13362855, 0.8647035) +bones/20/rotation = Quaternion(-0.10948259, -0.47254592, -0.13016704, 0.8647373) bones/20/scale = Vector3(1, 0.9999999, 1) -bones/22/position = Vector3(0.01127276, -0.0085772155, 0.0025254297) +bones/22/position = Vector3(0.010810219, -0.0085772155, 0.0026520095) bones/22/rotation = Quaternion(3.090862e-08, 0.70710677, 0.70710677, 3.090862e-08) bones/22/scale = Vector3(1, 1, 1) bones/24/position = Vector3(-0.011000002, 0.0050000013, 0.0070000007) @@ -156,7 +158,7 @@ origin_external_node = NodePath("../../../../look_at_root") transform = Transform3D(1, -2.2737368e-13, 0, 2.2737368e-13, 1, 0, -1.3552527e-20, 0, 1, 0, 0, 0) [node name="bone_attachment_3d" type="BoneAttachment3D" parent="." unique_id=1964353847] -transform = Transform3D(-35.09831, 3.8198574, -93.56024, -83.58222, 43.767776, 33.14208, 42.21521, 89.832, -12.169015, -0.47216344, 3.3898914, 0.7404115) +transform = Transform3D(-35.57116, 3.7920692, -93.38263, -83.207954, 44.21317, 33.490837, 42.55741, 89.61482, -12.571805, -0.4719007, 3.3932352, 0.7389869) bone_name = "HAND.R_end" bone_idx = 17 use_external_skeleton = true diff --git a/rust/src/game_manager.rs b/rust/src/game_manager.rs index 282c4f3..919a3e0 100644 --- a/rust/src/game_manager.rs +++ b/rust/src/game_manager.rs @@ -698,7 +698,7 @@ impl Game { if let Some(player) = self.find_player_mut(player_id) && let Some(character) = &mut player.character { - character.dyn_bind_mut().swap_weapon(weapon_type); + character.dyn_bind_mut().spawn_weapon_common(weapon_type); let player_id = player.id(); self.run_deferred(move |_| { if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer diff --git a/rust/src/net/protocol.rs b/rust/src/net/protocol.rs index 8127838..1c91859 100644 --- a/rust/src/net/protocol.rs +++ b/rust/src/net/protocol.rs @@ -386,6 +386,15 @@ impl NetworkManager { } } } + Package::SwapWeapon(_, weapon_type) => { + if let Some(game) = &mut Game::singleton() + && let Some(player) = + game.bind_mut().find_player_by_addr_mut(&conn.address) + && let Some(character) = &mut player.character + { + character.dyn_bind_mut().swap_weapon_proxy(weapon_type); + } + } _ => {} }, }, diff --git a/rust/src/player/mod.rs b/rust/src/player/mod.rs index cbc9c29..f9a447f 100644 --- a/rust/src/player/mod.rs +++ b/rust/src/player/mod.rs @@ -58,8 +58,10 @@ pub trait IPlayer { fn is_dead(&self) -> bool; fn kill(&mut self, source_player: DamageSource); fn dead_for(&self) -> f64; - fn swap_weapon(&mut self, weapon: WeaponType); + fn spawn_weapon_common(&mut self, weapon: WeaponType); + fn swap_weapon_proxy(&mut self, weapon: WeaponType); fn get_weapon(&self) -> Option>; + fn take_weapon(&mut self) -> Option>; fn drop_ball(&mut self, with_velocity: bool) -> bool; fn get_camera_origin(&self) -> Option>; } @@ -68,6 +70,7 @@ pub trait PlayerCommon { fn player_update(&mut self, delta: f64); fn try_jump(&mut self) -> bool; fn drop_ball_common(&mut self, with_velocity: bool) -> bool; + fn swap_weapon(&mut self, ty: WeaponType); fn get_look_dir(&self) -> Vector3; fn can_take_damage(&self, source: &DamageSource) -> bool; } @@ -105,14 +108,14 @@ impl PlayerCommon for T { } fn drop_ball_common(&mut self, with_velocity: bool) -> bool { - if let Some(weapon) = self.get_weapon() + if let Some(weapon) = self.take_weapon() && let Ok(_) = weapon.try_cast::() { self.run_deferred(move |s| { if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer && let PeerKind::Server(peer, _) = peer { - s.swap_weapon(WeaponType::Raygun); + s.spawn_weapon_common(WeaponType::Raygun); peer.broadcast_reliable(Package::SwapWeapon( s.get_player_id().unwrap_or(0), WeaponType::Raygun, @@ -143,6 +146,28 @@ impl PlayerCommon for T { } } + fn swap_weapon(&mut self, ty: WeaponType) { + if ty == WeaponType::Ball { + return; + } + self.spawn_weapon_common(ty); + self.run_deferred(move |s| { + if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer { + match peer { + PeerKind::Client(peer, socket_addr) => { + peer.send_reliable(socket_addr, Package::SwapWeapon(0, ty)); + } + PeerKind::Server(peer, _) => { + peer.broadcast_reliable(Package::SwapWeapon( + s.get_player_id().unwrap_or(0), + ty, + )); + } + } + } + }); + } + fn get_look_dir(&self) -> Vector3 { if let Some(camera_origin) = self.get_camera_origin() { camera_origin.get_global_basis().col_c().neg() @@ -359,7 +384,9 @@ impl IPlayer for LocalPlayer { } } - fn swap_weapon(&mut self, weapon: WeaponType) { + fn spawn_weapon_common(&mut self, weapon: WeaponType) { + self.drop_ball(true); + if let Some(mesh) = self.soldier_mesh.clone() { match weapon { WeaponType::Raygun => { @@ -367,6 +394,11 @@ impl IPlayer for LocalPlayer { self.spawn_weapon(raygun.instantiate_as::().into_dyn().upcast()); } } + WeaponType::Shotgun => { + if let Some(raygun) = &mesh.bind().shotgun { + self.spawn_weapon(raygun.instantiate_as::().into_dyn().upcast()); + } + } WeaponType::Ball => { if let Some(ball) = &mesh.bind().ball { self.spawn_weapon(ball.instantiate_as::().into_dyn().upcast()); @@ -376,10 +408,18 @@ impl IPlayer for LocalPlayer { } } + fn swap_weapon_proxy(&mut self, weapon: WeaponType) { + self.swap_weapon(weapon); + } + fn get_weapon(&self) -> Option> { self.weapon.clone() } + fn take_weapon(&mut self) -> Option> { + self.weapon.as_ref().take().cloned() + } + fn drop_ball(&mut self, with_velocity: bool) -> bool { self.drop_ball_common(with_velocity) } @@ -427,7 +467,7 @@ impl ICharacterBody3D for LocalPlayer { .connect_other(self, |s| s.update_lock()); } - self.swap_weapon(WeaponType::Raygun); + self.spawn_weapon_common(WeaponType::Raygun); if let Some(camera) = &mut self.camera { camera.set_current(true); @@ -602,6 +642,10 @@ impl ICharacterBody3D for LocalPlayer { } } } + } else if event.is_action_pressed("weapon_1") { + self.swap_weapon(WeaponType::Raygun); + } else if event.is_action_pressed("weapon_2") { + self.swap_weapon(WeaponType::Shotgun); } if let Ok(event) = event.try_cast::() { @@ -850,21 +894,34 @@ impl IPlayer for RemotePlayer { self.is_dead = true; } - fn swap_weapon(&mut self, weapon: WeaponType) { + fn spawn_weapon_common(&mut self, weapon: WeaponType) { + self.drop_ball(true); + if let Some(mesh) = &mut self.soldier_mesh { match weapon { WeaponType::Raygun => mesh.bind_mut().spawn_raygun(), + WeaponType::Shotgun => mesh.bind_mut().spawn_shotgun(), WeaponType::Ball => mesh.bind_mut().spawn_ball(), }; } } + fn swap_weapon_proxy(&mut self, weapon: WeaponType) { + self.swap_weapon(weapon); + } + fn get_weapon(&self) -> Option> { self.soldier_mesh .as_ref() .and_then(|m| m.bind().get_weapon()) } + fn take_weapon(&mut self) -> Option> { + self.soldier_mesh + .as_mut() + .and_then(|m| m.bind_mut().take_weapon().cloned()) + } + fn drop_ball(&mut self, with_velocity: bool) -> bool { self.drop_ball_common(with_velocity) } @@ -889,7 +946,7 @@ impl ICharacterBody3D for RemotePlayer { } }); - self.swap_weapon(WeaponType::Raygun); + self.spawn_weapon_common(WeaponType::Raygun); } fn process(&mut self, delta: f64) { diff --git a/rust/src/player/soldier_mesh.rs b/rust/src/player/soldier_mesh.rs index ffaf131..63c6c7d 100644 --- a/rust/src/player/soldier_mesh.rs +++ b/rust/src/player/soldier_mesh.rs @@ -20,6 +20,8 @@ pub struct SoldierMesh { #[export] pub raygun: Option>, #[export] + pub shotgun: Option>, + #[export] pub ball: Option>, #[export] look_at: Option>, @@ -58,6 +60,15 @@ impl SoldierMesh { } } + pub fn spawn_shotgun(&mut self) -> Option> { + if let Some(shotgun) = &self.shotgun { + let node = shotgun.instantiate_as::(); + self.spawn_weapon(node.into_dyn().upcast()) + } else { + None + } + } + pub fn spawn_ball(&mut self) -> Option> { if let Some(ball) = &self.ball { let node = ball.instantiate_as::(); @@ -86,4 +97,8 @@ impl SoldierMesh { pub fn get_weapon(&self) -> Option> { self.current_weapon.clone() } + + pub fn take_weapon(&self) -> Option<&DynGd> { + self.current_weapon.as_ref().take() + } } diff --git a/rust/src/player/weapon.rs b/rust/src/player/weapon.rs index 3d1382f..8255197 100644 --- a/rust/src/player/weapon.rs +++ b/rust/src/player/weapon.rs @@ -23,6 +23,7 @@ pub trait Weapon { #[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, PartialOrd)] pub enum WeaponType { Raygun, + Shotgun, Ball, }