Compare commits

..

No commits in common. "6d7567d0ae21d5f64449448d9829eb1d058e1525" and "97eb82e009a08568c6652f1eb0537d9eddbc1512" have entirely different histories.

4 changed files with 73 additions and 180 deletions

View File

@ -1,11 +1,9 @@
#![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() {
@ -16,7 +14,6 @@ pub fn main() {
camera::Plugin,
map::Plugin,
ui::Plugin,
time::Plugin,
FrameTimeDiagnosticsPlugin,
LogDiagnosticsPlugin {
filter: Some(vec![FrameTimeDiagnosticsPlugin::FPS]),

View File

@ -10,14 +10,14 @@ 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::<Fixed>::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, cells_regeneration, expand))
.add_systems(Update, update_map_mesh)
.insert_resource(ClearColor(Color::srgb(0., 0., 1.)))
.insert_resource(Seed(thread_rng().gen()));
}
@ -65,7 +65,7 @@ fn setup(
.set_lloyd_relaxation_iterations(3)
.build()
.unwrap();
let mut cells = Vec::with_capacity(SIZE);
let mut cells_data = Vec::with_capacity(SIZE);
let z_noise = Fbm::<Perlin>::new(seed.0);
let moisture_noise = Fbm::<Perlin>::new(seed.0+1)
.set_frequency(2.);
@ -89,23 +89,27 @@ fn setup(
} else {
CellKind::Stone
};
cells.push(Cell {
kind: k,
voronoi_id: i,
altitude: (z*255.) as u8,
vertices: vec![]
});
cells_data.push(CellData::new(k, i, (z*255.) as u8, (m*255.) as u8, 0, 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.iter_mut()).filter(|(_,cd)| cd.kind != CellKind::Forest) {
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.);
// }
let vs = c.iter_vertices().collect::<Vec<_>>();
let i = poss.len();
for v in vs.iter() {
poss.push(Vec3::new(v.x as f32, v.y as f32, 0.));
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);
}
for v in 1..(vs.len()-1) {
indices.extend_from_slice(&[i as u32, (i+v) as u32, (i+v+1) as u32]);
@ -113,7 +117,6 @@ 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.
@ -127,7 +130,7 @@ fn setup(
)
.with_inserted_indices(Indices::U32(indices));
let mut cells_entities = Vec::with_capacity(cells.len());
let mut cells_entities = Vec::with_capacity(cells_data.len());
cmds.spawn((
Mesh2d(meshes.add(mesh)),
MeshMaterial2d(materials.add(ColorMaterial::default())),
@ -137,40 +140,21 @@ fn setup(
MeshNeedsUpdate(true),
MapMarker
)).with_children(|parent| {
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<Pointer<Click>>,
mut cells: Query<&mut Cell>,
mut map_needs_update: Query<&mut MeshNeedsUpdate, With<MapMarker>>,
mut cmds: Commands,
gt: Res<GameTime>
| {
if trigger.duration > Duration::from_millis(200) {
for cd in cells_data {
let mut cmd = parent.spawn((cd, LastUpdate(0)));
cmd.observe(|trigger: Trigger<Pointer<Click>>, mut cells: Query<&mut CellData>, mut map_needs_update: Query<&mut MeshNeedsUpdate, With<MapMarker>>| {
if trigger.duration > Duration::from_millis(100) {
return
}
let mut cell = cells.get_mut(trigger.target).unwrap();
match cell.kind {
let mut cd = cells.get_mut(trigger.target).unwrap();
match cd.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;
cd.kind = CellKind::Forest;
map_needs_update.single_mut().0 = true;
},
_ => {}
}
dbg!(trigger.duration);
});
cells_entities.push(cmd.id());
}
@ -178,8 +162,30 @@ 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<MapMarker>>
) {
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<(&Cell, Option<&Wealth>)>,
cells: Query<&CellData>,
mut map: Query<(&Mesh2d, &mut MapColors, &mut MeshNeedsUpdate), With<MapMarker>>,
mut meshes: ResMut<Assets<Mesh>>
) {
@ -187,9 +193,9 @@ fn update_map_mesh(
if needs_update.0 {
if let Some(mesh) = meshes.get_mut(mesh) {
let mut modified = false;
for (cell, wealth) in cells.iter() {
let col = cell.color(wealth.map(|w| w.0).unwrap_or_default());
for id in cell.vertices.iter() {
for cd in cells.iter() {
let col = cd.color();
for id in cd.vertices.iter() {
modified = modified || cols.0[*id] != col;
cols.0[*id] = col.clone();
}

View File

@ -1,14 +1,8 @@
use std::time::Duration;
use bevy::prelude::*;
use rand::{seq::IteratorRandom, thread_rng, Rng};
use crate::time::GameTime;
use super::{CellsEntities, MapMarker, MeshNeedsUpdate, Voronoi};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CellKind {
Void,
Sea,
Beach,
Forest,
@ -16,112 +10,36 @@ pub enum CellKind {
Stone,
Grass
}
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
}
}
}
#[derive(Debug, Component)]
pub struct Cell {
pub struct CellData {
pub kind: CellKind,
pub voronoi_id: usize,
pub altitude: u8,
pub cid: usize,
z: u8,
pub moisture: u8,
pub resource: u8, // How much resource there is (between 0 and 4)
pub vertices: Vec<usize>
}
impl Cell {
pub fn color(&self, wealth: u8) -> [f32; 4] {
impl CellData {
pub fn new(kind: CellKind, cell: usize, z: u8, moisture: u8, resource: u8, vertices: Vec<usize>) -> Self {
Self {
kind,
cid: cell,
z,
moisture,
resource,
vertices
}
}
pub fn color(&self) -> [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 - (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 => [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::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), 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<MapMarker>>,
gt: Res<GameTime>
) {
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;
}
}
}
pub fn expand(
mut cells: Query<(&mut Cell, Option<&Wealth>)>,
map: Query<(&Voronoi, &CellsEntities)>,
mut cmds: Commands,
t: Res<Time>,
gt: Res<GameTime>
) {
let (voronoi, cells_entities) = map.single();
let mut random = thread_rng();
let mut changes = Vec::new();
for (cell, wealth) in cells.iter() {
// TODO: search beter numbers, for now they are arbitrary
if cell.kind == CellKind::Forest || cell.kind == CellKind::Grass {
let wealth = if cell.kind == CellKind::Forest {
1.
} else {
wealth.unwrap().0 as f64/255.
};
if random.gen_bool((t.elapsed_secs_f64()*(gt.speed as f64)*wealth/(60.*60.*24.*30.)).min(1.)) { // Let say that grass takes 1 months to expand
let target = voronoi.0.cell(cell.voronoi_id).iter_neighbors().choose(&mut random).unwrap();
changes.push((target, CellKind::Grass));
}
}
if cell.kind == CellKind::Forest {
if random.gen_bool((t.elapsed_secs_f64()*(gt.speed as f64)*(wealth.unwrap().0 as f64/255.).sqrt()/(60.*60.*24.*365.*5.)).min(1.)) { // Let say that forest takes 5 years to expand
let target = voronoi.0.cell(cell.voronoi_id).iter_neighbors().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
}));
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.]
}
}
}

View File

@ -1,28 +0,0 @@
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. * 10.
})
.add_systems(PreUpdate, update_time);
}
}
#[derive(Resource)]
pub struct GameTime {
pub current: Duration,
pub speed: f32 // = game time / real time
}
fn update_time(
mut gt: ResMut<GameTime>,
time: Res<Time>
) {
let speed = gt.speed;
gt.current += Duration::from_secs_f32(time.delta_secs() * speed);
}