use godot::{ classes::{Area3D, AudioStreamPlayer3D}, prelude::*, }; use serde::{Deserialize, Serialize}; use crate::{ game::Game, map::Map, net::network_manager::NetworkManager, player::IPlayer, replay::StandaloneReplayManager, }; #[derive(Debug, Clone, Copy, Serialize, Deserialize)] pub enum PickupKind { Health, } #[derive(Debug, Default)] pub enum ReplayVisibility { #[default] NoReplay, Hidden, Visible, } #[derive(GodotClass)] #[class(base=Node3D, init, tool)] pub struct HealthPickup { #[export] pub pickup_area: Option>, #[export] pub sfx: Option>, time_elapsed: f64, pickup_id: Option, respawn: f64, replay_visibility: ReplayVisibility, base: Base, } #[godot_api] impl INode3D for HealthPickup { fn ready(&mut self) { if let Some(area) = self.pickup_area.clone() { area.signals() .body_entered() .connect_other(self, Self::on_collision); } if let Some(game) = &Game::singleton() { game.signals() .on_map_loaded() .connect_other(self, Self::register_pickup); } else { StandaloneReplayManager::singleton() .signals() .on_map_loaded() .connect_other(self, Self::register_pickup); } } fn process(&mut self, delta: f64) { self.time_elapsed += delta; self.respawn -= delta; self.update_common(self.time_elapsed); if let Some(mut area) = self.pickup_area.clone() { match self.replay_visibility { ReplayVisibility::NoReplay => { area.set_visible(self.is_active()); } ReplayVisibility::Hidden => area.set_visible(false), ReplayVisibility::Visible => area.set_visible(true), } } } } impl HealthPickup { pub fn register_pickup(&mut self, mut map: Gd) { self.run_deferred(move |s| { let id = map .bind_mut() .register_pickup(s.to_gd().into_dyn().upcast()); s.pickup_id = Some(id); }); } } #[godot_dyn] impl Pickup for HealthPickup { fn area(&mut self) -> &mut Option> { &mut self.pickup_area } fn kind(&self) -> PickupKind { PickupKind::Health } fn id(&self) -> Option { self.pickup_id } fn on_pickup(&mut self) { self.respawn = 30.; if let Some(sfx) = &mut self.sfx { sfx.play(); } } fn is_active(&self) -> bool { self.respawn <= 0. } fn set_replay(&mut self, visible: ReplayVisibility) { self.replay_visibility = visible; } } pub trait Pickup { fn area(&mut self) -> &mut Option>; fn kind(&self) -> PickupKind; fn id(&self) -> Option; fn on_pickup(&mut self); fn is_active(&self) -> bool; fn set_replay(&mut self, visibility: ReplayVisibility); } pub trait PickupCommon { fn update_common(&mut self, time_elapsed: f64); fn on_collision(&mut self, collider: Gd); } impl PickupCommon for T { fn update_common(&mut self, time_elapsed: f64) { if let Some(area) = self.area() { area.set_position(Vector3::UP * 1.5 + Vector3::UP * (time_elapsed.sin() / 4.) as f32); area.set_rotation(Vector3::UP * time_elapsed as f32); } } fn on_collision(&mut self, collider: Gd) { if !NetworkManager::singleton().bind().is_host() || !self.is_active() { return; } if let Some(game) = &mut Game::singleton() && let Ok(player) = collider.try_dynify::() { game.bind_mut().on_pickup( self.id().unwrap_or(0), player.dyn_bind().get_player_id().unwrap_or(0), ); } } }