diff --git a/crates/camera/src/camera.rs b/crates/camera/src/camera.rs index 7969c5ce..fe8699b0 100644 --- a/crates/camera/src/camera.rs +++ b/crates/camera/src/camera.rs @@ -3,6 +3,7 @@ use std::f32::consts::FRAC_PI_2; use bevy::prelude::*; use de_conf::{CameraConf, Configuration}; use de_core::{ + cleanup::DespawnOnGameExit, events::ResendEventPlugin, projection::ToAltitude, stages::GameStage, @@ -313,10 +314,14 @@ fn setup(mut commands: Commands, conf: Res) { point: Vec3::ZERO, distance, }); - commands.spawn(Camera3dBundle { - transform: Transform::from_xyz(0.0, distance.into(), 0.0).looking_at(Vec3::ZERO, -Vec3::Z), - ..Default::default() - }); + commands.spawn(( + Camera3dBundle { + transform: Transform::from_xyz(0.0, distance.into(), 0.0) + .looking_at(Vec3::ZERO, -Vec3::Z), + ..Default::default() + }, + DespawnOnGameExit, + )); } fn cleanup(mut commands: Commands) { diff --git a/crates/combat/src/trail.rs b/crates/combat/src/trail.rs index 5b2bcf5c..094df7e1 100644 --- a/crates/combat/src/trail.rs +++ b/crates/combat/src/trail.rs @@ -12,6 +12,7 @@ use bevy::{ }, }; use de_core::{ + cleanup::DespawnOnGameExit, stages::GameStage, state::{AppState, GameState}, }; @@ -131,6 +132,7 @@ fn spawn( ..Default::default() }, Trail::default(), + DespawnOnGameExit, )); } } diff --git a/crates/controller/src/draft.rs b/crates/controller/src/draft.rs index 548bb8f5..52c372fa 100644 --- a/crates/controller/src/draft.rs +++ b/crates/controller/src/draft.rs @@ -1,5 +1,6 @@ use bevy::prelude::*; use de_core::{ + cleanup::DespawnOnGameExit, gconfig::GameConfig, objects::{BuildingType, ObjectType}, stages::GameStage, @@ -93,6 +94,7 @@ fn spawn( commands.spawn(( SpawnBundle::new(object_type, transform), game_config.player(), + DespawnOnGameExit, )); } } @@ -112,12 +114,15 @@ fn new_drafts( commands.entity(entity).despawn_recursive(); } - commands.spawn(DraftBundle::new( - event.building_type(), - Transform { - translation: event.point(), - ..Default::default() - }, + commands.spawn(( + DraftBundle::new( + event.building_type(), + Transform { + translation: event.point(), + ..Default::default() + }, + ), + DespawnOnGameExit, )); } diff --git a/crates/controller/src/hud/minimap/nodes.rs b/crates/controller/src/hud/minimap/nodes.rs index 47ff7ee7..5d8ed915 100644 --- a/crates/controller/src/hud/minimap/nodes.rs +++ b/crates/controller/src/hud/minimap/nodes.rs @@ -1,5 +1,5 @@ use bevy::{prelude::*, render::texture::TextureFormatPixelInfo}; -use de_core::{stages::GameStage, state::GameState}; +use de_core::{cleanup::DespawnOnGameExit, stages::GameStage, state::GameState}; use iyes_loopless::prelude::*; use wgpu_types::{Extent3d, TextureDimension, TextureFormat}; @@ -43,7 +43,7 @@ fn setup(mut commands: Commands, mut images: ResMut>) { background_color: HUD_COLOR.into(), ..default() }) - .insert(InteractionBlocker) + .insert((InteractionBlocker, DespawnOnGameExit)) .with_children(|parent| { parent .spawn(ImageBundle { diff --git a/crates/controller/src/hud/panel.rs b/crates/controller/src/hud/panel.rs index 6bd2bc72..86e592f4 100644 --- a/crates/controller/src/hud/panel.rs +++ b/crates/controller/src/hud/panel.rs @@ -1,5 +1,5 @@ use bevy::prelude::*; -use de_core::state::GameState; +use de_core::{cleanup::DespawnOnGameExit, state::GameState}; use iyes_loopless::prelude::*; use super::{interaction::InteractionBlocker, HUD_COLOR}; @@ -33,6 +33,7 @@ fn spawn_details(mut commands: Commands) { background_color: HUD_COLOR.into(), ..default() }, + DespawnOnGameExit, InteractionBlocker, )); } @@ -57,6 +58,7 @@ fn spawn_action_bar(mut commands: Commands) { background_color: HUD_COLOR.into(), ..default() }, + DespawnOnGameExit, InteractionBlocker, )); } diff --git a/crates/controller/src/hud/selection.rs b/crates/controller/src/hud/selection.rs index f1e5bd99..574ad2c5 100644 --- a/crates/controller/src/hud/selection.rs +++ b/crates/controller/src/hud/selection.rs @@ -1,5 +1,7 @@ use bevy::prelude::*; -use de_core::{screengeom::ScreenRect, stages::GameStage, state::AppState}; +use de_core::{ + cleanup::DespawnOnGameExit, screengeom::ScreenRect, stages::GameStage, state::AppState, +}; use iyes_loopless::prelude::*; const SELECTION_BOX_COLOR: Color = Color::rgba(0., 0.5, 0.8, 0.2); @@ -67,6 +69,7 @@ fn process_events( ..Default::default() }, SelectionBox, + DespawnOnGameExit, )); } } diff --git a/crates/core/src/cleanup.rs b/crates/core/src/cleanup.rs new file mode 100644 index 00000000..17e115e6 --- /dev/null +++ b/crates/core/src/cleanup.rs @@ -0,0 +1,23 @@ +use bevy::prelude::*; +use iyes_loopless::prelude::*; + +use crate::state::AppState; + +pub(crate) struct CleanupPlugin; + +impl Plugin for CleanupPlugin { + fn build(&self, app: &mut App) { + app.add_exit_system(AppState::InGame, cleanup); + } +} + +/// Mark all entities which should be recursively despawned after the game is +/// exited with this component. +#[derive(Component)] +pub struct DespawnOnGameExit; + +fn cleanup(mut commands: Commands, query: Query>) { + for entity in query.iter() { + commands.entity(entity).despawn_recursive(); + } +} diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 19ae6996..a381d82d 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -1,10 +1,12 @@ use bevy::{app::PluginGroupBuilder, prelude::PluginGroup}; +use cleanup::CleanupPlugin; use iyes_progress::prelude::*; use stages::StagesPlugin; use state::{AppState, GameState}; use visibility::VisibilityPlugin; pub mod assets; +pub mod cleanup; mod errors; pub mod events; pub mod frustum; @@ -26,5 +28,6 @@ impl PluginGroup for CorePluginGroup { .add(ProgressPlugin::new(GameState::Loading).continue_to(GameState::Playing)) .add(StagesPlugin) .add(VisibilityPlugin) + .add(CleanupPlugin) } } diff --git a/crates/loader/src/map.rs b/crates/loader/src/map.rs index fc3555f8..56f0acd1 100644 --- a/crates/loader/src/map.rs +++ b/crates/loader/src/map.rs @@ -5,6 +5,7 @@ use bevy::{ use de_camera::MoveFocusEvent; use de_core::{ assets::asset_path, + cleanup::DespawnOnGameExit, gconfig::GameConfig, log_full_error, objects::{ActiveObjectType, BuildingType, ObjectType}, @@ -102,7 +103,10 @@ fn spawn_map( } setup_light(&mut commands); - commands.spawn(TerrainBundle::flat(map.metadata().bounds())); + commands.spawn(( + TerrainBundle::flat(map.metadata().bounds()), + DespawnOnGameExit, + )); for object in map.content().objects() { let mut entity_commands = commands.spawn_empty(); @@ -113,9 +117,9 @@ fn spawn_map( } InnerObject::Inactive(object) => ObjectType::Inactive(object.object_type()), }; - entity_commands.insert(SpawnBundle::new( - object_type, - object.placement().to_transform(), + entity_commands.insert(( + SpawnBundle::new(object_type, object.placement().to_transform()), + DespawnOnGameExit, )); } @@ -131,13 +135,16 @@ fn setup_light(commands: &mut Commands) { let mut transform = Transform::IDENTITY; transform.look_at(Vec3::new(1., -1., 0.), Vec3::new(1., 1., 0.)); - commands.spawn(DirectionalLightBundle { - directional_light: DirectionalLight { - color: Color::WHITE, - illuminance: 30000., + commands.spawn(( + DirectionalLightBundle { + directional_light: DirectionalLight { + color: Color::WHITE, + illuminance: 30000., + ..Default::default() + }, + transform, ..Default::default() }, - transform, - ..Default::default() - }); + DespawnOnGameExit, + )); }