Skip to content

Commit

Permalink
feat: make it more fun 🦕
Browse files Browse the repository at this point in the history
  • Loading branch information
eerii committed Dec 9, 2023
1 parent 55031d9 commit 61e871f
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 27 deletions.
4 changes: 2 additions & 2 deletions src/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ mod only_in_debug {
)
.with_style(Style {
position_type: PositionType::Absolute,
left: Val::Px(5.0),
top: Val::Px(5.0),
right: Val::Px(5.0),
bottom: Val::Px(5.0),
..default()
}),
FpsText,
Expand Down
5 changes: 5 additions & 0 deletions src/game.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ use bevy::{prelude::*, render::view::RenderLayers};

use crate::GameState;

// TODO: End screen (win/lose)
// TODO: Dialogues
// TODO: Add sprites
// TODO: Add animations

pub struct CharonPlugin;

impl Plugin for CharonPlugin {
Expand Down
67 changes: 67 additions & 0 deletions src/hud.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use bevy::prelude::*;

use crate::{game::GameScore, load::GameAssets, ui::*, GameState};

// ······
// Plugin
// ······

pub struct HudPlugin;

impl Plugin for HudPlugin {
fn build(&self, app: &mut App) {
app.add_systems(OnEnter(GameState::Play), init_hud)
.add_systems(Update, update_hud.run_if(in_state(GameState::Play)))
.add_systems(OnExit(GameState::Play), exit_hud);
}
}

// ··········
// Components
// ··········

#[derive(Component)]
struct ScoreText;

// ·······
// Systems
// ·······

fn init_hud(mut cmd: Commands, assets: Res<GameAssets>, mut node: Query<Entity, With<UiNode>>) {
// Main menu layout
if let Ok(node) = node.get_single_mut() {
if let Some(mut node) = cmd.get_entity(node) {
node.with_children(|parent| {
parent.spawn((
TextBundle::from_section(
"0",
TextStyle {
font: assets.font.clone(),
font_size: 24.0,
color: Color::WHITE,
},
)
.with_style(Style {
position_type: PositionType::Absolute,
right: Val::Px(5.0),
top: Val::Px(5.0),
..default()
}),
ScoreText,
));
});
}
}
}

fn update_hud(score: Res<GameScore>, mut text: Query<&mut Text, With<ScoreText>>) {
for mut text in text.iter_mut() {
text.sections[0].value = format!("{}", score.score);
}
}

fn exit_hud(mut cmd: Commands, score: Query<Entity, With<ScoreText>>) {
for score in score.iter() {
cmd.entity(score).despawn_recursive();
}
}
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ mod audio;
mod config;
mod debug;
mod game;
mod hud;
mod input;
mod load;
mod menu;
Expand Down Expand Up @@ -29,6 +30,7 @@ impl Plugin for GamePlugin {
load::LoadPlugin,
ui::UIPlugin,
menu::MenuPlugin,
hud::HudPlugin,
config::ConfigPlugin,
input::InputPlugin,
audio::AudioPlugin,
Expand Down
83 changes: 62 additions & 21 deletions src/spirits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ const SPIRIT_SPEED: f32 = 150.;
const SPIRIT_SIZE: f32 = 40.;
const MAX_SPIRITS_IN_TILE: u32 = 2;

const FUN_A: f32 = 0.75;
const FUN_B: f32 = 0.01;

// ······
// Plugin
// ······
Expand Down Expand Up @@ -54,6 +57,7 @@ impl Default for SpawnTimer {

#[derive(Component)]
pub struct Spirit {
prev_tile: Option<TilePos>,
curr_tile: TilePos,
next_tile: Option<TilePos>,
next_pos: Vec2,
Expand All @@ -63,6 +67,7 @@ pub struct Spirit {
impl Spirit {
pub fn new(curr_tile: TilePos, curr_pos: Vec2) -> Self {
Self {
prev_tile: Some(curr_tile),
curr_tile,
next_tile: None,
next_pos: curr_pos,
Expand Down Expand Up @@ -149,7 +154,14 @@ fn next_tile_spirit(
) {
// If it is on the current tile, continue
if spirit.next_tile.is_some() && spirit.curr_tile == tile_pos {
continue;
// Check if it is full
if let Some(entity) = storage.get(&tile_pos) {
if let Ok((_, path)) = paths.get(entity) {
if path.count < MAX_SPIRITS_IN_TILE {
continue;
}
}
}
}
spirit.curr_tile = tile_pos;

Expand All @@ -173,7 +185,7 @@ fn next_tile_spirit(

// Get the possible next tiles (they must be paths)
let neighbour_list = get_neighbours(&tile_pos, map_size);
let mut neighbours = neighbour_list
let neighbours = neighbour_list
.iter()
.filter_map(|pos| storage.get(pos))
.filter_map(|entity| {
Expand All @@ -182,46 +194,75 @@ fn next_tile_spirit(
} else {
None
}
})
.peekable();
});

let n = neighbours.clone().count();

// If there are no surrounding paths, check if the spirit is on a path
// If it is not, despawn
if neighbours.peek().is_none() {
if n == 0 {
if let Some(entity) = storage.get(&tile_pos) {
if paths.get(entity).is_err() {
cmd.get_entity(spirit_entity).unwrap().despawn_recursive();
}
}
}

let worst = if n > 1 {
Some(
neighbours
.clone()
.max_by(|(_, a), (_, b)| {
a.distance
.partial_cmp(&b.distance)
.unwrap_or(std::cmp::Ordering::Equal)
})
.unwrap(),
)
} else {
None
};

// 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 rng = rand::thread_rng();
let next = neighbours
.filter(|(_, path)| {
path.distance <= tile_distance && path.count < MAX_SPIRITS_IN_TILE
})
.map(|(pos, path)| {
(
*pos,
PathTile {
distance: path.distance + rng.gen_range(0.0..0.1),
..path.clone()
},
)
let mut dist = path.distance; // + path.count as f32 * 0.1;
if let Some((_, worst)) = worst {
if path.distance == worst.distance && spirit.prev_tile.is_none() {
dist += 90.;
}
}
if let Some(prev) = spirit.prev_tile {
if prev == *pos {
dist += 100.;
}
}
if n > 1 {
let r = rng.gen_range(
0.0..(MAX_SPIRITS_IN_TILE - path.count) as f32 * FUN_A + FUN_B,
);
dist -= r * r * r * r;
}

(*pos, dist, path.count)
})
.min_by(|(_, a), (_, b)| {
a.distance
.partial_cmp(&b.distance)
.unwrap_or(std::cmp::Ordering::Equal)
.filter(|(_, dist, count)| {
*count < MAX_SPIRITS_IN_TILE && *dist < tile_distance
})
.min_by(|(_, a, _), (_, b, _)| {
a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)
});

if next.is_none() {
spirit.prev_tile = None;
continue;
}

spirit.prev_tile = Some(spirit.curr_tile);
spirit.next_tile = Some(next.unwrap().0);
spirit.next_pos =
tile_to_pos(&spirit.next_tile.unwrap(), grid_size, map_type, map_trans);
Expand Down Expand Up @@ -273,10 +314,10 @@ fn spirit_collision(mut spirits: Query<(&mut Spirit, &Transform)>) {
fn integrate(mut spirits: Query<(&Spirit, &mut Transform)>, time: Res<Time>) {
for (spirit, mut trans) in spirits.iter_mut() {
// Ondulating motion
let offset = (time.elapsed_seconds() * 1.5).sin();
let offset = (time.elapsed_seconds() * 1.5).sin() * 0.05;
let cross = spirit.vel.perp();

// Update position
trans.translation += (spirit.vel + cross * offset * 0.1).extend(0.) * time.delta_seconds();
trans.translation += (spirit.vel + cross * offset).extend(0.) * time.delta_seconds();
}
}
35 changes: 31 additions & 4 deletions src/tilemap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ use crate::{
GameState,
};

// TODO: Automatically select path sprite (corner, straight, intersection)
// TODO: Multiple start/end points
// TODO: Bridges

const MAP_SIZE: TilemapSize = TilemapSize { x: 15, y: 10 };
const TILE_SIZE: TilemapTileSize = TilemapTileSize { x: 64., y: 64. };
Expand Down Expand Up @@ -131,7 +131,7 @@ fn init_tilemap(mut cmd: Commands, tile_assets: Res<TilemapAssets>) {
.insert((
EndTile,
PathTile {
distance: 0.,
distance: -std::f32::INFINITY,
..default()
},
));
Expand Down Expand Up @@ -310,7 +310,6 @@ fn pathfinding(
});
distances.insert(*end_pos, 0.);

// Iterate the pathfinding queue
while let Some(PathfindingNode { pos, distance }) = open.pop() {
// Get the neighbouring tiles
let neighbours = get_neighbours(&pos, size);
Expand All @@ -326,7 +325,7 @@ fn pathfinding(
pos: neighbour,
distance: dist,
});
path.distance = dist;
path.distance = dist * 2.;
}
}
}
Expand All @@ -336,6 +335,34 @@ fn pathfinding(
// Check if there is a path from the end to the start
if distances.contains_key(start_pos) {
start_tile.completed_once = true;

// Then do the algorithm in reverse to favour paths away from the start
open.push(PathfindingNode {
pos: *start_pos,
distance: 0.,
});
let mut distances = HashMap::new();
distances.insert(*start_pos, 0.);

while let Some(PathfindingNode { pos, distance }) = open.pop() {
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) {
let dist = distance + 1.;
if dist < *distances.get(&neighbour).unwrap_or(&f32::INFINITY) {
distances.insert(neighbour, dist);
open.push(PathfindingNode {
pos: neighbour,
distance: dist,
});
path.distance -= dist * 0.5;
}
}
}
}
}
}
}
}
Expand Down

0 comments on commit 61e871f

Please sign in to comment.