entities are well managed (and die lol)

This commit is contained in:
Arkitu 2024-09-06 19:31:23 +02:00
parent 83f76015d4
commit f54ca4ed19
5 changed files with 454 additions and 296 deletions

View File

@ -1,6 +1,5 @@
mod graphics; mod graphics;
mod state; mod state;
use std::time::Duration;
use state::State; use state::State;
use graphics::Graphics; use graphics::Graphics;

View File

@ -1,300 +1,31 @@
use std::time::Instant; use std::{collections::{BTreeMap, HashMap}, time::Instant};
use log::info; use log::info;
use noise::{Fbm, MultiFractal, NoiseFn, Perlin}; use map::{CellKind, Map};
use rand::prelude::*; 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 winit::{event::{DeviceEvent, Event, KeyEvent, MouseButton, MouseScrollDelta, WindowEvent}, keyboard::{KeyCode, PhysicalKey}, window::Window};
use crate::graphics::{Uniforms, Vertex}; use crate::graphics::{Uniforms, Vertex};
const FRAMERATE: usize = 1; // Update per second mod entity;
mod map;
#[derive(Debug, Clone, Copy, PartialEq, Eq)] mod ui;
enum CellKind { use entity::{Entity, EntityKind, ExternOp};
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::<Vec<_>>();
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<CellData>,
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::<Perlin>::new(seed);
let moisture_noise = Fbm::<Perlin>::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<Vertex>, indices: &mut Vec<u32>, 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) {
// Lets take 0.57 kg of grass / m2
// Lets take 7.5 kg of food / day for a horse
let r = dbg!(&map.cells_data[self.cell]).ressource();
}
}
pub struct State { pub struct State {
pub vertices: Vec<Vertex>, pub vertices: Vec<Vertex>,
pub indices: Vec<u32>, pub indices: Vec<u32>,
pub uniforms: Uniforms, pub uniforms: Uniforms,
map: Map, map: Map,
entities: Vec<Entity>,
start: Instant, start: Instant,
last_frame: Instant,
t: usize, // Time in frames t: usize, // Time in frames
selected_tile: usize, selected_tile: usize,
mouse_pressed: bool mouse_pressed: bool,
framerate: f32, // Update per second
pub entities: BTreeMap<usize, Entity>, // entity id --> Entities
next_eid: usize,
pub cells_entities: HashMap<usize, Vec<usize>> // cell id --> entities id
} }
impl State { impl State {
pub fn new() -> Self { pub fn new() -> Self {
@ -303,11 +34,16 @@ impl State {
indices: vec![], indices: vec![],
uniforms: Uniforms::default(), uniforms: Uniforms::default(),
start: Instant::now(), start: Instant::now(),
last_frame: Instant::now(),
t: 0, t: 0,
map: Map::new(0, 0), map: Map::new(0, 0),
entities: Vec::new(), // entities: Vec::new(),
selected_tile: 0, selected_tile: 0,
mouse_pressed: false mouse_pressed: false,
framerate: 1.,
entities: BTreeMap::new(),
next_eid: 0,
cells_entities: HashMap::new()
}; };
s.render(); s.render();
s s
@ -318,10 +54,11 @@ impl State {
if state.is_pressed() { if state.is_pressed() {
match button { match button {
MouseButton::Left => { MouseButton::Left => {
self.entities.push(Entity::new(self.selected_tile, EntityKind::Horse));
self.mouse_pressed = true; 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 { } else {
@ -353,11 +90,11 @@ impl State {
} }
}, },
Event::DeviceEvent { event: DeviceEvent::MouseWheel { delta }, ..} => { Event::DeviceEvent { event: DeviceEvent::MouseWheel { delta }, ..} => {
self.uniforms.camera[2] -= match delta { self.framerate -= match delta {
MouseScrollDelta::PixelDelta(pos) => pos.y as f32, MouseScrollDelta::PixelDelta(pos) => pos.y as f32,
MouseScrollDelta::LineDelta(_, y) => *y MouseScrollDelta::LineDelta(_, y) => *y
}; } * 0.1;
self.uniforms.camera[2] = self.uniforms.camera[2].clamp(0.1, 1.); self.framerate = self.framerate.max(0.);
}, },
Event::WindowEvent { event: WindowEvent::KeyboardInput { event: KeyEvent { physical_key: PhysicalKey::Code(kc), state, .. }, .. }, .. } => { Event::WindowEvent { event: WindowEvent::KeyboardInput { event: KeyEvent { physical_key: PhysicalKey::Code(kc), state, .. }, .. }, .. } => {
if state.is_pressed() { if state.is_pressed() {
@ -374,6 +111,14 @@ impl State {
KeyCode::KeyD => { KeyCode::KeyD => {
self.uniforms.camera[0] += 0.1; 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); e.render(&mut self.vertices, &mut self.indices, &self.map);
} }
@ -429,19 +174,25 @@ impl State {
} }
} }
pub fn update_if_needed(&mut self) { 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(); self.update();
} }
} }
pub fn update(&mut self) { pub fn update(&mut self) {
dbg!(self.framerate);
info!("update"); info!("update");
self.last_frame = Instant::now();
let mut rng = thread_rng(); let mut rng = thread_rng();
let mut new_kind = Vec::new(); 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() { for cd in self.map.cells_data.iter() {
if cd.kind == CellKind::Forest || cd.kind == CellKind::Grass { if cd.kind == CellKind::Forest || cd.kind == CellKind::Grass {
let r = rng.gen::<f32>(); let r = rng.gen::<f32>();
if r < (0.035*cd.moisture) { 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 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 { 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) Some(CellKind::Forest)
@ -457,13 +208,51 @@ impl State {
} }
} }
for (n, k) in new_kind { 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() { // if Option is None remove the entity
e.update(&mut self.map); let mut entities_to_move: Vec<(usize, Option<usize>)> = 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; 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]);
}
}
} }

222
src/state/entity.rs Normal file
View File

@ -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<Vertex>, indices: &mut Vec<u32>, 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<ExternOp> {
// Lets take 0.57 kg of grass / m2
// Lets take 7.5 kg of food / day for a horse
// Lets 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
}
}
}

140
src/state/map.rs Normal file
View File

@ -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<CellData>,
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::<Perlin>::new(seed);
let moisture_noise = Fbm::<Perlin>::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
}
}
}

8
src/state/ui.rs Normal file
View File

@ -0,0 +1,8 @@
struct UI {
}
impl UI {
fn new() -> Self {
Self {}
}
}