forestiles/src/state.rs
2024-09-08 18:38:26 +02:00

264 lines
10 KiB
Rust

use std::{collections::{BTreeMap, HashMap}, time::Instant};
use log::trace;
use map::{CellKind, Map};
use rand::prelude::*;
use voronoice::Point;
use winit::{dpi::PhysicalSize, event::{DeviceEvent, Event, KeyEvent, MouseButton, MouseScrollDelta, WindowEvent}, keyboard::{KeyCode, PhysicalKey}, window::Window};
use crate::graphics::{Uniforms, Vertex};
mod entity;
mod map;
mod ui;
use entity::{Entity, EntityKind, ExternOp};
use ui::UI;
pub struct State {
pub vertices: Vec<Vertex>,
pub indices: Vec<u32>,
pub uniforms: Uniforms,
map: Map,
ui: UI,
start: Instant,
last_frame: Instant,
t: usize, // Time in frames
selected_tile: usize,
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 {
pub fn new() -> Self {
let mut s = Self {
vertices: vec![],
indices: vec![],
uniforms: Uniforms::default(),
start: Instant::now(),
last_frame: Instant::now(),
t: 0,
map: Map::new(0, 0),
ui: UI::new(),
selected_tile: 0,
mouse_pressed: false,
framerate: 1.,
entities: BTreeMap::new(),
next_eid: 0,
cells_entities: HashMap::new()
};
// Create vertices / indices to estimate vertex / index buffer size
s.render(PhysicalSize::new(1, 1));
s
}
pub fn event(&mut self, event: &Event<()>, window: &Window) {
match event {
Event::WindowEvent { event: WindowEvent::MouseInput { state, button, ..}, ..} => {
if state.is_pressed() {
match button {
MouseButton::Left => {
self.mouse_pressed = true;
},
MouseButton::Right => {
self.spawn_entity(self.selected_tile, Entity::new(EntityKind::Horse, self.selected_tile));
},
_ => {}
};
} else {
match button {
MouseButton::Left => {
self.mouse_pressed = false;
},
_ => {}
}
}
},
Event::WindowEvent { event: WindowEvent::CursorMoved { position, .. }, ..} => {
let w_size = window.inner_size();
dbg!(position);
let pos = Point {
x: (((position.x / w_size.width as f64)*2.)-1.)*(w_size.width as f64/w_size.height as f64).min(1.)*(self.uniforms.camera[2] as f64) + self.uniforms.camera[0] as f64,
y: -(((position.y / w_size.height as f64)*2.)-1.)*(w_size.height as f64/w_size.width as f64).min(1.)*(self.uniforms.camera[2] as f64) + self.uniforms.camera[1] as f64
};
let c = self.map.voronoi.cell(self.selected_tile);
if self.mouse_pressed {
for i in c.iter_path(pos.clone()) {
self.selected_tile = i;
let cd = &mut self.map.cells_data[self.selected_tile];
if let CellKind::Dirt = cd.kind {
cd.kind = CellKind::Forest;
}
}
} else {
self.selected_tile = c.iter_path(pos).last().unwrap();
}
},
Event::DeviceEvent { event: DeviceEvent::MouseWheel { delta }, ..} => {
self.framerate -= match delta {
MouseScrollDelta::PixelDelta(pos) => pos.y as f32,
MouseScrollDelta::LineDelta(_, y) => *y
} * 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() {
match kc {
KeyCode::KeyW => {
self.uniforms.camera[1] += 0.1;
},
KeyCode::KeyS => {
self.uniforms.camera[1] -= 0.1;
},
KeyCode::KeyA => {
self.uniforms.camera[0] -= 0.1;
},
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.);
},
_ => {}
}
}
},
_ => {}
}
}
pub fn render(&mut self, screen_size: PhysicalSize<u32>) {
let screen_size = (screen_size.width as f32, screen_size.height as f32);
let y_ratio = (screen_size.0 / screen_size.1).max(1.);
let x_ratio = (screen_size.1 / screen_size.0).max(1.);
trace!("render");
self.vertices = Vec::new();
self.indices = Vec::new();
for (c, cd) in self.map.voronoi.iter_cells().zip(self.map.cells_data.iter()).filter(|(_,cd)| cd.kind != CellKind::Forest) {
let mut color = cd.color();
if c.site() == self.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 = self.vertices.len() as u32;
for v in vs.iter() {
self.vertices.push(Vertex { pos: [v.x as f32 * x_ratio, v.y as f32 * y_ratio], color });
}
for v in 1..(vs.len()-1) as u32 {
self.indices.push(i);
self.indices.push(i+v);
self.indices.push(i+v+1);
}
}
for e in self.entities.values() {
e.render(&mut self.vertices, &mut self.indices, &self.map);
}
for (c, cd) in self.map.voronoi.iter_cells().zip(self.map.cells_data.iter()).filter(|(_,cd)| cd.kind == CellKind::Forest) {
let mut color = cd.color();
if c.site() == self.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 = self.vertices.len() as u32;
for v in vs.iter() {
self.vertices.push(Vertex { pos: [v.x as f32 * x_ratio, v.y as f32 * y_ratio], color });
}
for v in 1..(vs.len()-1) as u32 {
self.indices.push(i);
self.indices.push(i+v);
self.indices.push(i+v+1);
}
}
}
pub fn update_if_needed(&mut self) {
while self.last_frame.elapsed().as_secs_f32() > 1. / self.framerate {
self.update();
}
}
pub fn update(&mut self) {
trace!("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::<f32>();
if r < (0.035*cd.moisture) {
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)
} else if self.map.cells_data[n].kind == CellKind::Dirt {
Some(CellKind::Grass)
} else {
None
};
if let Some(k) = k {
new_kind.push((n, k));
}
}
}
}
for (n, k) in new_kind {
let cd = &mut self.map.cells_data[n];
cd.kind = k;
cd.resource = 1.;
}
// if Option is None remove the entity
let mut entities_to_move: Vec<(usize, Option<usize>)> = Vec::new();
for (eid, e) in self.entities.iter_mut() {
match 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]);
}
}
}