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, pub indices: Vec, 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, // entity id --> Entities next_eid: usize, pub cells_entities: HashMap> // 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) { 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::>(); 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::>(); 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::(); 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)> = 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]); } } }