From 533cb2fae426b88ab3a4bc530b4c01cc83f4bb5b Mon Sep 17 00:00:00 2001 From: Arkitu <85173315+Arkitu@users.noreply.github.com> Date: Sun, 19 Jan 2025 22:40:19 +0100 Subject: [PATCH] reorganize cells, add custom game time, switch click detection to 200ms --- src/lib.rs | 3 ++ src/map.rs | 95 +++++++++++++++++++++++------------------------- src/map/cells.rs | 91 +++++++++++++++++++++++++++++++++------------- src/time.rs | 28 ++++++++++++++ 4 files changed, 142 insertions(+), 75 deletions(-) create mode 100644 src/time.rs diff --git a/src/lib.rs b/src/lib.rs index 92b71a9..4e06803 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,11 @@ +#![feature(duration_constructors)] use bevy::{prelude::*, diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}}; use bevy_inspector_egui::quick::WorldInspectorPlugin; pub mod map; pub mod camera; pub mod ui; +pub mod time; #[bevy_main] pub fn main() { @@ -14,6 +16,7 @@ pub fn main() { camera::Plugin, map::Plugin, ui::Plugin, + time::Plugin, FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin { filter: Some(vec![FrameTimeDiagnosticsPlugin::FPS]), diff --git a/src/map.rs b/src/map.rs index d8c32be..aba642e 100644 --- a/src/map.rs +++ b/src/map.rs @@ -10,14 +10,15 @@ mod picking; use picking::*; use cells::*; +use crate::time::GameTime; + pub struct Plugin; impl bevy::prelude::Plugin for Plugin { fn build(&self, app: &mut App) { app.add_systems(Startup, setup) .insert_resource(Time::::from_seconds(0.25)) // Time for a day - .add_systems(FixedUpdate, update_cells) .add_systems(PreUpdate, picking_backend.in_set(PickSet::Backend)) - .add_systems(Update, update_map_mesh) + .add_systems(Update, (update_map_mesh, cells_regeneration)) .insert_resource(ClearColor(Color::srgb(0., 0., 1.))) .insert_resource(Seed(thread_rng().gen())); } @@ -65,7 +66,7 @@ fn setup( .set_lloyd_relaxation_iterations(3) .build() .unwrap(); - let mut cells_data = Vec::with_capacity(SIZE); + let mut cells = Vec::with_capacity(SIZE); let z_noise = Fbm::::new(seed.0); let moisture_noise = Fbm::::new(seed.0+1) .set_frequency(2.); @@ -89,27 +90,23 @@ fn setup( } else { CellKind::Stone }; - cells_data.push(CellData::new(k, i, (z*255.) as u8, (m*255.) as u8, 0, vec![])); + cells.push(Cell { + kind: k, + voronoi_id: i, + altitude: (z*255.) as u8, + vertices: vec![] + }); } let mut poss = Vec::new(); - let mut colors = Vec::new(); let mut indices = Vec::new(); - for (c, cd) in voronoi.iter_cells().zip(cells_data.iter_mut()).filter(|(_,cd)| cd.kind != CellKind::Forest) { - let color = cd.color(); - // if c.site() == selected_tile { - // color[0] = (color[0]+0.4).clamp(0., 1.); - // color[1] = (color[1]+0.4).clamp(0., 1.); - // color[2] = (color[2]+0.4).clamp(0., 1.); - // } + for (c, cd) in voronoi.iter_cells().zip(cells.iter_mut()).filter(|(_,cd)| cd.kind != CellKind::Forest) { let vs = c.iter_vertices().collect::>(); let i = poss.len(); for v in vs.iter() { - poss.push(Vec3::new(v.x as f32, v.y as f32, 0.));// [v.x as f32, v.y as f32, 0.]); - // poss.push(Vertex::new_col([v.x as f32, v.y as f32], color, 1)); - colors.push(color); + poss.push(Vec3::new(v.x as f32, v.y as f32, 0.)); } for v in 1..(vs.len()-1) { indices.extend_from_slice(&[i as u32, (i+v) as u32, (i+v+1) as u32]); @@ -117,6 +114,7 @@ fn setup( } } + let colors = vec![[0.; 4]; poss.len()]; let mesh = Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::default()) // Add 4 vertices, each with its own position attribute (coordinate in // 3D space), for each of the corners of the parallelogram. @@ -130,7 +128,7 @@ fn setup( ) .with_inserted_indices(Indices::U32(indices)); - let mut cells_entities = Vec::with_capacity(cells_data.len()); + let mut cells_entities = Vec::with_capacity(cells.len()); cmds.spawn(( Mesh2d(meshes.add(mesh)), MeshMaterial2d(materials.add(ColorMaterial::default())), @@ -140,21 +138,40 @@ fn setup( MeshNeedsUpdate(true), MapMarker )).with_children(|parent| { - for cd in cells_data { - let mut cmd = parent.spawn((cd, LastUpdate(0))); - cmd.observe(|trigger: Trigger>, mut cells: Query<&mut CellData>, mut map_needs_update: Query<&mut MeshNeedsUpdate, With>| { - if trigger.duration > Duration::from_millis(100) { + for cell in cells { + let kind = cell.kind; + let mut cmd = parent.spawn(cell); + 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(| + trigger: Trigger>, + mut cells: Query<&mut Cell>, + mut map_needs_update: Query<&mut MeshNeedsUpdate, With>, + mut cmds: Commands, + gt: Res + | { + if trigger.duration > Duration::from_millis(200) { return } - let mut cd = cells.get_mut(trigger.target).unwrap(); - match cd.kind { + let mut cell = cells.get_mut(trigger.target).unwrap(); + match cell.kind { CellKind::Dirt | CellKind::Grass => { - cd.kind = CellKind::Forest; + cmds.entity(trigger.target).insert((Wealth(0), Regeneration { + last_update: gt.current, + full_growth_duration: CellKind::Forest.regen_full_growth_duration() + })); + cell.kind = CellKind::Forest; map_needs_update.single_mut().0 = true; }, _ => {} } - dbg!(trigger.duration); }); cells_entities.push(cmd.id()); } @@ -162,30 +179,8 @@ fn setup( } -#[derive(Component)] -pub struct LastUpdate(usize); - -fn update_cells( - mut cells: Query<(&mut CellData, &mut LastUpdate)>, - mut map_needs_update: Query<&mut MeshNeedsUpdate, With> -) { - let mut map_needs_update = map_needs_update.single_mut(); - for (mut cd, mut lu) in cells.iter_mut() { - lu.0 += 1; - if lu.0 > match cd.kind { - CellKind::Void | CellKind::Sea | CellKind::Beach | CellKind::Dirt | CellKind::Stone => usize::MAX, - CellKind::Forest => 100*365/4, // Let's say that a forest takes 100 years to mature - CellKind::Grass => 7*7/4 // Let's say that grass takes 7 weaks to reach its max - } { - lu.0 = 0; - cd.resource = (cd.resource + 1).clamp(0, 4); - map_needs_update.0 = true; - } - } -} - fn update_map_mesh( - cells: Query<&CellData>, + cells: Query<(&Cell, Option<&Wealth>)>, mut map: Query<(&Mesh2d, &mut MapColors, &mut MeshNeedsUpdate), With>, mut meshes: ResMut> ) { @@ -193,9 +188,9 @@ fn update_map_mesh( if needs_update.0 { if let Some(mesh) = meshes.get_mut(mesh) { let mut modified = false; - for cd in cells.iter() { - let col = cd.color(); - for id in cd.vertices.iter() { + for (cell, wealth) in cells.iter() { + let col = cell.color(wealth.map(|w| w.0).unwrap_or_default()); + for id in cell.vertices.iter() { modified = modified || cols.0[*id] != col; cols.0[*id] = col.clone(); } diff --git a/src/map/cells.rs b/src/map/cells.rs index 7217d2a..761d2dc 100644 --- a/src/map/cells.rs +++ b/src/map/cells.rs @@ -1,8 +1,13 @@ +use std::time::Duration; + use bevy::prelude::*; +use crate::time::GameTime; + +use super::{MapMarker, MeshNeedsUpdate}; + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum CellKind { - Void, Sea, Beach, Forest, @@ -10,36 +15,72 @@ pub enum CellKind { Stone, Grass } - -#[derive(Debug, Component)] -pub struct CellData { - pub kind: CellKind, - pub cid: usize, - z: u8, - pub moisture: u8, - pub resource: u8, // How much resource there is (between 0 and 4) - pub vertices: Vec -} -impl CellData { - pub fn new(kind: CellKind, cell: usize, z: u8, moisture: u8, resource: u8, vertices: Vec) -> Self { - Self { - kind, - cid: cell, - z, - moisture, - resource, - vertices +impl CellKind { + pub fn regen_full_growth_duration(&self) -> Duration { + match self { + CellKind::Sea | CellKind::Beach | CellKind::Dirt | CellKind::Stone => unreachable!(), + CellKind::Forest => Duration::from_days(365*100), // Let's say that a forest takes 100 years to mature + CellKind::Grass => Duration::from_weeks(7) // Let's say that grass takes 7 weeks to reach its max } } - pub fn color(&self) -> [f32; 4] { +} + +#[derive(Debug, Component)] +pub struct Cell { + pub kind: CellKind, + pub voronoi_id: usize, + pub altitude: u8, + pub vertices: Vec +} +impl Cell { + // pub fn new(kind: CellKind, voronoi_id: usize, altitude: u8, moisture: u8, vertices: Vec) -> Self { + // Self { + // kind, + // voronoi_id, + // altitude, + // moisture, + // vertices + // } + // } + pub fn color(&self, wealth: u8) -> [f32; 4] { match self.kind { - CellKind::Void => [0.; 4], CellKind::Sea => [0., 0., 1., 1.], CellKind::Beach => [0.82, 0.84, 0.51, 1.], - CellKind::Forest => [0., 0.5 - (self.resource as f32/4.*0.4), 0., 1.], - CellKind::Dirt => [0.53 - (self.resource as f32/4.*0.4), 0.38-(self.resource as f32/4.*0.4), 0.29-(self.resource as f32/4.*0.4), 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::Stone => [0.5, 0.5, 0.5, 1.], - CellKind::Grass => [(136./255.) - (self.resource as f32/4.*0.4), (204./255.) - (self.resource as f32/4.*0.4), (59./255.) - (self.resource as f32/4.*0.4), 1.] + CellKind::Grass => [(136./255.) - (wealth as f32/255.*0.4), (204./255.) - (wealth as f32/255.*0.4), (59./255.) - (wealth as f32/255.*0.4), 1.] + } + } +} + +#[derive(Component)] +pub struct Wealth(pub u8); + +impl Default for Wealth { + fn default() -> Self { + Wealth(u8::MAX) + } +} + +#[derive(Component)] +#[require(Wealth)] +pub struct Regeneration { + pub last_update: Duration, + pub full_growth_duration: Duration +} + +pub fn cells_regeneration( + mut cells: Query<(&mut Regeneration, &mut Wealth)>, + mut map_needs_update: Query<&mut MeshNeedsUpdate, With>, + gt: Res +) { + let mut map_needs_update = map_needs_update.single_mut(); + for (mut regen, mut wealth) in cells.iter_mut() { + if gt.current - regen.last_update > regen.full_growth_duration/u8::MAX as u32 { + regen.last_update = gt.current; + wealth.0 = wealth.0.saturating_add(1); + map_needs_update.0 = true; } } } \ No newline at end of file diff --git a/src/time.rs b/src/time.rs new file mode 100644 index 0000000..1125e33 --- /dev/null +++ b/src/time.rs @@ -0,0 +1,28 @@ +use std::time::Duration; + +use bevy::prelude::*; + +pub struct Plugin; +impl bevy::prelude::Plugin for Plugin { + fn build(&self, app: &mut App) { + app.insert_resource(GameTime { + current: Duration::ZERO, + speed: 24. * 60. * 60. * 365. + }) + .add_systems(PreUpdate, update_time); + } +} + +#[derive(Resource)] +pub struct GameTime { + pub current: Duration, + speed: f32 // = game time / real time +} + +fn update_time( + mut gt: ResMut, + time: Res