feat: Implement boss tractor beam and player capture
This commit introduces a new mechanic where boss enemies can use a tractor beam to capture the player. Key changes: - Bosses can now fire a tractor beam that targets the player. - If the player is caught in the beam for a certain duration, they are "captured". - Captured players are carried by the boss as it returns to its formation. - If the boss is destroyed while carrying a player, the player is freed. - If the player is captured, they lose a life and respawn. - Refactored player and enemy systems to handle the new capture logic and states. - Added GEMINI.md and CLAUDE.md to track assistant configurations.
This commit is contained in:
parent
d27d27bb5a
commit
efef8df102
5 changed files with 97 additions and 60 deletions
|
|
@ -1,4 +1,5 @@
|
|||
use bevy::prelude::*;
|
||||
use bevy::ecs::system::ParamSet;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::components::{Bullet, Captured, Enemy, Invincible, Player};
|
||||
|
|
@ -70,51 +71,40 @@ pub fn move_player(
|
|||
pub fn handle_captured_player(
|
||||
mut commands: Commands,
|
||||
time: Res<Time>,
|
||||
player_query: Query<(Entity, &Transform, &Captured)>,
|
||||
mut player_mut_query: Query<(&mut Transform, &mut Captured)>,
|
||||
enemy_query: Query<&Transform, With<Enemy>>,
|
||||
mut set: ParamSet<(
|
||||
Query<(Entity, &mut Transform, &mut Captured)>,
|
||||
Query<&Transform, (With<Enemy>, Without<Player>)>,
|
||||
)>,
|
||||
mut lives: ResMut<PlayerLives>,
|
||||
mut respawn_timer: ResMut<PlayerRespawnTimer>,
|
||||
mut next_state: ResMut<NextState<AppState>>,
|
||||
) {
|
||||
// First, collect data from all captured players
|
||||
let mut to_process = Vec::new();
|
||||
|
||||
for (entity, transform, captured) in player_query.iter() {
|
||||
// Check if the boss exists
|
||||
let boss_exists = enemy_query.get(captured.boss_entity).is_ok();
|
||||
let boss_pos = if boss_exists {
|
||||
enemy_query
|
||||
.get(captured.boss_entity)
|
||||
.map(|t| t.translation)
|
||||
.ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Create a copy of the timer to check if it would finish
|
||||
let mut timer_copy = captured.timer.clone();
|
||||
timer_copy.tick(time.delta());
|
||||
|
||||
to_process.push((
|
||||
entity,
|
||||
transform.translation,
|
||||
boss_pos,
|
||||
timer_copy.finished(),
|
||||
));
|
||||
// First, collect data about captured players and their bosses
|
||||
let mut captured_data = Vec::new();
|
||||
|
||||
// Get player data
|
||||
for (entity, transform, captured) in set.p0().iter() {
|
||||
captured_data.push((entity, transform.translation, captured.boss_entity, captured.timer.clone()));
|
||||
}
|
||||
|
||||
// Now process each player separately
|
||||
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) {
|
||||
// Tick the real timer
|
||||
|
||||
// Process each captured player
|
||||
for (player_entity, _player_pos, boss_entity, mut timer) in captured_data {
|
||||
// Check if the boss exists and get its position
|
||||
let boss_pos = set.p1().get(boss_entity).map(|t| t.translation).ok();
|
||||
|
||||
// Tick the timer
|
||||
timer.tick(time.delta());
|
||||
|
||||
// Update the player
|
||||
if let Ok((entity, mut transform, mut captured)) = set.p0().get_mut(player_entity) {
|
||||
// Update the actual timer
|
||||
captured.timer.tick(time.delta());
|
||||
|
||||
match boss_pos_opt {
|
||||
|
||||
match boss_pos {
|
||||
Some(boss_pos) => {
|
||||
// Boss exists, update player position
|
||||
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 = transform.translation.lerp(target_pos, 0.2);
|
||||
}
|
||||
None => {
|
||||
// Boss is gone, release player but lose a life
|
||||
|
|
@ -127,11 +117,12 @@ pub fn handle_captured_player(
|
|||
&mut next_state,
|
||||
entity,
|
||||
);
|
||||
continue; // Skip the rest of processing for this player
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// If capture duration expires, player escapes but loses a life
|
||||
if timer_would_finish || captured.timer.finished() {
|
||||
if captured.timer.finished() {
|
||||
println!("Player escaped from capture after timer expired!");
|
||||
commands.entity(entity).remove::<Captured>();
|
||||
lose_life_and_respawn(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue