save
This commit is contained in:
parent
0699c5da2e
commit
1a68ab5af1
45
src/map.rs
45
src/map.rs
@ -7,6 +7,7 @@ use bevy::{
|
|||||||
picking::PickSet,
|
picking::PickSet,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
render::mesh::{Indices, PrimitiveTopology},
|
render::mesh::{Indices, PrimitiveTopology},
|
||||||
|
tasks::AsyncComputeTaskPool,
|
||||||
utils::HashMap,
|
utils::HashMap,
|
||||||
};
|
};
|
||||||
use noise::{Fbm, MultiFractal, NoiseFn, Perlin};
|
use noise::{Fbm, MultiFractal, NoiseFn, Perlin};
|
||||||
@ -24,6 +25,8 @@ use cells::{
|
|||||||
};
|
};
|
||||||
use picking::*;
|
use picking::*;
|
||||||
|
|
||||||
|
use crate::map::cells::grow::{grow_thread, GrowThread};
|
||||||
|
|
||||||
pub struct Plugin;
|
pub struct Plugin;
|
||||||
impl bevy::prelude::Plugin for Plugin {
|
impl bevy::prelude::Plugin for Plugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
@ -57,7 +60,7 @@ struct Seed(u32);
|
|||||||
pub struct Voronoi(voronoice::Voronoi);
|
pub struct Voronoi(voronoice::Voronoi);
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct MapMarker;
|
pub struct MeshMarker;
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
struct MeshColors(Vec<[f32; 4]>);
|
struct MeshColors(Vec<[f32; 4]>);
|
||||||
@ -68,6 +71,9 @@ pub struct CellsEntities(Vec<Entity>);
|
|||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct MeshNeedsUpdate(bool);
|
pub struct MeshNeedsUpdate(bool);
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct Chunk(usize);
|
||||||
|
|
||||||
fn setup(
|
fn setup(
|
||||||
mut cmds: Commands,
|
mut cmds: Commands,
|
||||||
mut meshes: ResMut<Assets<Mesh>>,
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
@ -124,7 +130,6 @@ fn setup(
|
|||||||
cells.push(Cell {
|
cells.push(Cell {
|
||||||
kind: k,
|
kind: k,
|
||||||
voronoi_id: i,
|
voronoi_id: i,
|
||||||
vertices: vec![],
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,10 +195,11 @@ fn setup(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut cells_entities = Vec::with_capacity(cells.len());
|
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()
|
.into_iter()
|
||||||
.zip(indices.into_iter())
|
.zip(indices.into_iter())
|
||||||
.zip(cells.chunks_exact(CELLS_PER_CHUNK))
|
.zip(cells.chunks_exact(CELLS_PER_CHUNK))
|
||||||
|
.enumerate()
|
||||||
{
|
{
|
||||||
let colors = vec![[0.; 4]; poss.len()];
|
let colors = vec![[0.; 4]; poss.len()];
|
||||||
let mut mesh = Mesh::new(
|
let mut mesh = Mesh::new(
|
||||||
@ -223,7 +229,8 @@ fn setup(
|
|||||||
Transform::default(),
|
Transform::default(),
|
||||||
MeshColors(colors),
|
MeshColors(colors),
|
||||||
MeshNeedsUpdate(true),
|
MeshNeedsUpdate(true),
|
||||||
MapMarker,
|
MeshMarker,
|
||||||
|
Chunk(ch_id),
|
||||||
))
|
))
|
||||||
.with_children(|parent| {
|
.with_children(|parent| {
|
||||||
for cell in ch_cells {
|
for cell in ch_cells {
|
||||||
@ -233,19 +240,7 @@ fn setup(
|
|||||||
// if let Some(ch) = hybrid_cells.get(&id) {
|
// if let Some(ch) = hybrid_cells.get(&id) {
|
||||||
// cmd.insert(GhostCell(*ch));
|
// cmd.insert(GhostCell(*ch));
|
||||||
// }
|
// }
|
||||||
match kind {
|
cmd.observe(self::cells::input::on_click);
|
||||||
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);
|
|
||||||
cells_entities.push(cmd.id());
|
cells_entities.push(cmd.id());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -253,12 +248,18 @@ fn setup(
|
|||||||
|
|
||||||
cmds.insert_resource(Voronoi(voronoi));
|
cmds.insert_resource(Voronoi(voronoi));
|
||||||
cmds.insert_resource(CellsEntities(cells_entities));
|
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
|
// TODO: update this to take chunks into account
|
||||||
fn update_chunk_mesh(
|
fn update_chunk_mesh(
|
||||||
cells: Query<(&Cell, Option<&Wealth>)>,
|
cells: Query<&Cell>,
|
||||||
mut chunks: Query<(&Mesh3d, &mut MeshColors, &mut MeshNeedsUpdate, &Children), With<MapMarker>>,
|
mut chunks: Query<
|
||||||
|
(&Mesh3d, &mut MeshColors, &mut MeshNeedsUpdate, &Children),
|
||||||
|
With<MeshMarker>,
|
||||||
|
>,
|
||||||
mut meshes: ResMut<Assets<Mesh>>,
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
) {
|
) {
|
||||||
for (mesh, mut cols, mut needs_update, children) in chunks.iter_mut() {
|
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 modified = false;
|
||||||
// let mut rng = thread_rng();
|
// let mut rng = thread_rng();
|
||||||
for child in children.iter() {
|
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: [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;
|
modified = modified || cols.0[cell.voronoi_id % CELLS_PER_CHUNK] != col;
|
||||||
cols.0[cell.voronoi_id % CELLS_PER_CHUNK] = col;
|
cols.0[cell.voronoi_id % CELLS_PER_CHUNK] = col;
|
||||||
// for id in cell.vertices.iter() {
|
// for id in cell.vertices.iter() {
|
||||||
@ -282,6 +283,8 @@ fn update_chunk_mesh(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
needs_update.0 = false;
|
needs_update.0 = false;
|
||||||
|
// Update at most one chunk per frame to avoid freezes
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
358
src/map/cells.rs
358
src/map/cells.rs
@ -6,24 +6,26 @@ use rand::{seq::IteratorRandom, thread_rng, Rng};
|
|||||||
use crate::{time::GameTime, ui::CurrentAction};
|
use crate::{time::GameTime, ui::CurrentAction};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
animals::Animal, AnimalKind, CellsEntities, MapMarker, MeshNeedsUpdate, Voronoi,
|
animals::Animal, AnimalKind, CellsEntities, MeshMarker, MeshNeedsUpdate, Voronoi,
|
||||||
AVERAGE_NEIGHBORS_NUMBER, CELLS_PER_CHUNK, CELL_AREA,
|
AVERAGE_NEIGHBORS_NUMBER, CELLS_PER_CHUNK, CELL_AREA,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub mod grow;
|
||||||
|
pub mod input;
|
||||||
pub mod material;
|
pub mod material;
|
||||||
use material::CellMaterial;
|
use material::CellMaterial;
|
||||||
|
|
||||||
pub struct Plugin;
|
pub struct Plugin;
|
||||||
impl bevy::prelude::Plugin for Plugin {
|
impl bevy::prelude::Plugin for Plugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_systems(
|
// app.add_systems(
|
||||||
FixedUpdate,
|
// FixedUpdate,
|
||||||
(
|
// (
|
||||||
cells_regeneration.run_if(on_timer(Duration::from_secs(1))),
|
// cells_regeneration.run_if(on_timer(Duration::from_millis(500))),
|
||||||
expand.run_if(on_timer(Duration::from_secs(1))),
|
// expand.run_if(on_timer(Duration::from_millis(300))),
|
||||||
),
|
// ),
|
||||||
)
|
// )
|
||||||
.add_plugins(MaterialPlugin::<CellMaterial>::default());
|
app.add_plugins(MaterialPlugin::<CellMaterial>::default());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,50 +33,60 @@ impl bevy::prelude::Plugin for Plugin {
|
|||||||
pub enum CellKind {
|
pub enum CellKind {
|
||||||
Sea,
|
Sea,
|
||||||
Beach,
|
Beach,
|
||||||
Forest,
|
Forest { wealth: WealthType },
|
||||||
Dirt,
|
Dirt,
|
||||||
Stone,
|
Stone,
|
||||||
Grass,
|
Grass { wealth: WealthType },
|
||||||
}
|
}
|
||||||
impl CellKind {
|
impl CellKind {
|
||||||
pub fn regen_full_growth_duration(&self) -> Duration {
|
pub const fn regen_full_growth_duration(&self) -> Duration {
|
||||||
match self {
|
match self {
|
||||||
CellKind::Sea | CellKind::Beach | CellKind::Dirt | CellKind::Stone => unreachable!(),
|
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::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::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 {
|
match self {
|
||||||
CellKind::Sea => false,
|
CellKind::Sea => false,
|
||||||
_ => true,
|
_ => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub const fn grass_wealth(&self) -> Option<usize> {
|
||||||
|
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)]
|
#[derive(Debug, Component, Clone)]
|
||||||
pub struct Cell {
|
pub struct Cell {
|
||||||
pub kind: CellKind,
|
pub kind: CellKind,
|
||||||
pub voronoi_id: usize,
|
pub voronoi_id: usize,
|
||||||
pub vertices: Vec<usize>,
|
|
||||||
}
|
}
|
||||||
impl Cell {
|
impl Cell {
|
||||||
pub const fn color(&self, wealth: u8) -> [f32; 4] {
|
pub const fn color(&self) -> [f32; 4] {
|
||||||
match self.kind {
|
match self.kind {
|
||||||
CellKind::Sea => [0., 0., 1., 1.],
|
CellKind::Sea => [0., 0., 1., 1.],
|
||||||
CellKind::Beach => [0.82, 0.84, 0.51, 1.],
|
CellKind::Beach => [0.82, 0.84, 0.51, 1.],
|
||||||
CellKind::Forest => [0., 0.5 - (wealth as f32 / 255. * 0.4), 0., 1.],
|
CellKind::Forest { wealth } => [0., 0.5 - (wealth_to_unit(wealth) * 0.4), 0., 1.],
|
||||||
CellKind::Dirt => [
|
CellKind::Dirt => [0.53, 0.38, 0.29, 1.],
|
||||||
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::Stone => [0.5, 0.5, 0.5, 1.],
|
||||||
CellKind::Grass => [
|
CellKind::Grass { wealth } => [
|
||||||
(136. / 255.) - (wealth as f32 / 255. * 0.15),
|
(136. / 255.) - (wealth_to_unit(wealth) * 0.15),
|
||||||
(154. / 255.) + (wealth as f32 / 255. * 0.1),
|
(154. / 255.) + (wealth_to_unit(wealth) * 0.1),
|
||||||
(59. / 255.) - (wealth as f32 / 255. * 0.15),
|
(59. / 255.) - (wealth_to_unit(wealth) * 0.15),
|
||||||
1.,
|
1.,
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@ -93,189 +105,113 @@ impl Cell {
|
|||||||
// pub chunk:
|
// pub chunk:
|
||||||
// };
|
// };
|
||||||
|
|
||||||
#[derive(Component)]
|
pub type WealthType = usize;
|
||||||
pub struct Wealth(pub u8);
|
const fn wealth_to_unit(wealth: WealthType) -> f32 {
|
||||||
|
wealth as f32 / WealthType::MAX as f32
|
||||||
impl Default for Wealth {
|
|
||||||
fn default() -> Self {
|
|
||||||
Wealth(u8::MAX)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component)]
|
// const STEP: WealthType = WealthType::MAX / 4;
|
||||||
#[require(Wealth)]
|
// pub fn cells_regeneration(
|
||||||
pub struct Regeneration {
|
// mut cells: Query<(&mut Regeneration, &mut Wealth, &Parent)>,
|
||||||
pub last_update: Duration,
|
// mut chunks: Query<&mut MeshNeedsUpdate, With<MeshMarker>>,
|
||||||
pub full_growth_duration: Duration,
|
// gt: Res<GameTime>,
|
||||||
}
|
// ) {
|
||||||
|
// 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 expand(
|
||||||
pub fn cells_regeneration(
|
// mut cells: Query<(&mut Cell, Option<&Wealth>)>,
|
||||||
mut cells: Query<(&mut Regeneration, &mut Wealth, &Parent)>,
|
// voronoi: Res<Voronoi>,
|
||||||
mut chunks: Query<&mut MeshNeedsUpdate, With<MapMarker>>,
|
// cells_entities: Res<CellsEntities>,
|
||||||
gt: Res<GameTime>,
|
// mut cmds: Commands,
|
||||||
) {
|
// t: Res<Time>,
|
||||||
for (mut regen, mut wealth, parent) in cells.iter_mut() {
|
// gt: Res<GameTime>,
|
||||||
while gt.current - regen.last_update > regen.full_growth_duration / u8::MAX as u32 {
|
// ) {
|
||||||
regen.last_update = gt
|
// let mut random = thread_rng();
|
||||||
.current
|
// let mut changes = Vec::new();
|
||||||
.min(regen.last_update + (regen.full_growth_duration / u8::MAX as u32));
|
// for (cell, wealth) in cells.iter() {
|
||||||
wealth.0 = wealth.0.saturating_add(1);
|
// // TODO: search beter numbers, for now they are arbitrary
|
||||||
// Not update map each time for optimization
|
// if cell.kind == CellKind::Forest || cell.kind == CellKind::Grass {
|
||||||
if wealth.0 % STEP == 0 {
|
// let wealth = if cell.kind == CellKind::Forest {
|
||||||
chunks.get_mut(parent.get()).unwrap().0 = true;
|
// 1.
|
||||||
}
|
// } else {
|
||||||
}
|
// wealth.unwrap().0 as f64 / 255.
|
||||||
}
|
// };
|
||||||
}
|
// if (t.elapsed_secs_f64() * (gt.speed as f64) * wealth * 1e-7
|
||||||
|
// / (CELL_AREA as f64).sqrt())
|
||||||
pub fn expand(
|
// >= 1.
|
||||||
mut cells: Query<(&mut Cell, Option<&Wealth>)>,
|
// {
|
||||||
voronoi: Res<Voronoi>,
|
// // "Grass can expand into adjacent dirt at a rate of 2.5 to 7.5 cm (1 to 3 inches) per month.", DeepSeek
|
||||||
cells_entities: Res<CellsEntities>,
|
// // With some (way too complicated and probably false) computation, we get for a good grass an extension rate of (1e-7 / sqrt(cell_area)) cells per seconds
|
||||||
mut cmds: Commands,
|
// let target = voronoi
|
||||||
t: Res<Time>,
|
// .0
|
||||||
gt: Res<GameTime>,
|
// .cell(cell.voronoi_id)
|
||||||
) {
|
// .iter_neighbors()
|
||||||
let mut random = thread_rng();
|
// .choose(&mut random)
|
||||||
let mut changes = Vec::new();
|
// .unwrap();
|
||||||
for (cell, wealth) in cells.iter() {
|
// changes.extend(
|
||||||
// TODO: search beter numbers, for now they are arbitrary
|
// voronoi
|
||||||
if cell.kind == CellKind::Forest || cell.kind == CellKind::Grass {
|
// .0
|
||||||
let wealth = if cell.kind == CellKind::Forest {
|
// .cell(cell.voronoi_id)
|
||||||
1.
|
// .iter_neighbors()
|
||||||
} else {
|
// .map(|t| (t, CellKind::Grass)),
|
||||||
wealth.unwrap().0 as f64 / 255.
|
// );
|
||||||
};
|
// }
|
||||||
if random.gen_bool(
|
// }
|
||||||
dbg!(
|
// if cell.kind == CellKind::Forest {
|
||||||
t.elapsed_secs_f64() * (gt.speed as f64) * wealth * 1e-7
|
// if (t.elapsed_secs_f64()
|
||||||
/ (CELL_AREA as f64).sqrt()
|
// * (gt.speed as f64)
|
||||||
)
|
// * (wealth.unwrap().0 as f64 / 255.).sqrt()
|
||||||
.min(1.),
|
// * 5.6e-7
|
||||||
) {
|
// / (CELL_AREA as f64).sqrt()
|
||||||
// "Grass can expand into adjacent dirt at a rate of 2.5 to 7.5 cm (1 to 3 inches) per month.", DeepSeek
|
// * AVERAGE_NEIGHBORS_NUMBER)
|
||||||
// With some (way too complicated and probably false) computation, we get for a good grass an extension rate of (1e-7 / sqrt(cell_area)) cells per seconds
|
// >= 1.
|
||||||
let target = voronoi
|
// {
|
||||||
.0
|
// // "Forests can expand at a rate of 1 to 10 meters per year", DeepSeek
|
||||||
.cell(cell.voronoi_id)
|
// // Same computations as above : (5.6e-7 / sqrt(cell_area)) cells per seconds
|
||||||
.iter_neighbors()
|
// let target = voronoi
|
||||||
.choose(&mut random)
|
// .0
|
||||||
.unwrap();
|
// .cell(cell.voronoi_id)
|
||||||
changes.extend(
|
// .iter_neighbors()
|
||||||
voronoi
|
// .choose(&mut random)
|
||||||
.0
|
// .unwrap();
|
||||||
.cell(cell.voronoi_id)
|
// changes.push((target, CellKind::Forest));
|
||||||
.iter_neighbors()
|
// }
|
||||||
.map(|t| (t, CellKind::Grass)),
|
// }
|
||||||
);
|
// }
|
||||||
}
|
// for (v_id, kind) in changes {
|
||||||
}
|
// let target = cells_entities.0[v_id];
|
||||||
if cell.kind == CellKind::Forest {
|
// let (mut cell, _) = cells.get_mut(target).unwrap();
|
||||||
if random.gen_bool(
|
// if kind == CellKind::Forest && (cell.kind == CellKind::Dirt || cell.kind == CellKind::Grass)
|
||||||
dbg!(
|
// {
|
||||||
t.elapsed_secs_f64()
|
// cell.kind = CellKind::Forest;
|
||||||
* (gt.speed as f64)
|
// cmds.entity(target).insert((
|
||||||
* (wealth.unwrap().0 as f64 / 255.).sqrt()
|
// Wealth(0),
|
||||||
* 5.6e-7
|
// Regeneration {
|
||||||
/ (CELL_AREA as f64).sqrt()
|
// full_growth_duration: CellKind::Forest.regen_full_growth_duration(),
|
||||||
* AVERAGE_NEIGHBORS_NUMBER
|
// last_update: gt.current,
|
||||||
)
|
// },
|
||||||
.min(1.),
|
// ));
|
||||||
) {
|
// } else if kind == CellKind::Grass && cell.kind == CellKind::Dirt {
|
||||||
// "Forests can expand at a rate of 1 to 10 meters per year", DeepSeek
|
// cell.kind = CellKind::Grass;
|
||||||
// Same computations as above : (5.6e-7 / sqrt(cell_area)) cells per seconds
|
// cmds.entity(target).insert((
|
||||||
let target = voronoi
|
// Wealth(0),
|
||||||
.0
|
// Regeneration {
|
||||||
.cell(cell.voronoi_id)
|
// full_growth_duration: CellKind::Grass.regen_full_growth_duration(),
|
||||||
.iter_neighbors()
|
// last_update: gt.current,
|
||||||
.choose(&mut random)
|
// },
|
||||||
.unwrap();
|
// ));
|
||||||
changes.push((target, CellKind::Forest));
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
|
||||||
for (v_id, kind) in changes {
|
|
||||||
let target = cells_entities.0[v_id];
|
|
||||||
let (mut cell, _) = cells.get_mut(target).unwrap();
|
|
||||||
if kind == CellKind::Forest && (cell.kind == CellKind::Dirt || cell.kind == CellKind::Grass)
|
|
||||||
{
|
|
||||||
cell.kind = CellKind::Forest;
|
|
||||||
cmds.entity(target).insert((
|
|
||||||
Wealth(0),
|
|
||||||
Regeneration {
|
|
||||||
full_growth_duration: CellKind::Forest.regen_full_growth_duration(),
|
|
||||||
last_update: gt.current,
|
|
||||||
},
|
|
||||||
));
|
|
||||||
} else if kind == CellKind::Grass && cell.kind == CellKind::Dirt {
|
|
||||||
cell.kind = CellKind::Grass;
|
|
||||||
cmds.entity(target).insert((
|
|
||||||
Wealth(0),
|
|
||||||
Regeneration {
|
|
||||||
full_growth_duration: CellKind::Grass.regen_full_growth_duration(),
|
|
||||||
last_update: gt.current,
|
|
||||||
},
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn on_click(
|
|
||||||
trigger: Trigger<Pointer<Click>>,
|
|
||||||
mut cells: Query<&mut Cell>,
|
|
||||||
voronoi: Res<Voronoi>,
|
|
||||||
mut map_needs_update: Query<&mut MeshNeedsUpdate, With<MapMarker>>,
|
|
||||||
mut cmds: Commands,
|
|
||||||
ca: Res<CurrentAction>,
|
|
||||||
gt: Res<GameTime>,
|
|
||||||
) {
|
|
||||||
if trigger.duration > Duration::from_millis(200) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let mut cell = cells.get_mut(trigger.target).unwrap();
|
|
||||||
match *ca {
|
|
||||||
CurrentAction::ChangeCell(ck) => match ck {
|
|
||||||
CellKind::Forest => match cell.kind {
|
|
||||||
CellKind::Dirt | CellKind::Grass => {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
},
|
|
||||||
CellKind::Grass => match cell.kind {
|
|
||||||
CellKind::Dirt => {
|
|
||||||
cmds.entity(trigger.target).insert((
|
|
||||||
Wealth(0),
|
|
||||||
Regeneration {
|
|
||||||
last_update: gt.current,
|
|
||||||
full_growth_duration: CellKind::Grass.regen_full_growth_duration(),
|
|
||||||
},
|
|
||||||
));
|
|
||||||
cell.kind = CellKind::Grass;
|
|
||||||
map_needs_update.single_mut().0 = true;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
},
|
|
||||||
_ => {}
|
|
||||||
},
|
|
||||||
CurrentAction::AddAnimal(ak) => {
|
|
||||||
if cell.kind.can_place_animal(ak) {
|
|
||||||
let v_cell = voronoi.0.cell(cell.voronoi_id);
|
|
||||||
let cell_pos = v_cell.site_position();
|
|
||||||
cmds.spawn((
|
|
||||||
Animal { kind: ak },
|
|
||||||
Transform::from_xyz(cell_pos.x as f32, cell_pos.y as f32, 0.),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
79
src/map/cells/grow.rs
Normal file
79
src/map/cells/grow.rs
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use bevy::tasks::futures_lite::{future, FutureExt};
|
||||||
|
use bevy::tasks::{block_on, AsyncComputeTaskPool, IoTaskPool, Task};
|
||||||
|
use voronoice::Voronoi;
|
||||||
|
|
||||||
|
use crate::map::{CellsEntities, Chunk, MeshNeedsUpdate};
|
||||||
|
|
||||||
|
use super::Cell;
|
||||||
|
|
||||||
|
pub struct Plugin;
|
||||||
|
impl bevy::prelude::Plugin for Plugin {
|
||||||
|
fn build(&self, app: &mut App) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Background task that manages terrain
|
||||||
|
#[derive(Resource)]
|
||||||
|
pub struct GrowThread(pub Task<()>);
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
struct GetChunkTask(Task<Vec<Cell>>);
|
||||||
|
|
||||||
|
fn start_grow_task(mut cmds: Commands) {}
|
||||||
|
|
||||||
|
fn rx_chunks_updates(
|
||||||
|
mut chunks: Query<(&Chunk, &mut GetChunkTask, &mut MeshNeedsUpdate)>,
|
||||||
|
mut cells: Query<&mut Cell>,
|
||||||
|
cells_entities: Res<CellsEntities>,
|
||||||
|
) {
|
||||||
|
for (ch_id, mut task, mut needs_update) in chunks.iter_mut() {
|
||||||
|
let res = block_on(future::poll_once(&mut task.0));
|
||||||
|
if let Some(updated_cells) = res {
|
||||||
|
if updated_cells.len() > 0 {
|
||||||
|
needs_update.0 = true;
|
||||||
|
}
|
||||||
|
for c in updated_cells.into_iter() {
|
||||||
|
let id = c.voronoi_id;
|
||||||
|
*cells.get_mut(cells_entities.0[id]).unwrap() = c;
|
||||||
|
}
|
||||||
|
let task_pool = IoTaskPool::get();
|
||||||
|
task.0 = task_pool.spawn(async { Vec::new() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn grow_thread(voronoi: Voronoi, current_frame: Arc<AtomicU>) {}
|
||||||
|
|
||||||
|
// fn begin_generating_map_chunks(mut my_tasks: ResMut<MyMapGenTasks>) {
|
||||||
|
// let task_pool = AsyncComputeTaskPool::get();
|
||||||
|
// for chunk_coord in decide_what_chunks_to_generate(/* ... */) {
|
||||||
|
// // we might have already spawned a task for this `chunk_coord`
|
||||||
|
// if my_tasks.generating_chunks.contains_key(&chunk_coord) {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// let task = task_pool.spawn(async move {
|
||||||
|
// // TODO: do whatever you want here!
|
||||||
|
// generate_map_chunk(chunk_coord)
|
||||||
|
// });
|
||||||
|
// my_tasks.generating_chunks.insert(chunk_coord, task);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn receive_generated_map_chunks(mut my_tasks: ResMut<MyMapGenTasks>) {
|
||||||
|
// my_tasks.generating_chunks.retain(|chunk_coord, task| {
|
||||||
|
// // check on our task to see how it's doing :)
|
||||||
|
// let status = block_on(future::poll_once(task));
|
||||||
|
|
||||||
|
// // keep the entry in our HashMap only if the task is not done yet
|
||||||
|
// let retain = status.is_none();
|
||||||
|
|
||||||
|
// // if this task is done, handle the data it returned!
|
||||||
|
// if let Some(mut chunk_data) = status {
|
||||||
|
// // TODO: do something with the returned `chunk_data`
|
||||||
|
// }
|
||||||
|
|
||||||
|
// retain
|
||||||
|
// });
|
||||||
|
// }
|
60
src/map/cells/input.rs
Normal file
60
src/map/cells/input.rs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
map::{animals::Animal, MeshMarker, MeshNeedsUpdate, Voronoi},
|
||||||
|
time::GameTime,
|
||||||
|
ui::CurrentAction,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{Cell, CellKind, WealthType};
|
||||||
|
|
||||||
|
pub fn on_click(
|
||||||
|
trigger: Trigger<Pointer<Click>>,
|
||||||
|
mut cells: Query<(&mut Cell, &Parent)>,
|
||||||
|
voronoi: Res<Voronoi>,
|
||||||
|
mut chunks: Query<&mut MeshNeedsUpdate, With<MeshMarker>>,
|
||||||
|
mut cmds: Commands,
|
||||||
|
ca: Res<CurrentAction>,
|
||||||
|
gt: Res<GameTime>,
|
||||||
|
) {
|
||||||
|
if trigger.duration > Duration::from_millis(200) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let (mut cell, parent) = cells.get_mut(trigger.target).unwrap();
|
||||||
|
match *ca {
|
||||||
|
CurrentAction::ChangeCell(ck) => match ck {
|
||||||
|
CellKind::Forest { .. } => match cell.kind {
|
||||||
|
CellKind::Dirt | CellKind::Grass { .. } => {
|
||||||
|
cell.kind = CellKind::Forest {
|
||||||
|
wealth: WealthType::MAX / 2,
|
||||||
|
};
|
||||||
|
chunks.get_mut(parent.get()).unwrap().0 = true;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
CellKind::Grass { .. } => match cell.kind {
|
||||||
|
CellKind::Dirt => {
|
||||||
|
cell.kind = CellKind::Grass {
|
||||||
|
wealth: WealthType::MAX / 2,
|
||||||
|
};
|
||||||
|
chunks.get_mut(parent.get()).unwrap().0 = true;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
CurrentAction::AddAnimal(ak) => {
|
||||||
|
if cell.kind.can_place_animal(ak) {
|
||||||
|
let v_cell = voronoi.0.cell(cell.voronoi_id);
|
||||||
|
let cell_pos = v_cell.site_position();
|
||||||
|
cmds.spawn((
|
||||||
|
Animal { kind: ak },
|
||||||
|
Transform::from_xyz(cell_pos.x as f32, cell_pos.y as f32, 0.),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
@ -8,7 +8,7 @@ use bevy::{
|
|||||||
};
|
};
|
||||||
use voronoice::Point;
|
use voronoice::Point;
|
||||||
|
|
||||||
use super::{CellsEntities, MapMarker, Voronoi};
|
use super::{CellsEntities, MeshMarker, Voronoi};
|
||||||
use crate::camera::CameraMarker;
|
use crate::camera::CameraMarker;
|
||||||
|
|
||||||
pub fn picking_backend(
|
pub fn picking_backend(
|
||||||
|
@ -7,7 +7,7 @@ impl bevy::prelude::Plugin for Plugin {
|
|||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.insert_resource(GameTime {
|
app.insert_resource(GameTime {
|
||||||
current: Duration::ZERO,
|
current: Duration::ZERO,
|
||||||
speed: 24. * 60. * 60. * 10.,
|
speed: 24. * 60. * 60. * 10. * 10.,
|
||||||
})
|
})
|
||||||
.add_systems(PreUpdate, update_time);
|
.add_systems(PreUpdate, update_time);
|
||||||
}
|
}
|
||||||
|
@ -128,11 +128,11 @@ fn setup(mut world: Commands, asset_server: Res<AssetServer>) {
|
|||||||
BackgroundColor(TABBAR_COLOR);
|
BackgroundColor(TABBAR_COLOR);
|
||||||
.observe(move |trigger: Trigger<Pointer<Click>>, mut ca: ResMut<CurrentAction>, mut bg: Query<&mut BackgroundColor>| {
|
.observe(move |trigger: Trigger<Pointer<Click>>, mut ca: ResMut<CurrentAction>, mut bg: Query<&mut BackgroundColor>| {
|
||||||
if trigger.button == PointerButton::Primary {
|
if trigger.button == PointerButton::Primary {
|
||||||
if *ca == CurrentAction::ChangeCell(CellKind::Forest) {
|
if matches!(*ca, CurrentAction::ChangeCell(CellKind::Forest {..})) {
|
||||||
*ca = CurrentAction::None;
|
*ca = CurrentAction::None;
|
||||||
bg.get_mut(forest).unwrap().0 = TABBAR_COLOR;
|
bg.get_mut(forest).unwrap().0 = TABBAR_COLOR;
|
||||||
} else {
|
} else {
|
||||||
*ca = CurrentAction::ChangeCell(CellKind::Forest);
|
*ca = CurrentAction::ChangeCell(CellKind::Forest {wealth: 0});
|
||||||
bg.get_mut(forest).unwrap().0 = ENABLED_BUTTON_COLOR;
|
bg.get_mut(forest).unwrap().0 = ENABLED_BUTTON_COLOR;
|
||||||
}
|
}
|
||||||
bg.get_mut(grass).unwrap().0 = TABBAR_COLOR;
|
bg.get_mut(grass).unwrap().0 = TABBAR_COLOR;
|
||||||
@ -149,11 +149,11 @@ fn setup(mut world: Commands, asset_server: Res<AssetServer>) {
|
|||||||
BackgroundColor(TABBAR_COLOR);
|
BackgroundColor(TABBAR_COLOR);
|
||||||
.observe(move |trigger: Trigger<Pointer<Click>>, mut ca: ResMut<CurrentAction>, mut bg: Query<&mut BackgroundColor>| {
|
.observe(move |trigger: Trigger<Pointer<Click>>, mut ca: ResMut<CurrentAction>, mut bg: Query<&mut BackgroundColor>| {
|
||||||
if trigger.button == PointerButton::Primary {
|
if trigger.button == PointerButton::Primary {
|
||||||
if *ca == CurrentAction::ChangeCell(CellKind::Grass) {
|
if matches!(*ca, CurrentAction::ChangeCell(CellKind::Grass {..})) {
|
||||||
*ca = CurrentAction::None;
|
*ca = CurrentAction::None;
|
||||||
bg.get_mut(grass).unwrap().0 = TABBAR_COLOR;
|
bg.get_mut(grass).unwrap().0 = TABBAR_COLOR;
|
||||||
} else {
|
} else {
|
||||||
*ca = CurrentAction::ChangeCell(CellKind::Grass);
|
*ca = CurrentAction::ChangeCell(CellKind::Grass {wealth: 0});
|
||||||
bg.get_mut(grass).unwrap().0 = ENABLED_BUTTON_COLOR;
|
bg.get_mut(grass).unwrap().0 = ENABLED_BUTTON_COLOR;
|
||||||
}
|
}
|
||||||
bg.get_mut(forest).unwrap().0 = TABBAR_COLOR;
|
bg.get_mut(forest).unwrap().0 = TABBAR_COLOR;
|
||||||
|
Loading…
Reference in New Issue
Block a user