From ba7da5614c90cb65a1b21e07c32f3307405744e5 Mon Sep 17 00:00:00 2001 From: Sofia Date: Tue, 21 Jul 2026 21:07:50 +0300 Subject: [PATCH] Add ball and be able to pick it up --- godot/mesh/ball_mesh.tscn | 17 ++++++++ godot/scenes/misc/ball.tscn | 16 +------- godot/scenes/player/ball_weapon.tscn | 8 ++++ godot/scenes/player/soldier_m.tscn | 28 +++++++------- rust/src/game_manager.rs | 21 +++++++++- rust/src/net/network_manager.rs | 7 ++++ rust/src/player.rs | 58 ++++++++++++++++++++-------- rust/src/soldier_mesh.rs | 23 ++++++++++- rust/src/weapon.rs | 27 +++++++++++++ 9 files changed, 159 insertions(+), 46 deletions(-) create mode 100644 godot/mesh/ball_mesh.tscn create mode 100644 godot/scenes/player/ball_weapon.tscn diff --git a/godot/mesh/ball_mesh.tscn b/godot/mesh/ball_mesh.tscn new file mode 100644 index 0000000..13b9975 --- /dev/null +++ b/godot/mesh/ball_mesh.tscn @@ -0,0 +1,17 @@ +[gd_scene format=3 uid="uid://r13poamopmnv"] + +[sub_resource type="FastNoiseLite" id="FastNoiseLite_yxhor"] +noise_type = 2 + +[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_yli5f"] +noise = SubResource("FastNoiseLite_yxhor") + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_3g557"] +albedo_color = Color(0, 1, 0, 1) +albedo_texture = SubResource("NoiseTexture2D_yli5f") + +[sub_resource type="SphereMesh" id="SphereMesh_1cgct"] +material = SubResource("StandardMaterial3D_3g557") + +[node name="mesh_instance_3d" type="MeshInstance3D" unique_id=37904686] +mesh = SubResource("SphereMesh_1cgct") diff --git a/godot/scenes/misc/ball.tscn b/godot/scenes/misc/ball.tscn index 3a383b8..df93d50 100644 --- a/godot/scenes/misc/ball.tscn +++ b/godot/scenes/misc/ball.tscn @@ -1,17 +1,6 @@ [gd_scene format=3 uid="uid://dfnbc8sbufa4j"] -[sub_resource type="FastNoiseLite" id="FastNoiseLite_yxhor"] -noise_type = 2 - -[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_yli5f"] -noise = SubResource("FastNoiseLite_yxhor") - -[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_3g557"] -albedo_color = Color(0, 1, 0, 1) -albedo_texture = SubResource("NoiseTexture2D_yli5f") - -[sub_resource type="SphereMesh" id="SphereMesh_1cgct"] -material = SubResource("StandardMaterial3D_3g557") +[ext_resource type="PackedScene" uid="uid://r13poamopmnv" path="res://mesh/ball_mesh.tscn" id="1_texud"] [sub_resource type="SphereShape3D" id="SphereShape3D_1cgct"] @@ -21,8 +10,7 @@ collision_mask = 7 contact_monitor = true max_contacts_reported = 10 -[node name="mesh_instance_3d" type="MeshInstance3D" parent="." unique_id=2076561614] -mesh = SubResource("SphereMesh_1cgct") +[node name="mesh_instance_3d" parent="." unique_id=37904686 instance=ExtResource("1_texud")] [node name="collision_shape_3d" type="CollisionShape3D" parent="." unique_id=1408432914] shape = SubResource("SphereShape3D_1cgct") diff --git a/godot/scenes/player/ball_weapon.tscn b/godot/scenes/player/ball_weapon.tscn new file mode 100644 index 0000000..caaec23 --- /dev/null +++ b/godot/scenes/player/ball_weapon.tscn @@ -0,0 +1,8 @@ +[gd_scene format=3 uid="uid://ci8r4lgijtalh"] + +[ext_resource type="PackedScene" uid="uid://r13poamopmnv" path="res://mesh/ball_mesh.tscn" id="1_5wscr"] + +[node name="ball_weapon" type="BallWeapon" unique_id=53185750] + +[node name="mesh_instance_3d" parent="." unique_id=37904686 instance=ExtResource("1_5wscr")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.29060435) diff --git a/godot/scenes/player/soldier_m.tscn b/godot/scenes/player/soldier_m.tscn index 0bad915..87b97c8 100644 --- a/godot/scenes/player/soldier_m.tscn +++ b/godot/scenes/player/soldier_m.tscn @@ -2,6 +2,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"] [sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_a50o4"] animation = &"Soldier|StrafeLeft" @@ -48,6 +49,7 @@ hand_attachment = NodePath("bone_attachment_3d/hand") animation_tree = NodePath("animation_tree") skeleton = NodePath("SoldierM/Soldier/Skeleton3D") raygun = ExtResource("1_a50o4") +ball = ExtResource("2_jbny0") look_at = NodePath("look_at_root") [node name="SoldierM" parent="." unique_id=1868587353 instance=ExtResource("1_sxefq")] @@ -55,10 +57,10 @@ look_at = NodePath("look_at_root") [node name="Skeleton3D" parent="SoldierM/Soldier" parent_id_path=PackedInt32Array(1868587353, 803584260) index="0" unique_id=1574145981] bones/0/position = Vector3(0, 0, 0.00071418716) bones/1/position = Vector3(0, 0, -0.007) -bones/1/rotation = Quaternion(0.6933472, 0, 0, 0.7206036) +bones/1/rotation = Quaternion(0.69402224, 0, 0, 0.7199537) bones/1/scale = Vector3(1, 1, 1) bones/2/position = Vector3(0.002, 3.469447e-18, -1.4901118e-10) -bones/2/rotation = Quaternion(0.94524616, 2.1414793e-08, 2.3228983e-08, -0.3263583) +bones/2/rotation = Quaternion(0.9458209, 2.1373747e-08, 2.3266757e-08, -0.3246889) 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) @@ -66,7 +68,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.99965113, 1.8159536e-08, 1.8669295e-08, 0.026414612) +bones/6/rotation = Quaternion(0.9996778, 1.8178714e-08, 1.8650622e-08, 0.025387304) 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) @@ -75,33 +77,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.15285265, -0.13164668, 0.01958379, 0.9792455) +bones/10/rotation = Quaternion(-0.15427561, -0.13167602, 0.019766005, 0.97901475) bones/10/scale = Vector3(1, 1, 1) bones/11/position = Vector3(6.8186457e-17, 0.008, 0.001) -bones/11/rotation = Quaternion(0.064451575, 0.0001293997, -0.017637832, 0.99776495) +bones/11/rotation = Quaternion(0.058245, 0.0001169397, -0.01593934, 0.99817514) 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.8961373, 0.033810798, 0.43093914, -0.10043051) +bones/14/rotation = Quaternion(0.8948131, 0.033457648, 0.43200484, -0.10752621) 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.0371126e-08, -6.748454e-09, 0.9316012, 0.36348197) +bones/15/rotation = Quaternion(1.035401e-08, -6.774687e-09, 0.93067807, 0.3658392) bones/15/scale = Vector3(1, 1, 1) bones/16/position = Vector3(7.632945e-11, 0.007071071, 6.938894e-18) -bones/16/rotation = Quaternion(0.0037409808, -0.10074904, -0.18467231, 0.9776154) +bones/16/rotation = Quaternion(0.0062737567, -0.0943689, -0.18486792, 0.978202) 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.6575101, 0.16476046, -0.1328446, -0.7231091) +bones/18/rotation = Quaternion(0.6564217, 0.1684678, -0.13023898, -0.7237176) 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.1579417e-08, -3.5349842e-08, -0.2771271, 0.9608333) +bones/19/rotation = Quaternion(-2.1246766e-08, -3.5550773e-08, -0.26809898, 0.9633914) bones/19/scale = Vector3(1, 1, 1) bones/20/position = Vector3(-2.2286711e-10, 0.007071072, 0) -bones/20/rotation = Quaternion(-0.10949359, -0.472547, -0.13018106, 0.86473316) +bones/20/rotation = Quaternion(-0.112210214, -0.47133064, -0.13382514, 0.8644924) bones/20/scale = Vector3(1, 0.9999999, 1) -bones/22/position = Vector3(0.010811842, -0.0085772155, 0.0026515652) +bones/22/position = Vector3(0.011061782, -0.0085772155, 0.0025831666) 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) @@ -150,7 +152,7 @@ origin_from = 2 origin_external_node = NodePath("../../../../look_at_root") [node name="bone_attachment_3d" type="BoneAttachment3D" parent="." unique_id=1964353847] -transform = Transform3D(-35.56741, 3.792272, -93.38403, -83.20849, 44.2147, 33.48737, 42.559406, 89.614044, -12.570532, -0.4718982, 3.3932464, 0.73897976) +transform = Transform3D(-35.07064, 3.824201, -93.57043, -83.59483, 43.760143, 33.120216, 42.213154, 89.83552, -12.150139, -0.47213513, 3.3899364, 0.7408379) 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 d10e9a6..d179671 100644 --- a/rust/src/game_manager.rs +++ b/rust/src/game_manager.rs @@ -16,6 +16,7 @@ use crate::{ util::NetVector3, }, player::{DamageSource, IPlayer, LocalPlayer, NetTransform, RemotePlayer}, + weapon::WeaponType, }; pub const GAME_MANAGER_GLOBAL: &str = "GameManagerGlobal"; @@ -329,7 +330,25 @@ impl Game { } pub fn handle_ball_pickup(&mut self, player_id: u16) { - self.despawn_ball(); + if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer + && let PeerKind::Server(peer, _) = peer + { + self.despawn_ball(); + if let Some(player) = self.find_player_mut(player_id) + && let Some(character) = &mut player.character + { + character.dyn_bind_mut().swap_weapon(WeaponType::Ball); + peer.broadcast_reliable(Package::SwapWeapon(player.id, WeaponType::Ball)); + } + } + } + + pub fn handle_swap_weapon(&mut self, player_id: u16, weapon_type: WeaponType) { + if let Some(player) = self.find_player_mut(player_id) + && let Some(character) = &mut player.character + { + character.dyn_bind_mut().swap_weapon(weapon_type); + } } pub fn despawn_ball(&mut self) { diff --git a/rust/src/net/network_manager.rs b/rust/src/net/network_manager.rs index f7d9c56..7872f90 100644 --- a/rust/src/net/network_manager.rs +++ b/rust/src/net/network_manager.rs @@ -13,6 +13,7 @@ use crate::{ net::{net_stats::Stats, util::NetVector3}, player::{DamageSource, NetTransform}, ui::cli::{CliColor, CommandLinePanel}, + weapon::WeaponType, }; #[derive(GodotClass)] @@ -267,6 +268,11 @@ impl INode for NetworkManager { game.bind_mut().despawn_ball(); } } + Package::SwapWeapon(id, weapon_type) => { + if let Some(game) = &mut Game::singleton() { + game.bind_mut().handle_swap_weapon(id, weapon_type); + } + } _ => {} }, } @@ -586,6 +592,7 @@ pub enum Package { Kill(DamageSource, u16), SpawnBall(NetTransform), DespawnBall, + SwapWeapon(u16, WeaponType), } #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/rust/src/player.rs b/rust/src/player.rs index 339c031..7b2d9fe 100644 --- a/rust/src/player.rs +++ b/rust/src/player.rs @@ -22,7 +22,7 @@ use crate::{ }, soldier_mesh::SoldierMesh, ui::cli::CommandLinePanel, - weapon::Weapon, + weapon::{Weapon, WeaponType}, }; #[derive(Debug, Clone, Copy, Serialize, Deserialize)] @@ -49,6 +49,8 @@ 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 get_weapon(&self) -> Option>; } pub trait PlayerMovement { @@ -102,7 +104,6 @@ pub struct LocalPlayer { health: i32, pub player_id: Option, - pub weapon: Option>, movement_dir: Vector3, y_speed: f32, @@ -184,7 +185,9 @@ impl IPlayer for LocalPlayer { if self.shoot_cd <= 0. { let rid = self.base().get_rid(); - if let Some(weapon) = &mut self.weapon { + if let Some(mesh) = &mut self.soldier_mesh + && let Some(mut weapon) = mesh.bind().get_weapon() + { weapon .dyn_bind_mut() .shoot(rid, self.player_id.unwrap_or(0), to, deal_damage); @@ -219,9 +222,6 @@ impl IPlayer for LocalPlayer { if let Some(mut mesh) = self.soldier_mesh.take() { mesh.queue_free(); } - if let Some(mut weapon) = self.weapon.take() { - weapon.queue_free(); - } if let Some(mut collider) = self.collider.take() { collider.queue_free(); } @@ -233,6 +233,21 @@ impl IPlayer for LocalPlayer { self.killer_id = Some(player_id); } } + + fn swap_weapon(&mut self, weapon: WeaponType) { + if let Some(mesh) = &mut self.soldier_mesh { + match weapon { + WeaponType::Raygun => mesh.bind_mut().spawn_raygun(), + WeaponType::Ball => mesh.bind_mut().spawn_ball(), + }; + } + } + + fn get_weapon(&self) -> Option> { + self.soldier_mesh + .as_ref() + .and_then(|m| m.bind().get_weapon()) + } } #[godot_api] @@ -253,9 +268,7 @@ impl ICharacterBody3D for LocalPlayer { s.set_lock(true); }); - if let Some(mesh) = &mut self.soldier_mesh { - self.weapon = mesh.bind_mut().spawn_raygun(); - } + self.swap_weapon(WeaponType::Raygun); if let Some(camera) = &mut self.camera { camera.set_current(true); @@ -430,7 +443,6 @@ pub struct RemotePlayer { spectator_camera: Option>, pub player_id: Option, - pub weapon: Option>, movement_dir: Vector3, y_speed: f32, @@ -507,7 +519,9 @@ impl IPlayer for RemotePlayer { if self.shoot_cd <= 0. { let rid = self.base().get_rid(); - if let Some(weapon) = &mut self.weapon { + if let Some(mesh) = &mut self.soldier_mesh + && let Some(mut weapon) = mesh.bind().get_weapon() + { weapon .dyn_bind_mut() .shoot(rid, self.player_id.unwrap_or(0), to, deal_damage); @@ -542,9 +556,6 @@ impl IPlayer for RemotePlayer { if let Some(mut mesh) = self.soldier_mesh.take() { mesh.queue_free(); } - if let Some(mut weapon) = self.weapon.take() { - weapon.queue_free(); - } if let Some(mut collider) = self.collider.take() { collider.queue_free(); } @@ -553,14 +564,27 @@ impl IPlayer for RemotePlayer { } self.is_dead = true; } + + fn swap_weapon(&mut self, weapon: WeaponType) { + if let Some(mesh) = &mut self.soldier_mesh { + match weapon { + WeaponType::Raygun => mesh.bind_mut().spawn_raygun(), + WeaponType::Ball => mesh.bind_mut().spawn_ball(), + }; + } + } + + fn get_weapon(&self) -> Option> { + self.soldier_mesh + .as_ref() + .and_then(|m| m.bind().get_weapon()) + } } #[godot_api] impl ICharacterBody3D for RemotePlayer { fn ready(&mut self) { - if let Some(mesh) = &mut self.soldier_mesh { - self.weapon = mesh.bind_mut().spawn_raygun(); - } + self.swap_weapon(WeaponType::Raygun); } fn process(&mut self, delta: f64) { diff --git a/rust/src/soldier_mesh.rs b/rust/src/soldier_mesh.rs index 0f8cd6d..45131be 100644 --- a/rust/src/soldier_mesh.rs +++ b/rust/src/soldier_mesh.rs @@ -3,7 +3,7 @@ use godot::{ prelude::*, }; -use crate::weapon::{Raygun, Weapon}; +use crate::weapon::{BallWeapon, Raygun, Weapon}; #[derive(GodotClass)] #[class(base=Node3D, init)] @@ -17,8 +17,12 @@ pub struct SoldierMesh { #[export] raygun: Option>, #[export] + ball: Option>, + #[export] look_at: Option>, + pub current_weapon: Option>, + base: Base, } @@ -49,15 +53,32 @@ impl SoldierMesh { } } + pub fn spawn_ball(&mut self) -> Option> { + if let Some(ball) = &self.ball { + let node = ball.instantiate_as::(); + self.spawn_weapon(node.into_dyn().upcast()) + } else { + None + } + } + fn spawn_weapon( &mut self, node: DynGd, ) -> Option> { + if let Some(mut weapon) = self.current_weapon.take() { + weapon.queue_free(); + } if let Some(hand) = &mut self.hand_attachment { hand.add_child(&node); + self.current_weapon = Some(node.clone()); Some(node) } else { None } } + + pub fn get_weapon(&self) -> Option> { + self.current_weapon.clone() + } } diff --git a/rust/src/weapon.rs b/rust/src/weapon.rs index 06793a0..91b5bd2 100644 --- a/rust/src/weapon.rs +++ b/rust/src/weapon.rs @@ -1,4 +1,5 @@ use godot::{classes::AudioStreamPlayer3D, prelude::*}; +use serde::{Deserialize, Serialize}; use crate::{ game_manager::Game, @@ -8,6 +9,13 @@ use crate::{ pub trait Weapon { fn shoot(&mut self, player_rid: Rid, player_id: u16, to: Vector3, deal_damage: bool); + fn get_type(&self) -> WeaponType; +} + +#[derive(Debug, Clone, Copy, Deserialize, Serialize)] +pub enum WeaponType { + Raygun, + Ball, } #[derive(GodotClass)] @@ -68,6 +76,10 @@ impl Weapon for Raygun { } } } + + fn get_type(&self) -> WeaponType { + WeaponType::Raygun + } } #[derive(GodotClass)] @@ -113,3 +125,18 @@ impl Beam { } } } + +#[derive(GodotClass)] +#[class(base=Node3D, init)] +pub struct BallWeapon { + base: Base, +} + +#[godot_dyn] +impl Weapon for BallWeapon { + fn shoot(&mut self, player_rid: Rid, player_id: u16, to: Vector3, deal_damage: bool) {} + + fn get_type(&self) -> WeaponType { + WeaponType::Ball + } +}