use godot::{ classes::{IRigidBody3D, RigidBody3D}, prelude::*, }; use crate::{ net::{network_manager::BallSync, util::NetVector3}, player::{DamageSource, IPlayer, NetTransform, shootable::Shootable}, }; #[derive(GodotClass)] #[class(base=RigidBody3D, init)] pub struct Ball { pub dropper: Option, alive_for: f64, base: Base, } #[godot_api] impl IRigidBody3D for Ball { fn ready(&mut self) { self.base() .signals() .body_entered() .connect_other(self, |s, body| s.on_collision(body)); } fn process(&mut self, delta: f64) { self.alive_for += delta; } } #[godot_api] impl Ball { #[signal] pub fn on_ball_pickup(player_id: u16); pub fn get_transform(&self) -> NetTransform { NetTransform { location: self.base().get_global_position().into(), rotation: self.base().get_global_rotation().into(), } } pub fn get_velocity(&self) -> NetVector3 { self.base().get_linear_velocity().into() } fn on_collision(&mut self, node: Gd) { if let Ok(player) = node.try_dynify::() && let Some(player_id) = player.dyn_bind().get_player_id() { if let Some(dropper_id) = self.dropper && dropper_id == player_id && self.alive_for < 1.0 { return; } self.run_deferred(move |s| s.signals().on_ball_pickup().emit(player_id)); } } pub fn get_sync(&self) -> BallSync { BallSync { transform: NetTransform { location: self.base().get_global_position().into(), rotation: self.base().get_global_rotation().into(), }, velocity: self.base().get_linear_velocity().into(), } } pub fn apply_sync(&mut self, sync: BallSync) { self.base_mut() .set_global_position(sync.transform.location.into()); self.base_mut() .set_global_rotation(sync.transform.rotation.into()); self.base_mut().set_linear_velocity(sync.velocity.into()); } } #[godot_dyn] impl Shootable for Ball { fn on_shoot(&mut self, _: DamageSource, damage: i32, dir: Vector3) { let prev_velocity = self.base().get_linear_velocity(); let force = damage as f32 / 10.; self.base_mut() .set_linear_velocity(prev_velocity + dir * force); } }