map
This commit is contained in:
parent
a728639db0
commit
072521144b
1963
Cargo.lock
generated
1963
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -15,6 +15,9 @@ package = "com.forestiles.arkitu"
|
|||||||
build_targets = ["armv7-linux-androideabi", "aarch64-linux-android", "i686-linux-android", "x86_64-linux-android"]
|
build_targets = ["armv7-linux-androideabi", "aarch64-linux-android", "i686-linux-android", "x86_64-linux-android"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
bevy = { version = "0.15", default-features = false, features = [
|
||||||
|
"bevy_color","bevy_core_pipeline","bevy_render","bevy_winit","bevy_window","multi_threaded","wayland","bevy_sprite"
|
||||||
|
]}
|
||||||
winit = { version = "0.30", features = ["rwh_05", "android-native-activity"] }
|
winit = { version = "0.30", features = ["rwh_05", "android-native-activity"] }
|
||||||
env_logger = "0.11"
|
env_logger = "0.11"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
16
src/camera.rs
Normal file
16
src/camera.rs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
pub struct Plugin;
|
||||||
|
impl bevy::prelude::Plugin for Plugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
app.add_systems(Startup, setup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup(mut cmds: Commands, window: Query<&Window>) {
|
||||||
|
let zoom = 2./window.single().width().min(window.single().height());
|
||||||
|
cmds.spawn((
|
||||||
|
Camera2d,
|
||||||
|
Transform::from_scale(Vec3::new(zoom, zoom, 1.))
|
||||||
|
));
|
||||||
|
}
|
145
src/lib.rs
145
src/lib.rs
@ -1,79 +1,82 @@
|
|||||||
mod graphics;
|
// mod graphics;
|
||||||
mod state;
|
// mod state;
|
||||||
|
mod map;
|
||||||
|
mod camera;
|
||||||
|
|
||||||
use std::{fmt::Debug, sync::Arc};
|
use std::{fmt::Debug, sync::Arc};
|
||||||
|
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use state::State;
|
// use state::State;
|
||||||
use graphics::Graphics;
|
// use graphics::Graphics;
|
||||||
use winit::{application::ApplicationHandler, dpi::PhysicalSize, event::{Event, WindowEvent}, event_loop::EventLoop, window::{Window, WindowAttributes}};
|
use winit::{application::ApplicationHandler, dpi::PhysicalSize, event::{Event, WindowEvent}, event_loop::EventLoop, window::{Window, WindowAttributes}};
|
||||||
|
use bevy::app::App;
|
||||||
|
|
||||||
pub fn dbg<V: Debug>(v: V) -> V {
|
pub fn dbg<V: Debug>(v: V) -> V {
|
||||||
debug!(target: "app", "{:?}", v);
|
debug!(target: "app", "{:?}", v);
|
||||||
v
|
v
|
||||||
}
|
}
|
||||||
|
|
||||||
struct App<'a> {
|
// struct App<'a> {
|
||||||
// event_loop: EventLoop<()>,
|
// // event_loop: EventLoop<()>,
|
||||||
window: Option<Arc<Window>>,
|
// window: Option<Arc<Window>>,
|
||||||
graphics: Option<Graphics<'a>>,
|
// graphics: Option<Graphics<'a>>,
|
||||||
state: State
|
// state: State
|
||||||
}
|
// }
|
||||||
impl App<'_> {
|
// impl App<'_> {
|
||||||
fn new() -> Self {
|
// fn new() -> Self {
|
||||||
Self {
|
// Self {
|
||||||
window: None,
|
// window: None,
|
||||||
graphics: None,
|
// graphics: None,
|
||||||
state: State::new()
|
// state: State::new()
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
impl ApplicationHandler for App<'_> {
|
// impl ApplicationHandler for App<'_> {
|
||||||
fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
|
// fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
|
||||||
#[cfg(not(any(target_family = "wasm", target_os = "android")))]
|
// #[cfg(not(any(target_family = "wasm", target_os = "android")))]
|
||||||
let window = event_loop.create_window(
|
// let window = event_loop.create_window(
|
||||||
WindowAttributes::default()
|
// WindowAttributes::default()
|
||||||
.with_inner_size(PhysicalSize::new(1080*2/5, 2000*2/5))
|
// .with_inner_size(PhysicalSize::new(1080*2/5, 2000*2/5))
|
||||||
).unwrap();
|
// ).unwrap();
|
||||||
#[cfg(target_os = "android")]
|
// #[cfg(target_os = "android")]
|
||||||
let window = event_loop.create_window(
|
// let window = event_loop.create_window(
|
||||||
WindowAttributes::default()
|
// WindowAttributes::default()
|
||||||
// .with_inner_size(PhysicalSize::new(1080*2/5, 2000*2/5))
|
// // .with_inner_size(PhysicalSize::new(1080*2/5, 2000*2/5))
|
||||||
).unwrap();
|
// ).unwrap();
|
||||||
self.window = Some(Arc::new(window));
|
// self.window = Some(Arc::new(window));
|
||||||
self.graphics = Some(pollster::block_on(Graphics::init(&self.state, self.window.clone().unwrap())));
|
// self.graphics = Some(pollster::block_on(Graphics::init(&self.state, self.window.clone().unwrap())));
|
||||||
}
|
// }
|
||||||
fn window_event(
|
// fn window_event(
|
||||||
&mut self,
|
// &mut self,
|
||||||
event_loop: &winit::event_loop::ActiveEventLoop,
|
// event_loop: &winit::event_loop::ActiveEventLoop,
|
||||||
window_id: winit::window::WindowId,
|
// window_id: winit::window::WindowId,
|
||||||
event: WindowEvent,
|
// event: WindowEvent,
|
||||||
) {
|
// ) {
|
||||||
match &event {
|
// match &event {
|
||||||
WindowEvent::CloseRequested => event_loop.exit(),
|
// WindowEvent::CloseRequested => event_loop.exit(),
|
||||||
WindowEvent::RedrawRequested => {
|
// WindowEvent::RedrawRequested => {
|
||||||
if let Some(g) = &mut self.graphics {
|
// if let Some(g) = &mut self.graphics {
|
||||||
self.state.update_if_needed();
|
// self.state.update_if_needed();
|
||||||
self.state.render(self.window.as_ref().unwrap().inner_size());
|
// self.state.render(self.window.as_ref().unwrap().inner_size());
|
||||||
g.update(&self.state);
|
// g.update(&self.state);
|
||||||
g.render(&self.state);
|
// g.render(&self.state);
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
WindowEvent::MouseWheel { delta, .. } => {
|
// WindowEvent::MouseWheel { delta, .. } => {
|
||||||
dbg!(delta);
|
// dbg!(delta);
|
||||||
},
|
// },
|
||||||
_ => {}
|
// _ => {}
|
||||||
}
|
// }
|
||||||
self.graphics.as_mut().unwrap().window_event(&event, &self.window.as_ref().unwrap());
|
// self.graphics.as_mut().unwrap().window_event(&event, &self.window.as_ref().unwrap());
|
||||||
self.state.window_event(&event, &self.window.as_ref().unwrap());
|
// self.state.window_event(&event, &self.window.as_ref().unwrap());
|
||||||
}
|
// }
|
||||||
fn about_to_wait(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
|
// fn about_to_wait(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
|
||||||
if let Some(window) = self.window.as_ref() {
|
// if let Some(window) = self.window.as_ref() {
|
||||||
window.request_redraw();
|
// window.request_redraw();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[cfg(not(target_os = "android"))]
|
#[cfg(not(target_os = "android"))]
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
@ -96,14 +99,16 @@ pub fn main() {
|
|||||||
// .unwrap();
|
// .unwrap();
|
||||||
// (event_loop, window)
|
// (event_loop, window)
|
||||||
// };
|
// };
|
||||||
#[cfg(not(target_family = "wasm"))]
|
|
||||||
let event_loop = {
|
|
||||||
env_logger::init();
|
|
||||||
|
|
||||||
winit::event_loop::EventLoop::new().unwrap()
|
use bevy::DefaultPlugins;
|
||||||
};
|
|
||||||
|
|
||||||
event_loop.run_app(&mut App::new()).unwrap();
|
App::new()
|
||||||
|
.add_plugins(DefaultPlugins)
|
||||||
|
.add_plugins((
|
||||||
|
camera::Plugin,
|
||||||
|
map::Plugin
|
||||||
|
))
|
||||||
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
|
248
src/map.rs
Normal file
248
src/map.rs
Normal file
@ -0,0 +1,248 @@
|
|||||||
|
use bevy::{asset::RenderAssetUsages, prelude::*, render::mesh::{Indices, PrimitiveTopology}};
|
||||||
|
use noise::{Fbm, MultiFractal, NoiseFn, Perlin};
|
||||||
|
use rand::{Rng, SeedableRng};
|
||||||
|
use voronoice::{BoundingBox, Point, VoronoiBuilder};
|
||||||
|
|
||||||
|
pub struct Plugin;
|
||||||
|
impl bevy::prelude::Plugin for Plugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
app.add_systems(Startup, setup)
|
||||||
|
.insert_resource(ClearColor(Color::srgb(0., 0., 1.)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = REAL_HEIGHT * REAL_WIDTH / SIZE as f32;
|
||||||
|
pub const SIZE: usize = 10000;
|
||||||
|
pub const seed: u32 = 0;
|
||||||
|
|
||||||
|
#[derive(Resource)]
|
||||||
|
struct Voronoi (voronoice::Voronoi);
|
||||||
|
|
||||||
|
fn setup(
|
||||||
|
mut cmds: Commands,
|
||||||
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
|
mut materials: ResMut<Assets<ColorMaterial>>
|
||||||
|
) {
|
||||||
|
let mut rng = rand::rngs::SmallRng::seed_from_u64(seed as u64);
|
||||||
|
let mut sites = Vec::with_capacity(SIZE);
|
||||||
|
for _ in 0..SIZE {
|
||||||
|
sites.push(Point { x:rng.gen_range(-WIDTH/2.0..WIDTH/2.0) as f64, y:rng.gen_range(-HEIGHT/2.0..HEIGHT/2.0) as f64 })
|
||||||
|
}
|
||||||
|
let voronoi = VoronoiBuilder::default()
|
||||||
|
.set_sites(sites)
|
||||||
|
.set_bounding_box(BoundingBox::new_centered(WIDTH as f64, HEIGHT as f64))
|
||||||
|
.set_lloyd_relaxation_iterations(3)
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
let mut cells_data = Vec::with_capacity(SIZE);
|
||||||
|
let z_noise = Fbm::<Perlin>::new(seed);
|
||||||
|
let moisture_noise = Fbm::<Perlin>::new(seed+1)
|
||||||
|
.set_frequency(2.);
|
||||||
|
for i in 0..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.));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let mut poss = Vec::new();
|
||||||
|
let mut colors = Vec::new();
|
||||||
|
let mut indices = Vec::new();
|
||||||
|
|
||||||
|
for (c, cd) in voronoi.iter_cells().zip(cells_data.iter()).filter(|(_,cd)| cd.kind != CellKind::Forest) {
|
||||||
|
let mut color = cd.color();
|
||||||
|
// if c.site() == 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 = poss.len() as u32;
|
||||||
|
for v in vs.iter() {
|
||||||
|
poss.push(Vec3::new(v.x as f32, v.y as f32, 0.));// [v.x as f32, v.y as f32, 0.]);
|
||||||
|
// poss.push(Vertex::new_col([v.x as f32, v.y as f32], color, 1));
|
||||||
|
colors.push(color);
|
||||||
|
}
|
||||||
|
for v in 1..(vs.len()-1) as u32 {
|
||||||
|
indices.push(i);
|
||||||
|
indices.push(i+v);
|
||||||
|
indices.push(i+v+1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mesh = Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::default())
|
||||||
|
// Add 4 vertices, each with its own position attribute (coordinate in
|
||||||
|
// 3D space), for each of the corners of the parallelogram.
|
||||||
|
.with_inserted_attribute(
|
||||||
|
Mesh::ATTRIBUTE_POSITION,
|
||||||
|
poss
|
||||||
|
)
|
||||||
|
.with_inserted_attribute(
|
||||||
|
Mesh::ATTRIBUTE_COLOR,
|
||||||
|
colors
|
||||||
|
)
|
||||||
|
.with_inserted_indices(Indices::U32(indices));
|
||||||
|
|
||||||
|
cmds.spawn((
|
||||||
|
Mesh2d(meshes.add(mesh)),
|
||||||
|
MeshMaterial2d(materials.add(ColorMaterial::default())),
|
||||||
|
Transform::default()
|
||||||
|
));
|
||||||
|
|
||||||
|
cmds.insert_resource(Voronoi(voronoi));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum CellKind {
|
||||||
|
Void,
|
||||||
|
Sea,
|
||||||
|
Beach,
|
||||||
|
Forest,
|
||||||
|
Dirt,
|
||||||
|
Stone,
|
||||||
|
Grass
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Component)]
|
||||||
|
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) -> 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
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
136
src/state/map.rs
136
src/state/map.rs
@ -1,136 +0,0 @@
|
|||||||
use noise::{Fbm, MultiFractal, NoiseFn, Perlin};
|
|
||||||
use rand::{Rng, SeedableRng};
|
|
||||||
use voronoice::{BoundingBox, Point, Voronoi, VoronoiBuilder};
|
|
||||||
|
|
||||||
#[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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user