Skip to content
Snippets Groups Projects
Verified Commit 466f4db8 authored by Louis's avatar Louis :fire:
Browse files

Support clean up when exiting game, add debug snapshot options

parent 378f2fc0
No related branches found
No related tags found
No related merge requests found
Pipeline #243 failed with stages
in 4 minutes and 37 seconds
Showing
with 312 additions and 72 deletions
......@@ -578,6 +578,17 @@ dependencies = [
"radsort",
]
[[package]]
name = "bevy_prototype_lyon"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c063aff10ca720d5cadf9cf669800eff2166f6f28cf7f20648ece1c3bdb2442"
dependencies = [
"bevy",
"lyon_tessellation",
"svgtypes",
]
[[package]]
name = "bevy_ptr"
version = "0.9.1"
......@@ -1528,6 +1539,15 @@ dependencies = [
"miniz_oxide",
]
[[package]]
name = "float_next_after"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fc612c5837986b7104a87a0df74a5460931f1c5274be12f8d0f40aa2f30d632"
dependencies = [
"num-traits",
]
[[package]]
name = "flume"
version = "0.10.14"
......@@ -1620,6 +1640,7 @@ dependencies = [
"anyhow",
"bevy",
"bevy_ecs_tilemap",
"bevy_prototype_lyon",
"bevy_tweening",
"directories",
"fake",
......@@ -2202,6 +2223,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "libm"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb"
[[package]]
name = "libudev-sys"
version = "0.1.4"
......@@ -2231,6 +2258,38 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "lyon_geom"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5237e77afe5a112d5ce8060e3473e78b09b5f8cea722a251dcafa1254711f440"
dependencies = [
"arrayvec",
"euclid",
"num-traits",
]
[[package]]
name = "lyon_path"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b2a6c0d4a27bb3fe8b747184caf79a57b8bd2caeee49c0f9e59d068d30f7a0d"
dependencies = [
"lyon_geom",
"num-traits",
]
[[package]]
name = "lyon_tessellation"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "583cfbb96beba3d5843b92a3a4db89c6dc6ba731ae80e28c80bb587636ca28d9"
dependencies = [
"float_next_after",
"lyon_path",
"thiserror",
]
[[package]]
name = "lzma-sys"
version = "0.1.20"
......@@ -2692,6 +2751,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
"libm",
]
[[package]]
......@@ -3353,6 +3413,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3"
[[package]]
name = "siphasher"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
[[package]]
name = "slab"
version = "0.4.7"
......@@ -3485,6 +3551,15 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fb1df15f412ee2e9dfc1c504260fa695c1c3f10fe9f4a6ee2d2184d7d6450e2"
[[package]]
name = "svgtypes"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22975e8a2bac6a76bb54f898a6b18764633b00e780330f0b689f65afb3975564"
dependencies = [
"siphasher",
]
[[package]]
name = "symphonia"
version = "0.5.1"
......
......@@ -33,6 +33,7 @@ kayak_ui.workspace = true
kayak_font.workspace = true
fake = "2.5.0"
directories = "4.0.1"
bevy_prototype_lyon = "0.7.2"
#remote_events = { git = "https://lab.lcr.gr/microhacks/micro-bevy-remote-events.git", rev = "be0c6b43a73e4c5e7ece20797e3d6f59340147b4"}
......
use bevy::prelude::*;
#[derive(Component, Copy, Clone)]
pub struct AlignedBackground;
pub fn adjust_aligned_backgrounds(
mut query: Query<(&mut Transform, &Handle<Image>), With<AlignedBackground>>,
windows: Res<Windows>,
images: Res<Assets<Image>>,
) {
if let Some(window) = windows.get_primary() {
let width = window.width();
let height = window.height();
for (mut transform, handle) in &mut query {
if let Some(image) = images.get(handle) {
if width > height {
let scale = image.texture_descriptor.size.width as f32 / width;
transform.scale = Vec3::splat(scale);
} else {
let scale = image.texture_descriptor.size.height as f32 / height;
transform.scale = Vec3::splat(scale);
}
}
}
}
}
pub struct GraphicsPlugin;
impl Plugin for GraphicsPlugin {
fn build(&self, app: &mut App) {
app.add_system(adjust_aligned_backgrounds);
}
}
#![feature(result_option_inspect)]
pub mod assets;
pub mod graphics;
pub mod multiplayer;
pub mod persistance;
pub mod splash_screen;
......
use bevy::prelude::*;
use bevy_ecs_tilemap::TilemapPlugin;
use game_core::assets::AssetHandles;
use game_core::system::flow::AppState;
use game_core::system::resources::InitAppPlugins;
use game_core::ui::AdventUIPlugins;
use iyes_loopless::prelude::AppLooplessStateExt;
use micro_musicbox::CombinedAudioPlugins;
fn main() {
App::new()
.add_loopless_state(AppState::Preload)
.add_plugins(InitAppPlugins)
.add_plugins(game_core::system::resources::InitAppPlugins)
.add_plugin(game_core::assets::AssetsPlugin)
.add_plugins(CombinedAudioPlugins::<AssetHandles>::new())
.add_plugins(micro_musicbox::CombinedAudioPlugins::<AssetHandles>::new())
.add_plugin(game_core::splash_screen::SplashScreenPlugin)
.add_plugin(game_core::system::camera::CameraManagementPlugin)
.add_plugin(game_core::states::StatesPlugin)
.add_plugin(micro_asset_io::MicroAssetIOPlugin)
.add_plugin(bevy_tweening::TweeningPlugin)
.add_plugin(game_core::world::WorldPlugin)
.add_plugin(TilemapPlugin)
.add_plugin(game_core::graphics::GraphicsPlugin)
.add_plugin(bevy_ecs_tilemap::TilemapPlugin)
.add_plugins(micro_banimate::BanimatePluginGroup)
.add_plugins(AdventUIPlugins)
.add_plugins(game_core::ui::AdventUIPlugins)
.add_plugin(game_core::persistance::PersistencePlugin)
.add_plugin(bevy_prototype_lyon::plugin::ShapePlugin)
.run();
}
......@@ -142,7 +142,6 @@ pub fn handle_load_event(mut commands: Commands, mut events: ResMut<Events<LoadF
match std::fs::File::open(path) {
Ok(file) => match serde_json::from_reader::<File, PersistenceState>(file) {
Ok(value) => {
log::info!("GET PER: {:?}", &value);
commands.insert_resource(PendingLoadState(value));
commands.insert_resource(NextState(AppState::InGame));
}
......
use std::collections::HashSet;
use std::hash::{Hash, Hasher};
use std::ops::Not;
use std::os::linux::raw::stat;
use bevy::input::Input;
use bevy::prelude::{
Color, Commands, Component, Entity, EventWriter, KeyCode, Query, Rect, Res, ResMut, Resource,
Transform, With,
};
use bevy_prototype_lyon::draw::DrawMode;
use bevy_prototype_lyon::prelude::{FillMode, GeometryBuilder, RectangleOrigin, StrokeMode};
use bevy_prototype_lyon::shapes;
use iyes_loopless::state::NextState;
use crate::persistance::{LoadFileEvent, SaveFileEvent};
use crate::system::flow::AppState;
use crate::world::WorldZones;
#[derive(Debug, Copy, Clone, Resource, Ord, PartialOrd, Eq, PartialEq)]
pub enum DebugStatus {
On,
Off,
}
impl DebugStatus {
pub fn inverse(&self) -> Self {
match self {
Self::Off => Self::On,
Self::On => Self::Off,
}
}
}
impl Not for DebugStatus {
type Output = DebugStatus;
fn not(self) -> Self::Output {
self.inverse()
}
}
pub fn toggle_debug_mode(input: Res<Input<KeyCode>>, mut status: ResMut<DebugStatus>) {
if input.just_released(KeyCode::F5) {
*status = !*status;
}
}
const DEBUG_SNAPSHOT: &str = "debug_snapshot.json";
pub fn handle_debug_snapshots(
mut commands: Commands,
input: Res<Input<KeyCode>>,
mut save_events: EventWriter<SaveFileEvent>,
mut load_events: EventWriter<LoadFileEvent>,
) {
if input.just_released(KeyCode::F6) {
save_events.send(SaveFileEvent {
filename: Some(DEBUG_SNAPSHOT.to_string()),
});
} else if input.just_released(KeyCode::F7) {
commands.insert_resource(NextState(AppState::Menu));
load_events.send(LoadFileEvent {
filename: Some(DEBUG_SNAPSHOT.to_string()),
});
}
}
#[derive(Component)]
pub struct ZoneShape;
#[derive(Component, Copy, Clone)]
pub struct ZoneData(Rect);
impl Eq for ZoneData {}
impl PartialEq for ZoneData {
fn eq(&self, other: &Self) -> bool {
other.0 == self.0
}
}
impl Hash for ZoneData {
fn hash<H: Hasher>(&self, state: &mut H) {
state.write_i128((self.0.min.x * 1000.0) as i128);
state.write_i128((self.0.min.y * 1000.0) as i128);
state.write_i128((self.0.max.x * 1000.0) as i128);
state.write_i128((self.0.max.y * 1000.0) as i128);
}
}
pub fn show_zone_shapes(
mut commands: Commands,
query: Query<(Entity, &ZoneData), With<ZoneShape>>,
debug: Res<DebugStatus>,
zones: Res<WorldZones>,
) {
if *debug == DebugStatus::Off {
for (entity, _) in &query {
commands.entity(entity).despawn();
}
} else {
let mut handled = HashSet::with_capacity(zones.0.len());
let map_zones = zones
.0
.iter()
.map(|zone| ZoneData(zone.area))
.collect::<HashSet<ZoneData>>();
for (entity, zone) in &query {
if map_zones.contains(zone) {
handled.insert(*zone);
} else {
commands.entity(entity).despawn();
}
}
for encounter in zones.0.iter() {
let data = ZoneData(encounter.area);
if !handled.contains(&data) {
commands.spawn(GeometryBuilder::build_as(
&shapes::Rectangle {
origin: RectangleOrigin::Center,
extents: data.0.half_size(),
},
DrawMode::Outlined {
fill_mode: FillMode::color(Color::rgba(0.54, 0.23, 0.74, 0.25)),
outline_mode: StrokeMode::color(Color::rgb(0.54, 0.23, 0.74)),
},
Transform::from_translation(data.0.center().extend(900.0)),
));
}
}
}
}
use std::time::Duration;
use bevy::prelude::Component;
use bevy::input::Input;
use bevy::prelude::{Commands, Component, KeyCode, Res};
use iyes_loopless::state::NextState;
use micro_musicbox::prelude::{AudioEasing, AudioTween, MusicBox};
use crate::assets::AssetHandles;
use crate::system::flow::AppState;
#[derive(Component, Debug, Default, Copy, Clone)]
pub struct Player;
......@@ -14,3 +17,13 @@ pub fn on_enter_game(mut musicbox: MusicBox<AssetHandles>) {
AudioTween::new(Duration::from_secs(2), AudioEasing::Linear),
);
}
pub fn on_leave_game(mut musicbox: MusicBox<AssetHandles>) {
musicbox.fade_out_music(AudioTween::new(Duration::from_secs(2), AudioEasing::Linear));
}
pub fn quit_game(mut commands: Commands, input: Res<Input<KeyCode>>) {
if input.just_released(KeyCode::Escape) {
commands.insert_resource(NextState(AppState::Menu));
}
}
......@@ -4,6 +4,7 @@ use iyes_loopless::prelude::{AppLooplessStateExt, ConditionSet};
use crate::states::menu_state::go_to_game;
use crate::system::flow::AppState;
mod debug_state;
mod game_state;
mod menu_state;
......@@ -13,13 +14,32 @@ impl Plugin for StatesPlugin {
app.add_enter_system(AppState::Menu, menu_state::spawn_menu_entities)
.add_exit_system(AppState::Menu, menu_state::despawn_menu_entities)
.add_enter_system(AppState::InGame, game_state::on_enter_game)
.add_exit_system(AppState::InGame, game_state::on_leave_game)
.add_system_set(
ConditionSet::new()
.run_in_state(AppState::Menu)
.with_system(go_to_game)
.into(),
)
.add_system_set(
ConditionSet::new()
.run_in_state(AppState::InGame)
.with_system(quit_game)
.into(),
)
.insert_resource(debug_state::DebugStatus::Off)
.add_system(debug_state::toggle_debug_mode)
.add_system_set(
ConditionSet::new()
.run_in_state(AppState::InGame)
.run_if_resource_equals(debug_state::DebugStatus::On)
.with_system(debug_state::handle_debug_snapshots)
.with_system(debug_state::show_zone_shapes)
.into(),
);
}
}
pub use game_state::Player;
use crate::states::game_state::quit_game;
......@@ -143,3 +143,7 @@ where
serde_json::from_value(serde_json::to_value(&self).unwrap()).unwrap()
}
}
pub fn format_ui_distance(distance: f32) -> String {
format!("{:.0} km", distance / 10.0)
}
......@@ -2,6 +2,7 @@ use bevy::prelude::*;
use kayak_ui::prelude::*;
use kayak_ui::widgets::{ElementBundle, KayakAppBundle, TextProps, TextWidgetBundle};
use crate::system::utilities::format_ui_distance;
use crate::ui::components::*;
use crate::ui::prelude::{px, stretch, value};
use crate::ui::sync::UITravelInfo;
......@@ -30,25 +31,31 @@ pub fn render_game_panels(
left: stretch(1.0),
right: stretch(1.0),
bottom: px(50.0),
height: px(60.0),
height: px(50.0),
width: stretch(0.6),
max_width: px(200.0),
padding: value(Edge::all(Units::Stretch(1.0))),
..Default::default()
};
let show_distance_panel = !encounter_state.is_in_encounter()
&& !ui_data.is_in_town
&& ui_data.distance_remaining > 0.1;
rsx! {
<ElementBundle>
{ if !encounter_state.is_in_encounter() && ui_data.distance_remaining > 0.1 {
{ if show_distance_panel {
constructor! {
<PanelWidget styles={distance_style}>
<TextWidgetBundle
text={TextProps {
content: format!("{:.2}KM", ui_data.distance_remaining),
size: 48.0,
content: format_ui_distance(ui_data.distance_remaining),
size: 40.0,
..Default::default()
}}
styles={KStyle {
bottom: px(7.5),
color: value(Color::BLACK),
bottom: px(6.0),
..Default::default()
}}
/>
......
......@@ -39,7 +39,7 @@ pub fn render_encounter_panel(
height: pct(80.0),
min_width: px(400.0),
min_height: px(300.0),
max_width: px(850.0),
max_width: px(600.0),
max_height: px(550.0),
top: stretch(1.0),
left: stretch(1.0),
......
......@@ -7,6 +7,7 @@ use kayak_ui::widgets::{
use crate::assets::AssetHandles;
use crate::states::Player;
use crate::system::utilities::format_ui_distance;
use crate::ui::components::*;
use crate::ui::prelude::*;
use crate::ui::sync::UITravelInfo;
......@@ -98,7 +99,7 @@ pub fn render_transit_panel(
..Default::default()
}
}
props={ButtonWidgetProps::text(format!("{}: {:.2}KM", &place, distance), 28.0)}
props={ButtonWidgetProps::text(format!("{}: {}", &place, format_ui_distance(*distance)), 28.0)}
on_event={buysell_button_factory(place.clone())}
/>
}
......
......@@ -7,6 +7,7 @@ use kayak_ui::widgets::{
use crate::assets::AssetHandles;
use crate::states::Player;
use crate::system::utilities::format_ui_distance;
use crate::ui::components::*;
use crate::ui::prelude::*;
use crate::ui::sync::UITravelInfo;
......@@ -98,7 +99,7 @@ pub fn render_transit_panel(
..Default::default()
}
}
props={ButtonWidgetProps::text(format!("{}: {:.2}KM", &place, distance), 28.0)}
props={ButtonWidgetProps::text(format!("{}: {}", &place, format_ui_distance(*distance)), 28.0)}
on_event={transit_button_factory(place.clone())}
/>
}
......
......@@ -23,8 +23,12 @@ impl Plugin for WorldPlugin {
.init_resource::<EncounterState>()
.add_event::<PopulateWorldEvent>()
.add_enter_system(AppState::InGame, |mut commands: Commands| {
commands.insert_resource(ActiveLevel::new("Grantswaith"));
commands.insert_resource(ActiveLevel {
map: String::from("Grantswaith"),
dirty: true,
});
})
.add_enter_system(AppState::Menu, spawning::clean_game_state)
.add_system_set(
ConditionSet::new()
.run_in_state(AppState::InGame)
......
......@@ -12,7 +12,7 @@ use crate::world::encounters::WorldZones;
use crate::world::towns::{CurrentResidence, TownPaths};
use crate::world::utils::{grid_to_px, px_to_grid, ActiveLevel, WorldLinked, TILE_SCALE_F32};
use crate::world::world_query::MapQuery;
use crate::world::{TravelPath, TravelTarget};
use crate::world::{EncounterState, HungerState, TradingState, TravelPath, TravelTarget};
#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
pub struct PopulateWorldEvent;
......@@ -65,7 +65,7 @@ pub fn spawn_world_data(
return;
}
let map_entity = commands.spawn_empty().id();
let map_entity = commands.spawn(WorldLinked).id();
let mut storage = TileStorage::empty(tilemap_size);
layer.for_each_tile(|x, y, tile| {
......@@ -121,8 +121,6 @@ pub fn spawn_world_data(
let grid_size = tilemap_tile_size.into();
let map_type = TilemapType::Square;
log::info!("Spawning tilemap");
let bg = level
.level_bg_color
.clone()
......@@ -140,19 +138,16 @@ pub fn spawn_world_data(
}
}
commands.entity(map_entity).insert((
TilemapBundle {
grid_size,
map_type,
size: tilemap_size,
storage,
texture: TilemapTexture::Single(assets.image("overworld")),
tile_size: tilemap_tile_size,
transform: Transform::from_xyz(0.0, 0.0, 5.0 + layer.get_z_delta()), // get_tilemap_center_transform(&tilemap_size, &grid_size, 5.0),
..Default::default()
},
WorldLinked,
));
commands.entity(map_entity).insert((TilemapBundle {
grid_size,
map_type,
size: tilemap_size,
storage,
texture: TilemapTexture::Single(assets.image("overworld")),
tile_size: tilemap_tile_size,
transform: Transform::from_xyz(0.0, 0.0, 5.0 + layer.get_z_delta()), // get_tilemap_center_transform(&tilemap_size, &grid_size, 5.0),
..Default::default()
},));
});
events.send(PopulateWorldEvent);
......@@ -218,6 +213,7 @@ pub fn populate_world(
},
Player,
ChaseCam,
WorldLinked,
));
match &pending_load {
......@@ -278,3 +274,29 @@ pub fn populate_world(
);
}
}
pub fn clean_game_state(
mut commands: Commands,
world_entities: Query<Entity, (Without<TileStorage>, With<WorldLinked>)>,
mut tile_storages: Query<Entity, Or<(With<TilemapId>, With<TileStorage>)>>,
) {
commands.remove_resource::<PendingLoadState>();
commands.remove_resource::<PersistenceState>();
commands.remove_resource::<TradingState>();
commands.remove_resource::<HungerState>();
commands.insert_resource(TownPaths::default());
commands.insert_resource(WorldZones::default());
commands.insert_resource(EncounterState::default());
//
log::info!("Cleaning {} world objects", world_entities.iter().len());
for entity in &world_entities {
commands.entity(entity).despawn_recursive();
}
//
log::info!("Cleaning {} tile storages", tile_storages.iter().len());
for entity in &mut tile_storages {
commands.entity(entity).despawn();
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment