Make stages data-driven
This commit is contained in:
+4
-4
@@ -2,10 +2,11 @@
|
||||
|
||||
|
||||
use bevy::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::math::{segment::FGSegment2d, vec::FGVec2};
|
||||
|
||||
#[derive(Reflect, Clone, Copy, PartialEq)]
|
||||
#[derive(Reflect, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||
pub enum StagePolyLineSegmentType {
|
||||
Floor,
|
||||
Wall,
|
||||
@@ -19,7 +20,7 @@ pub struct StagePolyLineSegment {
|
||||
pub segment_type: StagePolyLineSegmentType,
|
||||
}
|
||||
|
||||
#[derive(Reflect, Clone, Default)]
|
||||
#[derive(Reflect, Clone, Default, Serialize, Deserialize, Copy)]
|
||||
pub enum StagePolyType {
|
||||
#[default]
|
||||
Closed,
|
||||
@@ -32,8 +33,7 @@ pub struct StageLineID {
|
||||
pub segment: usize,
|
||||
}
|
||||
|
||||
#[derive(Component, Reflect, Clone, Default)]
|
||||
#[reflect(Component)]
|
||||
#[derive(Reflect, Clone, Default)]
|
||||
pub struct StagePoly {
|
||||
poly_type: StagePolyType,
|
||||
pub segments: Vec<StagePolyLineSegment>,
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use bevy_asset_loader::asset_collection::AssetCollection;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use strum_macros::{EnumIter};
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use crate::{
|
||||
AppState, game_settings::GameSettings, math::{int::FGi32, vec::FGVec2}, stage::{StagePoly, line::{StageCollision, StagePolyLineSegmentType, StagePolyType}},
|
||||
};
|
||||
use bevy::prelude::*;
|
||||
|
||||
#[derive(Reflect, Serialize, Deserialize, Copy, Clone, Eq, PartialEq, Hash, Debug, EnumIter)]
|
||||
pub enum StageId {
|
||||
TestStage,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Reflect)]
|
||||
#[reflect(opaque)]
|
||||
pub struct StagePolyDefinition {
|
||||
poly_type: StagePolyType,
|
||||
lines: Vec<(FGVec2, StagePolyLineSegmentType)>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Asset, Reflect)]
|
||||
pub struct StageManifest {
|
||||
pub id: StageId,
|
||||
pub model_path: String,
|
||||
pub polygons: Vec<StagePolyDefinition>,
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
pub struct StageManifestAssets(pub Vec<Handle<StageManifest>>);
|
||||
|
||||
impl StageManifestAssets {
|
||||
fn requested(world: &World) -> Self {
|
||||
let settings = world.resource::<GameSettings>();
|
||||
let asset_server = world.resource::<AssetServer>();
|
||||
|
||||
Self(
|
||||
settings
|
||||
.stage_manifest_paths
|
||||
.iter()
|
||||
.map(|path| asset_server.load(path.clone()))
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl AssetCollection for StageManifestAssets {
|
||||
fn create(world: &mut World) -> Self {
|
||||
Self::requested(world)
|
||||
}
|
||||
|
||||
fn load(world: &mut World) -> Vec<UntypedHandle> {
|
||||
Self::requested(world)
|
||||
.0
|
||||
.into_iter()
|
||||
.map(Handle::untyped)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
pub struct StageManifestRegistry(HashMap<StageId, Handle<StageManifest>>);
|
||||
|
||||
impl StageManifestRegistry {
|
||||
pub fn get(&self, stage: StageId) -> Option<Handle<StageManifest>> {
|
||||
self.0.get(&stage).cloned()
|
||||
}
|
||||
}
|
||||
|
||||
fn fail(next_state: &mut NextState<AppState>, message: &str) {
|
||||
error!("Stage manifest preparation failed: {message}");
|
||||
next_state.set(AppState::CommonAssetLoadFailed);
|
||||
}
|
||||
|
||||
|
||||
pub fn prepare_stage_manifests(
|
||||
mut commands: Commands,
|
||||
handles: Res<StageManifestAssets>,
|
||||
manifests: Res<Assets<StageManifest>>,
|
||||
mut next_state: ResMut<NextState<AppState>>,
|
||||
) {
|
||||
let mut registry = HashMap::with_capacity(handles.0.len());
|
||||
|
||||
for handle in &handles.0 {
|
||||
let Some(manifest) = manifests.get(handle) else {
|
||||
fail(&mut next_state, "a loaded fighter manifest is unavailable");
|
||||
return;
|
||||
};
|
||||
if manifest.model_path.is_empty() {
|
||||
fail(
|
||||
&mut next_state,
|
||||
&format!("{:?} has an empty model path", manifest.id),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if registry.insert(manifest.id, handle.clone()).is_some() {
|
||||
fail(
|
||||
&mut next_state,
|
||||
&format!("duplicate manifest for stage {:?}", manifest.id),
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for stage in StageId::iter() {
|
||||
if !registry.contains_key(&stage) {
|
||||
fail(
|
||||
&mut next_state,
|
||||
&format!("no manifest configured for stage {stage:?}"),
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
commands.insert_resource(StageManifestRegistry(registry));
|
||||
next_state.set(AppState::Idle);
|
||||
}
|
||||
|
||||
impl StageManifest {
|
||||
pub fn to_collision(&self) -> StageCollision {
|
||||
let stage_polys: Vec<StagePoly> = self.polygons.iter().map(|def| {
|
||||
StagePoly::build(def.poly_type, &def.lines)
|
||||
}).collect();
|
||||
|
||||
StageCollision {
|
||||
stage_polys
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-2
@@ -6,9 +6,12 @@ use bevy::prelude::*;
|
||||
pub mod debug;
|
||||
pub mod line;
|
||||
pub mod scene;
|
||||
pub mod manifest;
|
||||
|
||||
pub use line::{StagePlaneIntersectResult, StagePoly};
|
||||
|
||||
use crate::stage::line::StageCollision;
|
||||
|
||||
/// Owns the stage geometry: which scene gets spawned and how it is drawn.
|
||||
#[derive(Default)]
|
||||
pub struct StagePlugin;
|
||||
@@ -22,7 +25,7 @@ impl Plugin for StagePlugin {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spawn_stage(commands: &mut Commands, match_root: Entity, visual: Handle<WorldAsset>) {
|
||||
pub fn spawn_stage(commands: &mut Commands, match_root: Entity, visual: Handle<WorldAsset>, collision: StageCollision) {
|
||||
commands.entity(match_root).insert(WorldAssetRoot(visual));
|
||||
scene::spawn_stage_support(commands, match_root);
|
||||
scene::spawn_stage_support(commands, match_root, collision);
|
||||
}
|
||||
|
||||
+4
-30
@@ -3,43 +3,17 @@
|
||||
use std::f32::consts::PI;
|
||||
|
||||
use bevy::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
math::vec::FGVec2,
|
||||
stage::{
|
||||
math::{int::FGi32, vec::FGVec2}, stage::{
|
||||
StagePoly,
|
||||
line::{StageCollision, StagePolyLineSegmentType, StagePolyType},
|
||||
},
|
||||
};
|
||||
|
||||
pub fn spawn_stage_support(commands: &mut Commands, match_root: Entity) {
|
||||
let stage_poly = StagePoly::build(
|
||||
StagePolyType::Closed,
|
||||
&[
|
||||
(
|
||||
FGVec2::lit("-56.0", "-3.5"),
|
||||
StagePolyLineSegmentType::Floor,
|
||||
),
|
||||
(FGVec2::lit("-39.2", "0.0"), StagePolyLineSegmentType::Floor),
|
||||
(FGVec2::lit("0.0", "0.0"), StagePolyLineSegmentType::Floor),
|
||||
(FGVec2::lit("39.2", "0.0"), StagePolyLineSegmentType::Floor),
|
||||
(FGVec2::lit("56.0", "-3.5"), StagePolyLineSegmentType::Wall),
|
||||
(
|
||||
FGVec2::lit("56.0", "-200.0"),
|
||||
StagePolyLineSegmentType::Ceiling,
|
||||
),
|
||||
(FGVec2::lit("-56", "-200.0"), StagePolyLineSegmentType::Wall),
|
||||
],
|
||||
);
|
||||
|
||||
commands.insert_resource(StageCollision {
|
||||
stage_polys: vec![stage_poly.clone()],
|
||||
});
|
||||
commands.spawn((
|
||||
Name::new("Stage Collision"),
|
||||
stage_poly,
|
||||
ChildOf(match_root),
|
||||
));
|
||||
pub fn spawn_stage_support(commands: &mut Commands, match_root: Entity, collision: StageCollision) {
|
||||
commands.insert_resource(collision);
|
||||
commands.spawn((
|
||||
DirectionalLight {
|
||||
shadow_maps_enabled: true,
|
||||
|
||||
Reference in New Issue
Block a user