refactor: clean up formatting and improve readability in components and player logic

This commit is contained in:
Harald Hoyer 2025-04-16 08:46:12 +02:00
parent 008f9cc24a
commit d27d27bb5a
3 changed files with 47 additions and 23 deletions

View file

@ -39,10 +39,10 @@ pub struct Captured {
// New component for the tractor beam visual effect // New component for the tractor beam visual effect
#[derive(Component)] #[derive(Component)]
pub struct TractorBeam { pub struct TractorBeam {
pub target: Entity, // The entity being targeted (usually player) pub target: Entity, // The entity being targeted (usually player)
pub timer: Timer, // How long the beam lasts pub timer: Timer, // How long the beam lasts
pub width: f32, // Visual width of the beam pub width: f32, // Visual width of the beam
pub active: bool, // Whether the beam is currently active pub active: bool, // Whether the beam is currently active
} }
#[derive(Component)] #[derive(Component)]
@ -53,8 +53,8 @@ pub struct FormationTarget {
// Enum defining different ways an enemy can attack // Enum defining different ways an enemy can attack
#[derive(Component, Clone, Copy, PartialEq, Debug)] #[derive(Component, Clone, Copy, PartialEq, Debug)]
pub enum AttackPattern { pub enum AttackPattern {
SwoopDive, // Original pattern: dive towards center, then off screen SwoopDive, // Original pattern: dive towards center, then off screen
DirectDive, // Dive straight down DirectDive, // Dive straight down
Kamikaze(Vec3), // Dive towards a specific target location (e.g., player's last known position) - Needs target Vec3 Kamikaze(Vec3), // Dive towards a specific target location (e.g., player's last known position) - Needs target Vec3
CaptureBeam, // New pattern: Boss dives and attempts to capture the player with a tractor beam CaptureBeam, // New pattern: Boss dives and attempts to capture the player with a tractor beam
// Add more patterns later (e.g., FigureEight, Looping) // Add more patterns later (e.g., FigureEight, Looping)

View file

@ -31,7 +31,8 @@ pub const BULLET_ENEMY_COLLISION_THRESHOLD: f32 = (BULLET_SIZE.x + ENEMY_SIZE.x)
// 22.5 // 22.5
pub const PLAYER_ENEMY_COLLISION_THRESHOLD: f32 = (PLAYER_SIZE.x + ENEMY_SIZE.x) * 0.5; pub const PLAYER_ENEMY_COLLISION_THRESHOLD: f32 = (PLAYER_SIZE.x + ENEMY_SIZE.x) * 0.5;
// 35.0 // 35.0
pub const ENEMY_BULLET_PLAYER_COLLISION_THRESHOLD: f32 = (ENEMY_BULLET_SIZE.x + PLAYER_SIZE.x) * 0.5; pub const ENEMY_BULLET_PLAYER_COLLISION_THRESHOLD: f32 =
(ENEMY_BULLET_SIZE.x + PLAYER_SIZE.x) * 0.5;
// Tractor beam constants // Tractor beam constants
pub const TRACTOR_BEAM_WIDTH: f32 = 20.0; pub const TRACTOR_BEAM_WIDTH: f32 = 20.0;

View file

@ -1,7 +1,7 @@
use bevy::prelude::*; use bevy::prelude::*;
use std::time::Duration; use std::time::Duration;
use crate::components::{Bullet, Enemy, Invincible, Player, Captured}; use crate::components::{Bullet, Captured, Enemy, Invincible, Player};
use crate::constants::{ use crate::constants::{
BULLET_SIZE, PLAYER_ENEMY_COLLISION_THRESHOLD, PLAYER_INVINCIBILITY_DURATION, PLAYER_SIZE, BULLET_SIZE, PLAYER_ENEMY_COLLISION_THRESHOLD, PLAYER_INVINCIBILITY_DURATION, PLAYER_SIZE,
PLAYER_SPEED, WINDOW_HEIGHT, WINDOW_WIDTH, PLAYER_SPEED, WINDOW_HEIGHT, WINDOW_WIDTH,
@ -79,48 +79,68 @@ pub fn handle_captured_player(
) { ) {
// First, collect data from all captured players // First, collect data from all captured players
let mut to_process = Vec::new(); let mut to_process = Vec::new();
for (entity, transform, captured) in player_query.iter() { for (entity, transform, captured) in player_query.iter() {
// Check if the boss exists // Check if the boss exists
let boss_exists = enemy_query.get(captured.boss_entity).is_ok(); let boss_exists = enemy_query.get(captured.boss_entity).is_ok();
let boss_pos = if boss_exists { let boss_pos = if boss_exists {
enemy_query.get(captured.boss_entity).map(|t| t.translation).ok() enemy_query
.get(captured.boss_entity)
.map(|t| t.translation)
.ok()
} else { } else {
None None
}; };
// Create a copy of the timer to check if it would finish // Create a copy of the timer to check if it would finish
let mut timer_copy = captured.timer.clone(); let mut timer_copy = captured.timer.clone();
timer_copy.tick(time.delta()); timer_copy.tick(time.delta());
to_process.push((entity, transform.translation, boss_pos, timer_copy.finished())); to_process.push((
entity,
transform.translation,
boss_pos,
timer_copy.finished(),
));
} }
// Now process each player separately // Now process each player separately
for (entity, current_pos, boss_pos_opt, timer_would_finish) in to_process { for (entity, current_pos, boss_pos_opt, timer_would_finish) in to_process {
if let Ok((mut transform, mut captured)) = player_mut_query.get_mut(entity) { if let Ok((mut transform, mut captured)) = player_mut_query.get_mut(entity) {
// Tick the real timer // Tick the real timer
captured.timer.tick(time.delta()); captured.timer.tick(time.delta());
match boss_pos_opt { match boss_pos_opt {
Some(boss_pos) => { Some(boss_pos) => {
// Boss exists, update player position // Boss exists, update player position
let target_pos = boss_pos - Vec3::new(0.0, PLAYER_SIZE.y + 10.0, 0.0); let target_pos = boss_pos - Vec3::new(0.0, PLAYER_SIZE.y + 10.0, 0.0);
transform.translation = current_pos.lerp(target_pos, 0.2); transform.translation = current_pos.lerp(target_pos, 0.2);
}, }
None => { None => {
// Boss is gone, release player but lose a life // Boss is gone, release player but lose a life
println!("Boss is gone, releasing captured player!"); println!("Boss is gone, releasing captured player!");
commands.entity(entity).remove::<Captured>(); commands.entity(entity).remove::<Captured>();
lose_life_and_respawn(&mut commands, &mut lives, &mut respawn_timer, &mut next_state, entity); lose_life_and_respawn(
&mut commands,
&mut lives,
&mut respawn_timer,
&mut next_state,
entity,
);
} }
} }
// If capture duration expires, player escapes but loses a life // If capture duration expires, player escapes but loses a life
if timer_would_finish || captured.timer.finished() { if timer_would_finish || captured.timer.finished() {
println!("Player escaped from capture after timer expired!"); println!("Player escaped from capture after timer expired!");
commands.entity(entity).remove::<Captured>(); commands.entity(entity).remove::<Captured>();
lose_life_and_respawn(&mut commands, &mut lives, &mut respawn_timer, &mut next_state, entity); lose_life_and_respawn(
&mut commands,
&mut lives,
&mut respawn_timer,
&mut next_state,
entity,
);
} }
} }
} }
@ -137,10 +157,10 @@ fn lose_life_and_respawn(
// Lose a life // Lose a life
lives.count = lives.count.saturating_sub(1); lives.count = lives.count.saturating_sub(1);
println!("Lives remaining: {}", lives.count); println!("Lives remaining: {}", lives.count);
// Destroy player // Destroy player
commands.entity(player_entity).despawn(); commands.entity(player_entity).despawn();
if lives.count > 0 { if lives.count > 0 {
respawn_timer.timer.reset(); respawn_timer.timer.reset();
respawn_timer.timer.unpause(); respawn_timer.timer.unpause();
@ -192,7 +212,10 @@ pub fn check_player_enemy_collisions(
mut respawn_timer: ResMut<PlayerRespawnTimer>, mut respawn_timer: ResMut<PlayerRespawnTimer>,
mut next_state: ResMut<NextState<AppState>>, // Resource to change state mut next_state: ResMut<NextState<AppState>>, // Resource to change state
// Query player without Invincible component - relies on run_if condition too // Query player without Invincible component - relies on run_if condition too
player_query: Query<(Entity, &Transform), (With<Player>, Without<Invincible>, Without<Captured>)>, // Don't check collisions for captured players player_query: Query<
(Entity, &Transform),
(With<Player>, Without<Invincible>, Without<Captured>),
>, // Don't check collisions for captured players
enemy_query: Query<(Entity, &Transform), With<Enemy>>, enemy_query: Query<(Entity, &Transform), With<Enemy>>,
) { ) {
// This system only runs if player exists and is not invincible, due to run_if // This system only runs if player exists and is not invincible, due to run_if
@ -271,4 +294,4 @@ pub fn manage_invincibility(
} }
} }
} }
} }