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.
125 lines
4.6 KiB
Rust
125 lines
4.6 KiB
Rust
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 components::TractorBeam;
|
|
use constants::{PLAYER_RESPAWN_DELAY, STARTING_LIVES, WINDOW_HEIGHT, WINDOW_WIDTH};
|
|
use resources::{
|
|
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;
|
|
|
|
pub fn run() {
|
|
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()
|
|
}))
|
|
.init_state::<AppState>()
|
|
.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())
|
|
.insert_resource(RestartPressed::default())
|
|
.add_systems(Startup, setup)
|
|
.add_systems(
|
|
Update,
|
|
(
|
|
update_window_title,
|
|
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,
|
|
move_bullets,
|
|
check_bullet_collisions,
|
|
move_enemy_bullets,
|
|
check_enemy_bullet_player_collisions.run_if(player_vulnerable),
|
|
move_enemies,
|
|
enemy_shoot,
|
|
boss_capture_attack,
|
|
update_tractor_beam_visual.run_if(any_with_component::<TractorBeam>),
|
|
)
|
|
.run_if(in_state(AppState::Playing)),
|
|
)
|
|
.add_systems(
|
|
Update,
|
|
(
|
|
check_formation_complete,
|
|
trigger_attack_dives.run_if(is_formation_complete),
|
|
check_stage_clear,
|
|
)
|
|
.chain()
|
|
.run_if(in_state(AppState::Playing)),
|
|
)
|
|
.add_systems(Update, scroll_starfield)
|
|
.add_systems(Update, animate_explosion)
|
|
.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();
|
|
}
|