From 1a68ab5af1ad45da341d9b6d6003fa0420dbcf2a Mon Sep 17 00:00:00 2001 From: Arkitu <85173315+Arkitu@users.noreply.github.com> Date: Sun, 22 Jun 2025 21:40:19 +0200 Subject: [PATCH] save --- src/map.rs | 45 +++--- src/map/cells.rs | 358 +++++++++++++++++------------------------ src/map/cells/grow.rs | 79 +++++++++ src/map/cells/input.rs | 60 +++++++ src/map/picking.rs | 2 +- src/time.rs | 2 +- src/ui.rs | 8 +- 7 files changed, 316 insertions(+), 238 deletions(-) create mode 100644 src/map/cells/grow.rs create mode 100644 src/map/cells/input.rs diff --git a/src/map.rs b/src/map.rs index d23047e..5137cde 100644 --- a/src/map.rs +++ b/src/map.rs @@ -7,6 +7,7 @@ use bevy::{ picking::PickSet, prelude::*, render::mesh::{Indices, PrimitiveTopology}, + tasks::AsyncComputeTaskPool, utils::HashMap, }; use noise::{Fbm, MultiFractal, NoiseFn, Perlin}; @@ -24,6 +25,8 @@ use cells::{ }; use picking::*; +use crate::map::cells::grow::{grow_thread, GrowThread}; + pub struct Plugin; impl bevy::prelude::Plugin for Plugin { fn build(&self, app: &mut App) { @@ -57,7 +60,7 @@ struct Seed(u32); pub struct Voronoi(voronoice::Voronoi); #[derive(Component)] -pub struct MapMarker; +pub struct MeshMarker; #[derive(Component)] struct MeshColors(Vec<[f32; 4]>); @@ -68,6 +71,9 @@ pub struct CellsEntities(Vec); #[derive(Component)] pub struct MeshNeedsUpdate(bool); +#[derive(Component)] +pub struct Chunk(usize); + fn setup( mut cmds: Commands, mut meshes: ResMut>, @@ -124,7 +130,6 @@ fn setup( cells.push(Cell { kind: k, voronoi_id: i, - vertices: vec![], }); } @@ -190,10 +195,11 @@ fn setup( } let mut cells_entities = Vec::with_capacity(cells.len()); - for ((poss, indices), ch_cells) in poss + for (ch_id, ((poss, indices), ch_cells)) in poss .into_iter() .zip(indices.into_iter()) .zip(cells.chunks_exact(CELLS_PER_CHUNK)) + .enumerate() { let colors = vec![[0.; 4]; poss.len()]; let mut mesh = Mesh::new( @@ -223,7 +229,8 @@ fn setup( Transform::default(), MeshColors(colors), MeshNeedsUpdate(true), - MapMarker, + MeshMarker, + Chunk(ch_id), )) .with_children(|parent| { for cell in ch_cells { @@ -233,19 +240,7 @@ fn setup( // if let Some(ch) = hybrid_cells.get(&id) { // cmd.insert(GhostCell(*ch)); // } - match kind { - CellKind::Grass | CellKind::Forest => { - cmd.insert(( - Wealth(0), - Regeneration { - last_update: Duration::ZERO, - full_growth_duration: kind.regen_full_growth_duration(), - }, - )); - } - _ => {} - } - cmd.observe(self::cells::on_click); + cmd.observe(self::cells::input::on_click); cells_entities.push(cmd.id()); } }); @@ -253,12 +248,18 @@ fn setup( cmds.insert_resource(Voronoi(voronoi)); cmds.insert_resource(CellsEntities(cells_entities)); + + let task_pool = AsyncComputeTaskPool::get(); + cmds.insert_resource(GrowThread(task_pool.spawn(grow_thread()))); } // TODO: update this to take chunks into account fn update_chunk_mesh( - cells: Query<(&Cell, Option<&Wealth>)>, - mut chunks: Query<(&Mesh3d, &mut MeshColors, &mut MeshNeedsUpdate, &Children), With>, + cells: Query<&Cell>, + mut chunks: Query< + (&Mesh3d, &mut MeshColors, &mut MeshNeedsUpdate, &Children), + With, + >, mut meshes: ResMut>, ) { for (mesh, mut cols, mut needs_update, children) in chunks.iter_mut() { @@ -267,9 +268,9 @@ fn update_chunk_mesh( let mut modified = false; // let mut rng = thread_rng(); for child in children.iter() { - let (cell, wealth) = cells.get(*child).unwrap(); + let cell = cells.get(*child).unwrap(); // let col: [f32; 4] = [rng.gen(), rng.gen(), rng.gen(), 1.]; - let col = cell.color(wealth.map(|w| w.0).unwrap_or_default()); + let col = cell.color(); modified = modified || cols.0[cell.voronoi_id % CELLS_PER_CHUNK] != col; cols.0[cell.voronoi_id % CELLS_PER_CHUNK] = col; // for id in cell.vertices.iter() { @@ -282,6 +283,8 @@ fn update_chunk_mesh( } } needs_update.0 = false; + // Update at most one chunk per frame to avoid freezes + return; } } } diff --git a/src/map/cells.rs b/src/map/cells.rs index 34245ce..bd49262 100644 --- a/src/map/cells.rs +++ b/src/map/cells.rs @@ -6,24 +6,26 @@ use rand::{seq::IteratorRandom, thread_rng, Rng}; use crate::{time::GameTime, ui::CurrentAction}; use super::{ - animals::Animal, AnimalKind, CellsEntities, MapMarker, MeshNeedsUpdate, Voronoi, + animals::Animal, AnimalKind, CellsEntities, MeshMarker, MeshNeedsUpdate, Voronoi, AVERAGE_NEIGHBORS_NUMBER, CELLS_PER_CHUNK, CELL_AREA, }; +pub mod grow; +pub mod input; pub mod material; use material::CellMaterial; pub struct Plugin; impl bevy::prelude::Plugin for Plugin { fn build(&self, app: &mut App) { - app.add_systems( - FixedUpdate, - ( - cells_regeneration.run_if(on_timer(Duration::from_secs(1))), - expand.run_if(on_timer(Duration::from_secs(1))), - ), - ) - .add_plugins(MaterialPlugin::::default()); + // app.add_systems( + // FixedUpdate, + // ( + // cells_regeneration.run_if(on_timer(Duration::from_millis(500))), + // expand.run_if(on_timer(Duration::from_millis(300))), + // ), + // ) + app.add_plugins(MaterialPlugin::::default()); } } @@ -31,50 +33,60 @@ impl bevy::prelude::Plugin for Plugin { pub enum CellKind { Sea, Beach, - Forest, + Forest { wealth: WealthType }, Dirt, Stone, - Grass, + Grass { wealth: WealthType }, } impl CellKind { - pub fn regen_full_growth_duration(&self) -> Duration { + pub const fn regen_full_growth_duration(&self) -> Duration { match self { CellKind::Sea | CellKind::Beach | CellKind::Dirt | CellKind::Stone => unreachable!(), - CellKind::Forest => Duration::from_secs(100 * 365 * 24 * 60 * 60), // Let's say that a forest takes 100 years to mature - CellKind::Grass => Duration::from_secs(7 * 7 * 24 * 60 * 60), // Let's say that grass takes 7 weeks to reach its max + CellKind::Forest { .. } => Duration::from_secs(100 * 365 * 24 * 60 * 60), // Let's say that a forest takes 100 years to mature + CellKind::Grass { .. } => Duration::from_secs(7 * 7 * 24 * 60 * 60), // Let's say that grass takes 7 weeks to reach its max } } - pub fn can_place_animal(&self, kind: AnimalKind) -> bool { + pub const fn can_place_animal(&self, kind: AnimalKind) -> bool { match self { CellKind::Sea => false, _ => true, } } + pub const fn grass_wealth(&self) -> Option { + match self { + CellKind::Grass { wealth } => Some(*wealth), + CellKind::Forest { wealth } => Some( + wealth.saturating_mul( + (CellKind::Forest { wealth: 0 } + .regen_full_growth_duration() + .as_secs() + / CellKind::Grass { wealth: 0 } + .regen_full_growth_duration() + .as_secs()) as usize, + ), + ), + _ => None, + } + } } #[derive(Debug, Component, Clone)] pub struct Cell { pub kind: CellKind, pub voronoi_id: usize, - pub vertices: Vec, } impl Cell { - pub const fn color(&self, wealth: u8) -> [f32; 4] { + pub const fn color(&self) -> [f32; 4] { match self.kind { CellKind::Sea => [0., 0., 1., 1.], CellKind::Beach => [0.82, 0.84, 0.51, 1.], - CellKind::Forest => [0., 0.5 - (wealth as f32 / 255. * 0.4), 0., 1.], - CellKind::Dirt => [ - 0.53 - (wealth as f32 / 255. * 0.4), - 0.38 - (wealth as f32 / 255. * 0.4), - 0.29 - (wealth as f32 / 255. * 0.4), - 1., - ], + CellKind::Forest { wealth } => [0., 0.5 - (wealth_to_unit(wealth) * 0.4), 0., 1.], + CellKind::Dirt => [0.53, 0.38, 0.29, 1.], CellKind::Stone => [0.5, 0.5, 0.5, 1.], - CellKind::Grass => [ - (136. / 255.) - (wealth as f32 / 255. * 0.15), - (154. / 255.) + (wealth as f32 / 255. * 0.1), - (59. / 255.) - (wealth as f32 / 255. * 0.15), + CellKind::Grass { wealth } => [ + (136. / 255.) - (wealth_to_unit(wealth) * 0.15), + (154. / 255.) + (wealth_to_unit(wealth) * 0.1), + (59. / 255.) - (wealth_to_unit(wealth) * 0.15), 1., ], } @@ -93,189 +105,113 @@ impl Cell { // pub chunk: // }; -#[derive(Component)] -pub struct Wealth(pub u8); - -impl Default for Wealth { - fn default() -> Self { - Wealth(u8::MAX) - } +pub type WealthType = usize; +const fn wealth_to_unit(wealth: WealthType) -> f32 { + wealth as f32 / WealthType::MAX as f32 } -#[derive(Component)] -#[require(Wealth)] -pub struct Regeneration { - pub last_update: Duration, - pub full_growth_duration: Duration, -} +// const STEP: WealthType = WealthType::MAX / 4; +// pub fn cells_regeneration( +// mut cells: Query<(&mut Regeneration, &mut Wealth, &Parent)>, +// mut chunks: Query<&mut MeshNeedsUpdate, With>, +// gt: Res, +// ) { +// for (mut regen, mut wealth, parent) in cells.iter_mut() { +// while gt.current - regen.last_update > regen.full_growth_duration / u8::MAX as u32 { +// regen.last_update = gt +// .current +// .min(regen.last_update + (regen.full_growth_duration / u8::MAX as u32)); +// wealth.0 = wealth.0.saturating_add(1); +// // Not update map each time for optimization +// if wealth.0 % STEP == 0 { +// chunks.get_mut(parent.get()).unwrap().0 = true; +// } +// } +// } +// } -const STEP: u8 = u8::MAX / 4; -pub fn cells_regeneration( - mut cells: Query<(&mut Regeneration, &mut Wealth, &Parent)>, - mut chunks: Query<&mut MeshNeedsUpdate, With>, - gt: Res, -) { - for (mut regen, mut wealth, parent) in cells.iter_mut() { - while gt.current - regen.last_update > regen.full_growth_duration / u8::MAX as u32 { - regen.last_update = gt - .current - .min(regen.last_update + (regen.full_growth_duration / u8::MAX as u32)); - wealth.0 = wealth.0.saturating_add(1); - // Not update map each time for optimization - if wealth.0 % STEP == 0 { - chunks.get_mut(parent.get()).unwrap().0 = true; - } - } - } -} - -pub fn expand( - mut cells: Query<(&mut Cell, Option<&Wealth>)>, - voronoi: Res, - cells_entities: Res, - mut cmds: Commands, - t: Res