From f54ca4ed196aa2451c49eb26a2d48649a664b11c Mon Sep 17 00:00:00 2001 From: Arkitu <85173315+Arkitu@users.noreply.github.com> Date: Fri, 6 Sep 2024 19:31:23 +0200 Subject: [PATCH] entities are well managed (and die lol) --- src/lib.rs | 1 - src/state.rs | 379 ++++++++++---------------------------------- src/state/entity.rs | 222 ++++++++++++++++++++++++++ src/state/map.rs | 140 ++++++++++++++++ src/state/ui.rs | 8 + 5 files changed, 454 insertions(+), 296 deletions(-) create mode 100644 src/state/entity.rs create mode 100644 src/state/map.rs create mode 100644 src/state/ui.rs diff --git a/src/lib.rs b/src/lib.rs index 3ed154b..e0882af 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,5 @@ mod graphics; mod state; -use std::time::Duration; use state::State; use graphics::Graphics; diff --git a/src/state.rs b/src/state.rs index e475bf6..f03ce6c 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,300 +1,31 @@ -use std::time::Instant; +use std::{collections::{BTreeMap, HashMap}, time::Instant}; use log::info; -use noise::{Fbm, MultiFractal, NoiseFn, Perlin}; +use map::{CellKind, Map}; use rand::prelude::*; -use voronoice::{BoundingBox, Point, Voronoi, VoronoiBuilder}; +use voronoice::Point; use winit::{event::{DeviceEvent, Event, KeyEvent, MouseButton, MouseScrollDelta, WindowEvent}, keyboard::{KeyCode, PhysicalKey}, window::Window}; use crate::graphics::{Uniforms, Vertex}; -const FRAMERATE: usize = 1; // Update per second - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum CellKind { - Void, - Sea, - Beach, - Forest, - Dirt, - Stone, - Grass -} - -#[derive(Debug)] -struct CellData { - kind: CellKind, - cell: usize, - z: f32, - area: f32, - moisture: f32, - ressource: (f32, usize) // How much ressource there is (between 0 and 1) and the last time it was updated (in frames) -} -impl CellData { - fn new(kind: CellKind, cell: usize, z: f32, moisture: f32, ressource: f32, t: usize, voronoi: &Voronoi) -> Self { - let mut area = 0.; - let c = voronoi.cell(cell); - let vs = c.iter_vertices().collect::>(); - let a = vs[0]; - for i in 1..(vs.len()-1) { - let b = vs[i]; - let c = vs[i+1]; - area += 0.5 * ((a.x*(b.y-c.y))+(b.x*(c.y-a.y))+(c.x*(a.y-b.y))).abs(); - } - Self { - kind, - cell, - z, - area: area as f32, - moisture, - ressource: (ressource, t) - } - } - fn color(&self) -> [f32; 4] { - // let mut rng = thread_rng(); - // [rng.gen(), rng.gen(), rng.gen(), 1.] - 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.moisture*0.4), 0., 1.], - CellKind::Dirt => [0.53 - (self.moisture*0.4), 0.38-(self.moisture*0.4), 0.29-(self.moisture*0.4), 1.], - CellKind::Stone => [0.5, 0.5, 0.5, 1.], - CellKind::Grass => [(136./255.) - (self.moisture*0.4), (204./255.) - (self.moisture*0.4), (59./255.) - (self.moisture*0.4), 1.] - } - } - fn ressource(&self) -> f32 { - // How much it get by day - let recuperation_rate = match self.kind { - CellKind::Void | CellKind::Sea | CellKind::Beach | CellKind::Dirt | CellKind::Stone => 0., - CellKind::Forest => 1. / (100. * 365.25), // Let's say that a forest takes 100 years to mature - CellKind::Grass => 1. / (7. * 7.) // Let's say that grass takes 7 weaks to reach its max - }; - self.ressource.0 + (self.ressource.1 as f32 * recuperation_rate) - } - fn set_ressource(&mut self, val: f32, t: usize) { - assert!(val >= 0. && val <= 1.); - self.ressource = (val, t); - } - fn refresh_ressource(&mut self, t: usize) { - self.ressource = (self.ressource(), t); - } -} - -struct Map { - voronoi: Voronoi, - cells_data: Vec, - seed: u32 -} -impl Map { - const HEIGHT: f32 = 2.; - const WIDTH: f32 = 2.; - const REAL_HEIGHT: f32 = 500.; - const REAL_WIDTH: f32 = 500.; - const SIZE: usize = 10_000; - fn new(seed: u32, t: usize) -> Self { - let mut rng = rand::rngs::SmallRng::seed_from_u64(seed as u64); - let mut sites = Vec::with_capacity(Self::SIZE); - for _ in 0..Self::SIZE { - sites.push(Point { x:rng.gen_range(-Self::WIDTH/2.0..Self::WIDTH/2.0) as f64, y:rng.gen_range(-Self::HEIGHT/2.0..Self::HEIGHT/2.0) as f64 }) - } - let voronoi = VoronoiBuilder::default() - .set_sites(sites) - .set_bounding_box(BoundingBox::new_centered(Self::WIDTH as f64, Self::HEIGHT as f64)) - .set_lloyd_relaxation_iterations(3) - .build() - .unwrap(); - let mut cells_data = Vec::with_capacity(Self::SIZE); - let z_noise = Fbm::::new(seed); - let moisture_noise = Fbm::::new(seed+1) - .set_frequency(2.); - for i in 0..Self::SIZE { - let c = voronoi.cell(i); - let site = c.site_position(); - let z = ( - 0.3 // Arbitrary value - + ((z_noise.get([site.x, site.y])+1.)/2.) // Noise + [0; 1] - - ((site.x.powi(2)+site.y.powi(2)).sqrt()*0.5) // Distance - [0; sqrt(2)] * 0.5 - ).clamp(0., 1.); - let m = ( - (moisture_noise.get([site.x, site.y])+1.)/2. // Noise + [0; 1] - ).clamp(0., 1.) as f32; - let k = if z <= 0.5 { - CellKind::Sea - } else if z <= 0.52 { - CellKind::Beach - } else if z < 0.8 { - CellKind::Dirt - } else { - CellKind::Stone - }; - cells_data.push(CellData::new(k, i, z as f32, m, 1., t, &voronoi)); - } - Self { - voronoi, - cells_data, - seed - } - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum EntityKind { - Horse -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum EntityState { - Walking(Instant), // Start of walk - Resting -} - -#[derive(Debug)] -struct Entity { - cell: usize, - kind: EntityKind, - start: Instant, - state: EntityState, - health: f32 // between 0 and 1 -} -impl Entity { - fn new(cell: usize, kind: EntityKind) -> Self { - Self { - cell, - kind, - start: Instant::now(), - state: EntityState::Resting, - health: 1. - } - } - fn pos<'a>(&self, map: &'a Map) -> &'a Point { - &map.voronoi.sites()[self.cell] - } - fn render(&self, vertices: &mut Vec, indices: &mut Vec, map: &Map) { - match self.kind { - EntityKind::Horse => { - let color = [171./255., 122./255., 50./255., 1.]; - let dark = [color[0]-0.3, color[1]-0.3, color[2]-0.3, 1.]; - let (vs, is) = match self.state { - EntityState::Walking(now) => { - let now = now.elapsed().as_secs_f32()*5.; - ( - [ - // back left leg - Vertex { pos: [-0.5, 0.3], color: dark }, - Vertex { pos: [-0.4 + (now.sin()*0.1), -0.7 + (now.cos().max(-0.5)*0.1)], color: dark }, - Vertex { pos: [-0.25, 0.1], color: dark }, - - // back right leg - Vertex { pos: [-0.5, 0.3], color }, - Vertex { pos: [-0.4 + ((now + 1.).sin()*0.1), -0.7 + ((now + 1.).cos().max(-0.5)*0.1)], color }, - Vertex { pos: [-0.25, 0.1], color }, - - // front left leg - Vertex { pos: [0.3, 0.2], color: dark }, - Vertex { pos: [0.4 + ((now-1.).sin()*0.1), -0.7 + ((now-1.).cos().max(-0.5)*0.1)], color: dark }, - Vertex { pos: [0.5, 0.3], color: dark }, - - // front right leg - Vertex { pos: [0.3, 0.2], color }, - Vertex { pos: [0.4 + ((now-2.).sin()*0.1), -0.7 + ((now-2.).cos().max(-0.5)*0.1)], color }, - Vertex { pos: [0.5, 0.3], color }, - - // body - // 3 - Vertex { pos: [-0.3, 0.], color }, - Vertex { pos: [0.4, -0.1], color }, - // 11 - Vertex { pos: [0.3, 0.4], color }, - ], - [ - 0,1,2, - 3,4,5, - 6,7,8, - 9,10,11, - 3,12,13, - 3,13,11, - 3,11,14 - ] - ) - }, - EntityState::Resting => { - ( - [ - // back left leg - Vertex { pos: [-0.5, 0.3], color: dark }, - Vertex { pos: [-0.4, -0.75], color: dark }, - Vertex { pos: [-0.25, 0.1], color: dark }, - - // back right leg - Vertex { pos: [-0.5, 0.3], color }, - Vertex { pos: [-0.4, -0.75], color }, - Vertex { pos: [-0.25, 0.1], color }, - - // front left leg - Vertex { pos: [0.3, 0.2], color: dark }, - Vertex { pos: [0.4, -0.75], color: dark }, - Vertex { pos: [0.5, 0.3], color: dark }, - - // front right leg - Vertex { pos: [0.3, 0.2], color }, - Vertex { pos: [0.4, -0.75], color }, - Vertex { pos: [0.5, 0.3], color }, - - // body - // 3 - Vertex { pos: [-0.3, 0.], color }, - Vertex { pos: [0.4, -0.1], color }, - // 11 - Vertex { pos: [0.3, 0.4], color }, - ], - [ - 0,1,2, - 3,4,5, - 6,7,8, - 9,10,11, - 3,12,13, - 3,13,11, - 3,11,14 - ] - ) - } - }; - - let pos = self.pos(map); - - vertices.reserve(vs.len()); - let base = vertices.len() as u32; - for mut v in vs { - v.pos[0] = v.pos[0]/50. + pos.x as f32; - v.pos[1] = (v.pos[1] + 0.75)/50. + pos.y as f32; - vertices.push(v) - } - - indices.reserve(is.len()); - for i in is { - indices.push(base + i); - } - } - } - } - fn update(&mut self, map: &mut Map) { - // Let’s take 0.57 kg of grass / m2 - // Let’s take 7.5 kg of food / day for a horse - let r = dbg!(&map.cells_data[self.cell]).ressource(); - - } -} +mod entity; +mod map; +mod ui; +use entity::{Entity, EntityKind, ExternOp}; pub struct State { pub vertices: Vec, pub indices: Vec, pub uniforms: Uniforms, map: Map, - entities: Vec, start: Instant, + last_frame: Instant, t: usize, // Time in frames selected_tile: usize, - mouse_pressed: bool + mouse_pressed: bool, + framerate: f32, // Update per second + pub entities: BTreeMap, // entity id --> Entities + next_eid: usize, + pub cells_entities: HashMap> // cell id --> entities id } impl State { pub fn new() -> Self { @@ -303,11 +34,16 @@ impl State { indices: vec![], uniforms: Uniforms::default(), start: Instant::now(), + last_frame: Instant::now(), t: 0, map: Map::new(0, 0), - entities: Vec::new(), + // entities: Vec::new(), selected_tile: 0, - mouse_pressed: false + mouse_pressed: false, + framerate: 1., + entities: BTreeMap::new(), + next_eid: 0, + cells_entities: HashMap::new() }; s.render(); s @@ -318,10 +54,11 @@ impl State { if state.is_pressed() { match button { MouseButton::Left => { - self.entities.push(Entity::new(self.selected_tile, EntityKind::Horse)); self.mouse_pressed = true; }, - MouseButton::Right => self.map = Map::new(self.map.seed + 1, self.t), + MouseButton::Right => { + self.spawn_entity(self.selected_tile, Entity::new(EntityKind::Horse, self.selected_tile)); + }, _ => {} }; } else { @@ -353,11 +90,11 @@ impl State { } }, Event::DeviceEvent { event: DeviceEvent::MouseWheel { delta }, ..} => { - self.uniforms.camera[2] -= match delta { + self.framerate -= match delta { MouseScrollDelta::PixelDelta(pos) => pos.y as f32, MouseScrollDelta::LineDelta(_, y) => *y - }; - self.uniforms.camera[2] = self.uniforms.camera[2].clamp(0.1, 1.); + } * 0.1; + self.framerate = self.framerate.max(0.); }, Event::WindowEvent { event: WindowEvent::KeyboardInput { event: KeyEvent { physical_key: PhysicalKey::Code(kc), state, .. }, .. }, .. } => { if state.is_pressed() { @@ -374,6 +111,14 @@ impl State { KeyCode::KeyD => { self.uniforms.camera[0] += 0.1; }, + KeyCode::KeyR => { + self.uniforms.camera[2] -= 0.1; + self.uniforms.camera[2] = self.uniforms.camera[2].clamp(0.1, 1.); + }, + KeyCode::KeyF => { + self.uniforms.camera[2] += 0.1; + self.uniforms.camera[2] = self.uniforms.camera[2].clamp(0.1, 1.); + }, _ => {} } } @@ -405,7 +150,7 @@ impl State { } } - for e in self.entities.iter() { + for e in self.entities.values() { e.render(&mut self.vertices, &mut self.indices, &self.map); } @@ -429,19 +174,25 @@ impl State { } } pub fn update_if_needed(&mut self) { - while self.start.elapsed().as_secs_f32() > (self.t as f32) / (FRAMERATE as f32) { + while self.last_frame.elapsed().as_secs_f32() > 1. / self.framerate { self.update(); } } pub fn update(&mut self) { + dbg!(self.framerate); info!("update"); + + self.last_frame = Instant::now(); let mut rng = thread_rng(); let mut new_kind = Vec::new(); + for cd in self.map.cells_data.iter_mut() { + cd.update(); + } for cd in self.map.cells_data.iter() { if cd.kind == CellKind::Forest || cd.kind == CellKind::Grass { let r = rng.gen::(); if r < (0.035*cd.moisture) { - let c = self.map.voronoi.cell(cd.cell); + let c = self.map.voronoi.cell(cd.cid); let n = c.iter_neighbors().choose(&mut rng).unwrap(); let k = if r < (0.005*cd.moisture) && (self.map.cells_data[n].kind == CellKind::Dirt || self.map.cells_data[n].kind == CellKind::Grass) && cd.kind == CellKind::Forest { Some(CellKind::Forest) @@ -457,13 +208,51 @@ impl State { } } for (n, k) in new_kind { - self.map.cells_data[n].kind = k; + let cd = &mut self.map.cells_data[n]; + cd.kind = k; + cd.resource = 1.; } - for e in self.entities.iter_mut() { - e.update(&mut self.map); + // if Option is None remove the entity + let mut entities_to_move: Vec<(usize, Option)> = Vec::new(); + for (eid, e) in self.entities.iter_mut() { + match dbg!(e.update(&mut self.map, self.t, &mut rng)) { + Some(ExternOp::Move(cid)) => { + entities_to_move.push((*eid, Some(cid))); + }, + Some(ExternOp::Remove) => { + entities_to_move.push((*eid, None)); + }, + None => {} + } } + for (eid, new_cid) in entities_to_move { + let entity = self.entities.get_mut(&eid).unwrap(); + let cell_entities = self.cells_entities.get_mut(&entity.cid).unwrap(); + cell_entities.remove(cell_entities.iter().position(|e| *e == eid).unwrap()); + match new_cid { + Some(new_cid) => { + entity.cid = new_cid; + match self.cells_entities.get_mut(&new_cid) { + Some(v) => v.push(eid), + None => {self.cells_entities.insert(new_cid, vec![eid]);} + } + }, + None => {self.entities.remove(&eid);} + } + } + self.t += 1; } + pub fn spawn_entity(&mut self, cid: usize, e: Entity) { + let eid = self.next_eid; + self.next_eid += 1; + self.entities.insert(eid, e); + if let Some(v) = self.cells_entities.get_mut(&cid) { + v.push(eid); + } else { + self.cells_entities.insert(cid, vec![eid]); + } + } } \ No newline at end of file diff --git a/src/state/entity.rs b/src/state/entity.rs new file mode 100644 index 0000000..c5d230a --- /dev/null +++ b/src/state/entity.rs @@ -0,0 +1,222 @@ +use std::time::Instant; + +use rand::{rngs::ThreadRng, seq::IteratorRandom, Rng}; +use voronoice::Point; + +use crate::graphics::Vertex; + +use super::{map::{CellData, CellKind}, Map}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum EntityKind { + Horse +} +impl EntityKind { + const fn is_herbivore(&self) -> bool { + match self { + Self::Horse => true + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum EntityState { + Walking(Instant), // Start of walk + Resting +} + +#[derive(Debug)] +pub enum ExternOp { + Remove, + Move(usize) +} + +#[derive(Debug)] +pub struct Entity { + pub cid: usize, + kind: EntityKind, + start: Instant, + state: EntityState, + health: f32 // between 0 and 1 +} +impl Entity { + pub fn new(kind: EntityKind, cid: usize, ) -> Self { + Self { + cid, + kind, + start: Instant::now(), + state: EntityState::Walking(Instant::now()), + health: 1. + } + } + pub fn set_health(&mut self, val: f32) { + self.health = val.clamp(0., 1.); + } + pub fn render(&self, vertices: &mut Vec, indices: &mut Vec, map: &Map) { + let pos = &map.voronoi.sites()[self.cid]; + match self.kind { + EntityKind::Horse => { + let color = [171./255. * self.health, 122./255. * self.health, 50./255. * self.health, 1.]; + let dark = [color[0]-0.3, color[1]-0.3, color[2]-0.3, 1.]; + let (vs, is) = match self.state { + EntityState::Walking(now) => { + let now = now.elapsed().as_secs_f32()*5.; + ( + [ + // back left leg + Vertex { pos: [-0.5, 0.3], color: dark }, + Vertex { pos: [-0.4 + (now.sin()*0.1), -0.7 + (now.cos().max(-0.5)*0.1)], color: dark }, + Vertex { pos: [-0.25, 0.1], color: dark }, + + // back right leg + Vertex { pos: [-0.5, 0.3], color }, + Vertex { pos: [-0.4 + ((now + 1.).sin()*0.1), -0.7 + ((now + 1.).cos().max(-0.5)*0.1)], color }, + Vertex { pos: [-0.25, 0.1], color }, + + // front left leg + Vertex { pos: [0.3, 0.2], color: dark }, + Vertex { pos: [0.4 + ((now-1.).sin()*0.1), -0.7 + ((now-1.).cos().max(-0.5)*0.1)], color: dark }, + Vertex { pos: [0.5, 0.3], color: dark }, + + // front right leg + Vertex { pos: [0.3, 0.2], color }, + Vertex { pos: [0.4 + ((now-2.).sin()*0.1), -0.7 + ((now-2.).cos().max(-0.5)*0.1)], color }, + Vertex { pos: [0.5, 0.3], color }, + + // body + // 3 + Vertex { pos: [-0.3, 0.], color }, + Vertex { pos: [0.4, -0.1], color }, + // 11 + Vertex { pos: [0.3, 0.4], color }, + ], + [ + 0,1,2, + 3,4,5, + 6,7,8, + 9,10,11, + 3,12,13, + 3,13,11, + 3,11,14 + ] + ) + }, + EntityState::Resting => { + ( + [ + // back left leg + Vertex { pos: [-0.5, 0.3], color: dark }, + Vertex { pos: [-0.4, -0.75], color: dark }, + Vertex { pos: [-0.25, 0.1], color: dark }, + + // back right leg + Vertex { pos: [-0.5, 0.3], color }, + Vertex { pos: [-0.4, -0.75], color }, + Vertex { pos: [-0.25, 0.1], color }, + + // front left leg + Vertex { pos: [0.3, 0.2], color: dark }, + Vertex { pos: [0.4, -0.75], color: dark }, + Vertex { pos: [0.5, 0.3], color: dark }, + + // front right leg + Vertex { pos: [0.3, 0.2], color }, + Vertex { pos: [0.4, -0.75], color }, + Vertex { pos: [0.5, 0.3], color }, + + // body + // 3 + Vertex { pos: [-0.3, 0.], color }, + Vertex { pos: [0.4, -0.1], color }, + // 11 + Vertex { pos: [0.3, 0.4], color }, + ], + [ + 0,1,2, + 3,4,5, + 6,7,8, + 9,10,11, + 3,12,13, + 3,13,11, + 3,11,14 + ] + ) + } + }; + + vertices.reserve(vs.len()); + let base = vertices.len() as u32; + for mut v in vs { + v.pos[0] = v.pos[0]/50. + pos.x as f32; + v.pos[1] = (v.pos[1] + 0.75)/50. + pos.y as f32; + vertices.push(v) + } + + indices.reserve(is.len()); + for i in is { + indices.push(base + i); + } + } + } + } + /// Returns new cell if entity moves + pub fn update(&mut self, map: &mut Map, t: usize, rng: &mut ThreadRng) -> Option { + // Let’s take 0.57 kg of grass / m2 + // Let’s take 7.5 kg of food / day for a horse + // Let’s say that a horse can survive up to 20 days without food + let cd = &mut map.cells_data[self.cid]; + if self.kind.is_herbivore() { + let food_needed: f32 = match self.kind { + EntityKind::Horse => 7.5 / (0.57 * Map::CELL_AREA) + }; // in cell resource fraction + match cd.kind { + CellKind::Forest => {}, // Infinite food in forests + CellKind::Grass => { + let food_eaten = food_needed.min(cd.resource); + self.set_health(self.health - ((((food_needed - food_eaten)/food_needed)-0.5)/20.)); + cd.set_resource(cd.resource-food_eaten, t); + }, + _ => { + self.set_health(self.health - 1./20.); + } + } + } + + if dbg!(self.health) == 0. { + return Some(ExternOp::Remove); + } + + let r = cd.resource; + if cd.kind != CellKind::Grass { + map.voronoi.cell(cd.cid).iter_neighbors().filter(|n| { + let cd = &map.cells_data[*n]; + cd.kind == CellKind::Grass + }).choose(rng).map(|c| ExternOp::Move(c)) + } else if r < 0.5 { + let cd = &map.cells_data[self.cid]; + Some(ExternOp::Move( + map.voronoi.cell(cd.cid) + .iter_neighbors() + .filter(|n| map.cells_data[*n].kind == CellKind::Grass) + .fold(cd, |acc, c| { + let cd = &map.cells_data[c]; + if acc.kind != CellKind::Grass { + cd + } else if cd.kind != CellKind::Grass { + acc + } else if acc.resource > cd.resource { + acc + } else if acc.resource < cd.resource { + cd + } else if rng.gen_bool(0.5) { + cd + } else { + acc + } + }).cid + )) + } else { + None + } + } +} \ No newline at end of file diff --git a/src/state/map.rs b/src/state/map.rs new file mode 100644 index 0000000..77d8bb0 --- /dev/null +++ b/src/state/map.rs @@ -0,0 +1,140 @@ +use std::collections::HashMap; + +use noise::{Fbm, MultiFractal, NoiseFn, Perlin}; +use rand::{Rng, SeedableRng}; +use voronoice::{BoundingBox, Point, Voronoi, VoronoiBuilder}; + +use super::entity::Entity; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum CellKind { + Void, + Sea, + Beach, + Forest, + Dirt, + Stone, + Grass +} + +#[derive(Debug)] +pub struct CellData { + pub kind: CellKind, + pub cid: usize, + z: f32, + pub moisture: f32, + pub resource: f32 // How much resource there is (between 0 and 1) +} +impl CellData { + pub fn new(kind: CellKind, cell: usize, z: f32, moisture: f32, resource: f32, t: usize) -> Self { + Self { + kind, + cid: cell, + z, + moisture, + resource + } + } + pub fn pos<'a>(&self, map: &'a Map) -> &'a Point { + &map.voronoi.sites()[self.cid] + } + pub fn color(&self) -> [f32; 4] { + // let mut rng = thread_rng(); + // [rng.gen(), rng.gen(), rng.gen(), 1.] + 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*0.4), 0., 1.], + CellKind::Dirt => [0.53 - (self.resource*0.4), 0.38-(self.resource*0.4), 0.29-(self.resource*0.4), 1.], + CellKind::Stone => [0.5, 0.5, 0.5, 1.], + CellKind::Grass => [(136./255.) - (self.resource*0.4), (204./255.) - (self.resource*0.4), (59./255.) - (self.resource*0.4), 1.] + } + } + pub fn update(&mut self) { + // How much it get by day + let recuperation_rate = match self.kind { + CellKind::Void | CellKind::Sea | CellKind::Beach | CellKind::Dirt | CellKind::Stone => 0., + CellKind::Forest => 1. / (100. * 365.25), // Let's say that a forest takes 100 years to mature + CellKind::Grass => 1. / (7. * 7.) // Let's say that grass takes 7 weaks to reach its max + }; + self.resource = (self.resource + recuperation_rate).clamp(0., 1.); + } + pub fn set_resource(&mut self, val: f32, t: usize) { + self.resource = val.clamp(0., 1.); + if self.resource == 0. { + match self.kind { + CellKind::Forest => { + self.kind = CellKind::Grass; + self.resource = 1.; + }, + CellKind::Grass => { + self.kind = CellKind::Dirt; + self.resource = 0.5; + }, + CellKind::Beach => { + self.kind = CellKind::Sea; + }, + _ => {} + } + } + } +} + +pub struct Map { + pub voronoi: Voronoi, + pub cells_data: Vec, + pub seed: u32 +} +impl Map { + pub const HEIGHT: f32 = 2.; + pub const WIDTH: f32 = 2.; + pub const REAL_HEIGHT: f32 = 500.; + pub const REAL_WIDTH: f32 = 500.; + pub const CELL_AREA: f32 = Self::REAL_HEIGHT * Self::REAL_WIDTH / Self::SIZE as f32; + pub const SIZE: usize = 10000; + pub fn new(seed: u32, t: usize) -> Self { + let mut rng = rand::rngs::SmallRng::seed_from_u64(seed as u64); + let mut sites = Vec::with_capacity(Self::SIZE); + for _ in 0..Self::SIZE { + sites.push(Point { x:rng.gen_range(-Self::WIDTH/2.0..Self::WIDTH/2.0) as f64, y:rng.gen_range(-Self::HEIGHT/2.0..Self::HEIGHT/2.0) as f64 }) + } + let voronoi = VoronoiBuilder::default() + .set_sites(sites) + .set_bounding_box(BoundingBox::new_centered(Self::WIDTH as f64, Self::HEIGHT as f64)) + .set_lloyd_relaxation_iterations(3) + .build() + .unwrap(); + let mut cells_data = Vec::with_capacity(Self::SIZE); + let z_noise = Fbm::::new(seed); + let moisture_noise = Fbm::::new(seed+1) + .set_frequency(2.); + for i in 0..Self::SIZE { + let c = voronoi.cell(i); + let site = c.site_position(); + let z = ( + 0.3 // Arbitrary value + + ((z_noise.get([site.x, site.y])+1.)/2.) // Noise + [0; 1] + - ((site.x.powi(2)+site.y.powi(2)).sqrt()*0.5) // Distance - [0; sqrt(2)] * 0.5 + ).clamp(0., 1.); + let m = ( + (moisture_noise.get([site.x, site.y])+1.)/2. // Noise + [0; 1] + ).clamp(0., 1.) as f32; + let k = if z <= 0.5 { + CellKind::Sea + } else if z <= 0.52 { + CellKind::Beach + } else if z < 0.8 { + CellKind::Dirt + } else { + CellKind::Stone + }; + cells_data.push(CellData::new(k, i, z as f32, m, 1., t)); + } + Self { + voronoi, + cells_data, + seed + } + } +} \ No newline at end of file diff --git a/src/state/ui.rs b/src/state/ui.rs new file mode 100644 index 0000000..f992abf --- /dev/null +++ b/src/state/ui.rs @@ -0,0 +1,8 @@ +struct UI { + +} +impl UI { + fn new() -> Self { + Self {} + } +} \ No newline at end of file