You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
161 lines
4.9 KiB
Rust
161 lines
4.9 KiB
Rust
use crate::prelude::*;
|
|
|
|
/// A Tweaks is resource used to specify game customization like asset names,
|
|
/// and non-user customizations made to the game during development.
|
|
pub(crate) struct TweakPlugin;
|
|
|
|
impl Plugin for TweakPlugin {
|
|
fn build(&self, app: &mut App) {
|
|
app.add_systems(OnEnter(GameState::Loading), load_tweakfile);
|
|
|
|
app.init_asset::<Tweaks>()
|
|
.register_asset_loader(TweaksLoader);
|
|
}
|
|
}
|
|
|
|
fn load_tweakfile(server: Res<AssetServer>, mut commands: Commands) {
|
|
let handle: Handle<Tweaks> = server.load("martian.tweak.toml");
|
|
commands.insert_resource(GameTweaks { handle });
|
|
}
|
|
|
|
#[derive(Debug, Resource)]
|
|
pub(crate) struct GameTweaks {
|
|
pub handle: Handle<Tweaks>,
|
|
}
|
|
|
|
#[derive(Debug, Asset, TypePath)]
|
|
pub struct Tweaks {
|
|
table: toml::Table,
|
|
handles: HashMap<String, UntypedHandle>,
|
|
}
|
|
|
|
impl Tweaks {
|
|
fn from_table(table: &toml::Table, load_context: &mut LoadContext) -> Tweaks {
|
|
let handles = Tweaks::iter_all(table, "")
|
|
.iter()
|
|
.filter_map(|(k, v)| match v {
|
|
toml::Value::String(s) => std::path::Path::new(format!("assets/{}", s).as_str())
|
|
.exists()
|
|
.then(|| {
|
|
if s.ends_with(".gltf") || s.ends_with(".glb") {
|
|
Some(load_context.load::<Gltf>(s).untyped())
|
|
} else if s.ends_with(".png") {
|
|
Some(load_context.load::<Image>(s).untyped())
|
|
} else if s.ends_with(".ttf") || s.ends_with(".otf") {
|
|
Some(load_context.load::<Font>(s).untyped())
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
.flatten()
|
|
.map(|h| (k.clone(), h)),
|
|
_ => None,
|
|
})
|
|
.collect();
|
|
|
|
Tweaks {
|
|
table: table.clone(),
|
|
handles,
|
|
}
|
|
}
|
|
|
|
pub fn get_handle<T: Asset>(&self, key: &str) -> Option<Handle<T>> {
|
|
self.handles.get(key).map(|h| h.clone().typed::<T>())
|
|
}
|
|
|
|
pub fn get_handle_unchecked<T: Asset>(&self, key: &str) -> Option<Handle<T>> {
|
|
self.handles
|
|
.get(key)
|
|
.map(|h| h.clone().typed_unchecked::<T>())
|
|
}
|
|
|
|
pub fn get<'de, T: Deserialize<'de>>(&self, key: &str) -> Option<T> {
|
|
Tweaks::locate(&self.table, key).map(|val| match val.try_into() {
|
|
Ok(val) => val,
|
|
Err(e) => panic!("{}", e.message()),
|
|
})
|
|
}
|
|
|
|
fn iter_all(t: &toml::Table, key: &str) -> Vec<(String, toml::Value)> {
|
|
t.iter()
|
|
.flat_map(|(k, v)| {
|
|
let nk = if key == "" {
|
|
k.to_string()
|
|
} else {
|
|
format!("{}_{}", key, k)
|
|
};
|
|
match v {
|
|
toml::Value::Table(nt) => Tweaks::iter_all(nt, nk.as_str()),
|
|
_ => vec![(nk, v.clone())],
|
|
}
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
fn locate(t: &toml::Table, key: &str) -> Option<toml::Value> {
|
|
t.iter().find_map(|(k, v)| {
|
|
if key == k {
|
|
Some(v.clone())
|
|
} else if key.starts_with(k) {
|
|
let prefix = format!("{}_", k);
|
|
match v {
|
|
toml::Value::Table(nt) => {
|
|
Tweaks::locate(nt, key.strip_prefix(prefix.as_str()).unwrap())
|
|
}
|
|
_ => Some(v.clone()),
|
|
}
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Default)]
|
|
pub struct TweaksLoader;
|
|
|
|
#[derive(Debug, Error)]
|
|
pub enum TweaksError {
|
|
#[error("Failed to read file")]
|
|
IO(#[from] std::io::Error),
|
|
#[error("Failed to decode file")]
|
|
Decode(#[from] Utf8Error),
|
|
#[error("Failed to parse file")]
|
|
Parse(#[from] toml::de::Error),
|
|
}
|
|
|
|
impl AssetLoader for TweaksLoader {
|
|
type Asset = Tweaks;
|
|
type Settings = ();
|
|
type Error = TweaksError;
|
|
|
|
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>> {
|
|
use bevy::asset::AsyncReadExt;
|
|
|
|
Box::pin(async move {
|
|
let mut bytes = Vec::new();
|
|
reader.read_to_end(&mut bytes).await?;
|
|
let s = std::str::from_utf8(bytes.as_slice())?;
|
|
match toml::from_str::<toml::Table>(s) {
|
|
Ok(parsed) => {
|
|
let result = Tweaks::from_table(&parsed, load_context);
|
|
Ok(result)
|
|
}
|
|
Err(e) => {
|
|
println!("Error parsing tweaks: {:?}", e.message());
|
|
Err(TweaksError::Parse(e))
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
fn extensions(&self) -> &[&str] {
|
|
&["tweak.toml"]
|
|
}
|
|
}
|