more world gen
This commit is contained in:
parent
f64a757ec6
commit
99f61fbd70
|
@ -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<Town>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
mod found_town;
|
||||||
|
mod world_genesis;
|
||||||
|
|
||||||
|
pub use found_town::FoundTown;
|
||||||
|
pub use world_genesis::WorldGenesis;
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -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::<Vec<&str>>();
|
||||||
|
let second = include_str!("names/towns/second.txt").split("\n").collect::<Vec<&str>>();
|
||||||
|
|
||||||
|
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..]
|
||||||
|
}
|
||||||
|
}
|
72
src/main.rs
72
src/main.rs
|
@ -1,10 +1,18 @@
|
||||||
mod world;
|
mod world;
|
||||||
mod state;
|
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 noise::utils::{NoiseMapBuilder, PlaneMapBuilder};
|
||||||
use image::{RgbImage, Rgb};
|
use image::{RgbImage, Rgb};
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
|
use world::World;
|
||||||
|
|
||||||
|
|
||||||
|
const N_TOWNS: usize = 10;
|
||||||
|
const WORLD_SIZE: usize = 256;
|
||||||
|
|
||||||
struct BorderNoise {
|
struct BorderNoise {
|
||||||
pub center: [f64; 2],
|
pub center: [f64; 2],
|
||||||
|
@ -36,14 +44,10 @@ impl NoiseFn<f64, 2> for BorderNoise {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn build_world() -> World {
|
||||||
|
|
||||||
let N_TOWNS = 10;
|
|
||||||
|
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
|
|
||||||
let map_size = 256;
|
let map_center = WORLD_SIZE / 2;
|
||||||
let map_center = map_size / 2;
|
|
||||||
let height = Add::new(
|
let height = Add::new(
|
||||||
ScaleBias::new(
|
ScaleBias::new(
|
||||||
Add::new(
|
Add::new(
|
||||||
|
@ -57,30 +61,23 @@ fn main() {
|
||||||
).set_scale(2000.0).set_bias(1000.0),
|
).set_scale(2000.0).set_bias(1000.0),
|
||||||
BorderNoise::new(
|
BorderNoise::new(
|
||||||
[map_center as f64, map_center as f64],
|
[map_center as f64, map_center as f64],
|
||||||
map_size as f64 * 0.4,
|
WORLD_SIZE as f64 * 0.4,
|
||||||
100.0
|
100.0
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
let plane = PlaneMapBuilder::<_, 2>::new(&height)
|
let plane = PlaneMapBuilder::<_, 2>::new(&height)
|
||||||
.set_size(map_size, map_size)
|
.set_size(WORLD_SIZE, WORLD_SIZE)
|
||||||
.set_x_bounds(0.0, map_size as f64)
|
.set_x_bounds(0.0, WORLD_SIZE as f64)
|
||||||
.set_y_bounds(0.0, map_size as f64)
|
.set_y_bounds(0.0, WORLD_SIZE as f64)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let min = plane.iter().fold(f64::MAX, |min, &val| val.min(min));
|
let mut img = RgbImage::new(WORLD_SIZE as u32, WORLD_SIZE as u32);
|
||||||
let max = plane.iter().fold(f64::MIN, |max, &val| val.max(max));
|
|
||||||
|
|
||||||
println!("Min: {}", min);
|
let mut world = world::World::new(WORLD_SIZE);
|
||||||
println!("Max: {}", max);
|
|
||||||
|
|
||||||
|
for x in 0..WORLD_SIZE {
|
||||||
let mut img = RgbImage::new(map_size as u32, map_size as u32);
|
for y in 0..WORLD_SIZE {
|
||||||
|
|
||||||
let mut world = world::World::new(map_size);
|
|
||||||
|
|
||||||
for x in 0..map_size {
|
|
||||||
for y in 0..map_size {
|
|
||||||
let h = plane.get_value(x, y);
|
let h = plane.get_value(x, y);
|
||||||
let t = world::Terrain::from_height(h);
|
let t = world::Terrain::from_height(h);
|
||||||
world.map[x][y] = world::WorldCell::new(t);
|
world.map[x][y] = world::WorldCell::new(t);
|
||||||
|
@ -88,27 +85,24 @@ fn main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut placed = 0;
|
world
|
||||||
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]));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let scaled = image::imageops::resize(
|
fn main() {
|
||||||
&img,
|
|
||||||
map_size as u32 * 4, map_size as u32 * 4,
|
|
||||||
image::imageops::FilterType::Nearest);
|
|
||||||
|
|
||||||
scaled.save("world.png").unwrap();
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
for event in &state.events {
|
||||||
|
println!("{}", event.description());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
iron
|
||||||
|
copper
|
||||||
|
silver
|
||||||
|
gold
|
||||||
|
wood
|
||||||
|
frost
|
||||||
|
summer
|
||||||
|
winter
|
||||||
|
spring
|
||||||
|
dagger
|
||||||
|
sword
|
||||||
|
hammer
|
|
@ -0,0 +1,12 @@
|
||||||
|
iron
|
||||||
|
copper
|
||||||
|
silver
|
||||||
|
gold
|
||||||
|
wood
|
||||||
|
frost
|
||||||
|
summer
|
||||||
|
winter
|
||||||
|
spring
|
||||||
|
dagger
|
||||||
|
sword
|
||||||
|
hammer
|
|
@ -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],
|
||||||
|
}
|
86
src/state.rs
86
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 struct GameState {
|
||||||
|
pub time: Time,
|
||||||
pub world: World,
|
pub world: World,
|
||||||
|
pub events: Vec<Box<Event>>
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Event {
|
||||||
|
pub time: Time,
|
||||||
|
pub effect: Box<dyn Effect>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
12
src/world.rs
12
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)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub enum Terrain {
|
pub enum Terrain {
|
||||||
Void,
|
Void,
|
||||||
|
@ -16,7 +21,7 @@ pub struct Town {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Structure {
|
pub enum Structure {
|
||||||
Town(Town),
|
Town(Rc<Town>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct WorldCell {
|
pub struct WorldCell {
|
||||||
|
@ -94,9 +99,10 @@ impl Terrain {
|
||||||
|
|
||||||
impl Town {
|
impl Town {
|
||||||
pub fn new() -> Town {
|
pub fn new() -> Town {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
Town {
|
Town {
|
||||||
name: "Town".to_string(),
|
name: TownNameGenerator::name(),
|
||||||
population: 100,
|
population: rng.gen_range(100..500),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue