Skip to content

Commit

Permalink
Workers build towers
Browse files Browse the repository at this point in the history
  • Loading branch information
rparrett committed Dec 7, 2023
1 parent c6434ae commit 51c221f
Show file tree
Hide file tree
Showing 4 changed files with 198 additions and 43 deletions.
3 changes: 3 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -47,6 +48,7 @@ mod spawner;
mod stone;
mod tilemap;
mod tool_selector;
mod tower;
mod waves;
mod worker;

Expand Down Expand Up @@ -89,6 +91,7 @@ fn main() {
StonePlugin,
CurrencyPlugin,
));
app.add_plugins(TowerPlugin);

app.add_plugins((
RadioButtonPlugin,
Expand Down
79 changes: 50 additions & 29 deletions src/tilemap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ pub enum TileKind {
Bridge,
Road,
Spawn,
Tower,
}

pub struct TileNotMappedError;
Expand Down Expand Up @@ -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,
}
}
}
Expand Down Expand Up @@ -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<Vec<Option<Entity>>>,
}
impl TileEntities {
pub fn get(&self, pos: TilePos) -> Option<&Option<Entity>> {
let col = self.entities.get(pos.x)?;
col.get(pos.y)
}

pub fn get_mut(&mut self, pos: TilePos) -> Option<&mut Option<Entity>> {
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 {
Expand Down Expand Up @@ -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<Tilemap>);

Expand Down
78 changes: 78 additions & 0 deletions src/tower.rs
Original file line number Diff line number Diff line change
@@ -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::<BuildTowerEvent>()
.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<BuildTowerEvent>,
mut designations: ResMut<Designations>,
mut tilemap_query: Query<(&mut Tilemap, &mut TileEntities)>,
atlas_handle: Res<AtlasHandle>,
) {
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();
}
}
}
81 changes: 67 additions & 14 deletions src/worker.rs
Original file line number Diff line number Diff line change
@@ -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::*;
Expand Down Expand Up @@ -35,6 +36,7 @@ pub struct Idle;
#[derive(Component)]
pub enum Job {
Dig(TilePos),
Build { hit_points: HitPoints, pos: TilePos },
}

#[derive(Component)]
Expand Down Expand Up @@ -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::<Idle>();

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.
Expand All @@ -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<Worker>, Without<Idle>, Without<PathState>),
>,
dig_query: Query<&HitPoints>,
tile_kind_query: Query<&TileKind>,
mut tilemap_query: Query<&TileEntities>,
mut events: EventWriter<HitStoneEvent>,
mut tower_events: EventWriter<BuildTowerEvent>,
) {
if query.is_empty() {
return;
Expand All @@ -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::<Job>();
commands.entity(entity).insert(Idle).remove::<Job>();
continue;
};

Expand All @@ -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::<Job>();
commands.entity(entity).insert(Idle).remove::<Job>();
continue;
};

if hp.is_zero() {
commands.entity(entity).insert(Idle);
commands.entity(entity).remove::<Job>();
commands.entity(entity).insert(Idle).remove::<Job>();
continue;
}

Expand All @@ -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::<Job>();
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::<Job>();
continue;
};

if !kind.buildable() {
warn!("Working trying to build at position without tile kind.");
commands.entity(entity).insert(Idle).remove::<Job>();
continue;
}

if hit_points.is_zero() {
commands.entity(entity).insert(Idle).remove::<Job>();
continue;
}

if !cooldown.0.finished() {
continue;
}

hit_points.sub(1);

if hit_points.is_zero() {
tower_events.send(BuildTowerEvent(*pos));
}

cooldown.0.reset();
}
}
Expand Down

0 comments on commit 51c221f

Please sign in to comment.