mirror of
https://github.com/Xevion/Pac-Man.git
synced 2025-12-10 10:08:02 -06:00
134 lines
5.6 KiB
Rust
134 lines
5.6 KiB
Rust
use crate::entity::graph::{Edge, EdgePermissions};
|
|
use crate::error::{EntityError, GameError};
|
|
use crate::map::builder::Map;
|
|
use crate::systems::components::{DeltaTime, EntityType, Position, Velocity};
|
|
use bevy_ecs::event::EventWriter;
|
|
use bevy_ecs::system::{Query, Res};
|
|
|
|
fn can_traverse(entity_type: EntityType, edge: Edge) -> bool {
|
|
match entity_type {
|
|
EntityType::Player => matches!(edge.permissions, EdgePermissions::All),
|
|
EntityType::Ghost => matches!(edge.permissions, EdgePermissions::All | EdgePermissions::GhostsOnly),
|
|
_ => matches!(edge.permissions, EdgePermissions::All),
|
|
}
|
|
}
|
|
|
|
pub fn movement_system(
|
|
map: Res<Map>,
|
|
delta_time: Res<DeltaTime>,
|
|
mut entities: Query<(&mut Velocity, &mut Position, &EntityType)>,
|
|
mut errors: EventWriter<GameError>,
|
|
) {
|
|
for (mut velocity, mut position, entity_type) in entities.iter_mut() {
|
|
let distance = velocity.speed * 60.0 * delta_time.0;
|
|
|
|
// Decrement the remaining frames for the next direction
|
|
if let Some((direction, remaining)) = velocity.next_direction {
|
|
if remaining > 0 {
|
|
velocity.next_direction = Some((direction, remaining - 1));
|
|
} else {
|
|
velocity.next_direction = None;
|
|
}
|
|
}
|
|
|
|
match *position {
|
|
Position::AtNode(node_id) => {
|
|
// We're not moving, but a buffered direction is available.
|
|
if let Some((next_direction, _)) = velocity.next_direction {
|
|
if let Some(edge) = map.graph.find_edge_in_direction(node_id, next_direction) {
|
|
if can_traverse(*entity_type, edge) {
|
|
// Start moving in that direction
|
|
*position = Position::BetweenNodes {
|
|
from: node_id,
|
|
to: edge.target,
|
|
traversed: distance,
|
|
};
|
|
velocity.direction = next_direction;
|
|
velocity.next_direction = None;
|
|
}
|
|
} else {
|
|
errors.write(
|
|
EntityError::InvalidMovement(format!(
|
|
"No edge found in direction {:?} from node {}",
|
|
next_direction, node_id
|
|
))
|
|
.into(),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
Position::BetweenNodes { from, to, traversed } => {
|
|
// There is no point in any of the next logic if we don't travel at all
|
|
if distance <= 0.0 {
|
|
return;
|
|
}
|
|
|
|
let edge = map
|
|
.graph
|
|
.find_edge(from, to)
|
|
.ok_or_else(|| {
|
|
errors.write(
|
|
EntityError::InvalidMovement(format!(
|
|
"Inconsistent state: Traverser is on a non-existent edge from {} to {}.",
|
|
from, to
|
|
))
|
|
.into(),
|
|
);
|
|
return;
|
|
})
|
|
.unwrap();
|
|
|
|
let new_traversed = traversed + distance;
|
|
|
|
if new_traversed < edge.distance {
|
|
// Still on the same edge, just update the distance.
|
|
*position = Position::BetweenNodes {
|
|
from,
|
|
to,
|
|
traversed: new_traversed,
|
|
};
|
|
} else {
|
|
let overflow = new_traversed - edge.distance;
|
|
let mut moved = false;
|
|
|
|
// If we buffered a direction, try to find an edge in that direction
|
|
if let Some((next_dir, _)) = velocity.next_direction {
|
|
if let Some(edge) = map.graph.find_edge_in_direction(to, next_dir) {
|
|
if can_traverse(*entity_type, edge) {
|
|
*position = Position::BetweenNodes {
|
|
from: to,
|
|
to: edge.target,
|
|
traversed: overflow,
|
|
};
|
|
|
|
velocity.direction = next_dir; // Remember our new direction
|
|
velocity.next_direction = None; // Consume the buffered direction
|
|
moved = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we didn't move, try to continue in the current direction
|
|
if !moved {
|
|
if let Some(edge) = map.graph.find_edge_in_direction(to, velocity.direction) {
|
|
if can_traverse(*entity_type, edge) {
|
|
*position = Position::BetweenNodes {
|
|
from: to,
|
|
to: edge.target,
|
|
traversed: overflow,
|
|
};
|
|
} else {
|
|
*position = Position::AtNode(to);
|
|
velocity.next_direction = None;
|
|
}
|
|
} else {
|
|
*position = Position::AtNode(to);
|
|
velocity.next_direction = None;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|