fmt + move cell::on_click into its own function
This commit is contained in:
parent
dbb17f0c48
commit
d210af93f8
@ -2,9 +2,7 @@ use bevy::prelude::*;
|
||||
|
||||
pub struct Plugin;
|
||||
impl bevy::prelude::Plugin for Plugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
|
||||
}
|
||||
fn build(&self, app: &mut App) {}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
@ -14,5 +12,5 @@ pub enum AnimalKind {
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Animal {
|
||||
kind: AnimalKind
|
||||
kind: AnimalKind,
|
||||
}
|
||||
|
116
src/camera.rs
116
src/camera.rs
@ -1,6 +1,13 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use bevy::{input::mouse::MouseWheel, math::NormedVectorSpace, picking::{focus::HoverMap, pointer::PointerId}, prelude::*, utils::HashMap, window::PrimaryWindow};
|
||||
use bevy::{
|
||||
input::mouse::MouseWheel,
|
||||
math::NormedVectorSpace,
|
||||
picking::{focus::HoverMap, pointer::PointerId},
|
||||
prelude::*,
|
||||
utils::HashMap,
|
||||
window::PrimaryWindow,
|
||||
};
|
||||
|
||||
use crate::ui;
|
||||
|
||||
@ -8,17 +15,14 @@ pub struct Plugin;
|
||||
impl bevy::prelude::Plugin for Plugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(Startup, setup);
|
||||
// .add_systems(Update, move_cam)
|
||||
// .init_resource::<Pointers>();
|
||||
// .add_systems(Update, move_cam)
|
||||
// .init_resource::<Pointers>();
|
||||
}
|
||||
}
|
||||
|
||||
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, zoom))
|
||||
));
|
||||
let zoom = 2. / window.single().width().min(window.single().height());
|
||||
cmds.spawn((Camera2d, Transform::from_scale(Vec3::new(zoom, zoom, zoom))));
|
||||
}
|
||||
|
||||
#[derive(Resource, Default)]
|
||||
@ -33,50 +37,80 @@ fn move_cam(
|
||||
window: Query<&Window, With<PrimaryWindow>>,
|
||||
mut pointers: ResMut<Pointers>,
|
||||
hover_map: Res<HoverMap>,
|
||||
time: Res<Time>
|
||||
time: Res<Time>,
|
||||
) {
|
||||
let window = window.single();
|
||||
let mut cam = cam.single_mut();
|
||||
let map_ui_entity = map_ui_entity.single();
|
||||
let ps = hover_map.iter().filter_map(|(id, hit_map)| match id {
|
||||
PointerId::Mouse => window.cursor_position().map(|p|
|
||||
match pointers.0.get(id) {
|
||||
Some(p_cache) => (p_cache.1.filter(|_| mouse_buttons.pressed(MouseButton::Left)), p, p_cache.0),
|
||||
None => (None, p, p)
|
||||
}
|
||||
),
|
||||
PointerId::Touch(i) => touches.get_pressed(*i).map(|t| (pointers.0.get(id).map(|(pos, start)| (*start).unwrap_or(time.elapsed())), t.position(), t.previous_position())),
|
||||
_ => None
|
||||
}.map(|(pressed_start,new_pos, old_pos)| (pressed_start,new_pos,old_pos,id,hit_map))
|
||||
).collect::<Vec<_>>();
|
||||
let pressed_on_map = ps.iter().filter(|p| p.0.is_some() && p.4.contains_key(&map_ui_entity)).collect::<Vec<_>>();
|
||||
let ps = hover_map
|
||||
.iter()
|
||||
.filter_map(|(id, hit_map)| {
|
||||
match id {
|
||||
PointerId::Mouse => window.cursor_position().map(|p| match pointers.0.get(id) {
|
||||
Some(p_cache) => (
|
||||
p_cache
|
||||
.1
|
||||
.filter(|_| mouse_buttons.pressed(MouseButton::Left)),
|
||||
p,
|
||||
p_cache.0,
|
||||
),
|
||||
None => (None, p, p),
|
||||
}),
|
||||
PointerId::Touch(i) => touches.get_pressed(*i).map(|t| {
|
||||
(
|
||||
pointers
|
||||
.0
|
||||
.get(id)
|
||||
.map(|(pos, start)| (*start).unwrap_or(time.elapsed())),
|
||||
t.position(),
|
||||
t.previous_position(),
|
||||
)
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
.map(|(pressed_start, new_pos, old_pos)| (pressed_start, new_pos, old_pos, id, hit_map))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let pressed_on_map = ps
|
||||
.iter()
|
||||
.filter(|p| p.0.is_some() && p.4.contains_key(&map_ui_entity))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let old_midpoint = pressed_on_map.iter().fold(Vec2::ZERO, |acc, (_, _, old_pos, _, _)| {
|
||||
acc + (old_pos/pressed_on_map.len() as f32)
|
||||
});
|
||||
let new_midpoint = pressed_on_map.iter().fold(Vec2::ZERO, |acc, (_, new_pos, _, _, _)| {
|
||||
acc + (new_pos/pressed_on_map.len() as f32)
|
||||
});
|
||||
let old_midpoint = pressed_on_map
|
||||
.iter()
|
||||
.fold(Vec2::ZERO, |acc, (_, _, old_pos, _, _)| {
|
||||
acc + (old_pos / pressed_on_map.len() as f32)
|
||||
});
|
||||
let new_midpoint = pressed_on_map
|
||||
.iter()
|
||||
.fold(Vec2::ZERO, |acc, (_, new_pos, _, _, _)| {
|
||||
acc + (new_pos / pressed_on_map.len() as f32)
|
||||
});
|
||||
|
||||
// move camera
|
||||
cam.translation.x -= (new_midpoint.x - old_midpoint.x)*cam.scale.x;
|
||||
cam.translation.y += (new_midpoint.y - old_midpoint.y)*cam.scale.y;
|
||||
cam.translation.x -= (new_midpoint.x - old_midpoint.x) * cam.scale.x;
|
||||
cam.translation.y += (new_midpoint.y - old_midpoint.y) * cam.scale.y;
|
||||
|
||||
// multiple fingers zoom
|
||||
if pressed_on_map.len() > 1 {
|
||||
let old_d_to_midpoint = pressed_on_map.iter().fold(0., |acc, (_, _, old_pos, _, _)| {
|
||||
acc + (old_midpoint-old_pos).norm()
|
||||
});
|
||||
let new_d_to_midpoint = pressed_on_map.iter().fold(0., |acc, (_, new_pos, _, _, _)| {
|
||||
acc + (new_midpoint-new_pos).norm()
|
||||
});
|
||||
let zoom = new_d_to_midpoint/old_d_to_midpoint;
|
||||
let old_d_to_midpoint = pressed_on_map
|
||||
.iter()
|
||||
.fold(0., |acc, (_, _, old_pos, _, _)| {
|
||||
acc + (old_midpoint - old_pos).norm()
|
||||
});
|
||||
let new_d_to_midpoint = pressed_on_map
|
||||
.iter()
|
||||
.fold(0., |acc, (_, new_pos, _, _, _)| {
|
||||
acc + (new_midpoint - new_pos).norm()
|
||||
});
|
||||
let zoom = new_d_to_midpoint / old_d_to_midpoint;
|
||||
cam.scale /= zoom;
|
||||
}
|
||||
|
||||
// mouse scroll zoom
|
||||
for ev in ev_scroll.read() {
|
||||
let scale = (cam.scale.x-(ev.y*0.1/window.width().min(window.height()))).clamp(0.0001, 2./window.width().min(window.height()));
|
||||
let scale = (cam.scale.x - (ev.y * 0.1 / window.width().min(window.height())))
|
||||
.clamp(0.0001, 2. / window.width().min(window.height()));
|
||||
cam.scale = Vec3::new(scale, scale, scale);
|
||||
}
|
||||
|
||||
@ -84,8 +118,12 @@ fn move_cam(
|
||||
pointers.0.clear();
|
||||
for (pressed_start, new_pos, _, id, _) in ps {
|
||||
match id {
|
||||
PointerId::Mouse => {pointers.0.insert(*id, (new_pos, pressed_start));},
|
||||
PointerId::Touch(_) => {pointers.0.insert(*id, (new_pos, pressed_start));},
|
||||
PointerId::Mouse => {
|
||||
pointers.0.insert(*id, (new_pos, pressed_start));
|
||||
}
|
||||
PointerId::Touch(_) => {
|
||||
pointers.0.insert(*id, (new_pos, pressed_start));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
15
src/lib.rs
15
src/lib.rs
@ -1,12 +1,15 @@
|
||||
#![feature(duration_constructors)]
|
||||
use bevy::{prelude::*, diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}};
|
||||
use bevy::{
|
||||
diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
|
||||
prelude::*,
|
||||
};
|
||||
use bevy_inspector_egui::quick::WorldInspectorPlugin;
|
||||
|
||||
pub mod map;
|
||||
pub mod camera;
|
||||
pub mod ui;
|
||||
pub mod time;
|
||||
pub mod animals;
|
||||
pub mod camera;
|
||||
pub mod map;
|
||||
pub mod time;
|
||||
pub mod ui;
|
||||
|
||||
#[bevy_main]
|
||||
pub fn main() {
|
||||
@ -23,7 +26,7 @@ pub fn main() {
|
||||
LogDiagnosticsPlugin {
|
||||
filter: Some(vec![FrameTimeDiagnosticsPlugin::FPS]),
|
||||
..Default::default()
|
||||
}
|
||||
},
|
||||
))
|
||||
.run();
|
||||
}
|
146
src/map.rs
146
src/map.rs
@ -1,15 +1,20 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use bevy::{asset::RenderAssetUsages, picking::PickSet, prelude::*, render::mesh::{Indices, PrimitiveTopology}};
|
||||
use bevy::{
|
||||
asset::RenderAssetUsages,
|
||||
picking::PickSet,
|
||||
prelude::*,
|
||||
render::mesh::{Indices, PrimitiveTopology},
|
||||
};
|
||||
use noise::{Fbm, MultiFractal, NoiseFn, Perlin};
|
||||
use rand::{thread_rng, Rng, SeedableRng};
|
||||
use voronoice::{BoundingBox, Point, VoronoiBuilder};
|
||||
|
||||
mod cells;
|
||||
mod picking;
|
||||
use picking::*;
|
||||
use cells::*;
|
||||
pub use cells::CellKind;
|
||||
use cells::*;
|
||||
use picking::*;
|
||||
|
||||
use crate::{time::GameTime, ui::CurrentAction};
|
||||
|
||||
@ -35,16 +40,16 @@ pub const SIZE: usize = 10000;
|
||||
struct Seed(u32);
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Voronoi (voronoice::Voronoi);
|
||||
pub struct Voronoi(voronoice::Voronoi);
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct MapMarker;
|
||||
|
||||
#[derive(Component)]
|
||||
struct MapColors (Vec<[f32; 4]>);
|
||||
struct MapColors(Vec<[f32; 4]>);
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct CellsEntities (Vec<Entity>);
|
||||
pub struct CellsEntities(Vec<Entity>);
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct MeshNeedsUpdate(bool);
|
||||
@ -53,12 +58,15 @@ fn setup(
|
||||
mut cmds: Commands,
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
mut materials: ResMut<Assets<ColorMaterial>>,
|
||||
seed: Res<Seed>
|
||||
seed: Res<Seed>,
|
||||
) {
|
||||
let mut rng = rand::rngs::SmallRng::seed_from_u64(seed.0 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 })
|
||||
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)
|
||||
@ -68,19 +76,22 @@ fn setup(
|
||||
.unwrap();
|
||||
let mut cells = Vec::with_capacity(SIZE);
|
||||
let z_noise = Fbm::<Perlin>::new(seed.0);
|
||||
let moisture_noise = Fbm::<Perlin>::new(seed.0+1)
|
||||
.set_frequency(2.);
|
||||
let moisture_noise = Fbm::<Perlin>::new(seed.0 + 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.);
|
||||
- ((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;
|
||||
(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 {
|
||||
@ -93,40 +104,40 @@ fn setup(
|
||||
cells.push(Cell {
|
||||
kind: k,
|
||||
voronoi_id: i,
|
||||
altitude: (z*255.) as u8,
|
||||
vertices: vec![]
|
||||
altitude: (z * 255.) as u8,
|
||||
vertices: vec![],
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
let mut poss = Vec::new();
|
||||
let mut indices = Vec::new();
|
||||
|
||||
for (c, cd) in voronoi.iter_cells().zip(cells.iter_mut()).filter(|(_,cd)| cd.kind != CellKind::Forest) {
|
||||
for (c, cd) in voronoi
|
||||
.iter_cells()
|
||||
.zip(cells.iter_mut())
|
||||
.filter(|(_, cd)| cd.kind != CellKind::Forest)
|
||||
{
|
||||
let vs = c.iter_vertices().collect::<Vec<_>>();
|
||||
let i = poss.len();
|
||||
for v in vs.iter() {
|
||||
poss.push(Vec3::new(v.x as f32, v.y as f32, 0.));
|
||||
}
|
||||
for v in 1..(vs.len()-1) {
|
||||
indices.extend_from_slice(&[i as u32, (i+v) as u32, (i+v+1) as u32]);
|
||||
cd.vertices.extend_from_slice(&[i, i+v, i+v+1]);
|
||||
for v in 1..(vs.len() - 1) {
|
||||
indices.extend_from_slice(&[i as u32, (i + v) as u32, (i + v + 1) as u32]);
|
||||
cd.vertices.extend_from_slice(&[i, i + v, i + v + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
let colors = vec![[0.; 4]; poss.len()];
|
||||
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.clone()
|
||||
)
|
||||
.with_inserted_indices(Indices::U32(indices));
|
||||
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.clone())
|
||||
.with_inserted_indices(Indices::U32(indices));
|
||||
|
||||
let mut cells_entities = Vec::with_capacity(cells.len());
|
||||
cmds.spawn((
|
||||
@ -136,74 +147,35 @@ fn setup(
|
||||
Voronoi(voronoi),
|
||||
MapColors(colors),
|
||||
MeshNeedsUpdate(true),
|
||||
MapMarker
|
||||
)).with_children(|parent| {
|
||||
MapMarker,
|
||||
))
|
||||
.with_children(|parent| {
|
||||
for cell in cells {
|
||||
let kind = cell.kind;
|
||||
let mut cmd = parent.spawn(cell);
|
||||
match kind {
|
||||
CellKind::Grass | CellKind::Forest => {
|
||||
cmd.insert((Wealth(0), Regeneration {
|
||||
last_update: Duration::ZERO,
|
||||
full_growth_duration: kind.regen_full_growth_duration()
|
||||
}));
|
||||
},
|
||||
cmd.insert((
|
||||
Wealth(0),
|
||||
Regeneration {
|
||||
last_update: Duration::ZERO,
|
||||
full_growth_duration: kind.regen_full_growth_duration(),
|
||||
},
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
cmd.observe(|
|
||||
trigger: Trigger<Pointer<Click>>,
|
||||
mut cells: Query<&mut Cell>,
|
||||
mut map_needs_update: Query<&mut MeshNeedsUpdate, With<MapMarker>>,
|
||||
mut cmds: Commands,
|
||||
ca: Res<CurrentAction>,
|
||||
gt: Res<GameTime>,
|
||||
| {
|
||||
if trigger.duration > Duration::from_millis(200) {
|
||||
return
|
||||
}
|
||||
match *ca {
|
||||
CurrentAction::ChangeCell(ck) => {
|
||||
let mut cell = cells.get_mut(trigger.target).unwrap();
|
||||
match ck {
|
||||
CellKind::Forest => match cell.kind {
|
||||
CellKind::Dirt | CellKind::Grass => {
|
||||
cmds.entity(trigger.target).insert((Wealth(0), Regeneration {
|
||||
last_update: gt.current,
|
||||
full_growth_duration: CellKind::Forest.regen_full_growth_duration()
|
||||
}));
|
||||
cell.kind = CellKind::Forest;
|
||||
map_needs_update.single_mut().0 = true;
|
||||
},
|
||||
_ => {}
|
||||
},
|
||||
CellKind::Grass => match cell.kind {
|
||||
CellKind::Dirt => {
|
||||
cmds.entity(trigger.target).insert((Wealth(0), Regeneration {
|
||||
last_update: gt.current,
|
||||
full_growth_duration: CellKind::Grass.regen_full_growth_duration()
|
||||
}));
|
||||
cell.kind = CellKind::Grass;
|
||||
map_needs_update.single_mut().0 = true;
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
});
|
||||
cmd.observe(self::cells::on_click);
|
||||
cells_entities.push(cmd.id());
|
||||
}
|
||||
}).insert(CellsEntities(cells_entities));
|
||||
|
||||
})
|
||||
.insert(CellsEntities(cells_entities));
|
||||
}
|
||||
|
||||
fn update_map_mesh(
|
||||
cells: Query<(&Cell, Option<&Wealth>)>,
|
||||
mut map: Query<(&Mesh2d, &mut MapColors, &mut MeshNeedsUpdate), With<MapMarker>>,
|
||||
mut meshes: ResMut<Assets<Mesh>>
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
) {
|
||||
let (mesh, mut cols, mut needs_update) = map.single_mut();
|
||||
if needs_update.0 {
|
||||
|
138
src/map/cells.rs
138
src/map/cells.rs
@ -14,14 +14,14 @@ pub enum CellKind {
|
||||
Forest,
|
||||
Dirt,
|
||||
Stone,
|
||||
Grass
|
||||
Grass,
|
||||
}
|
||||
impl CellKind {
|
||||
pub fn regen_full_growth_duration(&self) -> Duration {
|
||||
match self {
|
||||
CellKind::Sea | CellKind::Beach | CellKind::Dirt | CellKind::Stone => unreachable!(),
|
||||
CellKind::Forest => Duration::from_days(365*100), // Let's say that a forest takes 100 years to mature
|
||||
CellKind::Grass => Duration::from_weeks(7) // Let's say that grass takes 7 weeks to reach its max
|
||||
CellKind::Forest => Duration::from_days(365 * 100), // Let's say that a forest takes 100 years to mature
|
||||
CellKind::Grass => Duration::from_weeks(7), // Let's say that grass takes 7 weeks to reach its max
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -31,17 +31,27 @@ pub struct Cell {
|
||||
pub kind: CellKind,
|
||||
pub voronoi_id: usize,
|
||||
pub altitude: u8,
|
||||
pub vertices: Vec<usize>
|
||||
pub vertices: Vec<usize>,
|
||||
}
|
||||
impl Cell {
|
||||
pub fn color(&self, wealth: u8) -> [f32; 4] {
|
||||
match self.kind {
|
||||
CellKind::Sea => [0., 0., 1., 1.],
|
||||
CellKind::Beach => [0.82, 0.84, 0.51, 1.],
|
||||
CellKind::Forest => [0., 0.5 - (wealth as f32/255.*0.4), 0., 1.],
|
||||
CellKind::Dirt => [0.53 - (wealth as f32/255.*0.4), 0.38-(wealth as f32/255.*0.4), 0.29-(wealth as f32/255.*0.4), 1.],
|
||||
CellKind::Forest => [0., 0.5 - (wealth as f32 / 255. * 0.4), 0., 1.],
|
||||
CellKind::Dirt => [
|
||||
0.53 - (wealth as f32 / 255. * 0.4),
|
||||
0.38 - (wealth as f32 / 255. * 0.4),
|
||||
0.29 - (wealth as f32 / 255. * 0.4),
|
||||
1.,
|
||||
],
|
||||
CellKind::Stone => [0.5, 0.5, 0.5, 1.],
|
||||
CellKind::Grass => [(136./255.) - (wealth as f32/255.*0.15), (154./255.) + (wealth as f32/255.*0.1), (59./255.) - (wealth as f32/255.*0.15), 1.]
|
||||
CellKind::Grass => [
|
||||
(136. / 255.) - (wealth as f32 / 255. * 0.15),
|
||||
(154. / 255.) + (wealth as f32 / 255. * 0.1),
|
||||
(59. / 255.) - (wealth as f32 / 255. * 0.15),
|
||||
1.,
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -59,17 +69,17 @@ impl Default for Wealth {
|
||||
#[require(Wealth)]
|
||||
pub struct Regeneration {
|
||||
pub last_update: Duration,
|
||||
pub full_growth_duration: Duration
|
||||
pub full_growth_duration: Duration,
|
||||
}
|
||||
|
||||
pub fn cells_regeneration(
|
||||
mut cells: Query<(&mut Regeneration, &mut Wealth)>,
|
||||
mut map_needs_update: Query<&mut MeshNeedsUpdate, With<MapMarker>>,
|
||||
gt: Res<GameTime>
|
||||
gt: Res<GameTime>,
|
||||
) {
|
||||
let mut map_needs_update = map_needs_update.single_mut();
|
||||
for (mut regen, mut wealth) in cells.iter_mut() {
|
||||
if gt.current - regen.last_update > regen.full_growth_duration/u8::MAX as u32 {
|
||||
if gt.current - regen.last_update > regen.full_growth_duration / u8::MAX as u32 {
|
||||
regen.last_update = gt.current;
|
||||
wealth.0 = wealth.0.saturating_add(1);
|
||||
map_needs_update.0 = true;
|
||||
@ -82,7 +92,7 @@ pub fn expand(
|
||||
map: Query<(&Voronoi, &CellsEntities)>,
|
||||
mut cmds: Commands,
|
||||
t: Res<Time>,
|
||||
gt: Res<GameTime>
|
||||
gt: Res<GameTime>,
|
||||
) {
|
||||
let (voronoi, cells_entities) = map.single();
|
||||
let mut random = thread_rng();
|
||||
@ -93,16 +103,37 @@ pub fn expand(
|
||||
let wealth = if cell.kind == CellKind::Forest {
|
||||
1.
|
||||
} else {
|
||||
wealth.unwrap().0 as f64/255.
|
||||
wealth.unwrap().0 as f64 / 255.
|
||||
};
|
||||
if random.gen_bool((t.elapsed_secs_f64()*(gt.speed as f64)*wealth/(60.*60.*24.*30.)).min(1.)) { // Let say that grass takes 1 months to expand
|
||||
let target = voronoi.0.cell(cell.voronoi_id).iter_neighbors().choose(&mut random).unwrap();
|
||||
if random.gen_bool(
|
||||
(t.elapsed_secs_f64() * (gt.speed as f64) * wealth / (60. * 60. * 24. * 30.))
|
||||
.min(1.),
|
||||
) {
|
||||
// Let say that grass takes 1 months to expand
|
||||
let target = voronoi
|
||||
.0
|
||||
.cell(cell.voronoi_id)
|
||||
.iter_neighbors()
|
||||
.choose(&mut random)
|
||||
.unwrap();
|
||||
changes.push((target, CellKind::Grass));
|
||||
}
|
||||
}
|
||||
if cell.kind == CellKind::Forest {
|
||||
if random.gen_bool((t.elapsed_secs_f64()*(gt.speed as f64)*(wealth.unwrap().0 as f64/255.).sqrt()/(60.*60.*24.*365.*5.)).min(1.)) { // Let say that forest takes 5 years to expand
|
||||
let target = voronoi.0.cell(cell.voronoi_id).iter_neighbors().choose(&mut random).unwrap();
|
||||
if random.gen_bool(
|
||||
(t.elapsed_secs_f64()
|
||||
* (gt.speed as f64)
|
||||
* (wealth.unwrap().0 as f64 / 255.).sqrt()
|
||||
/ (60. * 60. * 24. * 365. * 5.))
|
||||
.min(1.),
|
||||
) {
|
||||
// Let say that forest takes 5 years to expand
|
||||
let target = voronoi
|
||||
.0
|
||||
.cell(cell.voronoi_id)
|
||||
.iter_neighbors()
|
||||
.choose(&mut random)
|
||||
.unwrap();
|
||||
changes.push((target, CellKind::Forest));
|
||||
}
|
||||
}
|
||||
@ -110,18 +141,75 @@ pub fn expand(
|
||||
for (v_id, kind) in changes {
|
||||
let target = cells_entities.0[v_id];
|
||||
let (mut cell, _) = cells.get_mut(target).unwrap();
|
||||
if kind == CellKind::Forest && (cell.kind == CellKind::Dirt || cell.kind == CellKind::Grass) {
|
||||
if kind == CellKind::Forest && (cell.kind == CellKind::Dirt || cell.kind == CellKind::Grass)
|
||||
{
|
||||
cell.kind = CellKind::Forest;
|
||||
cmds.entity(target).insert((Wealth(0), Regeneration {
|
||||
full_growth_duration: CellKind::Forest.regen_full_growth_duration(),
|
||||
last_update: gt.current
|
||||
}));
|
||||
cmds.entity(target).insert((
|
||||
Wealth(0),
|
||||
Regeneration {
|
||||
full_growth_duration: CellKind::Forest.regen_full_growth_duration(),
|
||||
last_update: gt.current,
|
||||
},
|
||||
));
|
||||
} else if kind == CellKind::Grass && cell.kind == CellKind::Dirt {
|
||||
cell.kind = CellKind::Grass;
|
||||
cmds.entity(target).insert((Wealth(0), Regeneration {
|
||||
full_growth_duration: CellKind::Grass.regen_full_growth_duration(),
|
||||
last_update: gt.current
|
||||
}));
|
||||
cmds.entity(target).insert((
|
||||
Wealth(0),
|
||||
Regeneration {
|
||||
full_growth_duration: CellKind::Grass.regen_full_growth_duration(),
|
||||
last_update: gt.current,
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_click(
|
||||
trigger: Trigger<Pointer<Click>>,
|
||||
mut cells: Query<&mut Cell>,
|
||||
mut map_needs_update: Query<&mut MeshNeedsUpdate, With<MapMarker>>,
|
||||
mut cmds: Commands,
|
||||
ca: Res<CurrentAction>,
|
||||
gt: Res<GameTime>,
|
||||
) {
|
||||
if trigger.duration > Duration::from_millis(200) {
|
||||
return;
|
||||
}
|
||||
match *ca {
|
||||
CurrentAction::ChangeCell(ck) => {
|
||||
let mut cell = cells.get_mut(trigger.target).unwrap();
|
||||
match ck {
|
||||
CellKind::Forest => match cell.kind {
|
||||
CellKind::Dirt | CellKind::Grass => {
|
||||
cmds.entity(trigger.target).insert((
|
||||
Wealth(0),
|
||||
Regeneration {
|
||||
last_update: gt.current,
|
||||
full_growth_duration: CellKind::Forest.regen_full_growth_duration(),
|
||||
},
|
||||
));
|
||||
cell.kind = CellKind::Forest;
|
||||
map_needs_update.single_mut().0 = true;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
CellKind::Grass => match cell.kind {
|
||||
CellKind::Dirt => {
|
||||
cmds.entity(trigger.target).insert((
|
||||
Wealth(0),
|
||||
Regeneration {
|
||||
last_update: gt.current,
|
||||
full_growth_duration: CellKind::Grass.regen_full_growth_duration(),
|
||||
},
|
||||
));
|
||||
cell.kind = CellKind::Grass;
|
||||
map_needs_update.single_mut().0 = true;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,11 @@
|
||||
use bevy::{picking::{backend::{HitData, PointerHits}, pointer::{PointerId, PointerLocation}}, prelude::*, window::PrimaryWindow};
|
||||
use bevy::{
|
||||
picking::{
|
||||
backend::{HitData, PointerHits},
|
||||
pointer::{PointerId, PointerLocation},
|
||||
},
|
||||
prelude::*,
|
||||
window::PrimaryWindow,
|
||||
};
|
||||
use voronoice::Point;
|
||||
|
||||
use super::{CellsEntities, MapMarker, Voronoi};
|
||||
@ -8,7 +15,7 @@ pub fn picking_backend(
|
||||
window: Query<&Window, With<PrimaryWindow>>,
|
||||
pointers: Query<(&PointerId, &PointerLocation)>,
|
||||
map: Query<(&Voronoi, &CellsEntities, &Transform), With<MapMarker>>,
|
||||
mut output: EventWriter<PointerHits>
|
||||
mut output: EventWriter<PointerHits>,
|
||||
) {
|
||||
let (cam, cam_id) = cam.single();
|
||||
let window = window.single();
|
||||
@ -16,24 +23,38 @@ pub fn picking_backend(
|
||||
let mut last_cell = 0;
|
||||
for (id, l) in pointers.iter() {
|
||||
if let Some(mut pos) = l.location().map(|l| l.position) {
|
||||
pos -= window.size()/2.;
|
||||
pos -= window.size() / 2.;
|
||||
pos *= cam.scale.xy();
|
||||
pos.x += cam.translation.x;
|
||||
pos.y -= cam.translation.y;
|
||||
if let Some(c) = voronoi.0.cell(last_cell).iter_path(Point { x: pos.x as f64, y: -pos.y as f64 }).last() {
|
||||
if let Some(c) = voronoi
|
||||
.0
|
||||
.cell(last_cell)
|
||||
.iter_path(Point {
|
||||
x: pos.x as f64,
|
||||
y: -pos.y as f64,
|
||||
})
|
||||
.last()
|
||||
{
|
||||
last_cell = c;
|
||||
output.send(PointerHits {
|
||||
pointer: *id,
|
||||
picks: vec![(cells_entities.0[c], HitData {
|
||||
camera: cam_id,
|
||||
depth: map_pos.translation.z,
|
||||
position: Some(Vec3 { x: pos.x, y: pos.y, z: map_pos.translation.z }),
|
||||
normal: None
|
||||
})],
|
||||
order: map_pos.translation.z
|
||||
picks: vec![(
|
||||
cells_entities.0[c],
|
||||
HitData {
|
||||
camera: cam_id,
|
||||
depth: map_pos.translation.z,
|
||||
position: Some(Vec3 {
|
||||
x: pos.x,
|
||||
y: pos.y,
|
||||
z: map_pos.translation.z,
|
||||
}),
|
||||
normal: None,
|
||||
},
|
||||
)],
|
||||
order: map_pos.translation.z,
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
15
src/time.rs
15
src/time.rs
@ -6,23 +6,20 @@ pub struct Plugin;
|
||||
impl bevy::prelude::Plugin for Plugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.insert_resource(GameTime {
|
||||
current: Duration::ZERO,
|
||||
speed: 24. * 60. * 60. * 10.
|
||||
})
|
||||
.add_systems(PreUpdate, update_time);
|
||||
current: Duration::ZERO,
|
||||
speed: 24. * 60. * 60. * 10.,
|
||||
})
|
||||
.add_systems(PreUpdate, update_time);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
pub struct GameTime {
|
||||
pub current: Duration,
|
||||
pub speed: f32 // = game time / real time
|
||||
pub speed: f32, // = game time / real time
|
||||
}
|
||||
|
||||
fn update_time(
|
||||
mut gt: ResMut<GameTime>,
|
||||
time: Res<Time>
|
||||
) {
|
||||
fn update_time(mut gt: ResMut<GameTime>, time: Res<Time>) {
|
||||
let speed = gt.speed;
|
||||
gt.current += Duration::from_secs_f32(time.delta_secs() * speed);
|
||||
}
|
39
src/ui.rs
39
src/ui.rs
@ -1,13 +1,24 @@
|
||||
use bevy::{
|
||||
asset::embedded_asset,
|
||||
input::mouse::MouseWheel,
|
||||
math::{NormedVectorSpace, VectorSpace},
|
||||
picking::{focus::HoverMap, pointer::PointerId},
|
||||
prelude::*,
|
||||
utils::{dbg, HashMap},
|
||||
window::PrimaryWindow,
|
||||
};
|
||||
use mevy::*;
|
||||
use bevy::{asset::embedded_asset, input::mouse::MouseWheel, math::{NormedVectorSpace, VectorSpace}, picking::{focus::HoverMap, pointer::PointerId}, prelude::*, utils::{dbg, HashMap}, window::PrimaryWindow};
|
||||
|
||||
use crate::{animals::AnimalKind, map::{self, CellKind, MapMarker}};
|
||||
use crate::{
|
||||
animals::AnimalKind,
|
||||
map::{self, CellKind, MapMarker},
|
||||
};
|
||||
|
||||
// #77767b
|
||||
const TABBAR_COLOR: Color = Color::srgb(119./255., 118./255., 123./255.);
|
||||
const TABBAR_COLOR: Color = Color::srgb(119. / 255., 118. / 255., 123. / 255.);
|
||||
|
||||
// #E8E8E8
|
||||
const ENABLED_BUTTON_COLOR: Color = Color::srgb(232./255., 232./255., 232./255.);
|
||||
const ENABLED_BUTTON_COLOR: Color = Color::srgb(232. / 255., 232. / 255., 232. / 255.);
|
||||
|
||||
pub struct Plugin;
|
||||
impl bevy::prelude::Plugin for Plugin {
|
||||
@ -31,7 +42,7 @@ pub enum CurrentAction {
|
||||
#[default]
|
||||
None,
|
||||
ChangeCell(CellKind),
|
||||
AddAnimal(AnimalKind)
|
||||
AddAnimal(AnimalKind),
|
||||
}
|
||||
|
||||
#[derive(Component, Debug)]
|
||||
@ -40,12 +51,9 @@ struct PointersDragging(HashMap<PointerId, Vec2>);
|
||||
#[derive(Component)]
|
||||
pub struct MapUIComponent;
|
||||
|
||||
fn setup(
|
||||
mut world: Commands,
|
||||
asset_server: Res<AssetServer>
|
||||
) {
|
||||
fn setup(mut world: Commands, asset_server: Res<AssetServer>) {
|
||||
// Spawn all ui elements as children of this one
|
||||
spawn!{
|
||||
spawn! {
|
||||
Node {width: 100%, height: 100%, display: Display::Flex, flex_direction: FlexDirection::Column, !};
|
||||
PickingBehavior {
|
||||
should_block_lower: false,
|
||||
@ -182,14 +190,19 @@ fn zoom_with_scroll(
|
||||
mut ev_scroll: EventReader<MouseWheel>,
|
||||
hover_map: Res<HoverMap>,
|
||||
window: Query<&Window, With<PrimaryWindow>>,
|
||||
map_ui_id: Query<Entity, With<MapUIComponent>>
|
||||
map_ui_id: Query<Entity, With<MapUIComponent>>,
|
||||
) {
|
||||
let map_ui_id = map_ui_id.single();
|
||||
if hover_map.get(&PointerId::Mouse).and_then(|hovered_ids| hovered_ids.get(&map_ui_id)).is_some() {
|
||||
if hover_map
|
||||
.get(&PointerId::Mouse)
|
||||
.and_then(|hovered_ids| hovered_ids.get(&map_ui_id))
|
||||
.is_some()
|
||||
{
|
||||
let window = window.single();
|
||||
let mut cam = cam.single_mut();
|
||||
for ev in ev_scroll.read() {
|
||||
let scale = (cam.scale.x-(ev.y*0.1/window.width().min(window.height()))).clamp(0.0001, 2./window.width().min(window.height()));
|
||||
let scale = (cam.scale.x - (ev.y * 0.1 / window.width().min(window.height())))
|
||||
.clamp(0.0001, 2. / window.width().min(window.height()));
|
||||
cam.scale = Vec3::new(scale, scale, scale);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user