Skip to content

Commit

Permalink
feat: final multiple tiles and spawning 🍓
Browse files Browse the repository at this point in the history
  • Loading branch information
eerii committed Dec 9, 2023
1 parent d640349 commit 21b1327
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 24 deletions.
9 changes: 7 additions & 2 deletions src/game.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#![allow(clippy::too_many_arguments)]

use bevy::{prelude::*, render::view::RenderLayers};
use bevy_ecs_tilemap::prelude::*;
use rand::Rng;

use crate::{
tilemap::{play_to_real_size, EndTile, LevelSize, PathTile, StartTile},
tilemap::{play_to_real_size, EndTile, LevelSize, PathTile, StartTile, TileChanged},
GameState,
};

Expand Down Expand Up @@ -70,6 +72,7 @@ fn spawn_start_end(
mut cmd: Commands,
score: Res<GameScore>,
mut level_size: ResMut<LevelSize>,
mut tile_changed: ResMut<TileChanged>,
tilemap: Query<&TileStorage>,
starts: Query<&TilePos, With<StartTile>>,
ends: Query<&TilePos, With<EndTile>>,
Expand Down Expand Up @@ -111,7 +114,7 @@ fn spawn_start_end(
level_size.0.x += 2;
level_size.0.y += 2;
}
let (offset, size) = play_to_real_size(&*level_size);
let (offset, size) = play_to_real_size(&level_size);

if let Ok(storage) = tilemap.get_single() {
if is_start {
Expand All @@ -126,6 +129,7 @@ fn spawn_start_end(
if let Some(pos) = pos {
cmd.entity(storage.get(&pos).unwrap())
.insert((StartTile::default(), PathTile::default()));
tile_changed.0 += 1;
}
}

Expand All @@ -141,6 +145,7 @@ fn spawn_start_end(
if let Some(pos) = pos {
cmd.entity(storage.get(&pos).unwrap())
.insert((EndTile, PathTile::default()));
tile_changed.0 += 1;
}
}
}
Expand Down
56 changes: 43 additions & 13 deletions src/spirits.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::time::Duration;

use bevy::prelude::*;
use bevy_ecs_tilemap::prelude::*;
use rand::Rng;
Expand All @@ -12,7 +14,6 @@ use crate::{
const SPIRIT_SPEED: f32 = 200.;
const SPIRIT_SIZE: f32 = 32.;
const MAX_SPIRITS_IN_TILE: u32 = 3;
const MAX_DISTANCE: f32 = std::f32::MAX;
const FUN_A: f32 = 10.;

// ······
Expand Down Expand Up @@ -45,6 +46,7 @@ impl Plugin for SpiritPlugin {
pub struct Spirit {
prev_tile: Option<TilePos>,
curr_tile: TilePos,
curr_distance: f32,
next_tile: Option<TilePos>,
next_pos: Vec2,
selected_end: Option<TilePos>,
Expand All @@ -56,6 +58,7 @@ impl Spirit {
Self {
prev_tile: Some(curr_tile),
curr_tile,
curr_distance: std::f32::MAX,
next_tile: None,
next_pos: curr_pos,
selected_end: None,
Expand All @@ -78,6 +81,14 @@ fn spawn_spirit(
) {
for (start_pos, mut start_tile, mut start_path) in start.iter_mut() {
if start_tile.spawn_timer.tick(time.delta()).just_finished() {
// Reduce timer 0.01 seconds until it is 0.5
let duration = start_tile.spawn_timer.duration().as_millis();
if duration > 600 {
start_tile
.spawn_timer
.set_duration(Duration::from_millis((duration - 5) as u64));
}

if let Ok((grid_size, map_type, trans)) = tilemap.get_single() {
// Don't spawn entities if the path is not complete
if !start_tile.completed_once {
Expand Down Expand Up @@ -162,8 +173,6 @@ fn next_tile_spirit(
// Get the next tile
if spirit.next_tile.is_none() || spirit.next_tile.unwrap() == tile_pos {
spirit.next_tile = None;
let mut tile_distance = &MAX_DISTANCE;

// If the spirit is on the end tile, despawn
if let Some(entity) = storage.get(&tile_pos) {
if end.get(entity).is_ok() {
Expand All @@ -174,9 +183,10 @@ fn next_tile_spirit(
}
continue;
}
if let Ok((_, path)) = paths.get(entity) {
if let Some(end) = spirit.selected_end {
tile_distance = path.distance.get(&end).unwrap_or(&MAX_DISTANCE);
if let Some(end) = spirit.selected_end {
if let Ok((_, path)) = paths.get(entity) {
spirit.curr_distance =
*path.distance.get(&end).unwrap_or(&spirit.curr_distance);
}
}
}
Expand All @@ -188,6 +198,9 @@ fn next_tile_spirit(
.filter_map(|pos| storage.get(pos))
.filter_map(|entity| {
if let Ok((pos, path)) = paths.get(entity) {
if start.get(entity).is_ok() {
return None;
}
Some((pos, path))
} else {
None
Expand All @@ -206,10 +219,18 @@ fn next_tile_spirit(
}
}

// If the spirit is not on a path, reset the current distance
if let Some(entity) = storage.get(&tile_pos) {
if paths.get(entity).is_err() {
spirit.curr_distance = std::f32::MAX;
}
}

// Choose the next tile to move to
// For this, it must have a path score less than the current one, or else it will stay put
// Also, we must check that there are not too many entities in this path
// From the possible next tiles, it chooses the one with the lowest score
let mut reset_distance = false;
let next = neighbours
.map(|(pos, path)| {
let min_dist = |a: &f32, b: &f32| {
Expand All @@ -222,19 +243,26 @@ fn next_tile_spirit(
let (end, dist) = if let Some(end) = spirit.selected_end {
(
spirit.selected_end.as_ref().unwrap(),
path.distance.get(&end).unwrap_or(&MAX_DISTANCE),
path.distance.get(&end).unwrap_or(&std::f32::MAX),
)
} else {
path.distance
.iter()
.min_by(|(_, a), (_, b)| min_dist(a, b))
.unwrap_or((&tile_pos, &MAX_DISTANCE))
.unwrap_or((&tile_pos, &std::f32::MAX))
};

// If the selected end is different from the current one, reset the distance
if let Some(s_end) = spirit.selected_end {
if s_end != *end {
reset_distance = true;
}
}

// Add a random offset to the distance
let r = rand::thread_rng().gen_range(0.0..0.1);

(*pos, dist.clone() + r, Some(end.clone()), path.count)
(*pos, *dist + r, Some(*end), path.count)
})
.filter(|(pos, dist, _, count)| {
let is_start = if let Some(entity) = storage.get(pos) {
Expand All @@ -247,15 +275,17 @@ fn next_tile_spirit(
} else {
false
};
*count < MAX_SPIRITS_IN_TILE
&& *dist < *tile_distance
&& !is_start
&& !is_prev
let is_further = *dist < spirit.curr_distance;
*count < MAX_SPIRITS_IN_TILE && is_further && !is_start && !is_prev
})
.min_by(|(_, a, _, _), (_, b, _, _)| {
a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)
});

if reset_distance {
spirit.curr_distance = std::f32::MAX;
}

if next.is_none() {
spirit.prev_tile = None;
spirit.selected_end = None;
Expand Down
38 changes: 29 additions & 9 deletions src/tilemap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ impl Plugin for TilePlugin {
// ·········

#[derive(Resource)]
pub struct TileChanged(u32);
pub struct TileChanged(pub u32);

#[derive(Resource)]
pub struct LevelSize(pub TilemapSize);
Expand Down Expand Up @@ -169,7 +169,7 @@ fn select_tile(

for (map_size, grid_size, map_type, tile_storage, trans) in tilemap.iter() {
if let Some(tile_pos) = pos_to_tile(&mouse.0, map_size, grid_size, map_type, trans) {
if !tile_in_level(&tile_pos, &*level_size) {
if !tile_in_level(&tile_pos, &level_size) {
return;
}
if let Some(tile_entity) = tile_storage.get(&tile_pos) {
Expand Down Expand Up @@ -273,7 +273,7 @@ fn highlight_tile(

for (mut tex, mut color, mut flip, pos, selected, path, start, end) in tiles.iter_mut() {
*color = TileColor::default();
if !tile_in_level(pos, &*level_size) {
if !tile_in_level(pos, &level_size) {
*color = TileColor(Color::rgb(0., 0., 0.1));
} else if selected.is_some() {
*tex = TileTextureIndex(3);
Expand Down Expand Up @@ -301,12 +301,12 @@ fn pathfinding(
tilemap: Query<(&TilemapSize, &TileStorage)>,
mut start: Query<(&TilePos, &mut StartTile)>,
end: Query<&TilePos, With<EndTile>>,
mut paths: Query<&mut PathTile>,
mut paths: Query<(&TilePos, &mut PathTile)>,
) {
// Clear all path distances
for mut path in paths.iter_mut() {
// Clear all paths
/*for (_, mut path) in paths.iter_mut() {
path.distance.clear();
}
}*/

if let Ok((size, storage)) = tilemap.get_single() {
for end_pos in end.iter() {
Expand All @@ -320,19 +320,26 @@ fn pathfinding(
distance: 0.,
});
if let Some(entity) = storage.get(end_pos) {
if let Ok(mut path) = paths.get_mut(entity) {
if let Ok((_, mut path)) = paths.get_mut(entity) {
path.distance.insert(*end_pos, 0.);
}
}

// Start iterating through the queue
while let Some(PathfindingNode { pos, distance }) = open.pop() {
// If the path is a start, cut this branch
if let Some(entity) = storage.get(&pos) {
if start.get_mut(entity).is_ok() {
continue;
}
}

// Get the neighbouring tiles
let neighbours = get_neighbours(&pos, size);

for neighbour in neighbours {
if let Some(entity) = storage.get(&neighbour) {
if let Ok(mut path) = paths.get_mut(entity) {
if let Ok((_, mut path)) = paths.get_mut(entity) {
// Djikstra's algorithm to find the shortest path from each tile
let dist = distance + 1.;
if dist < *distances.get(&neighbour).unwrap_or(&std::f32::INFINITY) {
Expand All @@ -348,10 +355,23 @@ fn pathfinding(
}
}

// Set the end distance to 0
if let Some(entity) = storage.get(end_pos) {
if let Ok((_, mut path)) = paths.get_mut(entity) {
path.distance.insert(*end_pos, 0.);
}
}

// Check if there is a path from the end to the start
for (start_pos, mut start_tile) in start.iter_mut() {
if distances.contains_key(start_pos) {
start_tile.completed_once = true;
// Set begin distance to MAX
if let Some(entity) = storage.get(start_pos) {
if let Ok((_, mut path)) = paths.get_mut(entity) {
path.distance.insert(*start_pos, std::f32::INFINITY);
}
}
}
}
}
Expand Down

0 comments on commit 21b1327

Please sign in to comment.