From 99f61fbd7035334a0ef84a56f2a36548f4c1b2cd Mon Sep 17 00:00:00 2001 From: Niko Abeler Date: Sat, 31 Dec 2022 08:54:18 +0100 Subject: [PATCH] more world gen --- src/events/found_town.rs | 19 ++++++++ src/events/mod.rs | 5 +++ src/events/world_genesis.rs | 13 ++++++ src/generators.rs | 18 ++++++++ src/main.rs | 78 ++++++++++++++++----------------- src/names/towns/first.txt | 12 ++++++ src/names/towns/second.txt | 12 ++++++ src/person.rs | 12 ++++++ src/state.rs | 86 ++++++++++++++++++++++++++++++++++++- src/time.rs | 17 ++++++++ src/world.rs | 12 ++++-- 11 files changed, 238 insertions(+), 46 deletions(-) create mode 100644 src/events/found_town.rs create mode 100644 src/events/mod.rs create mode 100644 src/events/world_genesis.rs create mode 100644 src/generators.rs create mode 100644 src/names/towns/first.txt create mode 100644 src/names/towns/second.txt create mode 100644 src/person.rs create mode 100644 src/time.rs diff --git a/src/events/found_town.rs b/src/events/found_town.rs new file mode 100644 index 0000000..2c466bb --- /dev/null +++ b/src/events/found_town.rs @@ -0,0 +1,19 @@ +use std::rc::Rc; + +use crate::{state::Effect, world::{Town, Structure}, state::GameState}; + +pub struct FoundTown{ + pub x: usize, + pub y: usize, + pub town: Rc, +} + +impl Effect for FoundTown { + fn apply(&self, state: &mut GameState) { + state.world.add_structure(self.x, self.y, Structure::Town(self.town.clone())); + } + + fn description(&self) -> String { + format!("{} was founded", self.town.name) + } +} \ No newline at end of file diff --git a/src/events/mod.rs b/src/events/mod.rs new file mode 100644 index 0000000..7586ce2 --- /dev/null +++ b/src/events/mod.rs @@ -0,0 +1,5 @@ +mod found_town; +mod world_genesis; + +pub use found_town::FoundTown; +pub use world_genesis::WorldGenesis; \ No newline at end of file diff --git a/src/events/world_genesis.rs b/src/events/world_genesis.rs new file mode 100644 index 0000000..a4fa468 --- /dev/null +++ b/src/events/world_genesis.rs @@ -0,0 +1,13 @@ +use crate::state::{GameState, Effect}; + +pub struct WorldGenesis; + +impl Effect for WorldGenesis { + fn apply(&self, _state: &mut GameState) { + return; + } + + fn description(&self) -> String { + "World was created".to_string() + } +} \ No newline at end of file diff --git a/src/generators.rs b/src/generators.rs new file mode 100644 index 0000000..9d38145 --- /dev/null +++ b/src/generators.rs @@ -0,0 +1,18 @@ +use rand::seq::SliceRandom; +pub struct TownNameGenerator { +} + +impl TownNameGenerator { + pub fn name() -> String { + let first = include_str!("names/towns/first.txt").split("\n").collect::>(); + let second = include_str!("names/towns/second.txt").split("\n").collect::>(); + + let mut rng = rand::thread_rng(); + let first = first.choose(&mut rng).unwrap(); + let second = second.choose(&mut rng).unwrap(); + + let name = format!("{}{}", first, second); + // capitalize first letter + name[0..1].to_uppercase() + &name[1..] + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 93bc242..e1eb97a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,18 @@ mod world; mod state; +mod events; +mod time; +mod generators; -use noise::{Perlin, ScalePoint, Add, NoiseFn, Multiply, ScaleBias}; +use noise::{Perlin, ScalePoint, Add, NoiseFn, ScaleBias}; use noise::utils::{NoiseMapBuilder, PlaneMapBuilder}; use image::{RgbImage, Rgb}; use rand::prelude::*; +use world::World; + + +const N_TOWNS: usize = 10; +const WORLD_SIZE: usize = 256; struct BorderNoise { pub center: [f64; 2], @@ -36,14 +44,10 @@ impl NoiseFn for BorderNoise { } } -fn main() { - - let N_TOWNS = 10; - +fn build_world() -> World { let mut rng = rand::thread_rng(); - let map_size = 256; - let map_center = map_size / 2; + let map_center = WORLD_SIZE / 2; let height = Add::new( ScaleBias::new( Add::new( @@ -57,30 +61,23 @@ fn main() { ).set_scale(2000.0).set_bias(1000.0), BorderNoise::new( [map_center as f64, map_center as f64], - map_size as f64 * 0.4, + WORLD_SIZE as f64 * 0.4, 100.0 ), ); let plane = PlaneMapBuilder::<_, 2>::new(&height) - .set_size(map_size, map_size) - .set_x_bounds(0.0, map_size as f64) - .set_y_bounds(0.0, map_size as f64) + .set_size(WORLD_SIZE, WORLD_SIZE) + .set_x_bounds(0.0, WORLD_SIZE as f64) + .set_y_bounds(0.0, WORLD_SIZE as f64) .build(); - let min = plane.iter().fold(f64::MAX, |min, &val| val.min(min)); - let max = plane.iter().fold(f64::MIN, |max, &val| val.max(max)); + let mut img = RgbImage::new(WORLD_SIZE as u32, WORLD_SIZE as u32); - println!("Min: {}", min); - println!("Max: {}", max); + let mut world = world::World::new(WORLD_SIZE); - - let mut img = RgbImage::new(map_size as u32, map_size as u32); - - let mut world = world::World::new(map_size); - - for x in 0..map_size { - for y in 0..map_size { + for x in 0..WORLD_SIZE { + for y in 0..WORLD_SIZE { let h = plane.get_value(x, y); let t = world::Terrain::from_height(h); world.map[x][y] = world::WorldCell::new(t); @@ -88,27 +85,24 @@ fn main() { } } - let mut placed = 0; - while placed < N_TOWNS { - let x = rng.gen_range(0..map_size); - let y = rng.gen_range(0..map_size); - if - world.map[x][y].terrain == world::Terrain::Flats || - world.map[x][y].terrain == world::Terrain::Hills - { - world.add_structure(x, y, world::Structure::Town(world::Town::new())); - placed += 1; - img.put_pixel(x as u32, y as u32, Rgb([255, 0, 0])); - } + world +} + +fn main() { + + let mut state = state::GameState::new(build_world()); + + + let mut rng = rand::thread_rng(); + for _ in 0..N_TOWNS { + + state.step_n(rng.gen_range(0..360*3)); + state.found_town(); } - let scaled = image::imageops::resize( - &img, - map_size as u32 * 4, map_size as u32 * 4, - image::imageops::FilterType::Nearest); - - scaled.save("world.png").unwrap(); - - + for event in &state.events { + println!("{}", event.description()); + } + } \ No newline at end of file diff --git a/src/names/towns/first.txt b/src/names/towns/first.txt new file mode 100644 index 0000000..1931c02 --- /dev/null +++ b/src/names/towns/first.txt @@ -0,0 +1,12 @@ +iron +copper +silver +gold +wood +frost +summer +winter +spring +dagger +sword +hammer \ No newline at end of file diff --git a/src/names/towns/second.txt b/src/names/towns/second.txt new file mode 100644 index 0000000..1931c02 --- /dev/null +++ b/src/names/towns/second.txt @@ -0,0 +1,12 @@ +iron +copper +silver +gold +wood +frost +summer +winter +spring +dagger +sword +hammer \ No newline at end of file diff --git a/src/person.rs b/src/person.rs new file mode 100644 index 0000000..27fa7c1 --- /dev/null +++ b/src/person.rs @@ -0,0 +1,12 @@ +pub enum Profession { + Peasant, + Adventurer, + Blacksmith, +} + +pub struct Person { + pub name: String, + pub age: u32, + pub profession: Profession, + pub location: [usize; 2], +} \ No newline at end of file diff --git a/src/state.rs b/src/state.rs index 149c53c..8752f39 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,4 +1,88 @@ -use crate::world::World; + +use std::rc::Rc; + +use rand::prelude::*; +use crate::time::Time; +use crate::world::{World, Terrain, Town}; +use crate::events::{FoundTown, WorldGenesis}; pub struct GameState { + pub time: Time, pub world: World, + pub events: Vec> +} + +pub struct Event { + pub time: Time, + pub effect: Box, +} + +pub trait Effect { + fn apply(&self, state: &mut GameState); + fn description(&self) -> String; +} + +impl Event { + pub fn description(&self) -> String { + format!("{}: {}", self.time, self.effect.description()) + } +} + + +impl GameState { + pub fn new(world: World) -> GameState { + let mut events = Vec::new(); + events.push(Box::new(Event { + time: Time { time: 0 }, + effect: Box::new(WorldGenesis), + })); + + GameState { + time: Time { time: 0 }, + world: world, + events: events, + } + } + + pub fn add_event(&mut self, event: Event) { + event.effect.apply(self); + self.events.push(Box::new(event)); + } + + pub fn step(&mut self) { + self.time.time += 1; + } + + pub fn step_n(&mut self, n: u32) { + for _ in 0..n { + self.step(); + } + } + + pub fn step_year(&mut self) { + for _ in 0..360 { + self.step(); + } + } + + pub fn found_town(&mut self) { + let mut rng = rand::thread_rng(); + loop { + let x = rng.gen_range(0..self.world.size); + let y = rng.gen_range(0..self.world.size); + if + self.world.map[x][y].terrain == Terrain::Flats || + self.world.map[x][y].terrain == Terrain::Hills + { + self.add_event(Event { + time: self.time, + effect: Box::new(FoundTown { + x: x, + y: y, + town: Rc::new(Town::new()), + }) + }); + break; + } + } + } } \ No newline at end of file diff --git a/src/time.rs b/src/time.rs new file mode 100644 index 0000000..a46d1c8 --- /dev/null +++ b/src/time.rs @@ -0,0 +1,17 @@ +use std::fmt; + +#[derive(Clone, Copy)] +pub struct Time{ + pub time: u32, +} + +impl fmt::Display for Time { + + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let year = self.time / 360; + let month = (self.time / 30) % 12; + let day = self.time % 30; + write!(f, "Year {}, {} of {}", year, day, month) + } +} + diff --git a/src/world.rs b/src/world.rs index bc70828..49048ea 100644 --- a/src/world.rs +++ b/src/world.rs @@ -1,3 +1,8 @@ +use std::rc::Rc; +use rand::prelude::*; + +use crate::generators::TownNameGenerator; + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Terrain { Void, @@ -16,7 +21,7 @@ pub struct Town { } pub enum Structure { - Town(Town), + Town(Rc), } pub struct WorldCell { @@ -94,9 +99,10 @@ impl Terrain { impl Town { pub fn new() -> Town { + let mut rng = rand::thread_rng(); Town { - name: "Town".to_string(), - population: 100, + name: TownNameGenerator::name(), + population: rng.gen_range(100..500), } } }