diff --git a/native/src/scripts/objects/building.rs b/native/src/scripts/objects/building.rs index 9e2b3e6..6be78ab 100644 --- a/native/src/scripts/objects/building.rs +++ b/native/src/scripts/objects/building.rs @@ -1,21 +1,17 @@ -use godot::{ - builtin::{math::ApproxEq, Array, Transform3D, Vector3}, - engine::{MeshInstance3D, Node, Node3D, PackedScene, Time}, - global::PropertyHint, - meta::{FromGodot, GodotConvert, ToGodot}, - obj::{Gd, Inherits}, - prelude::ConvertError, - tools::load, -}; -use godot_rust_script::{godot_script_impl, CastToScript, GodotScript, GodotScriptExport, RsRef}; -use rand::Rng; +mod fire; + +use godot::builtin::Array; +use godot::engine::{MeshInstance3D, Node}; +use godot::global::PropertyHint; +use godot::meta::{FromGodot, GodotConvert, ToGodot}; +use godot::obj::{Gd, Inherits}; +use godot::prelude::ConvertError; +use godot_rust_script::{godot_script_impl, GodotScript, GodotScriptExport}; use std::{any::Any, fmt::Debug}; -use crate::{ - scripts::{FireSpawner, IFireSpawner}, - util::logger, - world::city_data::TileCoords, -}; +use crate::{util::logger, world::city_data::TileCoords}; + +use fire::FireFeature; trait BuildingFeature>: Debug { fn process(&mut self, _delta: f64, _node: &mut Gd) {} @@ -106,134 +102,6 @@ enum BuildingNotification { WaterImpact(f64), } -#[derive(Debug)] -struct FireFeature { - packed_fire_scene: Gd, - fire_scene: Option>, - last_fire: u64, - fire_strength: f64, - last_fire_strength: f64, - building_mesh: Gd, - tile_coords: TileCoords, -} - -impl FireFeature { - const FIRE_SPAWNER_SCENE: &'static str = "res://resources/Objects/Spawner/fire_spawner.tscn"; - const RECOVERY_RATE: f64 = 0.01; - const WATER_IMPACT_RATE: f64 = 0.2; - - fn new(tile_coords: TileCoords, mesh: &Gd) -> Self { - let packed = load(Self::FIRE_SPAWNER_SCENE); - - Self { - packed_fire_scene: packed, - fire_scene: None, - fire_strength: 1.0, - last_fire_strength: 1.0, - last_fire: 0, - building_mesh: mesh.to_owned(), - tile_coords, - } - } - - fn is_dead(&self) -> bool { - self.fire_strength.approx_eq(&0.0) - } - - fn is_recovering(&self) -> bool { - !self.is_dead() && self.fire_strength - self.last_fire_strength >= 0.0 - } - - fn update_fire_strength(&mut self, fire: &mut RsRef) { - if self.fire_strength == self.last_fire_strength { - return; - } - - fire.set_fire_strength(self.fire_strength); - self.last_fire_strength = self.fire_strength; - } - - fn recover_fire_strength(&mut self, delta: f64) { - if !self.is_recovering() { - return; - } - - self.fire_strength = (self.fire_strength + Self::RECOVERY_RATE * delta).min(1.0); - } -} - -impl> BuildingFeature for FireFeature { - fn process(&mut self, _delta: f64, node: &mut Gd) { - let current_ticks = Time::singleton().get_ticks_msec(); - - if let Some(mut scene) = self.fire_scene.clone() { - self.update_fire_strength(&mut scene); - - if !self.is_dead() { - self.last_fire = current_ticks; - return; - } - - if current_ticks - self.last_fire < 60_000 { - return; - } - - scene.queue_free(); - self.fire_scene = None; - self.last_fire = current_ticks; - self.fire_strength = 1.0; - self.last_fire_strength = 1.0; - return; - } - - let tick_delta = current_ticks - self.last_fire; - let tick_damp = (tick_delta as f64 / 10_000.0).min(1.0); - let rng = rand::thread_rng().sample::(rand::distributions::OpenClosed01); - - let chance = rng * tick_damp; - - if chance < 0.5 { - return; - } - - logger::debug!("Building will burn! (tick_delta: {tick_delta}, tick_boost: {tick_damp}, rng: {rng}, chance: {chance})"); - - let Some(mut scene_instance) = self.packed_fire_scene.try_instantiate_as::() else { - logger::error!("Failed to instantiate fire_spawner scene as decendant of Node3D"); - return; - }; - - let aabb = self.building_mesh.get_aabb(); - let aabb_size = (Transform3D::new(self.building_mesh.get_basis(), Vector3::default()) - * aabb.size) - .abs(); - - scene_instance.call_deferred("resize".into(), &[aabb_size.to_variant()]); - - node.upcast_mut() - .add_child_ex(scene_instance.clone().upcast()) - .force_readable_name(true) - .done(); - - self.fire_scene = Some(scene_instance.to_script()); - - logger::info!("Building started burning: {:?}", self.tile_coords); - } - - fn physics_process(&mut self, delta: f64, _node: &mut Gd) { - self.recover_fire_strength(delta); - } - - fn dispatch_notification(&mut self, notification: BuildingNotification) { - match notification { - BuildingNotification::WaterImpact(delta) => { - self.fire_strength = - (self.fire_strength - Self::WATER_IMPACT_RATE * delta).max(0.0); - } - } - } -} - #[derive(GodotScript, Debug)] #[script(base = Node)] struct Building { diff --git a/native/src/scripts/objects/building/fire.rs b/native/src/scripts/objects/building/fire.rs new file mode 100644 index 0000000..53b08a9 --- /dev/null +++ b/native/src/scripts/objects/building/fire.rs @@ -0,0 +1,141 @@ +use godot::builtin::{math::ApproxEq, Transform3D, Vector3}; +use godot::engine::{MeshInstance3D, Node, Node3D, PackedScene, Time}; +use godot::meta::ToGodot; +use godot::obj::{Gd, Inherits}; +use godot::tools::load; +use godot_rust_script::{CastToScript, RsRef}; +use rand::Rng; + +use crate::scripts::{FireSpawner, IFireSpawner}; +use crate::util::logger; +use crate::world::city_data::TileCoords; + +use super::{BuildingFeature, BuildingNotification}; + +#[derive(Debug)] +pub(super) struct FireFeature { + packed_fire_scene: Gd, + fire_scene: Option>, + last_fire: u64, + fire_strength: f64, + last_fire_strength: f64, + building_mesh: Gd, + tile_coords: TileCoords, +} + +impl FireFeature { + const FIRE_SPAWNER_SCENE: &'static str = "res://resources/Objects/Spawner/fire_spawner.tscn"; + const RECOVERY_RATE: f64 = 0.01; + const WATER_IMPACT_RATE: f64 = 0.2; + + pub fn new(tile_coords: TileCoords, mesh: &Gd) -> Self { + let packed = load(Self::FIRE_SPAWNER_SCENE); + + Self { + packed_fire_scene: packed, + fire_scene: None, + fire_strength: 1.0, + last_fire_strength: 1.0, + last_fire: 0, + building_mesh: mesh.to_owned(), + tile_coords, + } + } + + pub fn is_dead(&self) -> bool { + self.fire_strength.approx_eq(&0.0) + } + + pub fn is_recovering(&self) -> bool { + !self.is_dead() && self.fire_strength - self.last_fire_strength >= 0.0 + } + + pub fn update_fire_strength(&mut self, fire: &mut RsRef) { + if self.fire_strength == self.last_fire_strength { + return; + } + + fire.set_fire_strength(self.fire_strength); + self.last_fire_strength = self.fire_strength; + } + + pub fn recover_fire_strength(&mut self, delta: f64) { + if !self.is_recovering() { + return; + } + + self.fire_strength = (self.fire_strength + Self::RECOVERY_RATE * delta).min(1.0); + } +} + +impl> BuildingFeature for FireFeature { + fn process(&mut self, _delta: f64, node: &mut Gd) { + let current_ticks = Time::singleton().get_ticks_msec(); + + if let Some(mut scene) = self.fire_scene.clone() { + self.update_fire_strength(&mut scene); + + if !self.is_dead() { + self.last_fire = current_ticks; + return; + } + + if current_ticks - self.last_fire < 60_000 { + return; + } + + scene.queue_free(); + self.fire_scene = None; + self.last_fire = current_ticks; + self.fire_strength = 1.0; + self.last_fire_strength = 1.0; + return; + } + + let tick_delta = current_ticks - self.last_fire; + let tick_damp = (tick_delta as f64 / 10_000.0).min(1.0); + let rng = rand::thread_rng().sample::(rand::distributions::OpenClosed01); + + let chance = rng * tick_damp; + + if chance < 0.5 { + return; + } + + logger::debug!("Building will burn! (tick_delta: {tick_delta}, tick_boost: {tick_damp}, rng: {rng}, chance: {chance})"); + + let Some(mut scene_instance) = self.packed_fire_scene.try_instantiate_as::() else { + logger::error!("Failed to instantiate fire_spawner scene as decendant of Node3D"); + return; + }; + + let aabb = self.building_mesh.get_aabb(); + let aabb_size = (Transform3D::new(self.building_mesh.get_basis(), Vector3::default()) + * aabb.size) + .abs(); + + scene_instance.call_deferred("resize".into(), &[aabb_size.to_variant()]); + + node.upcast_mut() + .add_child_ex(scene_instance.clone().upcast()) + .force_readable_name(true) + .done(); + + self.fire_scene = Some(scene_instance.to_script()); + + logger::info!("Building started burning: {:?}", self.tile_coords); + } + + fn physics_process(&mut self, delta: f64, _node: &mut Gd) { + self.recover_fire_strength(delta); + } + + fn dispatch_notification(&mut self, notification: BuildingNotification) { + match notification { + BuildingNotification::WaterImpact(delta) => { + self.fire_strength = + (self.fire_strength - Self::WATER_IMPACT_RATE * delta).max(0.0); + } + } + } +}