From 51c221fe67adb0d3cfd2d2a0c79c67b0f2ef79ea Mon Sep 17 00:00:00 2001 From: Rob Parrett Date: Thu, 7 Dec 2023 08:42:11 -0700 Subject: [PATCH] Workers build towers --- src/main.rs | 3 ++ src/tilemap.rs | 79 ++++++++++++++++++++++++++++++------------------ src/tower.rs | 78 ++++++++++++++++++++++++++++++++++++++++++++++++ src/worker.rs | 81 +++++++++++++++++++++++++++++++++++++++++--------- 4 files changed, 198 insertions(+), 43 deletions(-) create mode 100644 src/tower.rs diff --git a/src/main.rs b/src/main.rs index 9bf8b83..51d1c47 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,6 +19,7 @@ use spawner::SpawnerPlugin; use stone::StonePlugin; use tilemap::TilemapPlugin; use tool_selector::ToolSelectorPlugin; +use tower::TowerPlugin; use waves::WavesPlugin; use worker::WorkerPlugin; @@ -47,6 +48,7 @@ mod spawner; mod stone; mod tilemap; mod tool_selector; +mod tower; mod waves; mod worker; @@ -89,6 +91,7 @@ fn main() { StonePlugin, CurrencyPlugin, )); + app.add_plugins(TowerPlugin); app.add_plugins(( RadioButtonPlugin, diff --git a/src/tilemap.rs b/src/tilemap.rs index 8ba8a96..5ba494e 100644 --- a/src/tilemap.rs +++ b/src/tilemap.rs @@ -54,6 +54,7 @@ pub enum TileKind { Bridge, Road, Spawn, + Tower, } pub struct TileNotMappedError; @@ -123,6 +124,7 @@ impl TileKind { Self::Bridge => 103 * 4 + 0, Self::Road => 103 * 1 + 13, Self::Spawn => 103 * 17 + 80, + Self::Tower => 103 * 21 + 31, } } } @@ -184,12 +186,60 @@ impl Tilemap { y: pos.y.round() as usize, } } + + pub fn new(width: usize, height: usize) -> Self { + let map = Self { + tiles: vec![vec![TileKind::Empty; height]; width], + width, + height, + }; + + map + } + + pub fn new_random(width: usize, height: usize) -> Self { + let mut rng = thread_rng(); + + let mut map = Self::new(width, height); + + for x in 0..width { + for y in 0..height { + let kind: TileKind = rng.gen(); + + map.tiles[x][y] = kind; + } + } + + map + } + + pub fn get(&self, pos: TilePos) -> Option<&TileKind> { + let col = self.tiles.get(pos.x)?; + col.get(pos.y) + } + + pub fn get_mut(&mut self, pos: TilePos) -> Option<&mut TileKind> { + let col = self.tiles.get_mut(pos.x)?; + col.get_mut(pos.y) + } } +// TODO argh why is this not just a hashmap #[derive(Component, Default)] pub struct TileEntities { pub entities: Vec>>, } +impl TileEntities { + pub fn get(&self, pos: TilePos) -> Option<&Option> { + let col = self.entities.get(pos.x)?; + col.get(pos.y) + } + + pub fn get_mut(&mut self, pos: TilePos) -> Option<&mut Option> { + let col = self.entities.get_mut(pos.x)?; + col.get_mut(pos.y) + } +} #[derive(Reflect, Component, Debug, Clone, Copy, Default, Hash, Eq, PartialEq, Ord, PartialOrd)] pub struct TilePos { @@ -228,35 +278,6 @@ impl Into<(isize, isize)> for TilePos { (self.x as isize, self.y as isize) } } - -impl Tilemap { - pub fn new(width: usize, height: usize) -> Self { - let map = Self { - tiles: vec![vec![TileKind::Empty; height]; width], - width, - height, - }; - - map - } - - pub fn new_random(width: usize, height: usize) -> Self { - let mut rng = thread_rng(); - - let mut map = Self::new(width, height); - - for x in 0..width { - for y in 0..height { - let kind: TileKind = rng.gen(); - - map.tiles[x][y] = kind; - } - } - - map - } -} - #[derive(Resource)] pub struct TilemapHandle(pub Handle); diff --git a/src/tower.rs b/src/tower.rs new file mode 100644 index 0000000..b9d4f1c --- /dev/null +++ b/src/tower.rs @@ -0,0 +1,78 @@ +use bevy::prelude::*; + +use crate::{ + designate_tool::Designations, + tilemap::{AtlasHandle, TileEntities, TileKind, TilePos, Tilemap, SCALE}, + GameState, +}; + +pub struct TowerPlugin; +impl Plugin for TowerPlugin { + fn build(&self, app: &mut App) { + app.add_event::() + .add_systems(Update, build_tower.run_if(in_state(GameState::Playing))); + } +} + +#[derive(Component)] +pub struct Tower; + +#[derive(Event, Debug)] +pub struct BuildTowerEvent(pub TilePos); + +fn build_tower( + mut commands: Commands, + mut events: EventReader, + mut designations: ResMut, + mut tilemap_query: Query<(&mut Tilemap, &mut TileEntities)>, + atlas_handle: Res, +) { + for event in events.read() { + let Ok((mut tilemap, mut tile_entities)) = tilemap_query.get_single_mut() else { + continue; + }; + + let world = tilemap.pos_to_world(event.0).extend(0.); + + let Some(tile_kind) = tilemap.get_mut(event.0) else { + continue; + }; + + if !tile_kind.buildable() { + continue; + } + + let Some(maybe_tile_entity) = tile_entities.get_mut(event.0) else { + continue; + }; + + if let Some(entity) = maybe_tile_entity.take() { + commands.entity(entity).despawn(); + } + + let id = commands + .spawn(( + SpriteSheetBundle { + texture_atlas: atlas_handle.0.clone(), + sprite: TextureAtlasSprite::new(TileKind::Tower.atlas_index()), + transform: Transform { + scale: SCALE.extend(1.), + translation: world, + ..default() + }, + ..default() + }, + Tower, + TileKind::Tower, + event.0.clone(), + )) + .id(); + + *maybe_tile_entity = Some(id); + *tile_kind = TileKind::Tower; + + if let Some(designation) = designations.0.remove(&event.0) { + commands.entity(designation.indicator).despawn(); + } + } +} diff --git a/src/worker.rs b/src/worker.rs index 1ec8cfd..3b19e2c 100644 --- a/src/worker.rs +++ b/src/worker.rs @@ -1,10 +1,11 @@ use crate::{ - designate_tool::Designations, + designate_tool::{DesignationKind, Designations}, hit_points::HitPoints, movement::{MovingProgress, Speed}, pathfinding::{heuristic, worker_cost_fn, NeighborCostIter, PathState}, stone::HitStoneEvent, - tilemap::{AtlasHandle, TileEntities, TilePos, Tilemap}, + tilemap::{AtlasHandle, TileEntities, TileKind, TilePos, Tilemap}, + tower::BuildTowerEvent, GameState, }; use bevy::prelude::*; @@ -35,6 +36,7 @@ pub struct Idle; #[derive(Component)] pub enum Job { Dig(TilePos), + Build { hit_points: HitPoints, pos: TilePos }, } #[derive(Component)] @@ -178,13 +180,26 @@ fn find_job( continue; }; - commands - .entity(entity) + let mut command = commands.entity(entity); + + command .insert(MovingProgress::default()) .insert(PathState::from(result.0)) - .insert(Job::Dig(goal)) .remove::(); + match designation { + DesignationKind::Dig => { + command.insert(Job::Dig(goal)); + } + DesignationKind::BuildTower => { + command.insert(Job::Build { + hit_points: HitPoints::full(10), + pos: goal, + }); + } + _ => {} + } + jobs_assigned.push(goal); // limit the amount of pathfinding we do each frame. @@ -199,12 +214,14 @@ fn find_job( fn do_job( mut commands: Commands, mut query: Query< - (Entity, &TilePos, &Job, &mut WorkCooldown), + (Entity, &TilePos, &mut Job, &mut WorkCooldown), (With, Without, Without), >, dig_query: Query<&HitPoints>, + tile_kind_query: Query<&TileKind>, mut tilemap_query: Query<&TileEntities>, mut events: EventWriter, + mut tower_events: EventWriter, ) { if query.is_empty() { return; @@ -214,15 +231,14 @@ fn do_job( return; }; - for (entity, _pos, job, mut cooldown) in &mut query { + for (entity, _pos, mut job, mut cooldown) in &mut query { // TODO ensure we are actually near the job. - match job { + match &mut *job { Job::Dig(dig_pos) => { let Some(tile_entity) = map_entities.entities[dig_pos.x][dig_pos.y] else { warn!("Working trying to dig at position without entity."); - commands.entity(entity).insert(Idle); - commands.entity(entity).remove::(); + commands.entity(entity).insert(Idle).remove::(); continue; }; @@ -231,14 +247,12 @@ fn do_job( let Ok(hp) = dig_query.get(tile_entity) else { warn!("Working trying to dig at position without HP."); - commands.entity(entity).insert(Idle); - commands.entity(entity).remove::(); + commands.entity(entity).insert(Idle).remove::(); continue; }; if hp.is_zero() { - commands.entity(entity).insert(Idle); - commands.entity(entity).remove::(); + commands.entity(entity).insert(Idle).remove::(); continue; } @@ -251,6 +265,45 @@ fn do_job( damage: 1, }); + cooldown.0.reset(); + } + Job::Build { hit_points, pos } => { + let Some(tile_entity) = map_entities.entities[pos.x][pos.y] else { + warn!("Working trying to build at position without entity."); + commands.entity(entity).insert(Idle).remove::(); + continue; + }; + + // TODO maybe also just double check that this is a rock and not just any + // random thing with hitpoints. + + let Ok(kind) = tile_kind_query.get(tile_entity) else { + warn!("Working trying to build at position without tile kind."); + commands.entity(entity).insert(Idle).remove::(); + continue; + }; + + if !kind.buildable() { + warn!("Working trying to build at position without tile kind."); + commands.entity(entity).insert(Idle).remove::(); + continue; + } + + if hit_points.is_zero() { + commands.entity(entity).insert(Idle).remove::(); + continue; + } + + if !cooldown.0.finished() { + continue; + } + + hit_points.sub(1); + + if hit_points.is_zero() { + tower_events.send(BuildTowerEvent(*pos)); + } + cooldown.0.reset(); } }