|
|
|
|
@ -1,93 +1,28 @@
|
|
|
|
|
use bevy::asset::AsyncReadExt;
|
|
|
|
|
use std::str::Utf8Error;
|
|
|
|
|
use thiserror::Error;
|
|
|
|
|
|
|
|
|
|
use bevy::{
|
|
|
|
|
asset::{io::Reader, AssetLoader, LoadContext},
|
|
|
|
|
reflect::{TypePath, TypeUuid},
|
|
|
|
|
utils::BoxedFuture,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
use crate::prelude::*;
|
|
|
|
|
|
|
|
|
|
pub(crate) struct CreditsPlugin;
|
|
|
|
|
|
|
|
|
|
impl Plugin for CreditsPlugin {
|
|
|
|
|
fn build(&self, app: &mut App) {
|
|
|
|
|
app.register_asset_loader(CreditsTextLoader)
|
|
|
|
|
.init_asset::<CreditsText>()
|
|
|
|
|
.add_systems(Startup, init_credits_ui)
|
|
|
|
|
app.add_systems(Startup, init_credits_ui)
|
|
|
|
|
.add_systems(
|
|
|
|
|
Update,
|
|
|
|
|
(
|
|
|
|
|
update_credits.run_if(on_event::<AssetEvent<CreditsText>>()),
|
|
|
|
|
menu::exit_to_menu.run_if(in_state(GameState::Credits)),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
.add_systems(OnEnter(GameState::Credits), activate::<Credits>)
|
|
|
|
|
.add_systems(OnEnter(GameState::Credits), (
|
|
|
|
|
update_credits,
|
|
|
|
|
activate::<Credits>.after(update_credits),
|
|
|
|
|
))
|
|
|
|
|
.add_systems(OnExit(GameState::Credits), deactivate::<Credits>);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, TypeUuid, TypePath, Asset)]
|
|
|
|
|
#[uuid = "43df4a09-b5f0-4619-9223-8cf67dc9e844"]
|
|
|
|
|
pub struct CreditsText {
|
|
|
|
|
sections: Vec<TextSection>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Error)]
|
|
|
|
|
enum CreditsError {
|
|
|
|
|
#[error("Failed to read file")]
|
|
|
|
|
IO(#[from] std::io::Error),
|
|
|
|
|
#[error("Failed to decode file")]
|
|
|
|
|
Decode(#[from] Utf8Error),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
|
struct CreditsTextLoader;
|
|
|
|
|
|
|
|
|
|
impl AssetLoader for CreditsTextLoader {
|
|
|
|
|
type Asset = CreditsText;
|
|
|
|
|
type Settings = ();
|
|
|
|
|
type Error = CreditsError;
|
|
|
|
|
|
|
|
|
|
fn load<'a>(
|
|
|
|
|
&'a self,
|
|
|
|
|
reader: &'a mut Reader,
|
|
|
|
|
_settings: &'a Self::Settings,
|
|
|
|
|
_load_context: &'a mut LoadContext,
|
|
|
|
|
) -> BoxedFuture<'a, Result<Self::Asset, Self::Error>> {
|
|
|
|
|
Box::pin(async move {
|
|
|
|
|
let mut bytes = Vec::new();
|
|
|
|
|
reader.read_to_end(&mut bytes).await?;
|
|
|
|
|
let result = parse_credits(bytes.as_slice())?;
|
|
|
|
|
Ok(result)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn extensions(&self) -> &[&str] {
|
|
|
|
|
&["credits.txt"]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_credits(bytes: &[u8]) -> Result<CreditsText, Utf8Error> {
|
|
|
|
|
let s = std::str::from_utf8(bytes)?;
|
|
|
|
|
let sections: Vec<TextSection> = s
|
|
|
|
|
.split('\n')
|
|
|
|
|
.filter(|l| l.len() > 0)
|
|
|
|
|
.map(|l| TextSection {
|
|
|
|
|
value: String::from(l),
|
|
|
|
|
..default()
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
Ok(CreditsText { sections })
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Component)]
|
|
|
|
|
struct Credits;
|
|
|
|
|
|
|
|
|
|
fn init_credits_ui(mut commands: Commands, server: Res<AssetServer>) {
|
|
|
|
|
fn init_credits_ui(mut commands: Commands) {
|
|
|
|
|
commands
|
|
|
|
|
.spawn((
|
|
|
|
|
Credits,
|
|
|
|
|
@ -105,10 +40,8 @@ fn init_credits_ui(mut commands: Commands, server: Res<AssetServer>) {
|
|
|
|
|
},
|
|
|
|
|
))
|
|
|
|
|
.with_children(|parent| {
|
|
|
|
|
let handle: Handle<CreditsText> = server.load("martian-chess.credits.txt");
|
|
|
|
|
|
|
|
|
|
parent.spawn((
|
|
|
|
|
handle.clone(),
|
|
|
|
|
Credits,
|
|
|
|
|
TextBundle {
|
|
|
|
|
text: Text {
|
|
|
|
|
alignment: TextAlignment::Center,
|
|
|
|
|
@ -123,25 +56,26 @@ fn init_credits_ui(mut commands: Commands, server: Res<AssetServer>) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn update_credits(
|
|
|
|
|
mut reader: EventReader<AssetEvent<CreditsText>>,
|
|
|
|
|
credits_texts: Res<Assets<CreditsText>>,
|
|
|
|
|
mut query: Query<(&mut Text, &Handle<CreditsText>)>,
|
|
|
|
|
mut query: Query<&mut Text, With<Credits>>,
|
|
|
|
|
tweaks: Res<Assets<tweak::Tweaks>>,
|
|
|
|
|
tweaks_file: Res<tweak::GameTweaks>,
|
|
|
|
|
) {
|
|
|
|
|
reader.read().for_each(|event| match event {
|
|
|
|
|
AssetEvent::Added { id }
|
|
|
|
|
| AssetEvent::LoadedWithDependencies { id }
|
|
|
|
|
| AssetEvent::Modified { id } => {
|
|
|
|
|
query
|
|
|
|
|
.iter_mut()
|
|
|
|
|
.filter(|(_, this_handle)| *id == (*this_handle).id())
|
|
|
|
|
.for_each(|(mut text, this_handle)| {
|
|
|
|
|
if let Some(credits_text) = credits_texts.get(this_handle) {
|
|
|
|
|
text.sections = credits_text.sections.clone();
|
|
|
|
|
let tweak = tweaks
|
|
|
|
|
.get(tweaks_file.handle.clone())
|
|
|
|
|
.expect("Load tweakfile");
|
|
|
|
|
|
|
|
|
|
query
|
|
|
|
|
.iter_mut()
|
|
|
|
|
.for_each(|mut text| {
|
|
|
|
|
let credits_text = tweak.get::<String>("credits_text").unwrap();
|
|
|
|
|
text.sections = {
|
|
|
|
|
credits_text.split('\n').map(|l| {
|
|
|
|
|
TextSection {
|
|
|
|
|
value: format!("{}\n", l),
|
|
|
|
|
style: TextStyle { ..default() },
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
AssetEvent::Removed { .. } => {
|
|
|
|
|
warn!("Removed Credit resource... we don't handle that...");
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}).collect()
|
|
|
|
|
};
|
|
|
|
|
info!("Text sections: {:?}", text.sections);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|