refactor: split game logic into library + binary entry point

Move the App setup and module declarations from src/main.rs into a new
src/lib.rs that exposes a single pub fn run(). src/main.rs becomes a
three-line entry point that calls bglga::run(). This is the standard
Bevy-book pattern and lets the game logic be consumed as a library
(e.g. for integration tests, headless tooling, or alternate entry
points) without duplicating the App wiring.

Also gate update_tractor_beam_visual with
run_if(any_with_component::<TractorBeam>) so the system only runs while
a boss has an active tractor beam, instead of every Update tick.
This commit is contained in:
Harald Hoyer 2026-05-06 20:54:09 +02:00
parent 506a775b0c
commit 68e3051f77
2 changed files with 126 additions and 133 deletions

View file

@ -1,135 +1,3 @@
use bevy::prelude::*;
use std::time::Duration;
pub mod components;
pub mod constants;
pub mod resources;
pub mod game_state;
pub mod player;
pub mod enemy;
pub mod bullet;
pub mod stage;
pub mod systems;
pub mod starfield;
use constants::{PLAYER_RESPAWN_DELAY, STARTING_LIVES, WINDOW_HEIGHT, WINDOW_WIDTH};
use resources::{ // Added StageConfigurations
AttackDiveTimer, CurrentStage, EnemySpawnTimer, FormationState, PlayerLives,
PlayerRespawnTimer, Score, StageConfigurations,
};
use game_state::{
cleanup_game_entities, cleanup_game_over_ui, setup_game_over_ui, AppState,
setup_start_menu_ui, cleanup_start_menu_ui, start_menu_button_system,
handle_restart_input, restart_game_system,
};
use resources::RestartPressed;
use player::{
check_player_enemy_collisions, manage_invincibility, move_player, player_shoot,
respawn_player, handle_captured_player,
};
use enemy::{
check_formation_complete, enemy_shoot, is_formation_complete, move_enemies, spawn_enemies,
trigger_attack_dives, boss_capture_attack, update_tractor_beam_visual,
};
use bullet::{
check_bullet_collisions, check_enemy_bullet_player_collisions, move_bullets,
move_enemy_bullets,
};
use stage::check_stage_clear;
use systems::{player_exists, player_vulnerable, setup, should_respawn_player, update_window_title, animate_explosion};
use starfield::scroll_starfield;
fn main() {
App::new()
.insert_resource(ClearColor(Color::rgb(0.0, 0.0, 0.1)))
.add_plugins(DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
title: "BGLGA".into(),
resolution: (WINDOW_WIDTH, WINDOW_HEIGHT).into(),
resizable: false,
..default()
}),
..default()
}))
// Add states
.init_state::<AppState>() // Changed from add_state to init_state
// Initialize game resources
.insert_resource(EnemySpawnTimer {
timer: Timer::new(Duration::from_secs_f32(1.0), TimerMode::Once),
})
.insert_resource(PlayerLives { count: STARTING_LIVES })
.insert_resource(PlayerRespawnTimer {
timer: Timer::new(
Duration::from_secs_f32(PLAYER_RESPAWN_DELAY),
TimerMode::Once,
),
})
.insert_resource(Score { value: 0 })
.insert_resource(CurrentStage {
number: 1,
waiting_for_clear: false,
})
.insert_resource(FormationState {
formation_complete: false,
total_spawned_this_stage: 0,
next_slot_index: 0,
})
.insert_resource(AttackDiveTimer {
timer: Timer::new(Duration::from_secs_f32(3.0), TimerMode::Once),
})
.insert_resource(StageConfigurations::default()) // Use default stages for now
.insert_resource(RestartPressed::default())
// Add startup systems
.add_systems(Startup, setup)
// Core game systems
.add_systems(
Update,
(
update_window_title,
// Enemy and player systems
spawn_enemies,
move_player.run_if(player_exists),
player_shoot.run_if(player_exists),
check_player_enemy_collisions.run_if(player_vulnerable),
respawn_player.run_if(should_respawn_player),
manage_invincibility,
handle_captured_player, // New system for handling captured player
// Bullet systems
move_bullets,
check_bullet_collisions,
move_enemy_bullets,
check_enemy_bullet_player_collisions.run_if(player_vulnerable),
// Enemy systems
move_enemies,
enemy_shoot, // Consider run_if attacking state? (Handled internally for now)
boss_capture_attack, // New system for boss tractor beam
update_tractor_beam_visual,
)
.run_if(in_state(AppState::Playing)),
)
// Formation/Attack/Stage systems (some need specific ordering or run conditions)
.add_systems(
Update,
(
check_formation_complete,
trigger_attack_dives.run_if(is_formation_complete), // Run only when formation is complete
check_stage_clear, // Uses world access, run separately
)
.chain() // Ensure these run in order if needed, check_formation first
.run_if(in_state(AppState::Playing)),
)
// Starfield runs in all states
.add_systems(Update, scroll_starfield)
// Explosion animation runs in all states
.add_systems(Update, animate_explosion)
// UI and state management systems
.add_systems(OnEnter(AppState::StartMenu), setup_start_menu_ui)
.add_systems(OnExit(AppState::StartMenu), cleanup_start_menu_ui)
.add_systems(Update, start_menu_button_system.run_if(in_state(AppState::StartMenu)))
.add_systems(OnEnter(AppState::GameOver), setup_game_over_ui)
.add_systems(Update, handle_restart_input.run_if(in_state(AppState::GameOver)))
.add_systems(Update, restart_game_system.run_if(in_state(AppState::GameOver)))
.add_systems(OnExit(AppState::Playing), cleanup_game_entities)
.add_systems(OnExit(AppState::GameOver), cleanup_game_over_ui)
.run();
bglga::run();
}