mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Use assets_manager to load assets
This commit is contained in:
parent
6ecff460bf
commit
0cf164f33a
107
Cargo.lock
generated
107
Cargo.lock
generated
@ -52,6 +52,17 @@ version = "0.4.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e"
|
checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ahash"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a75b7e6a93ecd6dbd2c225154d0fa7f86205574ecaa6c87429fb5f66ee677c44"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.2.0",
|
||||||
|
"lazy_static",
|
||||||
|
"version_check 0.9.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "0.7.13"
|
version = "0.7.13"
|
||||||
@ -230,6 +241,23 @@ version = "1.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bbf56136a5198c7b01a49e3afcbef6cf84597273d298f54432926024107b0109"
|
checksum = "bbf56136a5198c7b01a49e3afcbef6cf84597273d298f54432926024107b0109"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "assets_manager"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8048fbc7e6314fa56e2c7faeb0e1c7be01d23c87bad301a7877afaa493ec3dbb"
|
||||||
|
dependencies = [
|
||||||
|
"ahash 0.6.2",
|
||||||
|
"bincode",
|
||||||
|
"crossbeam-channel 0.5.0",
|
||||||
|
"log",
|
||||||
|
"notify 4.0.15",
|
||||||
|
"parking_lot 0.11.0",
|
||||||
|
"ron",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-std"
|
name = "async-std"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@ -791,7 +819,7 @@ version = "0.1.8"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "25e4c606eb459dd29f7c57b2e0879f2b6f14ee130918c2b78ccb58a9624e6c7a"
|
checksum = "25e4c606eb459dd29f7c57b2e0879f2b6f14ee130918c2b78ccb58a9624e6c7a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom 0.1.15",
|
||||||
"proc-macro-hack",
|
"proc-macro-hack",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1745,6 +1773,16 @@ version = "0.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fsevent"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5ab7d1bd1bd33cc98b0889831b72da23c0aa4df9cec7e0702f46ecea04b35db6"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"fsevent-sys 2.0.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fsevent"
|
name = "fsevent"
|
||||||
version = "2.0.2"
|
version = "2.0.2"
|
||||||
@ -1752,7 +1790,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "97f347202c95c98805c216f9e1df210e8ebaec9fdb2365700a43c10797a35e63"
|
checksum = "97f347202c95c98805c216f9e1df210e8ebaec9fdb2365700a43c10797a35e63"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"fsevent-sys",
|
"fsevent-sys 3.0.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fsevent-sys"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f41b048a94555da0f42f1d632e2e19510084fb8e303b0daa2816e733fb3644a0"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1972,6 +2019,17 @@ dependencies = [
|
|||||||
"wasi 0.9.0+wasi-snapshot-preview1",
|
"wasi 0.9.0+wasi-snapshot-preview1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee8025cf36f917e6a52cce185b7c7177689b838b7ec138364e50cc2277a56cf4"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 0.1.10",
|
||||||
|
"libc",
|
||||||
|
"wasi 0.9.0+wasi-snapshot-preview1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gfx"
|
name = "gfx"
|
||||||
version = "0.18.2"
|
version = "0.18.2"
|
||||||
@ -2561,6 +2619,17 @@ dependencies = [
|
|||||||
"lazy_static",
|
"lazy_static",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "inotify"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4816c66d2c8ae673df83366c18341538f234a26d65a9ecea5c348b453ac1d02f"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"inotify-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "inotify"
|
name = "inotify"
|
||||||
version = "0.8.3"
|
version = "0.8.3"
|
||||||
@ -3249,6 +3318,24 @@ dependencies = [
|
|||||||
"version_check 0.9.2",
|
"version_check 0.9.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "notify"
|
||||||
|
version = "4.0.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "80ae4a7688d1fab81c5bf19c64fc8db920be8d519ce6336ed4e7efe024724dbd"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"filetime",
|
||||||
|
"fsevent 0.4.0",
|
||||||
|
"fsevent-sys 2.0.1",
|
||||||
|
"inotify 0.7.1",
|
||||||
|
"libc",
|
||||||
|
"mio 0.6.22",
|
||||||
|
"mio-extras",
|
||||||
|
"walkdir 2.3.1",
|
||||||
|
"winapi 0.3.9",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "notify"
|
name = "notify"
|
||||||
version = "5.0.0-pre.3"
|
version = "5.0.0-pre.3"
|
||||||
@ -3259,9 +3346,9 @@ dependencies = [
|
|||||||
"bitflags",
|
"bitflags",
|
||||||
"crossbeam-channel 0.4.4",
|
"crossbeam-channel 0.4.4",
|
||||||
"filetime",
|
"filetime",
|
||||||
"fsevent",
|
"fsevent 2.0.2",
|
||||||
"fsevent-sys",
|
"fsevent-sys 3.0.2",
|
||||||
"inotify",
|
"inotify 0.8.3",
|
||||||
"libc",
|
"libc",
|
||||||
"mio 0.6.22",
|
"mio 0.6.22",
|
||||||
"mio-extras",
|
"mio-extras",
|
||||||
@ -4033,7 +4120,7 @@ version = "0.7.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
|
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom 0.1.15",
|
||||||
"libc",
|
"libc",
|
||||||
"rand_chacha 0.2.2",
|
"rand_chacha 0.2.2",
|
||||||
"rand_core 0.5.1",
|
"rand_core 0.5.1",
|
||||||
@ -4082,7 +4169,7 @@ version = "0.5.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom 0.1.15",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -4231,7 +4318,7 @@ version = "0.3.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d"
|
checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom 0.1.15",
|
||||||
"redox_syscall",
|
"redox_syscall",
|
||||||
"rust-argon2",
|
"rust-argon2",
|
||||||
]
|
]
|
||||||
@ -5699,6 +5786,7 @@ name = "veloren-common"
|
|||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arraygen",
|
"arraygen",
|
||||||
|
"assets_manager",
|
||||||
"criterion",
|
"criterion",
|
||||||
"crossbeam-channel 0.5.0",
|
"crossbeam-channel 0.5.0",
|
||||||
"crossbeam-utils 0.8.1",
|
"crossbeam-utils 0.8.1",
|
||||||
@ -5710,7 +5798,6 @@ dependencies = [
|
|||||||
"image",
|
"image",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"notify",
|
|
||||||
"num-derive",
|
"num-derive",
|
||||||
"num-traits 0.2.14",
|
"num-traits 0.2.14",
|
||||||
"ordered-float 2.0.1",
|
"ordered-float 2.0.1",
|
||||||
@ -5933,7 +6020,7 @@ dependencies = [
|
|||||||
"inline_tweak",
|
"inline_tweak",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libloading 0.6.3",
|
"libloading 0.6.3",
|
||||||
"notify",
|
"notify 5.0.0-pre.3",
|
||||||
"tracing",
|
"tracing",
|
||||||
"vek 0.12.0",
|
"vek 0.12.0",
|
||||||
"veloren-common",
|
"veloren-common",
|
||||||
|
@ -30,10 +30,10 @@ vek = { version = "0.12.0", features = ["serde"] }
|
|||||||
uuid = { version = "0.8.1", default-features = false, features = ["serde", "v4"] }
|
uuid = { version = "0.8.1", default-features = false, features = ["serde", "v4"] }
|
||||||
|
|
||||||
# Assets
|
# Assets
|
||||||
|
assets_manager = {version = "0.4.1", features = ["bincode", "ron", "json", "hot-reloading"]}
|
||||||
directories-next = "2.0"
|
directories-next = "2.0"
|
||||||
dot_vox = "4.0"
|
dot_vox = "4.0"
|
||||||
image = { version = "0.23.12", default-features = false, features = ["png"] }
|
image = { version = "0.23.12", default-features = false, features = ["png"] }
|
||||||
notify = "5.0.0-pre.3"
|
|
||||||
|
|
||||||
# Data structures
|
# Data structures
|
||||||
hashbrown = { version = "0.9", features = ["rayon", "serde", "nightly"] }
|
hashbrown = { version = "0.9", features = ["rayon", "serde", "nightly"] }
|
||||||
|
@ -1,221 +1,58 @@
|
|||||||
//! Load assets (images or voxel data) from files
|
//! Load assets (images or voxel data) from files
|
||||||
pub mod watch;
|
|
||||||
|
|
||||||
use core::{any::Any, fmt, marker::PhantomData};
|
|
||||||
use dot_vox::DotVoxData;
|
use dot_vox::DotVoxData;
|
||||||
use hashbrown::HashMap;
|
|
||||||
use image::DynamicImage;
|
use image::DynamicImage;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_json::Value;
|
use std::{borrow::Cow, fs, io, path::{Path, PathBuf}, sync::Arc};
|
||||||
use std::{
|
|
||||||
fs::{self, File, ReadDir},
|
pub use assets_manager::{
|
||||||
io::{BufReader, Read},
|
Asset, AssetCache, BoxedError, Compound, Error, source,
|
||||||
path::PathBuf,
|
loader::{self, BytesLoader, BincodeLoader, Loader, JsonLoader, LoadFrom, RonLoader, StringLoader},
|
||||||
sync::{Arc, RwLock},
|
|
||||||
};
|
};
|
||||||
use tracing::{error, trace};
|
|
||||||
|
|
||||||
/// The error returned by asset loading functions
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum Error {
|
|
||||||
/// Parsing error occurred.
|
|
||||||
ParseError(Arc<dyn std::fmt::Debug>),
|
|
||||||
/// An asset of a different type has already been loaded with this
|
|
||||||
/// specifier.
|
|
||||||
InvalidType,
|
|
||||||
/// Asset does not exist.
|
|
||||||
NotFound(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error {
|
|
||||||
pub fn parse_error<E: std::fmt::Debug + 'static>(err: E) -> Self {
|
|
||||||
Self::ParseError(Arc::new(err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Error::ParseError(err) => write!(f, "{:?}", err),
|
|
||||||
Error::InvalidType => write!(
|
|
||||||
f,
|
|
||||||
"an asset of a different type has already been loaded with this specifier."
|
|
||||||
),
|
|
||||||
Error::NotFound(s) => write!(f, "{}", s),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Arc<dyn Any + 'static + Sync + Send>> for Error {
|
|
||||||
fn from(_: Arc<dyn Any + 'static + Sync + Send>) -> Self { Error::InvalidType }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<std::io::Error> for Error {
|
|
||||||
fn from(err: std::io::Error) -> Self { Error::NotFound(format!("{}", err)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
/// The HashMap where all loaded assets are stored in.
|
/// The HashMap where all loaded assets are stored in.
|
||||||
static ref ASSETS: RwLock<HashMap<String, Arc<dyn Any + 'static + Sync + Send>>> =
|
static ref ASSETS: AssetCache = AssetCache::new(&*ASSETS_PATH).unwrap();
|
||||||
RwLock::new(HashMap::new());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reload<A: Asset>(specifier: &str) -> Result<(), Error>
|
pub fn start_hot_reloading() {
|
||||||
where
|
ASSETS.enhance_hot_reloading();
|
||||||
A::Output: Send + Sync + 'static,
|
|
||||||
{
|
|
||||||
let asset = Arc::new(A::parse(load_file(specifier, A::ENDINGS)?, specifier)?);
|
|
||||||
let mut assets_write = ASSETS.write().unwrap();
|
|
||||||
match assets_write.get_mut(specifier) {
|
|
||||||
Some(a) => *a = asset,
|
|
||||||
None => {
|
|
||||||
assets_write.insert(specifier.to_owned(), asset);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type AssetHandle<T> = assets_manager::Handle<'static, T>;
|
||||||
|
pub type AssetDir<T> = assets_manager::DirReader<'static, T, source::FileSystem>;
|
||||||
|
|
||||||
/// The Asset trait, which is implemented by all structures that have their data
|
/// The Asset trait, which is implemented by all structures that have their data
|
||||||
/// stored in the filesystem.
|
/// stored in the filesystem.
|
||||||
pub trait Asset: Sized {
|
pub trait AssetExt: Sized + Send + Sync + 'static {
|
||||||
type Output = Self;
|
|
||||||
|
|
||||||
const ENDINGS: &'static [&'static str];
|
|
||||||
/// Parse the input file and return the correct Asset.
|
|
||||||
fn parse(buf_reader: BufReader<File>, specifier: &str) -> Result<Self::Output, Error>;
|
|
||||||
|
|
||||||
// TODO: Remove this function. It's only used in world/ in a really ugly way.To
|
|
||||||
// do this properly assets should have all their necessary data in one file. A
|
|
||||||
// ron file could be used to combine voxel data with positioning data for
|
|
||||||
// example.
|
|
||||||
/// Function used to load assets from the filesystem or the cache. Permits
|
|
||||||
/// manipulating the loaded asset with a mapping function. Example usage:
|
|
||||||
/// ```no_run
|
|
||||||
/// use vek::*;
|
|
||||||
/// use veloren_common::{assets::Asset, terrain::Structure};
|
|
||||||
///
|
|
||||||
/// let my_tree_structure = Structure::load_map("world.tree.oak_green.1", |s: Structure| {
|
|
||||||
/// s.with_center(Vec3::new(15, 18, 14))
|
|
||||||
/// })
|
|
||||||
/// .unwrap();
|
|
||||||
/// ```
|
|
||||||
fn load_map<F: FnOnce(Self::Output) -> Self::Output>(
|
|
||||||
specifier: &str,
|
|
||||||
f: F,
|
|
||||||
) -> Result<Arc<Self::Output>, Error>
|
|
||||||
where
|
|
||||||
Self::Output: Send + Sync + 'static,
|
|
||||||
{
|
|
||||||
let assets_read = ASSETS.read().unwrap();
|
|
||||||
match assets_read.get(specifier) {
|
|
||||||
Some(asset) => Ok(Arc::clone(asset).downcast()?),
|
|
||||||
None => {
|
|
||||||
drop(assets_read); // Drop the asset hashmap to permit recursive loading
|
|
||||||
let asset = Arc::new(f(Self::parse(
|
|
||||||
load_file(specifier, Self::ENDINGS)?,
|
|
||||||
specifier,
|
|
||||||
)?));
|
|
||||||
let clone = Arc::clone(&asset);
|
|
||||||
ASSETS.write().unwrap().insert(specifier.to_owned(), clone);
|
|
||||||
Ok(asset)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_glob(specifier: &str) -> Result<Arc<Vec<Arc<Self::Output>>>, Error>
|
|
||||||
where
|
|
||||||
Self::Output: Send + Sync + 'static,
|
|
||||||
{
|
|
||||||
if let Some(assets) = ASSETS.read().unwrap().get(specifier) {
|
|
||||||
return Ok(Arc::clone(assets).downcast()?);
|
|
||||||
}
|
|
||||||
|
|
||||||
match get_glob_matches(specifier) {
|
|
||||||
Ok(glob_matches) => {
|
|
||||||
let assets = Arc::new(
|
|
||||||
glob_matches
|
|
||||||
.into_iter()
|
|
||||||
.filter_map(|name| {
|
|
||||||
Self::load(&name)
|
|
||||||
.map_err(|e| {
|
|
||||||
error!(
|
|
||||||
?e,
|
|
||||||
"Failed to load \"{}\" as part of glob \"{}\"",
|
|
||||||
name,
|
|
||||||
specifier
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.ok()
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
);
|
|
||||||
let clone = Arc::clone(&assets);
|
|
||||||
|
|
||||||
let mut assets_write = ASSETS.write().unwrap();
|
|
||||||
assets_write.insert(specifier.to_owned(), clone);
|
|
||||||
Ok(assets)
|
|
||||||
},
|
|
||||||
Err(error) => Err(error),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_glob_cloned(specifier: &str) -> Result<Vec<(Self::Output, String)>, Error>
|
|
||||||
where
|
|
||||||
Self::Output: Clone + Send + Sync + 'static,
|
|
||||||
{
|
|
||||||
match get_glob_matches(specifier) {
|
|
||||||
Ok(glob_matches) => Ok(glob_matches
|
|
||||||
.into_iter()
|
|
||||||
.map(|name| {
|
|
||||||
let full_specifier = &specifier.replace("*", &name);
|
|
||||||
(
|
|
||||||
Self::load_expect_cloned(full_specifier),
|
|
||||||
full_specifier.to_string(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>()),
|
|
||||||
Err(error) => Err(error),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Function used to load assets from the filesystem or the cache.
|
/// Function used to load assets from the filesystem or the cache.
|
||||||
/// Example usage:
|
/// Example usage:
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// use image::DynamicImage;
|
/// use veloren_common::assets::{self, AssetExt};
|
||||||
/// use veloren_common::assets::Asset;
|
|
||||||
///
|
///
|
||||||
/// let my_image = DynamicImage::load("core.ui.backgrounds.city").unwrap();
|
/// let my_image = assets::Image::load("core.ui.backgrounds.city").unwrap();
|
||||||
/// ```
|
/// ```
|
||||||
fn load(specifier: &str) -> Result<Arc<Self::Output>, Error>
|
fn load(specifier: &str) -> Result<AssetHandle<Self>, Error>;
|
||||||
where
|
|
||||||
Self::Output: Send + Sync + 'static,
|
|
||||||
{
|
|
||||||
Self::load_map(specifier, |x| x)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Function used to load assets from the filesystem or the cache and return
|
/// Function used to load assets from the filesystem or the cache and return
|
||||||
/// a clone.
|
/// a clone.
|
||||||
fn load_cloned(specifier: &str) -> Result<Self::Output, Error>
|
fn load_cloned(specifier: &str) -> Result<Self, Error>
|
||||||
where
|
where
|
||||||
Self::Output: Clone + Send + Sync + 'static,
|
Self: Clone,
|
||||||
{
|
{
|
||||||
Self::load(specifier).map(|asset| (*asset).clone())
|
Self::load(specifier).map(AssetHandle::cloned)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Function used to load essential assets from the filesystem or the cache.
|
/// Function used to load essential assets from the filesystem or the cache.
|
||||||
/// It will panic if the asset is not found. Example usage:
|
/// It will panic if the asset is not found. Example usage:
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// use image::DynamicImage;
|
/// use veloren_common::assets::{self, AssetExt};
|
||||||
/// use veloren_common::assets::Asset;
|
|
||||||
///
|
///
|
||||||
/// let my_image = DynamicImage::load_expect("core.ui.backgrounds.city");
|
/// let my_image = assets::Image::load_expect("core.ui.backgrounds.city");
|
||||||
/// ```
|
/// ```
|
||||||
fn load_expect(specifier: &str) -> Arc<Self::Output>
|
#[track_caller]
|
||||||
where
|
fn load_expect(specifier: &str) -> AssetHandle<Self> {
|
||||||
Self::Output: Send + Sync + 'static,
|
|
||||||
{
|
|
||||||
Self::load(specifier).unwrap_or_else(|err| {
|
Self::load(specifier).unwrap_or_else(|err| {
|
||||||
panic!(
|
panic!(
|
||||||
"Failed loading essential asset: {} (error={:?})",
|
"Failed loading essential asset: {} (error={:?})",
|
||||||
@ -226,121 +63,79 @@ pub trait Asset: Sized {
|
|||||||
|
|
||||||
/// Function used to load essential assets from the filesystem or the cache
|
/// Function used to load essential assets from the filesystem or the cache
|
||||||
/// and return a clone. It will panic if the asset is not found.
|
/// and return a clone. It will panic if the asset is not found.
|
||||||
fn load_expect_cloned(specifier: &str) -> Self::Output
|
#[track_caller]
|
||||||
|
fn load_expect_cloned(specifier: &str) -> Self
|
||||||
where
|
where
|
||||||
Self::Output: Clone + Send + Sync + 'static,
|
Self: Clone,
|
||||||
{
|
{
|
||||||
Self::load_expect(specifier).as_ref().clone()
|
Self::load_expect(specifier).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load an asset while registering it to be watched and reloaded when it
|
fn load_owned(specifier: &str) -> Result<Self, Error>;
|
||||||
/// changes
|
}
|
||||||
fn load_watched(
|
|
||||||
specifier: &str,
|
|
||||||
indicator: &mut watch::ReloadIndicator,
|
|
||||||
) -> Result<Arc<Self::Output>, Error>
|
|
||||||
where
|
|
||||||
Self::Output: Send + Sync + 'static,
|
|
||||||
{
|
|
||||||
let asset = Self::load(specifier)?;
|
|
||||||
|
|
||||||
// Determine path to watch
|
pub fn load_dir<T: Asset>(specifier: &str) -> Result<AssetDir<T>, Error> {
|
||||||
let path = unpack_specifier(specifier);
|
Ok(ASSETS.load_dir(specifier)?)
|
||||||
let mut path_with_extension = None;
|
}
|
||||||
for ending in Self::ENDINGS {
|
|
||||||
let mut path = path.clone();
|
|
||||||
path.set_extension(ending);
|
|
||||||
|
|
||||||
if path.exists() {
|
impl<T: Compound> AssetExt for T {
|
||||||
path_with_extension = Some(path);
|
fn load(specifier: &str) -> Result<AssetHandle<Self>, Error> {
|
||||||
break;
|
ASSETS.load(specifier)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let owned_specifier = specifier.to_string();
|
fn load_owned(specifier: &str) -> Result<Self, Error> {
|
||||||
indicator.add(
|
ASSETS.load_owned(specifier)
|
||||||
path_with_extension
|
|
||||||
.ok_or_else(|| Error::NotFound(path.to_string_lossy().into_owned()))?,
|
|
||||||
move || {
|
|
||||||
if let Err(e) = reload::<Self>(&owned_specifier) {
|
|
||||||
error!(?e, ?owned_specifier, "Error reloading owned_specifier");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(asset)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Asset for DynamicImage {
|
pub struct Image(pub Arc<DynamicImage>);
|
||||||
const ENDINGS: &'static [&'static str] = &["png", "jpg"];
|
|
||||||
|
|
||||||
fn parse(mut buf_reader: BufReader<File>, _specifier: &str) -> Result<Self, Error> {
|
impl Image {
|
||||||
let mut buf = Vec::new();
|
pub fn to_image(&self) -> Arc<DynamicImage> {
|
||||||
buf_reader.read_to_end(&mut buf)?;
|
self.0.clone()
|
||||||
image::load_from_memory(&buf).map_err(Error::parse_error)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Asset for DotVoxData {
|
pub struct ImageLoader;
|
||||||
const ENDINGS: &'static [&'static str] = &["vox"];
|
impl Loader<Image> for ImageLoader {
|
||||||
|
fn load(content: Cow<[u8]>, _: &str) -> Result<Image, BoxedError> {
|
||||||
fn parse(mut buf_reader: BufReader<File>, _specifier: &str) -> Result<Self, Error> {
|
let image = image::load_from_memory(&content)?;
|
||||||
let mut buf = Vec::new();
|
Ok(Image(Arc::new(image)))
|
||||||
buf_reader.read_to_end(&mut buf)?;
|
|
||||||
dot_vox::load_bytes(&buf).map_err(Error::parse_error)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read a JSON file
|
impl Asset for Image {
|
||||||
impl Asset for Value {
|
const EXTENSIONS: &'static [&'static str] = &["png", "jpg"];
|
||||||
const ENDINGS: &'static [&'static str] = &["json"];
|
type Loader = ImageLoader;
|
||||||
|
}
|
||||||
|
|
||||||
fn parse(buf_reader: BufReader<File>, _specifier: &str) -> Result<Self, Error> {
|
pub struct DotVoxAsset(pub DotVoxData);
|
||||||
serde_json::from_reader(buf_reader).map_err(Error::parse_error)
|
|
||||||
|
pub struct DotVoxLoader;
|
||||||
|
impl Loader<DotVoxAsset> for DotVoxLoader {
|
||||||
|
fn load(content: std::borrow::Cow<[u8]>, _: &str) -> Result<DotVoxAsset, BoxedError> {
|
||||||
|
let data = dot_vox::load_bytes(&content).map_err(|err| err.to_owned())?;
|
||||||
|
Ok(DotVoxAsset(data))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load from an arbitrary RON file.
|
/// Load from an arbitrary RON file.
|
||||||
pub struct Ron<T>(pub PhantomData<T>);
|
#[derive(Deserialize)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct Ron<T>(pub T);
|
||||||
|
|
||||||
impl<T: Send + Sync + for<'de> Deserialize<'de>> Asset for Ron<T> {
|
impl<T> Asset for Ron<T>
|
||||||
type Output = T;
|
|
||||||
|
|
||||||
const ENDINGS: &'static [&'static str] = &["ron"];
|
|
||||||
|
|
||||||
fn parse(buf_reader: BufReader<File>, _specifier: &str) -> Result<T, Error> {
|
|
||||||
ron::de::from_reader(buf_reader).map_err(Error::parse_error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Load from a specific asset path.
|
|
||||||
pub struct AssetWith<T: Asset, const ASSET_PATH: &'static str> {
|
|
||||||
pub asset: Arc<T::Output>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Asset, const ASSET_PATH: &'static str> Clone for AssetWith<T, ASSET_PATH> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self {
|
|
||||||
asset: Arc::clone(&self.asset),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Asset, const ASSET_PATH: &'static str> AssetWith<T, ASSET_PATH>
|
|
||||||
where
|
where
|
||||||
T::Output: Send + Sync + 'static,
|
T: Send + Sync + for<'de> Deserialize<'de> + 'static,
|
||||||
{
|
{
|
||||||
#[inline]
|
const EXTENSION: &'static str = "ron";
|
||||||
pub fn load_watched(indicator: &mut watch::ReloadIndicator) -> Result<Self, Error> {
|
type Loader = RonLoader;
|
||||||
T::load_watched(ASSET_PATH, indicator).map(|asset| Self { asset })
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn reload(&mut self) -> Result<(), Error> {
|
impl Asset for DotVoxAsset {
|
||||||
self.asset = T::load(ASSET_PATH)?;
|
const EXTENSION: &'static str = "vox";
|
||||||
Ok(())
|
type Loader = DotVoxLoader;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
@ -423,86 +218,42 @@ lazy_static! {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts a specifier like "core.backgrounds.city" to
|
fn get_dir_files(files: &mut Vec<String>, path: &Path, specifier: &str) -> io::Result<()> {
|
||||||
/// ".../veloren/assets/core/backgrounds/city".
|
for entry in fs::read_dir(path)? {
|
||||||
fn unpack_specifier(specifier: &str) -> PathBuf {
|
if let Ok(entry) = entry {
|
||||||
let mut path = ASSETS_PATH.clone();
|
let path = entry.path();
|
||||||
path.push(specifier.replace(".", "/"));
|
let maybe_stem = path.file_stem().and_then(|stem| stem.to_str());
|
||||||
path
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Loads a file based on the specifier and possible extensions
|
if let Some(stem) = maybe_stem {
|
||||||
pub fn load_file(specifier: &str, endings: &[&str]) -> Result<BufReader<File>, Error> {
|
let specifier = format!("{}.{}", specifier, stem);
|
||||||
let path = unpack_specifier(specifier);
|
|
||||||
for ending in endings {
|
|
||||||
let mut path = path.clone();
|
|
||||||
path.set_extension(ending);
|
|
||||||
|
|
||||||
trace!(?path, "Trying to access");
|
if path.is_dir() {
|
||||||
if let Ok(file) = File::open(path) {
|
get_dir_files(files, &path, &specifier)?;
|
||||||
return Ok(BufReader::new(file));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(Error::NotFound(path.to_string_lossy().into_owned()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Loads a file based on the specifier and possible extensions
|
|
||||||
pub fn load_file_glob(specifier: &str, endings: &[&str]) -> Result<BufReader<File>, Error> {
|
|
||||||
let path = unpack_specifier(specifier);
|
|
||||||
for ending in endings {
|
|
||||||
let mut path = path.clone();
|
|
||||||
path.set_extension(ending);
|
|
||||||
|
|
||||||
trace!(?path, "Trying to access");
|
|
||||||
if let Ok(file) = File::open(path) {
|
|
||||||
return Ok(BufReader::new(file));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(Error::NotFound(path.to_string_lossy().into_owned()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read directory from `veloren/assets/*`
|
|
||||||
pub fn read_dir(specifier: &str) -> Result<ReadDir, Error> {
|
|
||||||
let dir_name = unpack_specifier(specifier);
|
|
||||||
if dir_name.exists() {
|
|
||||||
Ok(fs::read_dir(dir_name).expect("`read_dir` failed."))
|
|
||||||
} else {
|
|
||||||
Err(Error::NotFound(dir_name.to_string_lossy().into_owned()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finds all files matching the provided glob specifier - includes files from
|
|
||||||
// subdirectories
|
|
||||||
fn get_glob_matches(specifier: &str) -> Result<Vec<String>, Error> {
|
|
||||||
let specifier = specifier.trim_end_matches(".*");
|
|
||||||
read_dir(specifier).map(|dir| {
|
|
||||||
dir.filter_map(|direntry| {
|
|
||||||
direntry.ok().and_then(|dir_entry| {
|
|
||||||
if dir_entry.path().is_dir() {
|
|
||||||
let sub_dir_glob = format!(
|
|
||||||
"{}.{}.*",
|
|
||||||
specifier.to_string(),
|
|
||||||
dir_entry.file_name().to_string_lossy()
|
|
||||||
);
|
|
||||||
Some(get_glob_matches(&sub_dir_glob).ok()?)
|
|
||||||
} else {
|
} else {
|
||||||
Some(vec![format!(
|
files.push(specifier);
|
||||||
"{}.{}",
|
|
||||||
specifier,
|
|
||||||
dir_entry
|
|
||||||
.file_name()
|
|
||||||
.to_string_lossy()
|
|
||||||
.rsplitn(2, '.')
|
|
||||||
.last()
|
|
||||||
.map(|s| s.to_owned())
|
|
||||||
.unwrap()
|
|
||||||
)])
|
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
})
|
}
|
||||||
.flat_map(|x| x)
|
}
|
||||||
.collect::<Vec<_>>()
|
|
||||||
})
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Directory(Vec<String>);
|
||||||
|
|
||||||
|
impl Directory {
|
||||||
|
pub fn iter(&self) -> impl Iterator<Item=&String> {
|
||||||
|
self.0.iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Compound for Directory {
|
||||||
|
fn load<S: source::Source>(_: &AssetCache<S>, specifier: &str) -> Result<Self, Error> {
|
||||||
|
let root = ASSETS.source().path_of(specifier, "");
|
||||||
|
let mut files = Vec::new();
|
||||||
|
|
||||||
|
get_dir_files(&mut files, &root, specifier)?;
|
||||||
|
|
||||||
|
Ok(Directory(files))
|
||||||
|
}
|
||||||
|
}
|
@ -151,8 +151,9 @@ lazy_static! {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|s| s.to_string())
|
.map(|s| s.to_string())
|
||||||
.collect();
|
.collect();
|
||||||
|
/// TODO: Make this use hot-reloading
|
||||||
static ref ENTITIES: Vec<String> = {
|
static ref ENTITIES: Vec<String> = {
|
||||||
let npc_names = &*npc::NPC_NAMES;
|
let npc_names = &*npc::NPC_NAMES.read();
|
||||||
npc::ALL_NPCS
|
npc::ALL_NPCS
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&npc| npc_names[npc].keyword.clone())
|
.map(|&npc| npc_names[npc].keyword.clone())
|
||||||
|
@ -16,7 +16,8 @@ use arraygen::Arraygen;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use specs::{Component, FlaggedStorage};
|
use specs::{Component, FlaggedStorage};
|
||||||
use specs_idvs::IdvStorage;
|
use specs_idvs::IdvStorage;
|
||||||
use std::{fs::File, io::BufReader, time::Duration};
|
use tracing::error;
|
||||||
|
use std::time::Duration;
|
||||||
use vek::Vec3;
|
use vek::Vec3;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug, Serialize, Deserialize)]
|
||||||
@ -238,10 +239,18 @@ impl Default for CharacterAbility {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Asset for CharacterAbility {
|
impl Asset for CharacterAbility {
|
||||||
const ENDINGS: &'static [&'static str] = &["ron"];
|
const EXTENSION: &'static str = "ron";
|
||||||
|
type Loader = assets::RonLoader;
|
||||||
|
|
||||||
fn parse(buf_reader: BufReader<File>, _specifier: &str) -> Result<Self, assets::Error> {
|
fn default_value(specifier: &str, err: assets::Error) -> Result<Self, assets::Error> {
|
||||||
ron::de::from_reader(buf_reader).map_err(assets::Error::parse_error)
|
error!(
|
||||||
|
?err,
|
||||||
|
"Error loading CharacterAbility: {} for the ability \
|
||||||
|
map: replacing with default",
|
||||||
|
specifier
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(CharacterAbility::default())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,6 @@ use crate::{
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use specs::{Component, FlaggedStorage};
|
use specs::{Component, FlaggedStorage};
|
||||||
use specs_idvs::IdvStorage;
|
use specs_idvs::IdvStorage;
|
||||||
use std::{fs::File, io::BufReader};
|
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
make_case_elim!(
|
make_case_elim!(
|
||||||
@ -123,15 +122,12 @@ impl<'a, BodyMeta, SpeciesMeta> core::ops::Index<&'a Body> for AllBodies<BodyMet
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
BodyMeta: Send + Sync + for<'de> serde::Deserialize<'de>,
|
BodyMeta: Send + Sync + for<'de> serde::Deserialize<'de> + 'static,
|
||||||
SpeciesMeta: Send + Sync + for<'de> serde::Deserialize<'de>,
|
SpeciesMeta: Send + Sync + for<'de> serde::Deserialize<'de> + 'static,
|
||||||
> Asset for AllBodies<BodyMeta, SpeciesMeta>
|
> Asset for AllBodies<BodyMeta, SpeciesMeta>
|
||||||
{
|
{
|
||||||
const ENDINGS: &'static [&'static str] = &["json"];
|
const EXTENSION: &'static str = "json";
|
||||||
|
type Loader = assets::JsonLoader;
|
||||||
fn parse(buf_reader: BufReader<File>, _specifier: &str) -> Result<Self, assets::Error> {
|
|
||||||
serde_json::de::from_reader(buf_reader).map_err(assets::Error::parse_error)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Body {
|
impl Body {
|
||||||
|
@ -5,7 +5,7 @@ pub mod tool;
|
|||||||
pub use tool::{AbilitySet, Hands, Tool, ToolKind, UniqueKind};
|
pub use tool::{AbilitySet, Hands, Tool, ToolKind, UniqueKind};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
assets::{self, Asset, Error},
|
assets::{self, AssetExt, Error},
|
||||||
effect::Effect,
|
effect::Effect,
|
||||||
lottery::Lottery,
|
lottery::Lottery,
|
||||||
terrain::{Block, SpriteKind},
|
terrain::{Block, SpriteKind},
|
||||||
@ -16,8 +16,6 @@ use serde::{Deserialize, Serialize};
|
|||||||
use specs::{Component, FlaggedStorage};
|
use specs::{Component, FlaggedStorage};
|
||||||
use specs_idvs::IdvStorage;
|
use specs_idvs::IdvStorage;
|
||||||
use std::{
|
use std::{
|
||||||
fs::File,
|
|
||||||
io::BufReader,
|
|
||||||
num::{NonZeroU32, NonZeroU64},
|
num::{NonZeroU32, NonZeroU64},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
@ -160,28 +158,37 @@ impl PartialEq for Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Asset for ItemDef {
|
impl assets::Compound for ItemDef {
|
||||||
const ENDINGS: &'static [&'static str] = &["ron"];
|
fn load<S: assets_manager::source::Source>(cache: &assets_manager::AssetCache<S>, specifier: &str) -> Result<Self, Error> {
|
||||||
|
let raw = cache.load_owned::<RawItemDef>(specifier)?;
|
||||||
|
|
||||||
fn parse(buf_reader: BufReader<File>, specifier: &str) -> Result<Self, assets::Error> {
|
let RawItemDef { name, description, kind, quality} = raw;
|
||||||
let item: Result<Self, Error> =
|
let item_definition_id = specifier.replace('\\', ".");
|
||||||
ron::de::from_reader(buf_reader).map_err(Error::parse_error);
|
|
||||||
|
|
||||||
// Some commands like /give_item provide the asset specifier separated with \
|
Ok(ItemDef { item_definition_id, name, description, kind, quality })
|
||||||
// instead of .
|
|
||||||
let specifier = specifier.replace('\\', ".");
|
|
||||||
|
|
||||||
item.map(|item| ItemDef {
|
|
||||||
item_definition_id: specifier,
|
|
||||||
..item
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(rename = "ItemDef")]
|
||||||
|
struct RawItemDef {
|
||||||
|
name: String,
|
||||||
|
description: String,
|
||||||
|
kind: ItemKind,
|
||||||
|
quality: Quality,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl assets::Asset for RawItemDef {
|
||||||
|
const EXTENSION: &'static str = "ron";
|
||||||
|
type Loader = assets::RonLoader;
|
||||||
|
}
|
||||||
|
|
||||||
impl Item {
|
impl Item {
|
||||||
// TODO: consider alternatives such as default abilities that can be added to a
|
// TODO: consider alternatives such as default abilities that can be added to a
|
||||||
// loadout when no weapon is present
|
// loadout when no weapon is present
|
||||||
pub fn empty() -> Self { Item::new(ItemDef::load_expect("common.items.weapons.empty.empty")) }
|
pub fn empty() -> Self {
|
||||||
|
Item::new_from_asset_expect("common.items.weapons.empty.empty")
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new(inner_item: Arc<ItemDef>) -> Self {
|
pub fn new(inner_item: Arc<ItemDef>) -> Self {
|
||||||
Item {
|
Item {
|
||||||
@ -194,27 +201,36 @@ impl Item {
|
|||||||
/// Creates a new instance of an `Item` from the provided asset identifier
|
/// Creates a new instance of an `Item` from the provided asset identifier
|
||||||
/// Panics if the asset does not exist.
|
/// Panics if the asset does not exist.
|
||||||
pub fn new_from_asset_expect(asset_specifier: &str) -> Self {
|
pub fn new_from_asset_expect(asset_specifier: &str) -> Self {
|
||||||
let inner_item = ItemDef::load_expect(asset_specifier);
|
let inner_item = Arc::<ItemDef>::load_expect_cloned(asset_specifier);
|
||||||
Item::new(inner_item)
|
Item::new(inner_item)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a Vec containing one of each item that matches the provided
|
/// Creates a Vec containing one of each item that matches the provided
|
||||||
/// asset glob pattern
|
/// asset glob pattern
|
||||||
pub fn new_from_asset_glob(asset_glob: &str) -> Result<Vec<Self>, Error> {
|
pub fn new_from_asset_glob(asset_glob: &str) -> Result<Vec<Self>, Error> {
|
||||||
let items = ItemDef::load_glob(asset_glob)?;
|
//let items = ItemDef::load_glob(asset_glob)?;
|
||||||
|
|
||||||
|
let specifiers = assets::Directory::load(asset_glob)?;
|
||||||
|
|
||||||
|
specifiers.read()
|
||||||
|
.iter()
|
||||||
|
.map(|spec| Self::new_from_asset(&spec))
|
||||||
|
.collect()
|
||||||
|
|
||||||
|
/*
|
||||||
let result = items
|
let result = items
|
||||||
.iter()
|
.iter()
|
||||||
.map(|item_def| Item::new(Arc::clone(item_def)))
|
.map(|item_def| Item::new(Arc::clone(item_def)))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new instance of an `Item from the provided asset identifier if
|
/// Creates a new instance of an `Item from the provided asset identifier if
|
||||||
/// it exists
|
/// it exists
|
||||||
pub fn new_from_asset(asset: &str) -> Result<Self, Error> {
|
pub fn new_from_asset(asset: &str) -> Result<Self, Error> {
|
||||||
let inner_item = ItemDef::load(asset)?;
|
let inner_item = Arc::<ItemDef>::load_cloned(asset)?;
|
||||||
Ok(Item::new(inner_item))
|
Ok(Item::new(inner_item))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,30 +270,30 @@ impl Item {
|
|||||||
/// up by another player.
|
/// up by another player.
|
||||||
pub fn put_in_world(&mut self) { self.reset_item_id() }
|
pub fn put_in_world(&mut self) { self.reset_item_id() }
|
||||||
|
|
||||||
pub fn increase_amount(&mut self, increase_by: u32) -> Result<(), assets::Error> {
|
pub fn increase_amount(&mut self, increase_by: u32) -> Result<(), ()> {
|
||||||
let amount = u32::from(self.amount);
|
let amount = u32::from(self.amount);
|
||||||
self.amount = amount
|
self.amount = amount
|
||||||
.checked_add(increase_by)
|
.checked_add(increase_by)
|
||||||
.and_then(NonZeroU32::new)
|
.and_then(NonZeroU32::new)
|
||||||
.ok_or(assets::Error::InvalidType)?;
|
.ok_or(())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decrease_amount(&mut self, decrease_by: u32) -> Result<(), assets::Error> {
|
pub fn decrease_amount(&mut self, decrease_by: u32) -> Result<(), ()> {
|
||||||
let amount = u32::from(self.amount);
|
let amount = u32::from(self.amount);
|
||||||
self.amount = amount
|
self.amount = amount
|
||||||
.checked_sub(decrease_by)
|
.checked_sub(decrease_by)
|
||||||
.and_then(NonZeroU32::new)
|
.and_then(NonZeroU32::new)
|
||||||
.ok_or(assets::Error::InvalidType)?;
|
.ok_or(())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_amount(&mut self, give_amount: u32) -> Result<(), assets::Error> {
|
pub fn set_amount(&mut self, give_amount: u32) -> Result<(), ()> {
|
||||||
if give_amount == 1 || self.item_def.is_stackable() {
|
if give_amount == 1 || self.item_def.is_stackable() {
|
||||||
self.amount = NonZeroU32::new(give_amount).ok_or(assets::Error::InvalidType)?;
|
self.amount = NonZeroU32::new(give_amount).ok_or(())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(assets::Error::InvalidType)
|
Err(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,7 +343,7 @@ impl Item {
|
|||||||
3 => "common.loot_tables.loot_table_armor_cloth",
|
3 => "common.loot_tables.loot_table_armor_cloth",
|
||||||
4 => "common.loot_tables.loot_table_armor_heavy",
|
4 => "common.loot_tables.loot_table_armor_heavy",
|
||||||
_ => "common.loot_tables.loot_table_armor_misc",
|
_ => "common.loot_tables.loot_table_armor_misc",
|
||||||
});
|
}).read();
|
||||||
chosen.choose()
|
chosen.choose()
|
||||||
},
|
},
|
||||||
SpriteKind::ChestBurried => {
|
SpriteKind::ChestBurried => {
|
||||||
@ -336,7 +352,7 @@ impl Item {
|
|||||||
2 => "common.loot_tables.loot_table_armor_light",
|
2 => "common.loot_tables.loot_table_armor_light",
|
||||||
3 => "common.loot_tables.loot_table_armor_cloth",
|
3 => "common.loot_tables.loot_table_armor_cloth",
|
||||||
_ => "common.loot_tables.loot_table_armor_misc",
|
_ => "common.loot_tables.loot_table_armor_misc",
|
||||||
});
|
}).read();
|
||||||
chosen.choose()
|
chosen.choose()
|
||||||
},
|
},
|
||||||
SpriteKind::Mud => {
|
SpriteKind::Mud => {
|
||||||
@ -345,14 +361,14 @@ impl Item {
|
|||||||
1 => "common.loot_tables.loot_table_weapon_common",
|
1 => "common.loot_tables.loot_table_weapon_common",
|
||||||
2 => "common.loot_tables.loot_table_armor_misc",
|
2 => "common.loot_tables.loot_table_armor_misc",
|
||||||
_ => "common.loot_tables.loot_table_rocks",
|
_ => "common.loot_tables.loot_table_rocks",
|
||||||
});
|
}).read();
|
||||||
chosen.choose()
|
chosen.choose()
|
||||||
},
|
},
|
||||||
SpriteKind::Crate => {
|
SpriteKind::Crate => {
|
||||||
chosen = Lottery::<String>::load_expect(match rng.gen_range(0, 4) {
|
chosen = Lottery::<String>::load_expect(match rng.gen_range(0, 4) {
|
||||||
0 => "common.loot_tables.loot_table_crafting",
|
0 => "common.loot_tables.loot_table_crafting",
|
||||||
_ => "common.loot_tables.loot_table_food",
|
_ => "common.loot_tables.loot_table_food",
|
||||||
});
|
}).read();
|
||||||
chosen.choose()
|
chosen.choose()
|
||||||
},
|
},
|
||||||
SpriteKind::Beehive => "common.items.crafting_ing.honey",
|
SpriteKind::Beehive => "common.items.crafting_ing.honey",
|
||||||
|
@ -7,7 +7,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{fs::File, io::BufReader, time::Duration};
|
use std::{time::Duration};
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
@ -107,7 +107,7 @@ pub struct AbilitySet<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AbilitySet<CharacterAbility> {
|
impl AbilitySet<CharacterAbility> {
|
||||||
pub fn modified_by_tool(self, tool: &Tool) -> Self {
|
fn modified_by_tool(self, tool: &Tool) -> Self {
|
||||||
self.map(|a| a.adjusted_by_stats(tool.base_power(), tool.base_speed()))
|
self.map(|a| a.adjusted_by_stats(tool.base_power(), tool.base_speed()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -120,6 +120,14 @@ impl<T> AbilitySet<T> {
|
|||||||
skills: self.skills.into_iter().map(|x| f(x)).collect(),
|
skills: self.skills.into_iter().map(|x| f(x)).collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn map_ref<U, F: FnMut(&T) -> U>(&self, mut f: F) -> AbilitySet<U> {
|
||||||
|
AbilitySet {
|
||||||
|
primary: f(&self.primary),
|
||||||
|
secondary: f(&self.secondary),
|
||||||
|
skills: self.skills.iter().map(|x| f(x)).collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for AbilitySet<CharacterAbility> {
|
impl Default for AbilitySet<CharacterAbility> {
|
||||||
@ -135,37 +143,28 @@ impl Default for AbilitySet<CharacterAbility> {
|
|||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct AbilityMap<T = CharacterAbility>(HashMap<ToolKind, AbilitySet<T>>);
|
pub struct AbilityMap<T = CharacterAbility>(HashMap<ToolKind, AbilitySet<T>>);
|
||||||
|
|
||||||
impl Asset for AbilityMap {
|
impl Asset for AbilityMap<String> {
|
||||||
const ENDINGS: &'static [&'static str] = &["ron"];
|
const EXTENSION: &'static str = "ron";
|
||||||
|
type Loader = assets::RonLoader;
|
||||||
|
}
|
||||||
|
|
||||||
fn parse(buf_reader: BufReader<File>, specifier: &str) -> Result<Self, assets::Error> {
|
impl assets::Compound for AbilityMap {
|
||||||
ron::de::from_reader::<BufReader<File>, AbilityMap<String>>(buf_reader)
|
fn load<S: assets_manager::source::Source>(cache: &assets_manager::AssetCache<S>, specifier: &str) -> Result<Self, assets::Error> {
|
||||||
.map(|map| {
|
let manifest = cache.load::<AbilityMap<String>>(specifier)?.read();
|
||||||
AbilityMap(
|
|
||||||
map.0
|
Ok(AbilityMap(
|
||||||
.into_iter()
|
manifest.0
|
||||||
.map(|(kind, set)| {
|
.iter()
|
||||||
(
|
.map(|(kind, set)| {
|
||||||
kind,
|
(
|
||||||
set.map(|s| match CharacterAbility::load(&s) {
|
kind.clone(),
|
||||||
Ok(ability) => ability.as_ref().clone(),
|
// expect cannot fail because CharacterAbility always
|
||||||
Err(err) => {
|
// provides a default value in case of failure
|
||||||
error!(
|
set.map_ref(|s| cache.load_expect(&s).cloned())
|
||||||
?err,
|
)
|
||||||
"Error loading CharacterAbility: {} for the ability \
|
})
|
||||||
map: {} replacing with default",
|
.collect()
|
||||||
s,
|
))
|
||||||
specifier
|
|
||||||
);
|
|
||||||
CharacterAbility::default()
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.map_err(assets::Error::parse_error)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,7 +139,7 @@ fn loadout_insert(
|
|||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use veloren_common::{
|
/// use veloren_common::{
|
||||||
/// assets::Asset,
|
/// assets::AssetExt,
|
||||||
/// comp::{
|
/// comp::{
|
||||||
/// item::tool::AbilityMap,
|
/// item::tool::AbilityMap,
|
||||||
/// slot::{loadout_remove, EquipSlot},
|
/// slot::{loadout_remove, EquipSlot},
|
||||||
@ -263,7 +263,7 @@ pub fn swap(
|
|||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use veloren_common::{
|
/// use veloren_common::{
|
||||||
/// assets::Asset,
|
/// assets::AssetExt,
|
||||||
/// comp::{
|
/// comp::{
|
||||||
/// item::tool::AbilityMap,
|
/// item::tool::AbilityMap,
|
||||||
/// slot::{equip, EquipSlot},
|
/// slot::{equip, EquipSlot},
|
||||||
@ -324,7 +324,7 @@ pub fn equip(slot: usize, inventory: &mut Inventory, loadout: &mut Loadout, map:
|
|||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use veloren_common::{
|
/// use veloren_common::{
|
||||||
/// assets::Asset,
|
/// assets::AssetExt,
|
||||||
/// comp::{
|
/// comp::{
|
||||||
/// item::tool::AbilityMap,
|
/// item::tool::AbilityMap,
|
||||||
/// slot::{unequip, EquipSlot},
|
/// slot::{unequip, EquipSlot},
|
||||||
@ -365,7 +365,7 @@ pub fn unequip(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{comp::Item, LoadoutBuilder};
|
use crate::{assets::AssetExt, comp::Item, LoadoutBuilder};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_unequip_items_both_hands() {
|
fn test_unequip_items_both_hands() {
|
||||||
@ -374,7 +374,6 @@ mod tests {
|
|||||||
amount: 0,
|
amount: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::assets::Asset;
|
|
||||||
let map = AbilityMap::load_expect_cloned("common.abilities.weapon_ability_manifest");
|
let map = AbilityMap::load_expect_cloned("common.abilities.weapon_ability_manifest");
|
||||||
|
|
||||||
let sword = LoadoutBuilder::default_item_config_from_str(
|
let sword = LoadoutBuilder::default_item_config_from_str(
|
||||||
@ -419,7 +418,6 @@ mod tests {
|
|||||||
|
|
||||||
let mut loadout = LoadoutBuilder::new().defaults().build();
|
let mut loadout = LoadoutBuilder::new().defaults().build();
|
||||||
|
|
||||||
use crate::assets::Asset;
|
|
||||||
let map = AbilityMap::load_expect_cloned("common.abilities.weapon_ability_manifest");
|
let map = AbilityMap::load_expect_cloned("common.abilities.weapon_ability_manifest");
|
||||||
|
|
||||||
// We should start with the starting sandles
|
// We should start with the starting sandles
|
||||||
@ -444,7 +442,6 @@ mod tests {
|
|||||||
"common.items.armor.starter.sandals_0",
|
"common.items.armor.starter.sandals_0",
|
||||||
));
|
));
|
||||||
|
|
||||||
use crate::assets::Asset;
|
|
||||||
let map = AbilityMap::load_expect_cloned("common.abilities.weapon_ability_manifest");
|
let map = AbilityMap::load_expect_cloned("common.abilities.weapon_ability_manifest");
|
||||||
|
|
||||||
let mut loadout = LoadoutBuilder::new().defaults().build();
|
let mut loadout = LoadoutBuilder::new().defaults().build();
|
||||||
@ -469,7 +466,6 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_loadout_remove() {
|
fn test_loadout_remove() {
|
||||||
use crate::assets::Asset;
|
|
||||||
let map = AbilityMap::load_expect_cloned("common.abilities.weapon_ability_manifest");
|
let map = AbilityMap::load_expect_cloned("common.abilities.weapon_ability_manifest");
|
||||||
|
|
||||||
let sword = LoadoutBuilder::default_item_config_from_str(
|
let sword = LoadoutBuilder::default_item_config_from_str(
|
||||||
|
@ -115,22 +115,23 @@ impl EntityInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_automatic_name(mut self) -> Self {
|
pub fn with_automatic_name(mut self) -> Self {
|
||||||
|
let npc_names = NPC_NAMES.read();
|
||||||
self.name = match &self.body {
|
self.name = match &self.body {
|
||||||
Body::Humanoid(body) => Some(get_npc_name(&NPC_NAMES.humanoid, body.species)),
|
Body::Humanoid(body) => Some(get_npc_name(&npc_names.humanoid, body.species)),
|
||||||
Body::QuadrupedMedium(body) => {
|
Body::QuadrupedMedium(body) => {
|
||||||
Some(get_npc_name(&NPC_NAMES.quadruped_medium, body.species))
|
Some(get_npc_name(&npc_names.quadruped_medium, body.species))
|
||||||
},
|
},
|
||||||
Body::BirdMedium(body) => Some(get_npc_name(&NPC_NAMES.bird_medium, body.species)),
|
Body::BirdMedium(body) => Some(get_npc_name(&npc_names.bird_medium, body.species)),
|
||||||
Body::FishSmall(body) => Some(get_npc_name(&NPC_NAMES.fish_small, body.species)),
|
Body::FishSmall(body) => Some(get_npc_name(&npc_names.fish_small, body.species)),
|
||||||
Body::FishMedium(body) => Some(get_npc_name(&NPC_NAMES.fish_medium, body.species)),
|
Body::FishMedium(body) => Some(get_npc_name(&npc_names.fish_medium, body.species)),
|
||||||
Body::Theropod(body) => Some(get_npc_name(&NPC_NAMES.theropod, body.species)),
|
Body::Theropod(body) => Some(get_npc_name(&npc_names.theropod, body.species)),
|
||||||
Body::QuadrupedSmall(body) => {
|
Body::QuadrupedSmall(body) => {
|
||||||
Some(get_npc_name(&NPC_NAMES.quadruped_small, body.species))
|
Some(get_npc_name(&npc_names.quadruped_small, body.species))
|
||||||
},
|
},
|
||||||
Body::Dragon(body) => Some(get_npc_name(&NPC_NAMES.dragon, body.species)),
|
Body::Dragon(body) => Some(get_npc_name(&npc_names.dragon, body.species)),
|
||||||
Body::QuadrupedLow(body) => Some(get_npc_name(&NPC_NAMES.quadruped_low, body.species)),
|
Body::QuadrupedLow(body) => Some(get_npc_name(&npc_names.quadruped_low, body.species)),
|
||||||
Body::Golem(body) => Some(get_npc_name(&NPC_NAMES.golem, body.species)),
|
Body::Golem(body) => Some(get_npc_name(&npc_names.golem, body.species)),
|
||||||
Body::BipedLarge(body) => Some(get_npc_name(&NPC_NAMES.biped_large, body.species)),
|
Body::BipedLarge(body) => Some(get_npc_name(&npc_names.biped_large, body.species)),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
.map(|s| {
|
.map(|s| {
|
||||||
|
@ -11,7 +11,7 @@ use rand::Rng;
|
|||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use veloren_common::{
|
/// use veloren_common::{
|
||||||
/// assets::Asset,
|
/// assets::AssetExt,
|
||||||
/// comp::item::tool::AbilityMap,
|
/// comp::item::tool::AbilityMap,
|
||||||
/// LoadoutBuilder,
|
/// LoadoutBuilder,
|
||||||
/// };
|
/// };
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use crate::assets::{self, Asset};
|
use crate::assets;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use serde::{de::DeserializeOwned, Deserialize};
|
use serde::{de::DeserializeOwned, Deserialize};
|
||||||
use std::{fs::File, io::BufReader};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
||||||
pub struct Lottery<T> {
|
pub struct Lottery<T> {
|
||||||
@ -9,28 +8,25 @@ pub struct Lottery<T> {
|
|||||||
total: f32,
|
total: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: DeserializeOwned + Send + Sync> Asset for Lottery<T> {
|
impl<T: DeserializeOwned + Send + Sync + 'static> assets::Asset for Lottery<T> {
|
||||||
const ENDINGS: &'static [&'static str] = &["ron"];
|
const EXTENSION: &'static str = "ron";
|
||||||
|
type Loader = assets::LoadFrom<Vec<(f32, T)>, assets::RonLoader>;
|
||||||
|
}
|
||||||
|
|
||||||
fn parse(buf_reader: BufReader<File>, _specifier: &str) -> Result<Self, assets::Error> {
|
impl<T> From<Vec<(f32, T)>> for Lottery<T> {
|
||||||
ron::de::from_reader::<BufReader<File>, Vec<(f32, T)>>(buf_reader)
|
fn from(mut items: Vec<(f32, T)>) -> Lottery<T> {
|
||||||
.map(|items| Lottery::from_rates(items.into_iter()))
|
let mut total = 0.0;
|
||||||
.map_err(assets::Error::parse_error)
|
|
||||||
|
for (rate, _) in &mut items {
|
||||||
|
total += *rate;
|
||||||
|
*rate = total - *rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
Self { items, total }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Lottery<T> {
|
impl<T> Lottery<T> {
|
||||||
pub fn from_rates(items: impl Iterator<Item = (f32, T)>) -> Self {
|
|
||||||
let mut total = 0.0;
|
|
||||||
let items = items
|
|
||||||
.map(|(rate, item)| {
|
|
||||||
total += rate;
|
|
||||||
(total - rate, item)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
Self { items, total }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn choose_seeded(&self, seed: u32) -> &T {
|
pub fn choose_seeded(&self, seed: u32) -> &T {
|
||||||
let x = ((seed % 65536) as f32 / 65536.0) * self.total;
|
let x = ((seed % 65536) as f32 / 65536.0) * self.total;
|
||||||
&self.items[self
|
&self.items[self
|
||||||
@ -48,13 +44,13 @@ impl<T> Lottery<T> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{assets::Asset, comp::Item};
|
use crate::{assets::AssetExt, comp::Item};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_loot_table() {
|
fn test_loot_table() {
|
||||||
let test = Lottery::<String>::load_expect("common.loot_tables.loot_table");
|
let test = Lottery::<String>::load_expect("common.loot_tables.loot_table");
|
||||||
|
|
||||||
for (_, item_asset_specifier) in test.iter() {
|
for (_, item_asset_specifier) in test.read().iter() {
|
||||||
assert!(
|
assert!(
|
||||||
Item::new_from_asset(item_asset_specifier).is_ok(),
|
Item::new_from_asset(item_asset_specifier).is_ok(),
|
||||||
"Invalid loot table item '{}'",
|
"Invalid loot table item '{}'",
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
assets::Asset,
|
assets::{AssetExt, AssetHandle},
|
||||||
comp::{self, AllBodies, Body},
|
comp::{self, AllBodies, Body},
|
||||||
};
|
};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use rand::seq::SliceRandom;
|
use rand::seq::SliceRandom;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::{str::FromStr, sync::Arc};
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
pub enum NpcKind {
|
pub enum NpcKind {
|
||||||
@ -67,14 +67,14 @@ pub struct SpeciesNames {
|
|||||||
pub type NpcNames = AllBodies<BodyNames, SpeciesNames>;
|
pub type NpcNames = AllBodies<BodyNames, SpeciesNames>;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref NPC_NAMES: Arc<NpcNames> = NpcNames::load_expect("common.npc_names");
|
pub static ref NPC_NAMES: AssetHandle<NpcNames> = NpcNames::load_expect("common.npc_names");
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for NpcKind {
|
impl FromStr for NpcKind {
|
||||||
type Err = ();
|
type Err = ();
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, ()> {
|
||||||
let npc_names = &*NPC_NAMES;
|
let npc_names = &*NPC_NAMES.read();
|
||||||
ALL_NPCS
|
ALL_NPCS
|
||||||
.iter()
|
.iter()
|
||||||
.copied()
|
.copied()
|
||||||
@ -83,11 +83,12 @@ impl FromStr for NpcKind {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_npc_name(npc_type: NpcKind) -> &'static str {
|
pub fn get_npc_name(npc_type: NpcKind) -> String {
|
||||||
let BodyNames { keyword, names } = &NPC_NAMES[npc_type];
|
let npc_names = NPC_NAMES.read();
|
||||||
|
let BodyNames { keyword, names } = &npc_names[npc_type];
|
||||||
|
|
||||||
// If no pretty name is found, fall back to the keyword.
|
// If no pretty name is found, fall back to the keyword.
|
||||||
names.choose(&mut rand::thread_rng()).unwrap_or(keyword)
|
names.choose(&mut rand::thread_rng()).unwrap_or(keyword).clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Randomly generates a body associated with this NPC kind.
|
/// Randomly generates a body associated with this NPC kind.
|
||||||
@ -164,7 +165,7 @@ impl NpcBody {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
let npc_names = &NPC_NAMES;
|
let npc_names = &*NPC_NAMES.read();
|
||||||
// First, parse npc kind names.
|
// First, parse npc kind names.
|
||||||
NpcKind::from_str(s)
|
NpcKind::from_str(s)
|
||||||
.map(|kind| NpcBody(kind, Box::new(move || kind_to_body(kind))))
|
.map(|kind| NpcBody(kind, Box::new(move || kind_to_body(kind))))
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
assets::{self, Asset},
|
assets::{self, AssetExt, AssetHandle},
|
||||||
comp::{item::ItemDef, Inventory, Item},
|
comp::{item::ItemDef, Inventory, Item},
|
||||||
};
|
};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{fs::File, io::BufReader, sync::Arc};
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct Recipe {
|
pub struct Recipe {
|
||||||
@ -65,36 +65,40 @@ impl RecipeBook {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Asset for RecipeBook {
|
#[derive(Deserialize)]
|
||||||
const ENDINGS: &'static [&'static str] = &["ron"];
|
#[serde(transparent)]
|
||||||
|
struct RawRecipeBook(HashMap<String, ((String, u32), Vec<(String, u32)>)>);
|
||||||
|
|
||||||
fn parse(buf_reader: BufReader<File>, _specifier: &str) -> Result<Self, assets::Error> {
|
impl assets::Asset for RawRecipeBook {
|
||||||
ron::de::from_reader::<
|
const EXTENSION: &'static str = "ron";
|
||||||
BufReader<File>,
|
type Loader = assets::RonLoader;
|
||||||
HashMap<String, ((String, u32), Vec<(String, u32)>)>,
|
}
|
||||||
>(buf_reader)
|
|
||||||
.map_err(assets::Error::parse_error)
|
impl assets::Compound for RecipeBook {
|
||||||
.and_then(|recipes| {
|
|
||||||
Ok(RecipeBook {
|
fn load<S: assets_manager::source::Source>(cache: &assets_manager::AssetCache<S>, specifier: &str) -> Result<Self, assets_manager::Error> {
|
||||||
recipes: recipes
|
#[inline]
|
||||||
.into_iter()
|
fn load_item_def(spec: &(String, u32)) -> Result<(Arc<ItemDef>, u32), assets::Error> {
|
||||||
.map::<Result<(String, Recipe), assets::Error>, _>(
|
let def = Arc::<ItemDef>::load_cloned(&spec.0)?;
|
||||||
|(name, ((output, amount), inputs))| {
|
Ok((def, spec.1))
|
||||||
Ok((name, Recipe {
|
}
|
||||||
output: (ItemDef::load(&output)?, amount),
|
|
||||||
inputs: inputs
|
let raw = cache.load::<RawRecipeBook>(specifier)?.read();
|
||||||
.into_iter()
|
|
||||||
.map::<Result<(Arc<ItemDef>, u32), assets::Error>, _>(
|
let recipes = raw.0.iter()
|
||||||
|(name, amount)| Ok((ItemDef::load(&name)?, amount)),
|
.map(|(name, (output, inputs))| {
|
||||||
)
|
let inputs = inputs.iter()
|
||||||
.collect::<Result<_, _>>()?,
|
.map(load_item_def)
|
||||||
}))
|
.collect::<Result<_, _>>()?;
|
||||||
},
|
let output = load_item_def(output)?;
|
||||||
)
|
Ok((name.clone(), Recipe { inputs, output }))
|
||||||
.collect::<Result<_, _>>()?,
|
|
||||||
})
|
})
|
||||||
})
|
.collect::<Result<_, assets::Error>>()?;
|
||||||
|
|
||||||
|
Ok(RecipeBook { recipes })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn default_recipe_book() -> Arc<RecipeBook> { RecipeBook::load_expect("common.recipe_book") }
|
pub fn default_recipe_book() -> AssetHandle<RecipeBook> {
|
||||||
|
RecipeBook::load_expect("common.recipe_book")
|
||||||
|
}
|
||||||
|
@ -13,7 +13,7 @@ pub use self::{
|
|||||||
map::MapSizeLg,
|
map::MapSizeLg,
|
||||||
site::SitesKind,
|
site::SitesKind,
|
||||||
sprite::SpriteKind,
|
sprite::SpriteKind,
|
||||||
structure::Structure,
|
structure::{Structure, StructuresGroup},
|
||||||
};
|
};
|
||||||
use roots::find_roots_cubic;
|
use roots::find_roots_cubic;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
use super::BlockKind;
|
use super::BlockKind;
|
||||||
use crate::{
|
use crate::{
|
||||||
assets::{self, Asset, Ron},
|
assets::{self, AssetExt, AssetHandle, DotVoxAsset, Error},
|
||||||
make_case_elim,
|
make_case_elim,
|
||||||
vol::{BaseVol, ReadVol, SizedVol, WriteVol},
|
vol::{BaseVol, ReadVol, SizedVol, WriteVol},
|
||||||
volumes::dyna::{Dyna, DynaError},
|
volumes::dyna::{Dyna, DynaError},
|
||||||
};
|
};
|
||||||
use dot_vox::DotVoxData;
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::{fs::File, io::BufReader, sync::Arc};
|
use std::sync::Arc;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
make_case_elim!(
|
make_case_elim!(
|
||||||
@ -40,20 +39,46 @@ pub enum StructureError {}
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Structure {
|
pub struct Structure {
|
||||||
center: Vec3<i32>,
|
center: Vec3<i32>,
|
||||||
|
base: Arc<BaseStructure>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BaseStructure {
|
||||||
vol: Dyna<StructureBlock, ()>,
|
vol: Dyna<StructureBlock, ()>,
|
||||||
empty: StructureBlock,
|
empty: StructureBlock,
|
||||||
default_kind: BlockKind,
|
default_kind: BlockKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct StructuresGroup(Vec<Structure>);
|
||||||
|
|
||||||
|
impl std::ops::Deref for StructuresGroup {
|
||||||
|
type Target = [Structure];
|
||||||
|
|
||||||
|
fn deref(&self) -> &[Structure] {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl assets::Compound for StructuresGroup {
|
||||||
|
fn load<S: assets_manager::source::Source>(cache: &assets_manager::AssetCache<S>, specifier: &str) -> Result<Self, Error> {
|
||||||
|
let specs = cache.load::<StructuresGroupSpec>(specifier)?.read();
|
||||||
|
|
||||||
|
Ok(StructuresGroup(
|
||||||
|
specs.0.iter()
|
||||||
|
.map(|sp| {
|
||||||
|
let base = cache.load::<Arc<BaseStructure>>(&sp.specifier)?.cloned();
|
||||||
|
Ok(Structure {
|
||||||
|
center: Vec3::from(sp.center),
|
||||||
|
base,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Result<_, Error>>()?
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Structure {
|
impl Structure {
|
||||||
pub fn load_group(specifier: &str) -> Vec<Arc<Structure>> {
|
pub fn load_group(specifier: &str) -> AssetHandle<StructuresGroup> {
|
||||||
let spec = StructuresSpec::load_expect(&["world.manifests.", specifier].concat());
|
StructuresGroup::load_expect(&["world.manifests.", specifier].concat())
|
||||||
spec.iter()
|
|
||||||
.map(|sp| {
|
|
||||||
Structure::load_map(&sp.specifier[..], |s| s.with_center(Vec3::from(sp.center)))
|
|
||||||
.unwrap()
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_center(mut self, center: Vec3<i32>) -> Self {
|
pub fn with_center(mut self, center: Vec3<i32>) -> Self {
|
||||||
@ -61,19 +86,14 @@ impl Structure {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_default_kind(mut self, kind: BlockKind) -> Self {
|
|
||||||
self.default_kind = kind;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_bounds(&self) -> Aabb<i32> {
|
pub fn get_bounds(&self) -> Aabb<i32> {
|
||||||
Aabb {
|
Aabb {
|
||||||
min: -self.center,
|
min: -self.center,
|
||||||
max: self.vol.size().map(|e| e as i32) - self.center,
|
max: self.base.vol.size().map(|e| e as i32) - self.center,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn default_kind(&self) -> BlockKind { self.default_kind }
|
pub fn default_kind(&self) -> BlockKind { self.base.default_kind }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BaseVol for Structure {
|
impl BaseVol for Structure {
|
||||||
@ -84,18 +104,17 @@ impl BaseVol for Structure {
|
|||||||
impl ReadVol for Structure {
|
impl ReadVol for Structure {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn get(&self, pos: Vec3<i32>) -> Result<&Self::Vox, StructureError> {
|
fn get(&self, pos: Vec3<i32>) -> Result<&Self::Vox, StructureError> {
|
||||||
match self.vol.get(pos + self.center) {
|
match self.base.vol.get(pos + self.center) {
|
||||||
Ok(block) => Ok(block),
|
Ok(block) => Ok(block),
|
||||||
Err(DynaError::OutOfBounds) => Ok(&self.empty),
|
Err(DynaError::OutOfBounds) => Ok(&self.base.empty),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Asset for Structure {
|
impl assets::Compound for BaseStructure {
|
||||||
const ENDINGS: &'static [&'static str] = &["vox"];
|
fn load<S: assets_manager::source::Source>(cache: &assets_manager::AssetCache<S>, specifier: &str) -> Result<Self, Error> {
|
||||||
|
let dot_vox_data = cache.load::<DotVoxAsset>(specifier)?.read();
|
||||||
fn parse(buf_reader: BufReader<File>, specifier: &str) -> Result<Self, assets::Error> {
|
let dot_vox_data = &dot_vox_data.0;
|
||||||
let dot_vox_data = DotVoxData::parse(buf_reader, specifier)?;
|
|
||||||
|
|
||||||
if let Some(model) = dot_vox_data.models.get(0) {
|
if let Some(model) = dot_vox_data.models.get(0) {
|
||||||
let palette = dot_vox_data
|
let palette = dot_vox_data
|
||||||
@ -138,15 +157,13 @@ impl Asset for Structure {
|
|||||||
let _ = vol.set(Vec3::new(voxel.x, voxel.y, voxel.z).map(i32::from), block);
|
let _ = vol.set(Vec3::new(voxel.x, voxel.y, voxel.z).map(i32::from), block);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Structure {
|
Ok(BaseStructure {
|
||||||
center: Vec3::zero(),
|
|
||||||
vol,
|
vol,
|
||||||
empty: StructureBlock::None,
|
empty: StructureBlock::None,
|
||||||
default_kind: BlockKind::Misc,
|
default_kind: BlockKind::Misc,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Ok(Self {
|
Ok(BaseStructure {
|
||||||
center: Vec3::zero(),
|
|
||||||
vol: Dyna::filled(Vec3::zero(), StructureBlock::None, ()),
|
vol: Dyna::filled(Vec3::zero(), StructureBlock::None, ()),
|
||||||
empty: StructureBlock::None,
|
empty: StructureBlock::None,
|
||||||
default_kind: BlockKind::Misc,
|
default_kind: BlockKind::Misc,
|
||||||
@ -161,4 +178,10 @@ struct StructureSpec {
|
|||||||
center: [i32; 3],
|
center: [i32; 3],
|
||||||
}
|
}
|
||||||
|
|
||||||
type StructuresSpec = Ron<Vec<StructureSpec>>;
|
#[derive(Deserialize)]
|
||||||
|
struct StructuresGroupSpec(Vec<StructureSpec>);
|
||||||
|
|
||||||
|
impl assets::Asset for StructuresGroupSpec {
|
||||||
|
const EXTENSION: &'static str = "ron";
|
||||||
|
type Loader = assets::RonLoader;
|
||||||
|
}
|
@ -777,7 +777,7 @@ fn handle_spawn(
|
|||||||
.state
|
.state
|
||||||
.create_npc(
|
.create_npc(
|
||||||
pos,
|
pos,
|
||||||
comp::Stats::new(get_npc_name(id).into(), body),
|
comp::Stats::new(get_npc_name(id), body),
|
||||||
comp::Health::new(body, 1),
|
comp::Health::new(body, 1),
|
||||||
loadout,
|
loadout,
|
||||||
body,
|
body,
|
||||||
|
@ -5,7 +5,7 @@ use crate::{
|
|||||||
Server, SpawnPoint, StateExt,
|
Server, SpawnPoint, StateExt,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
assets::Asset,
|
assets::AssetExt,
|
||||||
comp::{
|
comp::{
|
||||||
self, aura, buff,
|
self, aura, buff,
|
||||||
chat::{KillSource, KillType},
|
chat::{KillSource, KillType},
|
||||||
@ -436,7 +436,7 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc
|
|||||||
let item = {
|
let item = {
|
||||||
let mut item_drops = state.ecs().write_storage::<comp::ItemDrop>();
|
let mut item_drops = state.ecs().write_storage::<comp::ItemDrop>();
|
||||||
item_drops.remove(entity).map_or_else(
|
item_drops.remove(entity).map_or_else(
|
||||||
|| Item::new_from_asset_expect(lottery().choose()),
|
|| Item::new_from_asset_expect(lottery().read().choose()),
|
||||||
|item_drop| item_drop.0,
|
|item_drop| item_drop.0,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
@ -446,7 +446,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
|
|||||||
.write_storage::<comp::Inventory>()
|
.write_storage::<comp::Inventory>()
|
||||||
.get_mut(entity)
|
.get_mut(entity)
|
||||||
{
|
{
|
||||||
let recipe_book = default_recipe_book();
|
let recipe_book = default_recipe_book().read();
|
||||||
let craft_result = recipe_book.get(&recipe).and_then(|r| r.perform(inv).ok());
|
let craft_result = recipe_book.get(&recipe).and_then(|r| r.perform(inv).ok());
|
||||||
|
|
||||||
// FIXME: We should really require the drop and write to be atomic!
|
// FIXME: We should really require the drop and write to be atomic!
|
||||||
|
@ -47,7 +47,7 @@ use crate::{
|
|||||||
sys::sentinel::{DeletedEntities, TrackedComps},
|
sys::sentinel::{DeletedEntities, TrackedComps},
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
assets::Asset,
|
assets::AssetExt,
|
||||||
cmd::ChatCommand,
|
cmd::ChatCommand,
|
||||||
comp,
|
comp,
|
||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
@ -939,7 +939,7 @@ impl Server {
|
|||||||
max_group_size: self.settings().max_player_group_size,
|
max_group_size: self.settings().max_player_group_size,
|
||||||
client_timeout: self.settings().client_timeout,
|
client_timeout: self.settings().client_timeout,
|
||||||
world_map: self.map.clone(),
|
world_map: self.map.clone(),
|
||||||
recipe_book: (&*default_recipe_book()).clone(),
|
recipe_book: default_recipe_book().cloned(),
|
||||||
ability_map: (&*self
|
ability_map: (&*self
|
||||||
.state
|
.state
|
||||||
.ecs()
|
.ecs()
|
||||||
|
@ -145,11 +145,13 @@ impl<'a> System<'a> for Sys {
|
|||||||
if entity.is_giant {
|
if entity.is_giant {
|
||||||
if rand::random::<f32>() < 0.65 && entity.alignment != Alignment::Enemy {
|
if rand::random::<f32>() < 0.65 && entity.alignment != Alignment::Enemy {
|
||||||
let body_new = comp::humanoid::Body::random();
|
let body_new = comp::humanoid::Body::random();
|
||||||
|
let npc_names = NPC_NAMES.read();
|
||||||
|
|
||||||
body = comp::Body::Humanoid(body_new);
|
body = comp::Body::Humanoid(body_new);
|
||||||
stats = comp::Stats::new(
|
stats = comp::Stats::new(
|
||||||
format!(
|
format!(
|
||||||
"Gentle Giant {}",
|
"Gentle Giant {}",
|
||||||
get_npc_name(&NPC_NAMES.humanoid, body_new.species)
|
get_npc_name(&npc_names.humanoid, body_new.species)
|
||||||
),
|
),
|
||||||
body,
|
body,
|
||||||
);
|
);
|
||||||
|
@ -4,7 +4,7 @@ use crate::{
|
|||||||
scene::Camera,
|
scene::Camera,
|
||||||
};
|
};
|
||||||
use client::Client;
|
use client::Client;
|
||||||
use common::{assets, vol::ReadVol};
|
use common::{assets::{self, AssetExt, AssetHandle}, vol::ReadVol};
|
||||||
use common_sys::state::State;
|
use common_sys::state::State;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
@ -27,7 +27,7 @@ pub struct AmbientItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct AmbientMgr {
|
pub struct AmbientMgr {
|
||||||
soundtrack: AmbientCollection,
|
soundtrack: AssetHandle<AmbientCollection>,
|
||||||
began_playing: Instant,
|
began_playing: Instant,
|
||||||
next_track_change: f32,
|
next_track_change: f32,
|
||||||
volume: f32,
|
volume: f32,
|
||||||
@ -56,7 +56,7 @@ impl AmbientMgr {
|
|||||||
client: &Client,
|
client: &Client,
|
||||||
camera: &Camera,
|
camera: &Camera,
|
||||||
) {
|
) {
|
||||||
if audio.sfx_enabled() && !self.soundtrack.tracks.is_empty() {
|
if audio.sfx_enabled() && !self.soundtrack.read().tracks.is_empty() {
|
||||||
let focus_off = camera.get_focus_pos().map(f32::trunc);
|
let focus_off = camera.get_focus_pos().map(f32::trunc);
|
||||||
let cam_pos = camera.dependents().cam_pos + focus_off;
|
let cam_pos = camera.dependents().cam_pos + focus_off;
|
||||||
|
|
||||||
@ -107,8 +107,8 @@ impl AmbientMgr {
|
|||||||
// Right now there is only wind non-positional sfx so it is always
|
// Right now there is only wind non-positional sfx so it is always
|
||||||
// selected. Modify this variable assignment when adding other non-
|
// selected. Modify this variable assignment when adding other non-
|
||||||
// positional sfx
|
// positional sfx
|
||||||
let track = &self
|
let soundtrack = self.soundtrack.read();
|
||||||
.soundtrack
|
let track = &soundtrack
|
||||||
.tracks
|
.tracks
|
||||||
.iter()
|
.iter()
|
||||||
.find(|track| track.tag == AmbientChannelTag::Wind);
|
.find(|track| track.tag == AmbientChannelTag::Wind);
|
||||||
@ -123,27 +123,21 @@ impl AmbientMgr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_soundtrack_items() -> AmbientCollection {
|
fn load_soundtrack_items() -> AssetHandle<AmbientCollection> {
|
||||||
match assets::load_file("voxygen.audio.ambient", &["ron"]) {
|
// Cannot fail: A default value is always provided
|
||||||
Ok(file) => match ron::de::from_reader(file) {
|
AmbientCollection::load_expect("voxygen.audio.ambient")
|
||||||
Ok(config) => config,
|
|
||||||
Err(error) => {
|
|
||||||
warn!(
|
|
||||||
"Error parsing music config file, music will not be available: {}",
|
|
||||||
format!("{:#?}", error)
|
|
||||||
);
|
|
||||||
|
|
||||||
AmbientCollection::default()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Err(error) => {
|
|
||||||
warn!(
|
|
||||||
"Error reading music config file, music will not be available: {}",
|
|
||||||
format!("{:#?}", error)
|
|
||||||
);
|
|
||||||
|
|
||||||
AmbientCollection::default()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl assets::Asset for AmbientCollection {
|
||||||
|
const EXTENSION: &'static str = "ron";
|
||||||
|
type Loader = assets::RonLoader;
|
||||||
|
|
||||||
|
fn default_value(_: &str, error: assets::Error) -> Result<Self, assets::Error> {
|
||||||
|
warn!(
|
||||||
|
"Error reading music config file, music will not be available: {:#?}", error
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(AmbientCollection::default())
|
||||||
|
}
|
||||||
|
}
|
@ -10,12 +10,12 @@ pub mod soundcache;
|
|||||||
use channel::{AmbientChannel, AmbientChannelTag, MusicChannel, MusicChannelTag, SfxChannel};
|
use channel::{AmbientChannel, AmbientChannelTag, MusicChannel, MusicChannelTag, SfxChannel};
|
||||||
use fader::Fader;
|
use fader::Fader;
|
||||||
use sfx::{SfxEvent, SfxTriggerItem};
|
use sfx::{SfxEvent, SfxTriggerItem};
|
||||||
use soundcache::SoundCache;
|
use soundcache::{OggSound, WavSound};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use common::assets;
|
use common::assets::AssetExt;
|
||||||
use rodio::{source::Source, Decoder, OutputStream, OutputStreamHandle, StreamError};
|
use rodio::{source::Source, OutputStream, OutputStreamHandle, StreamError};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone)]
|
||||||
@ -38,7 +38,6 @@ pub struct AudioFrontend {
|
|||||||
//pub audio_device: Option<Device>,
|
//pub audio_device: Option<Device>,
|
||||||
pub stream: Option<rodio::OutputStream>,
|
pub stream: Option<rodio::OutputStream>,
|
||||||
audio_stream: Option<rodio::OutputStreamHandle>,
|
audio_stream: Option<rodio::OutputStreamHandle>,
|
||||||
sound_cache: SoundCache,
|
|
||||||
|
|
||||||
music_channels: Vec<MusicChannel>,
|
music_channels: Vec<MusicChannel>,
|
||||||
ambient_channels: Vec<AmbientChannel>,
|
ambient_channels: Vec<AmbientChannel>,
|
||||||
@ -76,7 +75,6 @@ impl AudioFrontend {
|
|||||||
//audio_device,
|
//audio_device,
|
||||||
stream,
|
stream,
|
||||||
audio_stream,
|
audio_stream,
|
||||||
sound_cache: SoundCache::default(),
|
|
||||||
music_channels: Vec::new(),
|
music_channels: Vec::new(),
|
||||||
sfx_channels,
|
sfx_channels,
|
||||||
ambient_channels: Vec::new(),
|
ambient_channels: Vec::new(),
|
||||||
@ -95,7 +93,6 @@ impl AudioFrontend {
|
|||||||
//audio_device: None,
|
//audio_device: None,
|
||||||
stream: None,
|
stream: None,
|
||||||
audio_stream: None,
|
audio_stream: None,
|
||||||
sound_cache: SoundCache::default(),
|
|
||||||
music_channels: Vec::new(),
|
music_channels: Vec::new(),
|
||||||
sfx_channels: Vec::new(),
|
sfx_channels: Vec::new(),
|
||||||
ambient_channels: Vec::new(),
|
ambient_channels: Vec::new(),
|
||||||
@ -231,9 +228,9 @@ impl AudioFrontend {
|
|||||||
/// Play (once) an sfx file by file path at the give position and volume
|
/// Play (once) an sfx file by file path at the give position and volume
|
||||||
pub fn play_sfx(&mut self, sound: &str, pos: Vec3<f32>, vol: Option<f32>) {
|
pub fn play_sfx(&mut self, sound: &str, pos: Vec3<f32>, vol: Option<f32>) {
|
||||||
if self.audio_stream.is_some() {
|
if self.audio_stream.is_some() {
|
||||||
let sound = self
|
let sound = WavSound::load_expect(sound)
|
||||||
.sound_cache
|
.cloned()
|
||||||
.load_sound(sound)
|
.decoder()
|
||||||
.amplify(vol.unwrap_or(1.0));
|
.amplify(vol.unwrap_or(1.0));
|
||||||
|
|
||||||
let listener = self.listener.clone();
|
let listener = self.listener.clone();
|
||||||
@ -250,9 +247,9 @@ impl AudioFrontend {
|
|||||||
/// being underwater
|
/// being underwater
|
||||||
pub fn play_underwater_sfx(&mut self, sound: &str, pos: Vec3<f32>, vol: Option<f32>) {
|
pub fn play_underwater_sfx(&mut self, sound: &str, pos: Vec3<f32>, vol: Option<f32>) {
|
||||||
if self.audio_stream.is_some() {
|
if self.audio_stream.is_some() {
|
||||||
let sound = self
|
let sound = WavSound::load_expect(sound)
|
||||||
.sound_cache
|
.cloned()
|
||||||
.load_sound(sound)
|
.decoder()
|
||||||
.amplify(vol.unwrap_or(1.0));
|
.amplify(vol.unwrap_or(1.0));
|
||||||
|
|
||||||
let listener = self.listener.clone();
|
let listener = self.listener.clone();
|
||||||
@ -272,9 +269,7 @@ impl AudioFrontend {
|
|||||||
) {
|
) {
|
||||||
if self.audio_stream.is_some() {
|
if self.audio_stream.is_some() {
|
||||||
if let Some(channel) = self.get_ambient_channel(channel_tag, volume_multiplier) {
|
if let Some(channel) = self.get_ambient_channel(channel_tag, volume_multiplier) {
|
||||||
let file = assets::load_file(&sound, &["ogg"]).expect("Failed to load sound");
|
let sound = OggSound::load_expect(sound).cloned().decoder();
|
||||||
let sound = Decoder::new(file).expect("Failed to decode sound");
|
|
||||||
|
|
||||||
channel.play(sound);
|
channel.play(sound);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -326,9 +321,7 @@ impl AudioFrontend {
|
|||||||
fn play_music(&mut self, sound: &str, channel_tag: MusicChannelTag) {
|
fn play_music(&mut self, sound: &str, channel_tag: MusicChannelTag) {
|
||||||
if self.music_enabled() {
|
if self.music_enabled() {
|
||||||
if let Some(channel) = self.get_music_channel(channel_tag) {
|
if let Some(channel) = self.get_music_channel(channel_tag) {
|
||||||
let file = assets::load_file(&sound, &["ogg"]).expect("Failed to load sound");
|
let sound = OggSound::load_expect(sound).cloned().decoder();
|
||||||
let sound = Decoder::new(file).expect("Failed to decode sound");
|
|
||||||
|
|
||||||
channel.play(sound, channel_tag);
|
channel.play(sound, channel_tag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@
|
|||||||
use crate::audio::{AudioFrontend, MusicChannelTag};
|
use crate::audio::{AudioFrontend, MusicChannelTag};
|
||||||
use client::Client;
|
use client::Client;
|
||||||
use common::{
|
use common::{
|
||||||
assets,
|
assets::{self, AssetExt, AssetHandle},
|
||||||
terrain::{BiomeKind, SitesKind},
|
terrain::{BiomeKind, SitesKind},
|
||||||
};
|
};
|
||||||
use common_sys::state::State;
|
use common_sys::state::State;
|
||||||
@ -103,7 +103,7 @@ enum PlayState {
|
|||||||
/// Provides methods to control music playback
|
/// Provides methods to control music playback
|
||||||
pub struct MusicMgr {
|
pub struct MusicMgr {
|
||||||
/// Collection of all the tracks
|
/// Collection of all the tracks
|
||||||
soundtrack: SoundtrackCollection,
|
soundtrack: AssetHandle<SoundtrackCollection>,
|
||||||
/// Instant at which the current track began playing
|
/// Instant at which the current track began playing
|
||||||
began_playing: Instant,
|
began_playing: Instant,
|
||||||
/// Time until the next track should be played
|
/// Time until the next track should be played
|
||||||
@ -139,7 +139,7 @@ impl MusicMgr {
|
|||||||
//}
|
//}
|
||||||
|
|
||||||
if audio.music_enabled()
|
if audio.music_enabled()
|
||||||
&& !self.soundtrack.tracks.is_empty()
|
&& !self.soundtrack.read().tracks.is_empty()
|
||||||
&& self.began_playing.elapsed().as_secs_f32() > self.next_track_change
|
&& self.began_playing.elapsed().as_secs_f32() > self.next_track_change
|
||||||
{
|
{
|
||||||
self.play_random_track(audio, state, client);
|
self.play_random_track(audio, state, client);
|
||||||
@ -158,8 +158,8 @@ impl MusicMgr {
|
|||||||
let current_site = client.current_site();
|
let current_site = client.current_site();
|
||||||
|
|
||||||
// Filters out tracks not matching the timing, site, and biome
|
// Filters out tracks not matching the timing, site, and biome
|
||||||
let maybe_tracks = self
|
let soundtrack = self.soundtrack.read();
|
||||||
.soundtrack
|
let maybe_tracks = soundtrack
|
||||||
.tracks
|
.tracks
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|track| {
|
.filter(|track| {
|
||||||
@ -225,27 +225,21 @@ impl MusicMgr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_soundtrack_items() -> SoundtrackCollection {
|
fn load_soundtrack_items() -> AssetHandle<SoundtrackCollection> {
|
||||||
match assets::load_file("voxygen.audio.soundtrack", &["ron"]) {
|
// Cannot fail: A default value is always provided
|
||||||
Ok(file) => match ron::de::from_reader(file) {
|
SoundtrackCollection::load_expect("voxygen.audio.soundtrack")
|
||||||
Ok(config) => config,
|
|
||||||
Err(error) => {
|
|
||||||
warn!(
|
|
||||||
"Error parsing music config file, music will not be available: {}",
|
|
||||||
format!("{:#?}", error)
|
|
||||||
);
|
|
||||||
|
|
||||||
SoundtrackCollection::default()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Err(error) => {
|
|
||||||
warn!(
|
|
||||||
"Error reading music config file, music will not be available: {}",
|
|
||||||
format!("{:#?}", error)
|
|
||||||
);
|
|
||||||
|
|
||||||
SoundtrackCollection::default()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl assets::Asset for SoundtrackCollection {
|
||||||
|
const EXTENSION: &'static str = "ron";
|
||||||
|
type Loader = assets::RonLoader;
|
||||||
|
|
||||||
|
fn default_value(_: &str, error: assets::Error) -> Result<Self, assets::Error> {
|
||||||
|
warn!(
|
||||||
|
"Error reading music config file, music will not be available: {:#?}", error
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(SoundtrackCollection::default())
|
||||||
|
}
|
||||||
|
}
|
@ -89,7 +89,7 @@ use crate::{
|
|||||||
|
|
||||||
use client::Client;
|
use client::Client;
|
||||||
use common::{
|
use common::{
|
||||||
assets,
|
assets::{self, AssetExt, AssetHandle},
|
||||||
comp::{
|
comp::{
|
||||||
item::{ItemKind, ToolKind},
|
item::{ItemKind, ToolKind},
|
||||||
object, Body, CharacterAbilityType, InventoryUpdateEvent,
|
object, Body, CharacterAbilityType, InventoryUpdateEvent,
|
||||||
@ -237,7 +237,9 @@ impl SfxTriggers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct SfxMgr {
|
pub struct SfxMgr {
|
||||||
pub triggers: SfxTriggers,
|
/// This is an `AssetHandle` so it is reloaded automatically
|
||||||
|
/// when the manifest is edited.
|
||||||
|
pub triggers: AssetHandle<SfxTriggers>,
|
||||||
event_mapper: SfxEventMapper,
|
event_mapper: SfxEventMapper,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,12 +275,14 @@ impl SfxMgr {
|
|||||||
// same direction as the camera
|
// same direction as the camera
|
||||||
audio.set_listener_pos(cam_pos, camera.dependents().cam_dir);
|
audio.set_listener_pos(cam_pos, camera.dependents().cam_dir);
|
||||||
|
|
||||||
|
let triggers = self.triggers.read();
|
||||||
|
|
||||||
self.event_mapper.maintain(
|
self.event_mapper.maintain(
|
||||||
audio,
|
audio,
|
||||||
state,
|
state,
|
||||||
player_entity,
|
player_entity,
|
||||||
camera,
|
camera,
|
||||||
&self.triggers,
|
&triggers,
|
||||||
terrain,
|
terrain,
|
||||||
client,
|
client,
|
||||||
);
|
);
|
||||||
@ -357,27 +361,21 @@ impl SfxMgr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_sfx_items() -> SfxTriggers {
|
fn load_sfx_items() -> AssetHandle<SfxTriggers> {
|
||||||
match assets::load_file("voxygen.audio.sfx", &["ron"]) {
|
// Cannot fail: A default value is always provided
|
||||||
Ok(file) => match ron::de::from_reader(file) {
|
SfxTriggers::load_expect("voxygen.audio.sfx")
|
||||||
Ok(config) => config,
|
|
||||||
Err(error) => {
|
|
||||||
warn!(
|
|
||||||
"Error parsing sfx config file, sfx will not be available: {}",
|
|
||||||
format!("{:#?}", error)
|
|
||||||
);
|
|
||||||
|
|
||||||
SfxTriggers::default()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Err(error) => {
|
|
||||||
warn!(
|
|
||||||
"Error reading sfx config file, sfx will not be available: {}",
|
|
||||||
format!("{:#?}", error)
|
|
||||||
);
|
|
||||||
|
|
||||||
SfxTriggers::default()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl assets::Asset for SfxTriggers {
|
||||||
|
const EXTENSION: &'static str = "ron";
|
||||||
|
type Loader = assets::RonLoader;
|
||||||
|
|
||||||
|
fn default_value(_: &str, error: assets::Error) -> Result<Self, assets::Error> {
|
||||||
|
warn!(
|
||||||
|
"Error reading sfx config file, sfx will not be available: {:#?}", error
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(SfxTriggers::default())
|
||||||
|
}
|
||||||
|
}
|
@ -1,63 +1,82 @@
|
|||||||
//! Handles caching and retrieval of decoded `.wav` sfx sound data, eliminating
|
//! Handles caching and retrieval of decoded `.wav` sfx sound data, eliminating
|
||||||
//! the need to decode files on each playback
|
//! the need to decode files on each playback
|
||||||
use common::assets;
|
use common::assets;
|
||||||
use hashbrown::HashMap;
|
use std::{borrow::Cow, io, sync::Arc};
|
||||||
use std::{convert::AsRef, io, io::Read, sync::Arc};
|
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
// Implementation of sound taken from this github issue:
|
// Implementation of sound taken from this github issue:
|
||||||
// https://github.com/RustAudio/rodio/issues/141
|
// https://github.com/RustAudio/rodio/issues/141
|
||||||
pub struct Sound(Arc<Vec<u8>>);
|
#[derive(Clone)]
|
||||||
|
pub struct WavSound(Arc<Vec<u8>>);
|
||||||
|
|
||||||
impl AsRef<[u8]> for Sound {
|
impl AsRef<[u8]> for WavSound {
|
||||||
fn as_ref(&self) -> &[u8] { &self.0 }
|
fn as_ref(&self) -> &[u8] { &self.0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct SoundLoader;
|
||||||
|
|
||||||
|
impl assets::Loader<WavSound> for SoundLoader {
|
||||||
|
fn load(content: Cow<[u8]>, _: &str) -> Result<WavSound, assets::BoxedError> {
|
||||||
|
let arc = Arc::new(content.into_owned());
|
||||||
|
Ok(WavSound(arc))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl assets::Asset for WavSound {
|
||||||
|
const EXTENSION: &'static str = "wav";
|
||||||
|
type Loader = SoundLoader;
|
||||||
|
|
||||||
|
fn default_value(specifier: &str, error: assets::Error) -> Result<Self, assets::Error> {
|
||||||
|
warn!(?specifier, ?error, "Failed to load sound");
|
||||||
|
|
||||||
|
Ok(WavSound::empty())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Wrapper for decoded audio data
|
/// Wrapper for decoded audio data
|
||||||
impl Sound {
|
impl WavSound {
|
||||||
pub fn load(filename: &str) -> Result<Sound, assets::Error> {
|
pub fn decoder(self) -> rodio::Decoder<io::Cursor<WavSound>> {
|
||||||
let mut file = assets::load_file(filename, &["wav"])?;
|
let cursor = io::Cursor::new(self);
|
||||||
let mut buf = Vec::new();
|
rodio::Decoder::new(cursor).unwrap()
|
||||||
file.read_to_end(&mut buf)?;
|
|
||||||
Ok(Sound(Arc::new(buf)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cursor(&self) -> io::Cursor<Sound> { io::Cursor::new(Sound(Arc::clone(&self.0))) }
|
/// Returns a `WavSound` containing empty .wav data. This intentionally doesn't
|
||||||
|
|
||||||
pub fn decoder(&self) -> rodio::Decoder<io::Cursor<Sound>> {
|
|
||||||
rodio::Decoder::new(self.cursor()).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a `Sound` containing empty .wav data. This intentionally doesn't
|
|
||||||
/// load from the filesystem so we have a reliable fallback when there
|
/// load from the filesystem so we have a reliable fallback when there
|
||||||
/// is a failure to read a file.
|
/// is a failure to read a file.
|
||||||
///
|
///
|
||||||
/// The data below is the result of passing a very short, silent .wav file
|
/// The data below is the result of passing a very short, silent .wav file
|
||||||
/// to `Sound::load()`.
|
/// to `Sound::load()`.
|
||||||
pub fn empty() -> Sound {
|
pub fn empty() -> WavSound {
|
||||||
Sound(Arc::new(vec![
|
WavSound(Arc::new(vec![
|
||||||
82, 73, 70, 70, 40, 0, 0, 0, 87, 65, 86, 69, 102, 109, 116, 32, 16, 0, 0, 0, 1, 0, 1,
|
82, 73, 70, 70, 40, 0, 0, 0, 87, 65, 86, 69, 102, 109, 116, 32, 16, 0, 0, 0, 1, 0, 1,
|
||||||
0, 68, 172, 0, 0, 136, 88, 1, 0, 2, 0, 16, 0, 100, 97, 116, 97, 4, 0, 0, 0, 0, 0, 0, 0,
|
0, 68, 172, 0, 0, 136, 88, 1, 0, 2, 0, 16, 0, 100, 97, 116, 97, 4, 0, 0, 0, 0, 0, 0, 0,
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Clone)]
|
||||||
pub struct SoundCache {
|
pub struct OggSound(Arc<Vec<u8>>);
|
||||||
sounds: HashMap<String, Sound>,
|
|
||||||
|
impl AsRef<[u8]> for OggSound {
|
||||||
|
fn as_ref(&self) -> &[u8] { &self.0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SoundCache {
|
impl assets::Loader<OggSound> for SoundLoader {
|
||||||
pub fn load_sound(&mut self, name: &str) -> rodio::Decoder<io::Cursor<Sound>> {
|
fn load(content: Cow<[u8]>, _: &str) -> Result<OggSound, assets::BoxedError> {
|
||||||
self.sounds
|
let arc = Arc::new(content.into_owned());
|
||||||
.entry(name.to_string())
|
Ok(OggSound(arc))
|
||||||
.or_insert_with(|| {
|
|
||||||
Sound::load(name).unwrap_or_else(|_| {
|
|
||||||
warn!(?name, "SoundCache: Failed to load sound");
|
|
||||||
|
|
||||||
Sound::empty()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.decoder()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl assets::Asset for OggSound {
|
||||||
|
const EXTENSION: &'static str = "ogg";
|
||||||
|
type Loader = SoundLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wrapper for decoded audio data
|
||||||
|
impl OggSound {
|
||||||
|
pub fn decoder(self) -> rodio::Decoder<io::Cursor<OggSound>> {
|
||||||
|
let cursor = io::Cursor::new(self);
|
||||||
|
rodio::Decoder::new(cursor).unwrap()
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
use crate::ui::{Graphic, SampleStrat, Transform, Ui};
|
use crate::ui::{Graphic, SampleStrat, Transform, Ui};
|
||||||
use common::{
|
use common::{
|
||||||
assets::{self, watch::ReloadIndicator, Asset},
|
assets::{self, AssetExt, AssetHandle, DotVoxAsset},
|
||||||
comp::item::{
|
comp::item::{
|
||||||
armor::{Armor, ArmorKind},
|
armor::{Armor, ArmorKind},
|
||||||
Glider, ItemDesc, ItemKind, Lantern, Throwable, Utility,
|
Glider, ItemDesc, ItemKind, Lantern, Throwable, Utility,
|
||||||
@ -8,11 +8,10 @@ use common::{
|
|||||||
figure::Segment,
|
figure::Segment,
|
||||||
};
|
};
|
||||||
use conrod_core::image::Id;
|
use conrod_core::image::Id;
|
||||||
use dot_vox::DotVoxData;
|
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use image::DynamicImage;
|
use image::DynamicImage;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{fs::File, io::BufReader, sync::Arc};
|
use std::sync::Arc;
|
||||||
use tracing::{error, warn};
|
use tracing::{error, warn};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
@ -84,37 +83,34 @@ impl ImageSpec {
|
|||||||
}
|
}
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
struct ItemImagesSpec(HashMap<ItemKey, ImageSpec>);
|
struct ItemImagesSpec(HashMap<ItemKey, ImageSpec>);
|
||||||
impl Asset for ItemImagesSpec {
|
impl assets::Asset for ItemImagesSpec {
|
||||||
const ENDINGS: &'static [&'static str] = &["ron"];
|
const EXTENSION: &'static str = "ron";
|
||||||
|
type Loader = assets::RonLoader;
|
||||||
fn parse(buf_reader: BufReader<File>, _specifier: &str) -> Result<Self, assets::Error> {
|
|
||||||
ron::de::from_reader(buf_reader).map_err(assets::Error::parse_error)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: when there are more images don't load them all into memory
|
// TODO: when there are more images don't load them all into memory
|
||||||
pub struct ItemImgs {
|
pub struct ItemImgs {
|
||||||
map: HashMap<ItemKey, Id>,
|
map: HashMap<ItemKey, Id>,
|
||||||
indicator: ReloadIndicator,
|
manifest: AssetHandle<ItemImagesSpec>,
|
||||||
not_found: Id,
|
not_found: Id,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ItemImgs {
|
impl ItemImgs {
|
||||||
pub fn new(ui: &mut Ui, not_found: Id) -> Self {
|
pub fn new(ui: &mut Ui, not_found: Id) -> Self {
|
||||||
let mut indicator = ReloadIndicator::new();
|
let manifest = ItemImagesSpec::load_expect("voxygen.item_image_manifest");
|
||||||
Self {
|
let map = manifest
|
||||||
map: ItemImagesSpec::load_watched(
|
.read()
|
||||||
"voxygen.item_image_manifest",
|
|
||||||
&mut indicator,
|
|
||||||
)
|
|
||||||
.expect("Unable to load item image manifest")
|
|
||||||
.0
|
.0
|
||||||
.iter()
|
.iter()
|
||||||
// TODO: what if multiple kinds map to the same image, it would be nice to use the same
|
// TODO: what if multiple kinds map to the same image, it would be nice to use the same
|
||||||
// image id for both, although this does interfere with the current hot-reloading
|
// image id for both, although this does interfere with the current hot-reloading
|
||||||
// strategy
|
// strategy
|
||||||
.map(|(kind, spec)| (kind.clone(), ui.add_graphic(spec.create_graphic())))
|
.map(|(kind, spec)| (kind.clone(), ui.add_graphic(spec.create_graphic())))
|
||||||
.collect(),
|
.collect();
|
||||||
indicator,
|
|
||||||
|
Self {
|
||||||
|
map,
|
||||||
|
manifest,
|
||||||
not_found,
|
not_found,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -122,12 +118,8 @@ impl ItemImgs {
|
|||||||
/// Checks if the manifest has been changed and reloads the images if so
|
/// Checks if the manifest has been changed and reloads the images if so
|
||||||
/// Reuses img ids
|
/// Reuses img ids
|
||||||
pub fn reload_if_changed(&mut self, ui: &mut Ui) {
|
pub fn reload_if_changed(&mut self, ui: &mut Ui) {
|
||||||
if self.indicator.reloaded() {
|
if self.manifest.reloaded() {
|
||||||
for (kind, spec) in ItemImagesSpec::load("voxygen.item_image_manifest")
|
for (kind, spec) in self.manifest.read().0.iter() {
|
||||||
.expect("Unable to load item image manifest")
|
|
||||||
.0
|
|
||||||
.iter()
|
|
||||||
{
|
|
||||||
// Load new graphic
|
// Load new graphic
|
||||||
let graphic = spec.create_graphic();
|
let graphic = spec.create_graphic();
|
||||||
// See if we already have an id we can use
|
// See if we already have an id we can use
|
||||||
@ -163,30 +155,31 @@ impl ItemImgs {
|
|||||||
|
|
||||||
// Copied from figure/load.rs
|
// Copied from figure/load.rs
|
||||||
// TODO: remove code dup?
|
// TODO: remove code dup?
|
||||||
fn graceful_load_vox(specifier: &str) -> Arc<DotVoxData> {
|
fn graceful_load_vox(specifier: &str) -> AssetHandle<DotVoxAsset> {
|
||||||
let full_specifier: String = ["voxygen.", specifier].concat();
|
let full_specifier: String = ["voxygen.", specifier].concat();
|
||||||
match DotVoxData::load(full_specifier.as_str()) {
|
match DotVoxAsset::load(full_specifier.as_str()) {
|
||||||
Ok(dot_vox) => dot_vox,
|
Ok(dot_vox) => dot_vox,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
error!(?full_specifier, "Could not load vox file for item images",);
|
error!(?full_specifier, "Could not load vox file for item images",);
|
||||||
DotVoxData::load_expect("voxygen.voxel.not_found")
|
DotVoxAsset::load_expect("voxygen.voxel.not_found")
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn graceful_load_img(specifier: &str) -> Arc<DynamicImage> {
|
fn graceful_load_img(specifier: &str) -> Arc<DynamicImage> {
|
||||||
let full_specifier: String = ["voxygen.", specifier].concat();
|
let full_specifier: String = ["voxygen.", specifier].concat();
|
||||||
match DynamicImage::load(full_specifier.as_str()) {
|
let handle = match assets::Image::load(&full_specifier) {
|
||||||
Ok(img) => img,
|
Ok(img) => img,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
error!(?full_specifier, "Could not load image file for item images");
|
error!(?full_specifier, "Could not load image file for item images");
|
||||||
DynamicImage::load_expect("voxygen.element.not_found")
|
assets::Image::load_expect("voxygen.element.not_found")
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
|
handle.read().to_image()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn graceful_load_segment_no_skin(specifier: &str) -> Arc<Segment> {
|
fn graceful_load_segment_no_skin(specifier: &str) -> Arc<Segment> {
|
||||||
use common::figure::{mat_cell::MatCell, MatSegment};
|
use common::figure::{mat_cell::MatCell, MatSegment};
|
||||||
let mat_seg = MatSegment::from(&*graceful_load_vox(specifier));
|
let mat_seg = MatSegment::from(&graceful_load_vox(specifier).read().0);
|
||||||
let seg = mat_seg
|
let seg = mat_seg
|
||||||
.map(|mat_cell| match mat_cell {
|
.map(|mat_cell| match mat_cell {
|
||||||
MatCell::None => None,
|
MatCell::None => None,
|
||||||
|
@ -46,7 +46,7 @@ use spell::Spell;
|
|||||||
use crate::{
|
use crate::{
|
||||||
ecs::{comp as vcomp, comp::HpFloaterList},
|
ecs::{comp as vcomp, comp::HpFloaterList},
|
||||||
hud::img_ids::ImgsRot,
|
hud::img_ids::ImgsRot,
|
||||||
i18n::{i18n_asset_key, LanguageMetadata, Localization},
|
i18n::{LanguageMetadata, Localization},
|
||||||
render::{Consts, Globals, RenderMode, Renderer},
|
render::{Consts, Globals, RenderMode, Renderer},
|
||||||
scene::camera::{self, Camera},
|
scene::camera::{self, Camera},
|
||||||
ui::{fonts::Fonts, img_ids::Rotations, slot, Graphic, Ingameable, ScaleMode, Ui},
|
ui::{fonts::Fonts, img_ids::Rotations, slot, Graphic, Ingameable, ScaleMode, Ui},
|
||||||
@ -55,7 +55,6 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use client::Client;
|
use client::Client;
|
||||||
use common::{
|
use common::{
|
||||||
assets::Asset,
|
|
||||||
comp,
|
comp,
|
||||||
comp::{
|
comp::{
|
||||||
item::{ItemDesc, Quality},
|
item::{ItemDesc, Quality},
|
||||||
@ -627,7 +626,6 @@ pub struct Hud {
|
|||||||
tab_complete: Option<String>,
|
tab_complete: Option<String>,
|
||||||
pulse: f32,
|
pulse: f32,
|
||||||
velocity: f32,
|
velocity: f32,
|
||||||
i18n: std::sync::Arc<Localization>,
|
|
||||||
slot_manager: slots::SlotManager,
|
slot_manager: slots::SlotManager,
|
||||||
hotbar: hotbar::State,
|
hotbar: hotbar::State,
|
||||||
events: Vec<Event>,
|
events: Vec<Event>,
|
||||||
@ -660,12 +658,8 @@ impl Hud {
|
|||||||
let rot_imgs = ImgsRot::load(&mut ui).expect("Failed to load rot images!");
|
let rot_imgs = ImgsRot::load(&mut ui).expect("Failed to load rot images!");
|
||||||
// Load item images.
|
// Load item images.
|
||||||
let item_imgs = ItemImgs::new(&mut ui, imgs.not_found);
|
let item_imgs = ItemImgs::new(&mut ui, imgs.not_found);
|
||||||
// Load language.
|
|
||||||
let i18n = Localization::load_expect(&i18n_asset_key(
|
|
||||||
&global_state.settings.language.selected_language,
|
|
||||||
));
|
|
||||||
// Load fonts.
|
// Load fonts.
|
||||||
let fonts = Fonts::load(&i18n.fonts, &mut ui).expect("Impossible to load fonts!");
|
let fonts = Fonts::load(&global_state.i18n.read().fonts, &mut ui).expect("Impossible to load fonts!");
|
||||||
// Get the server name.
|
// Get the server name.
|
||||||
let server = &client.server_info().name;
|
let server = &client.server_info().name;
|
||||||
// Get the id, unwrap is safe because this CANNOT be None at this
|
// Get the id, unwrap is safe because this CANNOT be None at this
|
||||||
@ -726,7 +720,6 @@ impl Hud {
|
|||||||
tab_complete: None,
|
tab_complete: None,
|
||||||
pulse: 0.0,
|
pulse: 0.0,
|
||||||
velocity: 0.0,
|
velocity: 0.0,
|
||||||
i18n,
|
|
||||||
slot_manager,
|
slot_manager,
|
||||||
hotbar: hotbar_state,
|
hotbar: hotbar_state,
|
||||||
events: Vec::new(),
|
events: Vec::new(),
|
||||||
@ -734,10 +727,8 @@ impl Hud {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_language(&mut self, i18n: std::sync::Arc<Localization>) {
|
pub fn update_fonts(&mut self, i18n: &Localization) {
|
||||||
self.i18n = i18n;
|
self.fonts = Fonts::load(&i18n.fonts, &mut self.ui).expect("Impossible to load fonts!");
|
||||||
self.fonts =
|
|
||||||
Fonts::load(&self.i18n.fonts, &mut self.ui).expect("Impossible to load fonts!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::assign_op_pattern)] // TODO: Pending review in #587
|
#[allow(clippy::assign_op_pattern)] // TODO: Pending review in #587
|
||||||
@ -759,6 +750,7 @@ impl Hud {
|
|||||||
// FPS
|
// FPS
|
||||||
let fps = global_state.clock.stats().average_tps;
|
let fps = global_state.clock.stats().average_tps;
|
||||||
let version = common::util::DISPLAY_VERSION_LONG.clone();
|
let version = common::util::DISPLAY_VERSION_LONG.clone();
|
||||||
|
let i18n = &*global_state.i18n.read();
|
||||||
|
|
||||||
if self.show.ingame {
|
if self.show.ingame {
|
||||||
let ecs = client.state().ecs();
|
let ecs = client.state().ecs();
|
||||||
@ -1267,7 +1259,7 @@ impl Hud {
|
|||||||
in_group,
|
in_group,
|
||||||
&global_state.settings.gameplay,
|
&global_state.settings.gameplay,
|
||||||
self.pulse,
|
self.pulse,
|
||||||
&self.i18n,
|
i18n,
|
||||||
&self.imgs,
|
&self.imgs,
|
||||||
&self.fonts,
|
&self.fonts,
|
||||||
)
|
)
|
||||||
@ -1470,8 +1462,8 @@ impl Hud {
|
|||||||
Intro::Show => {
|
Intro::Show => {
|
||||||
if self.pulse > 20.0 {
|
if self.pulse > 20.0 {
|
||||||
self.show.want_grab = false;
|
self.show.want_grab = false;
|
||||||
let quest_headline = &self.i18n.get("hud.temp_quest_headline");
|
let quest_headline = &i18n.get("hud.temp_quest_headline");
|
||||||
let quest_text = &self.i18n.get("hud.temp_quest_text");
|
let quest_text = &i18n.get("hud.temp_quest_text");
|
||||||
Image::new(self.imgs.quest_bg)
|
Image::new(self.imgs.quest_bg)
|
||||||
.w_h(404.0, 858.0)
|
.w_h(404.0, 858.0)
|
||||||
.middle_of(ui_widgets.window)
|
.middle_of(ui_widgets.window)
|
||||||
@ -1508,7 +1500,7 @@ impl Hud {
|
|||||||
.hover_image(self.imgs.button_hover)
|
.hover_image(self.imgs.button_hover)
|
||||||
.press_image(self.imgs.button_press)
|
.press_image(self.imgs.button_press)
|
||||||
.mid_bottom_with_margin_on(self.ids.q_text_bg, -120.0)
|
.mid_bottom_with_margin_on(self.ids.q_text_bg, -120.0)
|
||||||
.label(&self.i18n.get("common.accept"))
|
.label(&i18n.get("common.accept"))
|
||||||
.label_font_id(self.fonts.cyri.conrod_id)
|
.label_font_id(self.fonts.cyri.conrod_id)
|
||||||
.label_font_size(self.fonts.cyri.scale(22))
|
.label_font_size(self.fonts.cyri.scale(22))
|
||||||
.label_color(TEXT_COLOR)
|
.label_color(TEXT_COLOR)
|
||||||
@ -1685,8 +1677,7 @@ impl Hud {
|
|||||||
// Help Window
|
// Help Window
|
||||||
if let Some(help_key) = global_state.settings.controls.get_binding(GameInput::Help) {
|
if let Some(help_key) = global_state.settings.controls.get_binding(GameInput::Help) {
|
||||||
Text::new(
|
Text::new(
|
||||||
&self
|
&i18n
|
||||||
.i18n
|
|
||||||
.get("hud.press_key_to_toggle_keybindings_fmt")
|
.get("hud.press_key_to_toggle_keybindings_fmt")
|
||||||
.replace("{key}", help_key.to_string().as_str()),
|
.replace("{key}", help_key.to_string().as_str()),
|
||||||
)
|
)
|
||||||
@ -1703,8 +1694,7 @@ impl Hud {
|
|||||||
.get_binding(GameInput::ToggleDebug)
|
.get_binding(GameInput::ToggleDebug)
|
||||||
{
|
{
|
||||||
Text::new(
|
Text::new(
|
||||||
&self
|
&i18n
|
||||||
.i18n
|
|
||||||
.get("hud.press_key_to_toggle_debug_info_fmt")
|
.get("hud.press_key_to_toggle_debug_info_fmt")
|
||||||
.replace("{key}", toggle_debug_key.to_string().as_str()),
|
.replace("{key}", toggle_debug_key.to_string().as_str()),
|
||||||
)
|
)
|
||||||
@ -1718,8 +1708,7 @@ impl Hud {
|
|||||||
// Help Window
|
// Help Window
|
||||||
if let Some(help_key) = global_state.settings.controls.get_binding(GameInput::Help) {
|
if let Some(help_key) = global_state.settings.controls.get_binding(GameInput::Help) {
|
||||||
Text::new(
|
Text::new(
|
||||||
&self
|
&i18n
|
||||||
.i18n
|
|
||||||
.get("hud.press_key_to_show_keybindings_fmt")
|
.get("hud.press_key_to_show_keybindings_fmt")
|
||||||
.replace("{key}", help_key.to_string().as_str()),
|
.replace("{key}", help_key.to_string().as_str()),
|
||||||
)
|
)
|
||||||
@ -1736,8 +1725,7 @@ impl Hud {
|
|||||||
.get_binding(GameInput::ToggleDebug)
|
.get_binding(GameInput::ToggleDebug)
|
||||||
{
|
{
|
||||||
Text::new(
|
Text::new(
|
||||||
&self
|
&i18n
|
||||||
.i18n
|
|
||||||
.get("hud.press_key_to_show_debug_info_fmt")
|
.get("hud.press_key_to_show_debug_info_fmt")
|
||||||
.replace("{key}", toggle_debug_key.to_string().as_str()),
|
.replace("{key}", toggle_debug_key.to_string().as_str()),
|
||||||
)
|
)
|
||||||
@ -1754,8 +1742,7 @@ impl Hud {
|
|||||||
.get_binding(GameInput::ToggleLantern)
|
.get_binding(GameInput::ToggleLantern)
|
||||||
{
|
{
|
||||||
Text::new(
|
Text::new(
|
||||||
&self
|
&i18n
|
||||||
.i18n
|
|
||||||
.get("hud.press_key_to_toggle_lantern_fmt")
|
.get("hud.press_key_to_toggle_lantern_fmt")
|
||||||
.replace("{key}", toggle_lantern_key.to_string().as_str()),
|
.replace("{key}", toggle_lantern_key.to_string().as_str()),
|
||||||
)
|
)
|
||||||
@ -1800,7 +1787,7 @@ impl Hud {
|
|||||||
global_state,
|
global_state,
|
||||||
&self.rot_imgs,
|
&self.rot_imgs,
|
||||||
tooltip_manager,
|
tooltip_manager,
|
||||||
&self.i18n,
|
i18n,
|
||||||
&player_stats,
|
&player_stats,
|
||||||
)
|
)
|
||||||
.set(self.ids.buttons, ui_widgets)
|
.set(self.ids.buttons, ui_widgets)
|
||||||
@ -1822,7 +1809,7 @@ impl Hud {
|
|||||||
&self.fonts,
|
&self.fonts,
|
||||||
&self.rot_imgs,
|
&self.rot_imgs,
|
||||||
tooltip_manager,
|
tooltip_manager,
|
||||||
&self.i18n,
|
i18n,
|
||||||
&player_buffs,
|
&player_buffs,
|
||||||
self.pulse,
|
self.pulse,
|
||||||
&global_state,
|
&global_state,
|
||||||
@ -1842,7 +1829,7 @@ impl Hud {
|
|||||||
&self.imgs,
|
&self.imgs,
|
||||||
&self.rot_imgs,
|
&self.rot_imgs,
|
||||||
&self.fonts,
|
&self.fonts,
|
||||||
&self.i18n,
|
i18n,
|
||||||
self.pulse,
|
self.pulse,
|
||||||
&global_state,
|
&global_state,
|
||||||
tooltip_manager,
|
tooltip_manager,
|
||||||
@ -1859,7 +1846,7 @@ impl Hud {
|
|||||||
}
|
}
|
||||||
// Popup (waypoint saved and similar notifications)
|
// Popup (waypoint saved and similar notifications)
|
||||||
Popup::new(
|
Popup::new(
|
||||||
&self.i18n,
|
i18n,
|
||||||
client,
|
client,
|
||||||
&self.new_notifications,
|
&self.new_notifications,
|
||||||
&self.fonts,
|
&self.fonts,
|
||||||
@ -1895,7 +1882,7 @@ impl Hud {
|
|||||||
tooltip_manager,
|
tooltip_manager,
|
||||||
&mut self.slot_manager,
|
&mut self.slot_manager,
|
||||||
self.pulse,
|
self.pulse,
|
||||||
&self.i18n,
|
i18n,
|
||||||
&player_stats,
|
&player_stats,
|
||||||
&self.show,
|
&self.show,
|
||||||
)
|
)
|
||||||
@ -1963,7 +1950,7 @@ impl Hud {
|
|||||||
&self.hotbar,
|
&self.hotbar,
|
||||||
tooltip_manager,
|
tooltip_manager,
|
||||||
&mut self.slot_manager,
|
&mut self.slot_manager,
|
||||||
&self.i18n,
|
i18n,
|
||||||
&self.show,
|
&self.show,
|
||||||
&ability_map,
|
&ability_map,
|
||||||
)
|
)
|
||||||
@ -1978,7 +1965,7 @@ impl Hud {
|
|||||||
client,
|
client,
|
||||||
&self.imgs,
|
&self.imgs,
|
||||||
&self.fonts,
|
&self.fonts,
|
||||||
&self.i18n,
|
i18n,
|
||||||
&self.rot_imgs,
|
&self.rot_imgs,
|
||||||
tooltip_manager,
|
tooltip_manager,
|
||||||
&self.item_imgs,
|
&self.item_imgs,
|
||||||
@ -2017,7 +2004,7 @@ impl Hud {
|
|||||||
global_state,
|
global_state,
|
||||||
&self.imgs,
|
&self.imgs,
|
||||||
&self.fonts,
|
&self.fonts,
|
||||||
&self.i18n,
|
i18n,
|
||||||
)
|
)
|
||||||
.and_then(self.force_chat_input.take(), |c, input| c.input(input))
|
.and_then(self.force_chat_input.take(), |c, input| c.input(input))
|
||||||
.and_then(self.tab_complete.take(), |c, input| {
|
.and_then(self.tab_complete.take(), |c, input| {
|
||||||
@ -2054,7 +2041,7 @@ impl Hud {
|
|||||||
&self.show,
|
&self.show,
|
||||||
&self.imgs,
|
&self.imgs,
|
||||||
&self.fonts,
|
&self.fonts,
|
||||||
&self.i18n,
|
i18n,
|
||||||
fps as f32,
|
fps as f32,
|
||||||
)
|
)
|
||||||
.set(self.ids.settings_window, ui_widgets)
|
.set(self.ids.settings_window, ui_widgets)
|
||||||
@ -2210,7 +2197,7 @@ impl Hud {
|
|||||||
client,
|
client,
|
||||||
&self.imgs,
|
&self.imgs,
|
||||||
&self.fonts,
|
&self.fonts,
|
||||||
&self.i18n,
|
i18n,
|
||||||
info.selected_entity,
|
info.selected_entity,
|
||||||
&self.rot_imgs,
|
&self.rot_imgs,
|
||||||
tooltip_manager,
|
tooltip_manager,
|
||||||
@ -2238,7 +2225,7 @@ impl Hud {
|
|||||||
|
|
||||||
// Spellbook
|
// Spellbook
|
||||||
if self.show.spell {
|
if self.show.spell {
|
||||||
match Spell::new(&self.show, client, &self.imgs, &self.fonts, &self.i18n)
|
match Spell::new(&self.show, client, &self.imgs, &self.fonts, i18n)
|
||||||
.set(self.ids.spell, ui_widgets)
|
.set(self.ids.spell, ui_widgets)
|
||||||
{
|
{
|
||||||
Some(spell::Event::Close) => {
|
Some(spell::Event::Close) => {
|
||||||
@ -2258,7 +2245,7 @@ impl Hud {
|
|||||||
&self.world_map,
|
&self.world_map,
|
||||||
&self.fonts,
|
&self.fonts,
|
||||||
self.pulse,
|
self.pulse,
|
||||||
&self.i18n,
|
i18n,
|
||||||
&global_state,
|
&global_state,
|
||||||
tooltip_manager,
|
tooltip_manager,
|
||||||
)
|
)
|
||||||
@ -2302,7 +2289,7 @@ impl Hud {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if self.show.esc_menu {
|
if self.show.esc_menu {
|
||||||
match EscMenu::new(&self.imgs, &self.fonts, &self.i18n)
|
match EscMenu::new(&self.imgs, &self.fonts, i18n)
|
||||||
.set(self.ids.esc_menu, ui_widgets)
|
.set(self.ids.esc_menu, ui_widgets)
|
||||||
{
|
{
|
||||||
Some(esc_menu::Event::OpenSettings(tab)) => {
|
Some(esc_menu::Event::OpenSettings(tab)) => {
|
||||||
@ -2344,8 +2331,7 @@ impl Hud {
|
|||||||
{
|
{
|
||||||
if self.show.free_look {
|
if self.show.free_look {
|
||||||
Text::new(
|
Text::new(
|
||||||
&self
|
&i18n
|
||||||
.i18n
|
|
||||||
.get("hud.free_look_indicator")
|
.get("hud.free_look_indicator")
|
||||||
.replace("{key}", freelook_key.to_string().as_str()),
|
.replace("{key}", freelook_key.to_string().as_str()),
|
||||||
)
|
)
|
||||||
@ -2355,8 +2341,7 @@ impl Hud {
|
|||||||
.font_size(self.fonts.cyri.scale(20))
|
.font_size(self.fonts.cyri.scale(20))
|
||||||
.set(self.ids.free_look_bg, ui_widgets);
|
.set(self.ids.free_look_bg, ui_widgets);
|
||||||
Text::new(
|
Text::new(
|
||||||
&self
|
&i18n
|
||||||
.i18n
|
|
||||||
.get("hud.free_look_indicator")
|
.get("hud.free_look_indicator")
|
||||||
.replace("{key}", freelook_key.to_string().as_str()),
|
.replace("{key}", freelook_key.to_string().as_str()),
|
||||||
)
|
)
|
||||||
@ -2370,13 +2355,13 @@ impl Hud {
|
|||||||
|
|
||||||
// Auto walk indicator
|
// Auto walk indicator
|
||||||
if self.show.auto_walk {
|
if self.show.auto_walk {
|
||||||
Text::new(&self.i18n.get("hud.auto_walk_indicator"))
|
Text::new(i18n.get("hud.auto_walk_indicator"))
|
||||||
.color(TEXT_BG)
|
.color(TEXT_BG)
|
||||||
.mid_top_with_margin_on(ui_widgets.window, 70.0)
|
.mid_top_with_margin_on(ui_widgets.window, 70.0)
|
||||||
.font_id(self.fonts.cyri.conrod_id)
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
.font_size(self.fonts.cyri.scale(20))
|
.font_size(self.fonts.cyri.scale(20))
|
||||||
.set(self.ids.auto_walk_bg, ui_widgets);
|
.set(self.ids.auto_walk_bg, ui_widgets);
|
||||||
Text::new(&self.i18n.get("hud.auto_walk_indicator"))
|
Text::new(i18n.get("hud.auto_walk_indicator"))
|
||||||
.color(KILL_COLOR)
|
.color(KILL_COLOR)
|
||||||
.top_left_with_margins_on(self.ids.auto_walk_bg, -1.0, -1.0)
|
.top_left_with_margins_on(self.ids.auto_walk_bg, -1.0, -1.0)
|
||||||
.font_id(self.fonts.cyri.conrod_id)
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
use common::assets::{self, Asset};
|
use common::assets::{self, AssetExt};
|
||||||
use deunicode::deunicode;
|
use deunicode::deunicode;
|
||||||
use hashbrown::{HashMap, HashSet};
|
|
||||||
use ron::de::from_reader;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{fs::File, io::BufReader};
|
use std::{
|
||||||
|
borrow::Cow,
|
||||||
|
};
|
||||||
|
use hashbrown::{HashMap, HashSet};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
/// The reference language, aka the more up-to-date localization data.
|
/// The reference language, aka the more up-to-date localization data.
|
||||||
@ -44,7 +45,7 @@ impl Font {
|
|||||||
pub type Fonts = HashMap<String, Font>;
|
pub type Fonts = HashMap<String, Font>;
|
||||||
|
|
||||||
/// Store internationalization data
|
/// Store internationalization data
|
||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Localization {
|
pub struct Localization {
|
||||||
/// A map storing the localized texts
|
/// A map storing the localized texts
|
||||||
///
|
///
|
||||||
@ -92,9 +93,9 @@ impl Localization {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Return the missing keys compared to the reference language
|
/// Return the missing keys compared to the reference language
|
||||||
pub fn list_missing_entries(&self) -> (HashSet<String>, HashSet<String>) {
|
fn list_missing_entries(&self) -> (HashSet<String>, HashSet<String>) {
|
||||||
let reference_localization =
|
let reference_localization =
|
||||||
Localization::load_expect(i18n_asset_key(REFERENCE_LANG).as_ref());
|
Localization::load_expect(&i18n_asset_key(REFERENCE_LANG)).read();
|
||||||
|
|
||||||
let reference_string_keys: HashSet<_> =
|
let reference_string_keys: HashSet<_> =
|
||||||
reference_localization.string_map.keys().cloned().collect();
|
reference_localization.string_map.keys().cloned().collect();
|
||||||
@ -133,15 +134,15 @@ impl Localization {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Asset for Localization {
|
impl assets::Asset for Localization {
|
||||||
const ENDINGS: &'static [&'static str] = &["ron"];
|
const EXTENSION: &'static str = "ron";
|
||||||
|
type Loader = LocalizationLoader;
|
||||||
|
}
|
||||||
|
|
||||||
/// Load the translations located in the input buffer and convert them
|
pub struct LocalizationLoader;
|
||||||
/// into a `Localization` object.
|
impl assets::Loader<Localization> for LocalizationLoader {
|
||||||
#[allow(clippy::into_iter_on_ref)] // TODO: Pending review in #587
|
fn load(content: Cow<[u8]>, ext: &str) -> Result<Localization, assets::BoxedError> {
|
||||||
fn parse(buf_reader: BufReader<File>, _specifier: &str) -> Result<Self, assets::Error> {
|
let mut asked_localization: Localization = assets::RonLoader::load(content, ext)?;
|
||||||
let mut asked_localization: Localization =
|
|
||||||
from_reader(buf_reader).map_err(assets::Error::parse_error)?;
|
|
||||||
|
|
||||||
// Update the text if UTF-8 to ASCII conversion is enabled
|
// Update the text if UTF-8 to ASCII conversion is enabled
|
||||||
if asked_localization.convert_utf8_to_ascii {
|
if asked_localization.convert_utf8_to_ascii {
|
||||||
@ -162,9 +163,13 @@ impl Asset for Localization {
|
|||||||
|
|
||||||
/// Load all the available languages located in the voxygen asset directory
|
/// Load all the available languages located in the voxygen asset directory
|
||||||
pub fn list_localizations() -> Vec<LanguageMetadata> {
|
pub fn list_localizations() -> Vec<LanguageMetadata> {
|
||||||
let voxygen_locales_assets = "voxygen.i18n.*";
|
assets::load_dir::<Localization>("voxygen.i18n")
|
||||||
let lang_list = Localization::load_glob(voxygen_locales_assets).unwrap();
|
.unwrap()
|
||||||
lang_list.iter().map(|e| (*e).metadata.clone()).collect()
|
.iter_all()
|
||||||
|
.filter_map(|(_, lang)| {
|
||||||
|
lang.ok().map(|e| e.read().metadata.clone())
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the asset associated with the language_id
|
/// Return the asset associated with the language_id
|
||||||
|
@ -34,12 +34,13 @@ pub use crate::error::Error;
|
|||||||
use crate::singleplayer::Singleplayer;
|
use crate::singleplayer::Singleplayer;
|
||||||
use crate::{
|
use crate::{
|
||||||
audio::AudioFrontend,
|
audio::AudioFrontend,
|
||||||
|
i18n::Localization,
|
||||||
profile::Profile,
|
profile::Profile,
|
||||||
render::Renderer,
|
render::Renderer,
|
||||||
settings::Settings,
|
settings::Settings,
|
||||||
window::{Event, Window},
|
window::{Event, Window},
|
||||||
};
|
};
|
||||||
use common::{assets::watch, clock::Clock, span};
|
use common::{assets::AssetHandle, clock::Clock, span};
|
||||||
|
|
||||||
/// A type used to store state that is shared between all play states.
|
/// A type used to store state that is shared between all play states.
|
||||||
pub struct GlobalState {
|
pub struct GlobalState {
|
||||||
@ -52,7 +53,7 @@ pub struct GlobalState {
|
|||||||
#[cfg(feature = "singleplayer")]
|
#[cfg(feature = "singleplayer")]
|
||||||
pub singleplayer: Option<Singleplayer>,
|
pub singleplayer: Option<Singleplayer>,
|
||||||
// TODO: redo this so that the watcher doesn't have to exist for reloading to occur
|
// TODO: redo this so that the watcher doesn't have to exist for reloading to occur
|
||||||
pub localization_watcher: watch::ReloadIndicator,
|
pub i18n: AssetHandle<Localization>,
|
||||||
pub clipboard: Option<iced_winit::Clipboard>,
|
pub clipboard: Option<iced_winit::Clipboard>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ use veloren_voxygen::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use common::{
|
use common::{
|
||||||
assets::{watch, Asset},
|
assets::{self, AssetExt},
|
||||||
clock::Clock,
|
clock::Clock,
|
||||||
};
|
};
|
||||||
use std::panic;
|
use std::panic;
|
||||||
@ -138,6 +138,8 @@ fn main() {
|
|||||||
default_hook(panic_info);
|
default_hook(panic_info);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
assets::start_hot_reloading();
|
||||||
|
|
||||||
// Initialise watcher for animation hotreloading
|
// Initialise watcher for animation hotreloading
|
||||||
#[cfg(feature = "hot-anim")]
|
#[cfg(feature = "hot-anim")]
|
||||||
anim::init();
|
anim::init();
|
||||||
@ -155,10 +157,8 @@ fn main() {
|
|||||||
// Load the profile.
|
// Load the profile.
|
||||||
let profile = Profile::load();
|
let profile = Profile::load();
|
||||||
|
|
||||||
let mut localization_watcher = watch::ReloadIndicator::new();
|
let i18n = Localization::load(
|
||||||
let localized_strings = Localization::load_watched(
|
|
||||||
&i18n_asset_key(&settings.language.selected_language),
|
&i18n_asset_key(&settings.language.selected_language),
|
||||||
&mut localization_watcher,
|
|
||||||
)
|
)
|
||||||
.unwrap_or_else(|error| {
|
.unwrap_or_else(|error| {
|
||||||
let selected_language = &settings.language.selected_language;
|
let selected_language = &settings.language.selected_language;
|
||||||
@ -168,13 +168,9 @@ fn main() {
|
|||||||
"Impossible to load language: change to the default language (English) instead.",
|
"Impossible to load language: change to the default language (English) instead.",
|
||||||
);
|
);
|
||||||
settings.language.selected_language = i18n::REFERENCE_LANG.to_owned();
|
settings.language.selected_language = i18n::REFERENCE_LANG.to_owned();
|
||||||
Localization::load_watched(
|
Localization::load_expect(&i18n_asset_key(&settings.language.selected_language))
|
||||||
&i18n_asset_key(&settings.language.selected_language),
|
|
||||||
&mut localization_watcher,
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
});
|
});
|
||||||
localized_strings.log_missing_entries();
|
i18n.read().log_missing_entries();
|
||||||
|
|
||||||
// Create window
|
// Create window
|
||||||
let (window, event_loop) = Window::new(&settings).expect("Failed to create window!");
|
let (window, event_loop) = Window::new(&settings).expect("Failed to create window!");
|
||||||
@ -192,7 +188,7 @@ fn main() {
|
|||||||
info_message: None,
|
info_message: None,
|
||||||
#[cfg(feature = "singleplayer")]
|
#[cfg(feature = "singleplayer")]
|
||||||
singleplayer: None,
|
singleplayer: None,
|
||||||
localization_watcher,
|
i18n,
|
||||||
clipboard,
|
clipboard,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
mod ui;
|
mod ui;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
i18n::{i18n_asset_key, Localization},
|
|
||||||
render::Renderer,
|
render::Renderer,
|
||||||
scene::simple::{self as scene, Scene},
|
scene::simple::{self as scene, Scene},
|
||||||
session::SessionState,
|
session::SessionState,
|
||||||
@ -10,7 +9,7 @@ use crate::{
|
|||||||
Direction, GlobalState, PlayState, PlayStateResult,
|
Direction, GlobalState, PlayState, PlayStateResult,
|
||||||
};
|
};
|
||||||
use client::{self, Client};
|
use client::{self, Client};
|
||||||
use common::{assets::Asset, comp, resources::DeltaTime, span};
|
use common::{comp, resources::DeltaTime, span};
|
||||||
use specs::WorldExt;
|
use specs::WorldExt;
|
||||||
use std::{cell::RefCell, rc::Rc};
|
use std::{cell::RefCell, rc::Rc};
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
@ -64,11 +63,7 @@ impl PlayState for CharSelectionState {
|
|||||||
self.client.borrow_mut().load_character_list();
|
self.client.borrow_mut().load_character_list();
|
||||||
|
|
||||||
// Updated localization in case the selected language was changed
|
// Updated localization in case the selected language was changed
|
||||||
let localized_strings = crate::i18n::Localization::load_expect(
|
self.char_selection_ui.update_language(global_state.i18n);
|
||||||
&crate::i18n::i18n_asset_key(&global_state.settings.language.selected_language),
|
|
||||||
);
|
|
||||||
self.char_selection_ui
|
|
||||||
.update_language(std::sync::Arc::clone(&localized_strings));
|
|
||||||
// Set scale mode in case it was change
|
// Set scale mode in case it was change
|
||||||
self.char_selection_ui
|
self.char_selection_ui
|
||||||
.set_scale_mode(global_state.settings.gameplay.ui_scale);
|
.set_scale_mode(global_state.settings.gameplay.ui_scale);
|
||||||
@ -167,9 +162,7 @@ impl PlayState for CharSelectionState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Tick the client (currently only to keep the connection alive).
|
// Tick the client (currently only to keep the connection alive).
|
||||||
let localized_strings = Localization::load_expect(&i18n_asset_key(
|
let localized_strings = &*global_state.i18n.read();
|
||||||
&global_state.settings.language.selected_language,
|
|
||||||
));
|
|
||||||
|
|
||||||
match self.client.borrow_mut().tick(
|
match self.client.borrow_mut().tick(
|
||||||
comp::ControllerInputs::default(),
|
comp::ControllerInputs::default(),
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
i18n::{i18n_asset_key, Localization},
|
i18n::Localization,
|
||||||
render::Renderer,
|
render::Renderer,
|
||||||
ui::{
|
ui::{
|
||||||
self,
|
self,
|
||||||
@ -22,7 +22,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use client::Client;
|
use client::Client;
|
||||||
use common::{
|
use common::{
|
||||||
assets::Asset,
|
assets::AssetHandle,
|
||||||
character::{CharacterId, CharacterItem, MAX_CHARACTERS_PER_PLAYER},
|
character::{CharacterId, CharacterItem, MAX_CHARACTERS_PER_PLAYER},
|
||||||
comp::{self, humanoid, item::tool::AbilityMap},
|
comp::{self, humanoid, item::tool::AbilityMap},
|
||||||
LoadoutBuilder,
|
LoadoutBuilder,
|
||||||
@ -220,7 +220,6 @@ enum InfoContent {
|
|||||||
struct Controls {
|
struct Controls {
|
||||||
fonts: Fonts,
|
fonts: Fonts,
|
||||||
imgs: Imgs,
|
imgs: Imgs,
|
||||||
i18n: std::sync::Arc<Localization>,
|
|
||||||
// Voxygen version
|
// Voxygen version
|
||||||
version: String,
|
version: String,
|
||||||
// Alpha disclaimer
|
// Alpha disclaimer
|
||||||
@ -267,7 +266,6 @@ impl Controls {
|
|||||||
fn new(
|
fn new(
|
||||||
fonts: Fonts,
|
fonts: Fonts,
|
||||||
imgs: Imgs,
|
imgs: Imgs,
|
||||||
i18n: std::sync::Arc<Localization>,
|
|
||||||
selected: Option<CharacterId>,
|
selected: Option<CharacterId>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let version = common::util::DISPLAY_VERSION_LONG.clone();
|
let version = common::util::DISPLAY_VERSION_LONG.clone();
|
||||||
@ -276,7 +274,6 @@ impl Controls {
|
|||||||
Self {
|
Self {
|
||||||
fonts,
|
fonts,
|
||||||
imgs,
|
imgs,
|
||||||
i18n,
|
|
||||||
version,
|
version,
|
||||||
alpha,
|
alpha,
|
||||||
|
|
||||||
@ -287,12 +284,13 @@ impl Controls {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn view(
|
fn view<'a>(
|
||||||
&mut self,
|
&'a mut self,
|
||||||
_settings: &Settings,
|
_settings: &Settings,
|
||||||
client: &Client,
|
client: &Client,
|
||||||
error: &Option<String>,
|
error: &Option<String>,
|
||||||
) -> Element<Message> {
|
i18n: &'a Localization,
|
||||||
|
) -> Element<'a, Message> {
|
||||||
// TODO: use font scale thing for text size (use on button size for buttons with
|
// TODO: use font scale thing for text size (use on button size for buttons with
|
||||||
// text)
|
// text)
|
||||||
|
|
||||||
@ -301,7 +299,6 @@ impl Controls {
|
|||||||
|
|
||||||
let imgs = &self.imgs;
|
let imgs = &self.imgs;
|
||||||
let fonts = &self.fonts;
|
let fonts = &self.fonts;
|
||||||
let i18n = &self.i18n;
|
|
||||||
let tooltip_manager = &self.tooltip_manager;
|
let tooltip_manager = &self.tooltip_manager;
|
||||||
|
|
||||||
let button_style = style::button::Style::new(imgs.button)
|
let button_style = style::button::Style::new(imgs.button)
|
||||||
@ -1298,7 +1295,7 @@ impl Controls {
|
|||||||
body.skin = rng.gen_range(0, species.num_skin_colors());
|
body.skin = rng.gen_range(0, species.num_skin_colors());
|
||||||
body.eye_color = rng.gen_range(0, species.num_eye_colors());
|
body.eye_color = rng.gen_range(0, species.num_eye_colors());
|
||||||
body.eyes = rng.gen_range(0, species.num_eyes(body_type));
|
body.eyes = rng.gen_range(0, species.num_eyes(body_type));
|
||||||
*name = npc::get_npc_name(npc::NpcKind::Humanoid).to_string();
|
*name = npc::get_npc_name(npc::NpcKind::Humanoid);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Message::ConfirmDeletion => {
|
Message::ConfirmDeletion => {
|
||||||
@ -1402,20 +1399,10 @@ impl CharSelectionUi {
|
|||||||
let selected_character = global_state.profile.get_selected_character(server_name);
|
let selected_character = global_state.profile.get_selected_character(server_name);
|
||||||
|
|
||||||
// Load language
|
// Load language
|
||||||
let i18n = Localization::load_expect(&i18n_asset_key(
|
let i18n = global_state.i18n.read();
|
||||||
&global_state.settings.language.selected_language,
|
|
||||||
));
|
|
||||||
|
|
||||||
// TODO: don't add default font twice
|
// TODO: don't add default font twice
|
||||||
let font = {
|
let font = ui::ice::load_font(&i18n.fonts.get("cyri").unwrap().asset_key);
|
||||||
use std::io::Read;
|
|
||||||
let mut buf = Vec::new();
|
|
||||||
common::assets::load_file(&i18n.fonts.get("cyri").unwrap().asset_key, &["ttf"])
|
|
||||||
.unwrap()
|
|
||||||
.read_to_end(&mut buf)
|
|
||||||
.unwrap();
|
|
||||||
ui::ice::Font::try_from_vec(buf).unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut ui = Ui::new(
|
let mut ui = Ui::new(
|
||||||
&mut global_state.window,
|
&mut global_state.window,
|
||||||
@ -1429,7 +1416,6 @@ impl CharSelectionUi {
|
|||||||
let controls = Controls::new(
|
let controls = Controls::new(
|
||||||
fonts,
|
fonts,
|
||||||
Imgs::load(&mut ui).expect("Failed to load images"),
|
Imgs::load(&mut ui).expect("Failed to load images"),
|
||||||
i18n,
|
|
||||||
selected_character,
|
selected_character,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1476,19 +1462,12 @@ impl CharSelectionUi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_language(&mut self, i18n: std::sync::Arc<Localization>) {
|
pub fn update_language(&mut self, i18n: AssetHandle<Localization>) {
|
||||||
let font = {
|
let i18n = i18n.read();
|
||||||
use std::io::Read;
|
let font = ui::ice::load_font(&i18n.fonts.get("cyri").unwrap().asset_key);
|
||||||
let mut buf = Vec::new();
|
|
||||||
common::assets::load_file(&i18n.fonts.get("cyri").unwrap().asset_key, &["ttf"])
|
|
||||||
.unwrap()
|
|
||||||
.read_to_end(&mut buf)
|
|
||||||
.unwrap();
|
|
||||||
ui::ice::Font::try_from_vec(buf).unwrap()
|
|
||||||
};
|
|
||||||
self.controls.i18n = i18n;
|
|
||||||
self.ui.clear_fonts(font);
|
self.ui.clear_fonts(font);
|
||||||
self.controls.fonts = Fonts::load(&self.controls.i18n.fonts, &mut self.ui)
|
self.controls.fonts = Fonts::load(&i18n.fonts, &mut self.ui)
|
||||||
.expect("Impossible to load fonts!");
|
.expect("Impossible to load fonts!");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1503,10 +1482,11 @@ impl CharSelectionUi {
|
|||||||
// TODO: do we need whole client here or just character list?
|
// TODO: do we need whole client here or just character list?
|
||||||
pub fn maintain(&mut self, global_state: &mut GlobalState, client: &Client) -> Vec<Event> {
|
pub fn maintain(&mut self, global_state: &mut GlobalState, client: &Client) -> Vec<Event> {
|
||||||
let mut events = Vec::new();
|
let mut events = Vec::new();
|
||||||
|
let i18n = global_state.i18n.read();
|
||||||
|
|
||||||
let (mut messages, _) = self.ui.maintain(
|
let (mut messages, _) = self.ui.maintain(
|
||||||
self.controls
|
self.controls
|
||||||
.view(&global_state.settings, &client, &self.error),
|
.view(&global_state.settings, &client, &self.error, &i18n),
|
||||||
global_state.window.renderer_mut(),
|
global_state.window.renderer_mut(),
|
||||||
global_state.clipboard.as_ref(),
|
global_state.clipboard.as_ref(),
|
||||||
);
|
);
|
||||||
|
@ -12,7 +12,7 @@ use crate::{
|
|||||||
Direction, GlobalState, PlayState, PlayStateResult,
|
Direction, GlobalState, PlayState, PlayStateResult,
|
||||||
};
|
};
|
||||||
use client_init::{ClientInit, Error as InitError, Msg as InitMsg};
|
use client_init::{ClientInit, Error as InitError, Msg as InitMsg};
|
||||||
use common::{assets::Asset, comp, span};
|
use common::{assets::AssetExt, comp, span};
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
use ui::{Event as MainMenuEvent, MainMenuUi};
|
use ui::{Event as MainMenuEvent, MainMenuUi};
|
||||||
|
|
||||||
@ -48,13 +48,7 @@ impl PlayState for MainMenuState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Updated localization in case the selected language was changed
|
// Updated localization in case the selected language was changed
|
||||||
let localized_strings = crate::i18n::Localization::load_expect(
|
self.main_menu_ui.update_language(global_state.i18n, &global_state.settings);
|
||||||
&crate::i18n::i18n_asset_key(&global_state.settings.language.selected_language),
|
|
||||||
);
|
|
||||||
self.main_menu_ui.update_language(
|
|
||||||
std::sync::Arc::clone(&localized_strings),
|
|
||||||
&global_state.settings,
|
|
||||||
);
|
|
||||||
// Set scale mode in case it was change
|
// Set scale mode in case it was change
|
||||||
self.main_menu_ui
|
self.main_menu_ui
|
||||||
.set_scale_mode(global_state.settings.gameplay.ui_scale);
|
.set_scale_mode(global_state.settings.gameplay.ui_scale);
|
||||||
@ -63,9 +57,6 @@ impl PlayState for MainMenuState {
|
|||||||
#[allow(clippy::single_match)] // TODO: remove when event match has multiple arms
|
#[allow(clippy::single_match)] // TODO: remove when event match has multiple arms
|
||||||
fn tick(&mut self, global_state: &mut GlobalState, events: Vec<Event>) -> PlayStateResult {
|
fn tick(&mut self, global_state: &mut GlobalState, events: Vec<Event>) -> PlayStateResult {
|
||||||
span!(_guard, "tick", "<MainMenuState as PlayState>::tick");
|
span!(_guard, "tick", "<MainMenuState as PlayState>::tick");
|
||||||
let mut localized_strings = crate::i18n::Localization::load_expect(
|
|
||||||
&crate::i18n::i18n_asset_key(&global_state.settings.language.selected_language),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Poll server creation
|
// Poll server creation
|
||||||
#[cfg(feature = "singleplayer")]
|
#[cfg(feature = "singleplayer")]
|
||||||
@ -121,6 +112,7 @@ impl PlayState for MainMenuState {
|
|||||||
)));
|
)));
|
||||||
},
|
},
|
||||||
Some(InitMsg::Done(Err(err))) => {
|
Some(InitMsg::Done(Err(err))) => {
|
||||||
|
let localized_strings = global_state.i18n.read();
|
||||||
self.client_init = None;
|
self.client_init = None;
|
||||||
global_state.info_message = Some({
|
global_state.info_message = Some({
|
||||||
let err = match err {
|
let err = match err {
|
||||||
@ -268,12 +260,12 @@ impl PlayState for MainMenuState {
|
|||||||
MainMenuEvent::ChangeLanguage(new_language) => {
|
MainMenuEvent::ChangeLanguage(new_language) => {
|
||||||
global_state.settings.language.selected_language =
|
global_state.settings.language.selected_language =
|
||||||
new_language.language_identifier;
|
new_language.language_identifier;
|
||||||
localized_strings = Localization::load_expect(&i18n_asset_key(
|
global_state.i18n = Localization::load_expect(&i18n_asset_key(
|
||||||
&global_state.settings.language.selected_language,
|
&global_state.settings.language.selected_language,
|
||||||
));
|
));
|
||||||
localized_strings.log_missing_entries();
|
global_state.i18n.read().log_missing_entries();
|
||||||
self.main_menu_ui.update_language(
|
self.main_menu_ui.update_language(
|
||||||
std::sync::Arc::clone(&localized_strings),
|
global_state.i18n,
|
||||||
&global_state.settings,
|
&global_state.settings,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -5,12 +5,12 @@ mod login;
|
|||||||
mod servers;
|
mod servers;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
i18n::{i18n_asset_key, LanguageMetadata, Localization},
|
i18n::{LanguageMetadata, Localization},
|
||||||
render::Renderer,
|
render::Renderer,
|
||||||
ui::{
|
ui::{
|
||||||
self,
|
self,
|
||||||
fonts::IcedFonts as Fonts,
|
fonts::IcedFonts as Fonts,
|
||||||
ice::{style, widget, Element, Font, IcedUi as Ui},
|
ice::{style, widget, Element, IcedUi as Ui, load_font},
|
||||||
img_ids::{ImageGraphic, VoxelGraphic},
|
img_ids::{ImageGraphic, VoxelGraphic},
|
||||||
Graphic,
|
Graphic,
|
||||||
},
|
},
|
||||||
@ -19,8 +19,7 @@ use crate::{
|
|||||||
use iced::{text_input, Column, Container, HorizontalAlignment, Length, Row, Space};
|
use iced::{text_input, Column, Container, HorizontalAlignment, Length, Row, Space};
|
||||||
//ImageFrame, Tooltip,
|
//ImageFrame, Tooltip,
|
||||||
use crate::settings::Settings;
|
use crate::settings::Settings;
|
||||||
use common::assets::Asset;
|
use common::assets::{self, AssetExt, AssetHandle};
|
||||||
use image::DynamicImage;
|
|
||||||
use rand::{seq::SliceRandom, thread_rng};
|
use rand::{seq::SliceRandom, thread_rng};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
@ -132,7 +131,7 @@ struct Controls {
|
|||||||
fonts: Fonts,
|
fonts: Fonts,
|
||||||
imgs: Imgs,
|
imgs: Imgs,
|
||||||
bg_img: widget::image::Handle,
|
bg_img: widget::image::Handle,
|
||||||
i18n: std::sync::Arc<Localization>,
|
i18n: AssetHandle<Localization>,
|
||||||
// Voxygen version
|
// Voxygen version
|
||||||
version: String,
|
version: String,
|
||||||
// Alpha disclaimer
|
// Alpha disclaimer
|
||||||
@ -177,7 +176,7 @@ impl Controls {
|
|||||||
fonts: Fonts,
|
fonts: Fonts,
|
||||||
imgs: Imgs,
|
imgs: Imgs,
|
||||||
bg_img: widget::image::Handle,
|
bg_img: widget::image::Handle,
|
||||||
i18n: std::sync::Arc<Localization>,
|
i18n: AssetHandle<Localization>,
|
||||||
settings: &Settings,
|
settings: &Settings,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let version = common::util::DISPLAY_VERSION_LONG.clone();
|
let version = common::util::DISPLAY_VERSION_LONG.clone();
|
||||||
@ -281,7 +280,7 @@ impl Controls {
|
|||||||
&self.imgs,
|
&self.imgs,
|
||||||
&self.login_info,
|
&self.login_info,
|
||||||
error.as_deref(),
|
error.as_deref(),
|
||||||
&self.i18n,
|
&self.i18n.read(),
|
||||||
self.is_selecting_language,
|
self.is_selecting_language,
|
||||||
self.selected_language_index,
|
self.selected_language_index,
|
||||||
&language_metadatas,
|
&language_metadatas,
|
||||||
@ -293,7 +292,7 @@ impl Controls {
|
|||||||
&self.imgs,
|
&self.imgs,
|
||||||
&settings.networking.servers,
|
&settings.networking.servers,
|
||||||
self.selected_server_index,
|
self.selected_server_index,
|
||||||
&self.i18n,
|
&self.i18n.read(),
|
||||||
button_style,
|
button_style,
|
||||||
),
|
),
|
||||||
Screen::Connecting {
|
Screen::Connecting {
|
||||||
@ -304,7 +303,7 @@ impl Controls {
|
|||||||
&self.imgs,
|
&self.imgs,
|
||||||
&connection_state,
|
&connection_state,
|
||||||
self.time,
|
self.time,
|
||||||
&self.i18n,
|
&self.i18n.read(),
|
||||||
button_style,
|
button_style,
|
||||||
settings.gameplay.loading_tips,
|
settings.gameplay.loading_tips,
|
||||||
),
|
),
|
||||||
@ -482,20 +481,9 @@ pub struct MainMenuUi {
|
|||||||
impl<'a> MainMenuUi {
|
impl<'a> MainMenuUi {
|
||||||
pub fn new(global_state: &mut GlobalState) -> Self {
|
pub fn new(global_state: &mut GlobalState) -> Self {
|
||||||
// Load language
|
// Load language
|
||||||
let i18n = Localization::load_expect(&i18n_asset_key(
|
let i18n = &*global_state.i18n.read();
|
||||||
&global_state.settings.language.selected_language,
|
|
||||||
));
|
|
||||||
|
|
||||||
// TODO: don't add default font twice
|
// TODO: don't add default font twice
|
||||||
let font = {
|
let font = load_font(&i18n.fonts.get("cyri").unwrap().asset_key);
|
||||||
use std::io::Read;
|
|
||||||
let mut buf = Vec::new();
|
|
||||||
common::assets::load_file(&i18n.fonts.get("cyri").unwrap().asset_key, &["ttf"])
|
|
||||||
.unwrap()
|
|
||||||
.read_to_end(&mut buf)
|
|
||||||
.unwrap();
|
|
||||||
Font::try_from_vec(buf).unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut ui = Ui::new(
|
let mut ui = Ui::new(
|
||||||
&mut global_state.window,
|
&mut global_state.window,
|
||||||
@ -508,30 +496,24 @@ impl<'a> MainMenuUi {
|
|||||||
|
|
||||||
let bg_img_spec = BG_IMGS.choose(&mut thread_rng()).unwrap();
|
let bg_img_spec = BG_IMGS.choose(&mut thread_rng()).unwrap();
|
||||||
|
|
||||||
|
let bg_img = assets::Image::load_expect(bg_img_spec).read().to_image();
|
||||||
let controls = Controls::new(
|
let controls = Controls::new(
|
||||||
fonts,
|
fonts,
|
||||||
Imgs::load(&mut ui).expect("Failed to load images"),
|
Imgs::load(&mut ui).expect("Failed to load images"),
|
||||||
ui.add_graphic(Graphic::Image(DynamicImage::load_expect(bg_img_spec), None)),
|
ui.add_graphic(Graphic::Image(bg_img, None)),
|
||||||
i18n,
|
global_state.i18n,
|
||||||
&global_state.settings,
|
&global_state.settings,
|
||||||
);
|
);
|
||||||
|
|
||||||
Self { ui, controls }
|
Self { ui, controls }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_language(&mut self, i18n: std::sync::Arc<Localization>, settings: &Settings) {
|
pub fn update_language(&mut self, i18n: AssetHandle<Localization>, settings: &Settings) {
|
||||||
let font = {
|
|
||||||
use std::io::Read;
|
|
||||||
let mut buf = Vec::new();
|
|
||||||
common::assets::load_file(&i18n.fonts.get("cyri").unwrap().asset_key, &["ttf"])
|
|
||||||
.unwrap()
|
|
||||||
.read_to_end(&mut buf)
|
|
||||||
.unwrap();
|
|
||||||
Font::try_from_vec(buf).unwrap()
|
|
||||||
};
|
|
||||||
self.controls.i18n = i18n;
|
self.controls.i18n = i18n;
|
||||||
|
let i18n = &*i18n.read();
|
||||||
|
let font = load_font(&i18n.fonts.get("cyri").unwrap().asset_key);
|
||||||
self.ui.clear_fonts(font);
|
self.ui.clear_fonts(font);
|
||||||
self.controls.fonts = Fonts::load(&self.controls.i18n.fonts, &mut self.ui)
|
self.controls.fonts = Fonts::load(&i18n.fonts, &mut self.ui)
|
||||||
.expect("Impossible to load fonts!");
|
.expect("Impossible to load fonts!");
|
||||||
let language_metadatas = crate::i18n::list_localizations();
|
let language_metadatas = crate::i18n::list_localizations();
|
||||||
self.controls.selected_language_index = language_metadatas
|
self.controls.selected_language_index = language_metadatas
|
||||||
|
@ -13,7 +13,7 @@ use super::{
|
|||||||
ShadowMapMode, ShadowMode, WrapMode,
|
ShadowMapMode, ShadowMode, WrapMode,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
assets::{self, watch::ReloadIndicator, Asset},
|
assets::{self, AssetExt, AssetHandle},
|
||||||
span,
|
span,
|
||||||
};
|
};
|
||||||
use core::convert::TryFrom;
|
use core::convert::TryFrom;
|
||||||
@ -24,11 +24,6 @@ use gfx::{
|
|||||||
traits::{Device, Factory, FactoryExt},
|
traits::{Device, Factory, FactoryExt},
|
||||||
};
|
};
|
||||||
use glsl_include::Context as IncludeContext;
|
use glsl_include::Context as IncludeContext;
|
||||||
use image::DynamicImage;
|
|
||||||
use std::{
|
|
||||||
fs::File,
|
|
||||||
io::{BufReader, Read},
|
|
||||||
};
|
|
||||||
use tracing::{error, warn};
|
use tracing::{error, warn};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
@ -104,17 +99,119 @@ pub type ColLightInfo = (
|
|||||||
);
|
);
|
||||||
|
|
||||||
/// Load from a GLSL file.
|
/// Load from a GLSL file.
|
||||||
pub struct Glsl;
|
pub struct Glsl(String);
|
||||||
|
|
||||||
impl Asset for Glsl {
|
impl From<String> for Glsl {
|
||||||
type Output = String;
|
fn from(s: String) -> Glsl {
|
||||||
|
Glsl(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const ENDINGS: &'static [&'static str] = &["glsl"];
|
impl assets::Asset for Glsl {
|
||||||
|
const EXTENSION: &'static str = "glsl";
|
||||||
|
type Loader = assets::LoadFrom<String, assets::StringLoader>;
|
||||||
|
}
|
||||||
|
|
||||||
fn parse(mut buf_reader: BufReader<File>, _specifier: &str) -> Result<String, assets::Error> {
|
struct Shaders {
|
||||||
let mut string = String::new();
|
constants: AssetHandle<Glsl>,
|
||||||
buf_reader.read_to_string(&mut string)?;
|
globals: AssetHandle<Glsl>,
|
||||||
Ok(string)
|
sky: AssetHandle<Glsl>,
|
||||||
|
light: AssetHandle<Glsl>,
|
||||||
|
srgb: AssetHandle<Glsl>,
|
||||||
|
random: AssetHandle<Glsl>,
|
||||||
|
lod: AssetHandle<Glsl>,
|
||||||
|
shadows: AssetHandle<Glsl>,
|
||||||
|
|
||||||
|
anti_alias_none: AssetHandle<Glsl>,
|
||||||
|
anti_alias_fxaa: AssetHandle<Glsl>,
|
||||||
|
anti_alias_msaa_x4: AssetHandle<Glsl>,
|
||||||
|
anti_alias_msaa_x8: AssetHandle<Glsl>,
|
||||||
|
anti_alias_msaa_x16: AssetHandle<Glsl>,
|
||||||
|
cloud_none: AssetHandle<Glsl>,
|
||||||
|
cloud_regular: AssetHandle<Glsl>,
|
||||||
|
figure_vert: AssetHandle<Glsl>,
|
||||||
|
|
||||||
|
terrain_point_shadow_vert: AssetHandle<Glsl>,
|
||||||
|
terrain_directed_shadow_vert: AssetHandle<Glsl>,
|
||||||
|
figure_directed_shadow_vert: AssetHandle<Glsl>,
|
||||||
|
directed_shadow_frag: AssetHandle<Glsl>,
|
||||||
|
|
||||||
|
skybox_vert: AssetHandle<Glsl>,
|
||||||
|
skybox_frag: AssetHandle<Glsl>,
|
||||||
|
figure_frag: AssetHandle<Glsl>,
|
||||||
|
terrain_vert: AssetHandle<Glsl>,
|
||||||
|
terrain_frag: AssetHandle<Glsl>,
|
||||||
|
fluid_vert: AssetHandle<Glsl>,
|
||||||
|
fluid_frag_cheap: AssetHandle<Glsl>,
|
||||||
|
fluid_frag_shiny: AssetHandle<Glsl>,
|
||||||
|
sprite_vert: AssetHandle<Glsl>,
|
||||||
|
sprite_frag: AssetHandle<Glsl>,
|
||||||
|
particle_vert: AssetHandle<Glsl>,
|
||||||
|
particle_frag: AssetHandle<Glsl>,
|
||||||
|
ui_vert: AssetHandle<Glsl>,
|
||||||
|
ui_frag: AssetHandle<Glsl>,
|
||||||
|
lod_terrain_vert: AssetHandle<Glsl>,
|
||||||
|
lod_terrain_frag: AssetHandle<Glsl>,
|
||||||
|
clouds_vert: AssetHandle<Glsl>,
|
||||||
|
clouds_frag: AssetHandle<Glsl>,
|
||||||
|
postprocess_vert: AssetHandle<Glsl>,
|
||||||
|
postprocess_frag: AssetHandle<Glsl>,
|
||||||
|
player_shadow_frag: AssetHandle<Glsl>,
|
||||||
|
light_shadows_geom: AssetHandle<Glsl>,
|
||||||
|
light_shadows_frag: AssetHandle<Glsl>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl assets::Compound for Shaders {
|
||||||
|
fn load<S: assets::source::Source>(_: &assets::AssetCache<S>, _: &str) -> Result<Shaders, assets::Error> {
|
||||||
|
Ok(Shaders {
|
||||||
|
constants: AssetExt::load("voxygen.shaders.include.constants")?,
|
||||||
|
globals: AssetExt::load("voxygen.shaders.include.globals")?,
|
||||||
|
sky: AssetExt::load("voxygen.shaders.include.sky")?,
|
||||||
|
light: AssetExt::load("voxygen.shaders.include.light")?,
|
||||||
|
srgb: AssetExt::load("voxygen.shaders.include.srgb")?,
|
||||||
|
random: AssetExt::load("voxygen.shaders.include.random")?,
|
||||||
|
lod: AssetExt::load("voxygen.shaders.include.lod")?,
|
||||||
|
shadows: AssetExt::load("voxygen.shaders.include.shadows")?,
|
||||||
|
|
||||||
|
anti_alias_none: AssetExt::load("voxygen.shaders.antialias.none")?,
|
||||||
|
anti_alias_fxaa: AssetExt::load("voxygen.shaders.antialias.fxaa")?,
|
||||||
|
anti_alias_msaa_x4: AssetExt::load("voxygen.shaders.antialias.msaa-x4")?,
|
||||||
|
anti_alias_msaa_x8: AssetExt::load("voxygen.shaders.antialias.msaa-x8")?,
|
||||||
|
anti_alias_msaa_x16: AssetExt::load("voxygen.shaders.antialias.msaa-x16")?,
|
||||||
|
cloud_none: AssetExt::load("voxygen.shaders.include.cloud.none")?,
|
||||||
|
cloud_regular: AssetExt::load("voxygen.shaders.include.cloud.regular")?,
|
||||||
|
figure_vert: AssetExt::load("voxygen.shaders.figure-vert")?,
|
||||||
|
|
||||||
|
terrain_point_shadow_vert: AssetExt::load("voxygen.shaders.light-shadows-vert")?,
|
||||||
|
terrain_directed_shadow_vert: AssetExt::load("voxygen.shaders.light-shadows-directed-vert")?,
|
||||||
|
figure_directed_shadow_vert: AssetExt::load("voxygen.shaders.light-shadows-figure-vert")?,
|
||||||
|
directed_shadow_frag: AssetExt::load("voxygen.shaders.light-shadows-directed-frag")?,
|
||||||
|
|
||||||
|
skybox_vert: AssetExt::load("voxygen.shaders.skybox-vert")?,
|
||||||
|
skybox_frag: AssetExt::load("voxygen.shaders.skybox-frag")?,
|
||||||
|
figure_frag: AssetExt::load("voxygen.shaders.figure-frag")?,
|
||||||
|
terrain_vert: AssetExt::load("voxygen.shaders.terrain-vert")?,
|
||||||
|
terrain_frag: AssetExt::load("voxygen.shaders.terrain-frag")?,
|
||||||
|
fluid_vert: AssetExt::load("voxygen.shaders.fluid-vert")?,
|
||||||
|
fluid_frag_cheap: AssetExt::load("voxygen.shaders.fluid-frag.cheap")?,
|
||||||
|
fluid_frag_shiny: AssetExt::load("voxygen.shaders.fluid-frag.shiny")?,
|
||||||
|
sprite_vert: AssetExt::load("voxygen.shaders.sprite-vert")?,
|
||||||
|
sprite_frag: AssetExt::load("voxygen.shaders.sprite-frag")?,
|
||||||
|
particle_vert: AssetExt::load("voxygen.shaders.particle-vert")?,
|
||||||
|
particle_frag: AssetExt::load("voxygen.shaders.particle-frag")?,
|
||||||
|
ui_vert: AssetExt::load("voxygen.shaders.ui-vert")?,
|
||||||
|
ui_frag: AssetExt::load("voxygen.shaders.ui-frag")?,
|
||||||
|
lod_terrain_vert: AssetExt::load("voxygen.shaders.lod-terrain-vert")?,
|
||||||
|
lod_terrain_frag: AssetExt::load("voxygen.shaders.lod-terrain-frag")?,
|
||||||
|
clouds_vert: AssetExt::load("voxygen.shaders.clouds-vert")?,
|
||||||
|
clouds_frag: AssetExt::load("voxygen.shaders.clouds-frag")?,
|
||||||
|
postprocess_vert: AssetExt::load("voxygen.shaders.postprocess-vert")?,
|
||||||
|
postprocess_frag: AssetExt::load("voxygen.shaders.postprocess-frag")?,
|
||||||
|
player_shadow_frag: AssetExt::load("voxygen.shaders.player-shadow-frag")?,
|
||||||
|
light_shadows_geom: AssetExt::load("voxygen.shaders.light-shadows-geom")?,
|
||||||
|
light_shadows_frag: AssetExt::load("voxygen.shaders.light-shadows-frag")?,
|
||||||
|
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,7 +269,7 @@ pub struct Renderer {
|
|||||||
postprocess_pipeline: GfxPipeline<postprocess::pipe::Init<'static>>,
|
postprocess_pipeline: GfxPipeline<postprocess::pipe::Init<'static>>,
|
||||||
player_shadow_pipeline: GfxPipeline<figure::pipe::Init<'static>>,
|
player_shadow_pipeline: GfxPipeline<figure::pipe::Init<'static>>,
|
||||||
|
|
||||||
shader_reload_indicator: ReloadIndicator,
|
shaders: AssetHandle<Shaders>,
|
||||||
|
|
||||||
noise_tex: Texture<(gfx::format::R8, gfx::format::Unorm)>,
|
noise_tex: Texture<(gfx::format::R8, gfx::format::Unorm)>,
|
||||||
|
|
||||||
@ -198,7 +295,6 @@ impl Renderer {
|
|||||||
|
|
||||||
let dims = win_color_view.get_dimensions();
|
let dims = win_color_view.get_dimensions();
|
||||||
|
|
||||||
let mut shader_reload_indicator = ReloadIndicator::new();
|
|
||||||
let shadow_views = Self::create_shadow_views(
|
let shadow_views = Self::create_shadow_views(
|
||||||
&mut factory,
|
&mut factory,
|
||||||
(dims.0, dims.1),
|
(dims.0, dims.1),
|
||||||
@ -209,6 +305,8 @@ impl Renderer {
|
|||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
|
|
||||||
|
let shaders = Shaders::load_expect("");
|
||||||
|
|
||||||
let (
|
let (
|
||||||
skybox_pipeline,
|
skybox_pipeline,
|
||||||
figure_pipeline,
|
figure_pipeline,
|
||||||
@ -226,9 +324,9 @@ impl Renderer {
|
|||||||
figure_directed_shadow_pipeline,
|
figure_directed_shadow_pipeline,
|
||||||
) = create_pipelines(
|
) = create_pipelines(
|
||||||
&mut factory,
|
&mut factory,
|
||||||
|
&shaders.read(),
|
||||||
&mode,
|
&mode,
|
||||||
shadow_views.is_some(),
|
shadow_views.is_some(),
|
||||||
&mut shader_reload_indicator,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let (
|
let (
|
||||||
@ -285,7 +383,7 @@ impl Renderer {
|
|||||||
|
|
||||||
let noise_tex = Texture::new(
|
let noise_tex = Texture::new(
|
||||||
&mut factory,
|
&mut factory,
|
||||||
&DynamicImage::load_expect("voxygen.texture.noise"),
|
&assets::Image::load_expect("voxygen.texture.noise").read().0,
|
||||||
Some(gfx::texture::FilterMethod::Trilinear),
|
Some(gfx::texture::FilterMethod::Trilinear),
|
||||||
Some(gfx::texture::WrapMode::Tile),
|
Some(gfx::texture::WrapMode::Tile),
|
||||||
None,
|
None,
|
||||||
@ -323,7 +421,7 @@ impl Renderer {
|
|||||||
postprocess_pipeline,
|
postprocess_pipeline,
|
||||||
player_shadow_pipeline,
|
player_shadow_pipeline,
|
||||||
|
|
||||||
shader_reload_indicator,
|
shaders,
|
||||||
|
|
||||||
noise_tex,
|
noise_tex,
|
||||||
|
|
||||||
@ -789,7 +887,7 @@ impl Renderer {
|
|||||||
self.device.cleanup();
|
self.device.cleanup();
|
||||||
|
|
||||||
// If the shaders files were changed attempt to recreate the shaders
|
// If the shaders files were changed attempt to recreate the shaders
|
||||||
if self.shader_reload_indicator.reloaded() {
|
if self.shaders.reloaded() {
|
||||||
self.recreate_pipelines();
|
self.recreate_pipelines();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -798,9 +896,9 @@ impl Renderer {
|
|||||||
fn recreate_pipelines(&mut self) {
|
fn recreate_pipelines(&mut self) {
|
||||||
match create_pipelines(
|
match create_pipelines(
|
||||||
&mut self.factory,
|
&mut self.factory,
|
||||||
|
&self.shaders.read(),
|
||||||
&self.mode,
|
&self.mode,
|
||||||
self.shadow_map.is_some(),
|
self.shadow_map.is_some(),
|
||||||
&mut self.shader_reload_indicator,
|
|
||||||
) {
|
) {
|
||||||
Ok((
|
Ok((
|
||||||
skybox_pipeline,
|
skybox_pipeline,
|
||||||
@ -1771,9 +1869,9 @@ struct GfxPipeline<P: gfx::pso::PipelineInit> {
|
|||||||
#[allow(clippy::type_complexity)] // TODO: Pending review in #587
|
#[allow(clippy::type_complexity)] // TODO: Pending review in #587
|
||||||
fn create_pipelines(
|
fn create_pipelines(
|
||||||
factory: &mut gfx_backend::Factory,
|
factory: &mut gfx_backend::Factory,
|
||||||
|
shaders: &Shaders,
|
||||||
mode: &RenderMode,
|
mode: &RenderMode,
|
||||||
has_shadow_views: bool,
|
has_shadow_views: bool,
|
||||||
shader_reload_indicator: &mut ReloadIndicator,
|
|
||||||
) -> Result<
|
) -> Result<
|
||||||
(
|
(
|
||||||
GfxPipeline<skybox::pipe::Init<'static>>,
|
GfxPipeline<skybox::pipe::Init<'static>>,
|
||||||
@ -1793,20 +1891,6 @@ fn create_pipelines(
|
|||||||
),
|
),
|
||||||
RenderError,
|
RenderError,
|
||||||
> {
|
> {
|
||||||
let constants =
|
|
||||||
Glsl::load_watched("voxygen.shaders.include.constants", shader_reload_indicator).unwrap();
|
|
||||||
let globals =
|
|
||||||
Glsl::load_watched("voxygen.shaders.include.globals", shader_reload_indicator).unwrap();
|
|
||||||
let sky = Glsl::load_watched("voxygen.shaders.include.sky", shader_reload_indicator).unwrap();
|
|
||||||
let light =
|
|
||||||
Glsl::load_watched("voxygen.shaders.include.light", shader_reload_indicator).unwrap();
|
|
||||||
let srgb = Glsl::load_watched("voxygen.shaders.include.srgb", shader_reload_indicator).unwrap();
|
|
||||||
let random =
|
|
||||||
Glsl::load_watched("voxygen.shaders.include.random", shader_reload_indicator).unwrap();
|
|
||||||
let lod = Glsl::load_watched("voxygen.shaders.include.lod", shader_reload_indicator).unwrap();
|
|
||||||
let shadows =
|
|
||||||
Glsl::load_watched("voxygen.shaders.include.shadows", shader_reload_indicator).unwrap();
|
|
||||||
|
|
||||||
// We dynamically add extra configuration settings to the constants file.
|
// We dynamically add extra configuration settings to the constants file.
|
||||||
let constants = format!(
|
let constants = format!(
|
||||||
r#"
|
r#"
|
||||||
@ -1819,7 +1903,7 @@ fn create_pipelines(
|
|||||||
#define SHADOW_MODE {}
|
#define SHADOW_MODE {}
|
||||||
|
|
||||||
"#,
|
"#,
|
||||||
constants,
|
shaders.constants.read().0,
|
||||||
// TODO: Configurable vertex/fragment shader preference.
|
// TODO: Configurable vertex/fragment shader preference.
|
||||||
"VOXYGEN_COMPUTATION_PREFERENCE_FRAGMENT",
|
"VOXYGEN_COMPUTATION_PREFERENCE_FRAGMENT",
|
||||||
match mode.fluid {
|
match mode.fluid {
|
||||||
@ -1846,74 +1930,37 @@ fn create_pipelines(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let anti_alias = Glsl::load_watched(
|
let anti_alias = &match mode.aa {
|
||||||
&["voxygen.shaders.antialias.", match mode.aa {
|
AaMode::None => shaders.anti_alias_none,
|
||||||
AaMode::None => "none",
|
AaMode::Fxaa => shaders.anti_alias_fxaa,
|
||||||
AaMode::Fxaa => "fxaa",
|
AaMode::MsaaX4 => shaders.anti_alias_msaa_x4,
|
||||||
AaMode::MsaaX4 => "msaa-x4",
|
AaMode::MsaaX8 => shaders.anti_alias_msaa_x8,
|
||||||
AaMode::MsaaX8 => "msaa-x8",
|
AaMode::MsaaX16 => shaders.anti_alias_msaa_x16,
|
||||||
AaMode::MsaaX16 => "msaa-x16",
|
};
|
||||||
}]
|
|
||||||
.concat(),
|
|
||||||
shader_reload_indicator,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let cloud = Glsl::load_watched(
|
let cloud = &match mode.cloud {
|
||||||
&["voxygen.shaders.include.cloud.", match mode.cloud {
|
CloudMode::None => shaders.cloud_none,
|
||||||
CloudMode::None => "none",
|
_ => shaders.cloud_regular,
|
||||||
_ => "regular",
|
};
|
||||||
}]
|
|
||||||
.concat(),
|
|
||||||
shader_reload_indicator,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut include_ctx = IncludeContext::new();
|
let mut include_ctx = IncludeContext::new();
|
||||||
include_ctx.include("constants.glsl", &constants);
|
include_ctx.include("constants.glsl", &constants);
|
||||||
include_ctx.include("globals.glsl", &globals);
|
include_ctx.include("globals.glsl", &shaders.globals.read().0);
|
||||||
include_ctx.include("shadows.glsl", &shadows);
|
include_ctx.include("shadows.glsl", &shaders.shadows.read().0);
|
||||||
include_ctx.include("sky.glsl", &sky);
|
include_ctx.include("sky.glsl", &shaders.sky.read().0);
|
||||||
include_ctx.include("light.glsl", &light);
|
include_ctx.include("light.glsl", &shaders.light.read().0);
|
||||||
include_ctx.include("srgb.glsl", &srgb);
|
include_ctx.include("srgb.glsl", &shaders.srgb.read().0);
|
||||||
include_ctx.include("random.glsl", &random);
|
include_ctx.include("random.glsl", &shaders.random.read().0);
|
||||||
include_ctx.include("lod.glsl", &lod);
|
include_ctx.include("lod.glsl", &shaders.lod.read().0);
|
||||||
include_ctx.include("anti-aliasing.glsl", &anti_alias);
|
include_ctx.include("anti-aliasing.glsl", &anti_alias.read().0);
|
||||||
include_ctx.include("cloud.glsl", &cloud);
|
include_ctx.include("cloud.glsl", &cloud.read().0);
|
||||||
|
|
||||||
let figure_vert =
|
|
||||||
Glsl::load_watched("voxygen.shaders.figure-vert", shader_reload_indicator).unwrap();
|
|
||||||
|
|
||||||
let terrain_point_shadow_vert = Glsl::load_watched(
|
|
||||||
"voxygen.shaders.light-shadows-vert",
|
|
||||||
shader_reload_indicator,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let terrain_directed_shadow_vert = Glsl::load_watched(
|
|
||||||
"voxygen.shaders.light-shadows-directed-vert",
|
|
||||||
shader_reload_indicator,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let figure_directed_shadow_vert = Glsl::load_watched(
|
|
||||||
"voxygen.shaders.light-shadows-figure-vert",
|
|
||||||
shader_reload_indicator,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let directed_shadow_frag = Glsl::load_watched(
|
|
||||||
"voxygen.shaders.light-shadows-directed-frag",
|
|
||||||
shader_reload_indicator,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Construct a pipeline for rendering skyboxes
|
// Construct a pipeline for rendering skyboxes
|
||||||
let skybox_pipeline = create_pipeline(
|
let skybox_pipeline = create_pipeline(
|
||||||
factory,
|
factory,
|
||||||
skybox::pipe::new(),
|
skybox::pipe::new(),
|
||||||
&Glsl::load_watched("voxygen.shaders.skybox-vert", shader_reload_indicator).unwrap(),
|
&shaders.skybox_vert.read().0,
|
||||||
&Glsl::load_watched("voxygen.shaders.skybox-frag", shader_reload_indicator).unwrap(),
|
&shaders.skybox_frag.read().0,
|
||||||
&include_ctx,
|
&include_ctx,
|
||||||
gfx::state::CullFace::Back,
|
gfx::state::CullFace::Back,
|
||||||
)?;
|
)?;
|
||||||
@ -1922,8 +1969,8 @@ fn create_pipelines(
|
|||||||
let figure_pipeline = create_pipeline(
|
let figure_pipeline = create_pipeline(
|
||||||
factory,
|
factory,
|
||||||
figure::pipe::new(),
|
figure::pipe::new(),
|
||||||
&figure_vert,
|
&shaders.figure_vert.read().0,
|
||||||
&Glsl::load_watched("voxygen.shaders.figure-frag", shader_reload_indicator).unwrap(),
|
&shaders.figure_frag.read().0,
|
||||||
&include_ctx,
|
&include_ctx,
|
||||||
gfx::state::CullFace::Back,
|
gfx::state::CullFace::Back,
|
||||||
)?;
|
)?;
|
||||||
@ -1932,8 +1979,8 @@ fn create_pipelines(
|
|||||||
let terrain_pipeline = create_pipeline(
|
let terrain_pipeline = create_pipeline(
|
||||||
factory,
|
factory,
|
||||||
terrain::pipe::new(),
|
terrain::pipe::new(),
|
||||||
&Glsl::load_watched("voxygen.shaders.terrain-vert", shader_reload_indicator).unwrap(),
|
&shaders.terrain_vert.read().0,
|
||||||
&Glsl::load_watched("voxygen.shaders.terrain-frag", shader_reload_indicator).unwrap(),
|
&shaders.terrain_frag.read().0,
|
||||||
&include_ctx,
|
&include_ctx,
|
||||||
gfx::state::CullFace::Back,
|
gfx::state::CullFace::Back,
|
||||||
)?;
|
)?;
|
||||||
@ -1942,16 +1989,11 @@ fn create_pipelines(
|
|||||||
let fluid_pipeline = create_pipeline(
|
let fluid_pipeline = create_pipeline(
|
||||||
factory,
|
factory,
|
||||||
fluid::pipe::new(),
|
fluid::pipe::new(),
|
||||||
&Glsl::load_watched("voxygen.shaders.fluid-vert", shader_reload_indicator).unwrap(),
|
&shaders.fluid_vert.read().0,
|
||||||
&Glsl::load_watched(
|
&match mode.fluid {
|
||||||
&["voxygen.shaders.fluid-frag.", match mode.fluid {
|
FluidMode::Cheap => shaders.fluid_frag_cheap,
|
||||||
FluidMode::Cheap => "cheap",
|
FluidMode::Shiny => shaders.fluid_frag_shiny,
|
||||||
FluidMode::Shiny => "shiny",
|
}.read().0,
|
||||||
}]
|
|
||||||
.concat(),
|
|
||||||
shader_reload_indicator,
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
&include_ctx,
|
&include_ctx,
|
||||||
gfx::state::CullFace::Nothing,
|
gfx::state::CullFace::Nothing,
|
||||||
)?;
|
)?;
|
||||||
@ -1960,8 +2002,8 @@ fn create_pipelines(
|
|||||||
let sprite_pipeline = create_pipeline(
|
let sprite_pipeline = create_pipeline(
|
||||||
factory,
|
factory,
|
||||||
sprite::pipe::new(),
|
sprite::pipe::new(),
|
||||||
&Glsl::load_watched("voxygen.shaders.sprite-vert", shader_reload_indicator).unwrap(),
|
&shaders.sprite_vert.read().0,
|
||||||
&Glsl::load_watched("voxygen.shaders.sprite-frag", shader_reload_indicator).unwrap(),
|
&shaders.sprite_frag.read().0,
|
||||||
&include_ctx,
|
&include_ctx,
|
||||||
gfx::state::CullFace::Back,
|
gfx::state::CullFace::Back,
|
||||||
)?;
|
)?;
|
||||||
@ -1970,8 +2012,8 @@ fn create_pipelines(
|
|||||||
let particle_pipeline = create_pipeline(
|
let particle_pipeline = create_pipeline(
|
||||||
factory,
|
factory,
|
||||||
particle::pipe::new(),
|
particle::pipe::new(),
|
||||||
&Glsl::load_watched("voxygen.shaders.particle-vert", shader_reload_indicator).unwrap(),
|
&shaders.particle_vert.read().0,
|
||||||
&Glsl::load_watched("voxygen.shaders.particle-frag", shader_reload_indicator).unwrap(),
|
&shaders.particle_frag.read().0,
|
||||||
&include_ctx,
|
&include_ctx,
|
||||||
gfx::state::CullFace::Back,
|
gfx::state::CullFace::Back,
|
||||||
)?;
|
)?;
|
||||||
@ -1980,8 +2022,8 @@ fn create_pipelines(
|
|||||||
let ui_pipeline = create_pipeline(
|
let ui_pipeline = create_pipeline(
|
||||||
factory,
|
factory,
|
||||||
ui::pipe::new(),
|
ui::pipe::new(),
|
||||||
&Glsl::load_watched("voxygen.shaders.ui-vert", shader_reload_indicator).unwrap(),
|
&shaders.ui_vert.read().0,
|
||||||
&Glsl::load_watched("voxygen.shaders.ui-frag", shader_reload_indicator).unwrap(),
|
&shaders.ui_frag.read().0,
|
||||||
&include_ctx,
|
&include_ctx,
|
||||||
gfx::state::CullFace::Back,
|
gfx::state::CullFace::Back,
|
||||||
)?;
|
)?;
|
||||||
@ -1990,8 +2032,8 @@ fn create_pipelines(
|
|||||||
let lod_terrain_pipeline = create_pipeline(
|
let lod_terrain_pipeline = create_pipeline(
|
||||||
factory,
|
factory,
|
||||||
lod_terrain::pipe::new(),
|
lod_terrain::pipe::new(),
|
||||||
&Glsl::load_watched("voxygen.shaders.lod-terrain-vert", shader_reload_indicator).unwrap(),
|
&shaders.lod_terrain_vert.read().0,
|
||||||
&Glsl::load_watched("voxygen.shaders.lod-terrain-frag", shader_reload_indicator).unwrap(),
|
&shaders.lod_terrain_frag.read().0,
|
||||||
&include_ctx,
|
&include_ctx,
|
||||||
gfx::state::CullFace::Back,
|
gfx::state::CullFace::Back,
|
||||||
)?;
|
)?;
|
||||||
@ -2000,8 +2042,8 @@ fn create_pipelines(
|
|||||||
let clouds_pipeline = create_pipeline(
|
let clouds_pipeline = create_pipeline(
|
||||||
factory,
|
factory,
|
||||||
clouds::pipe::new(),
|
clouds::pipe::new(),
|
||||||
&Glsl::load_watched("voxygen.shaders.clouds-vert", shader_reload_indicator).unwrap(),
|
&shaders.clouds_vert.read().0,
|
||||||
&Glsl::load_watched("voxygen.shaders.clouds-frag", shader_reload_indicator).unwrap(),
|
&shaders.clouds_frag.read().0,
|
||||||
&include_ctx,
|
&include_ctx,
|
||||||
gfx::state::CullFace::Back,
|
gfx::state::CullFace::Back,
|
||||||
)?;
|
)?;
|
||||||
@ -2010,8 +2052,8 @@ fn create_pipelines(
|
|||||||
let postprocess_pipeline = create_pipeline(
|
let postprocess_pipeline = create_pipeline(
|
||||||
factory,
|
factory,
|
||||||
postprocess::pipe::new(),
|
postprocess::pipe::new(),
|
||||||
&Glsl::load_watched("voxygen.shaders.postprocess-vert", shader_reload_indicator).unwrap(),
|
&shaders.postprocess_vert.read().0,
|
||||||
&Glsl::load_watched("voxygen.shaders.postprocess-frag", shader_reload_indicator).unwrap(),
|
&shaders.postprocess_frag.read().0,
|
||||||
&include_ctx,
|
&include_ctx,
|
||||||
gfx::state::CullFace::Back,
|
gfx::state::CullFace::Back,
|
||||||
)?;
|
)?;
|
||||||
@ -2028,12 +2070,8 @@ fn create_pipelines(
|
|||||||
),*/),
|
),*/),
|
||||||
..figure::pipe::new()
|
..figure::pipe::new()
|
||||||
},
|
},
|
||||||
&figure_vert,
|
&shaders.figure_vert.read().0,
|
||||||
&Glsl::load_watched(
|
&shaders.player_shadow_frag.read().0,
|
||||||
"voxygen.shaders.player-shadow-frag",
|
|
||||||
shader_reload_indicator,
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
&include_ctx,
|
&include_ctx,
|
||||||
gfx::state::CullFace::Back,
|
gfx::state::CullFace::Back,
|
||||||
)?;
|
)?;
|
||||||
@ -2042,19 +2080,9 @@ fn create_pipelines(
|
|||||||
let point_shadow_pipeline = match create_shadow_pipeline(
|
let point_shadow_pipeline = match create_shadow_pipeline(
|
||||||
factory,
|
factory,
|
||||||
shadow::pipe::new(),
|
shadow::pipe::new(),
|
||||||
&terrain_point_shadow_vert,
|
&shaders.terrain_point_shadow_vert.read().0,
|
||||||
Some(
|
Some(&shaders.light_shadows_geom.read().0),
|
||||||
&Glsl::load_watched(
|
&shaders.light_shadows_frag.read().0,
|
||||||
"voxygen.shaders.light-shadows-geom",
|
|
||||||
shader_reload_indicator,
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
),
|
|
||||||
&Glsl::load_watched(
|
|
||||||
"voxygen.shaders.light-shadows-frag",
|
|
||||||
shader_reload_indicator,
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
&include_ctx,
|
&include_ctx,
|
||||||
gfx::state::CullFace::Back,
|
gfx::state::CullFace::Back,
|
||||||
None, // Some(gfx::state::Offset(2, 0))
|
None, // Some(gfx::state::Offset(2, 0))
|
||||||
@ -2070,9 +2098,9 @@ fn create_pipelines(
|
|||||||
let terrain_directed_shadow_pipeline = match create_shadow_pipeline(
|
let terrain_directed_shadow_pipeline = match create_shadow_pipeline(
|
||||||
factory,
|
factory,
|
||||||
shadow::pipe::new(),
|
shadow::pipe::new(),
|
||||||
&terrain_directed_shadow_vert,
|
&shaders.terrain_directed_shadow_vert.read().0,
|
||||||
None,
|
None,
|
||||||
&directed_shadow_frag,
|
&shaders.directed_shadow_frag.read().0,
|
||||||
&include_ctx,
|
&include_ctx,
|
||||||
gfx::state::CullFace::Back,
|
gfx::state::CullFace::Back,
|
||||||
None, // Some(gfx::state::Offset(2, 1))
|
None, // Some(gfx::state::Offset(2, 1))
|
||||||
@ -2091,9 +2119,9 @@ fn create_pipelines(
|
|||||||
let figure_directed_shadow_pipeline = match create_shadow_pipeline(
|
let figure_directed_shadow_pipeline = match create_shadow_pipeline(
|
||||||
factory,
|
factory,
|
||||||
shadow::figure_pipe::new(),
|
shadow::figure_pipe::new(),
|
||||||
&figure_directed_shadow_vert,
|
&shaders.figure_directed_shadow_vert.read().0,
|
||||||
None,
|
None,
|
||||||
&directed_shadow_frag,
|
&shaders.directed_shadow_frag.read().0,
|
||||||
&include_ctx,
|
&include_ctx,
|
||||||
gfx::state::CullFace::Back,
|
gfx::state::CullFace::Back,
|
||||||
None, // Some(gfx::state::Offset(2, 1))
|
None, // Some(gfx::state::Offset(2, 1))
|
||||||
|
@ -8,7 +8,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use anim::Skeleton;
|
use anim::Skeleton;
|
||||||
use common::{
|
use common::{
|
||||||
assets::watch::ReloadIndicator,
|
assets::AssetHandle,
|
||||||
comp::{
|
comp::{
|
||||||
item::{
|
item::{
|
||||||
armor::{Armor, ArmorKind},
|
armor::{Armor, ArmorKind},
|
||||||
@ -234,8 +234,7 @@ where
|
|||||||
Skel::Body: BodySpec,
|
Skel::Body: BodySpec,
|
||||||
{
|
{
|
||||||
models: HashMap<FigureKey<Skel::Body>, ((FigureModelEntryFuture<LOD_COUNT>, Skel::Attr), u64)>,
|
models: HashMap<FigureKey<Skel::Body>, ((FigureModelEntryFuture<LOD_COUNT>, Skel::Attr), u64)>,
|
||||||
manifests: Arc<<Skel::Body as BodySpec>::Spec>,
|
manifests: AssetHandle<<Skel::Body as BodySpec>::Spec>,
|
||||||
manifest_indicator: ReloadIndicator,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Skel: Skeleton> FigureModelCache<Skel>
|
impl<Skel: Skeleton> FigureModelCache<Skel>
|
||||||
@ -244,15 +243,10 @@ where
|
|||||||
{
|
{
|
||||||
#[allow(clippy::new_without_default)] // TODO: Pending review in #587
|
#[allow(clippy::new_without_default)] // TODO: Pending review in #587
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let mut manifest_indicator = ReloadIndicator::new();
|
|
||||||
Self {
|
Self {
|
||||||
models: HashMap::new(),
|
models: HashMap::new(),
|
||||||
// NOTE: It might be better to bubble this error up rather than panicking.
|
// NOTE: It might be better to bubble this error up rather than panicking.
|
||||||
manifests: Arc::new(
|
manifests: <Skel::Body as BodySpec>::load_spec().unwrap(),
|
||||||
<Skel::Body as BodySpec>::load_watched(&mut manifest_indicator)
|
|
||||||
.expect("Could not load manifests for body type"),
|
|
||||||
),
|
|
||||||
manifest_indicator,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -364,13 +358,13 @@ where
|
|||||||
Entry::Vacant(v) => {
|
Entry::Vacant(v) => {
|
||||||
let key = v.key().clone();
|
let key = v.key().clone();
|
||||||
let slot = Arc::new(atomic::AtomicCell::new(None));
|
let slot = Arc::new(atomic::AtomicCell::new(None));
|
||||||
let manifests = Arc::clone(&self.manifests);
|
let manifests = self.manifests;
|
||||||
let slot_ = Arc::clone(&slot);
|
let slot_ = Arc::clone(&slot);
|
||||||
|
|
||||||
thread_pool.execute(move || {
|
thread_pool.execute(move || {
|
||||||
// First, load all the base vertex data.
|
// First, load all the base vertex data.
|
||||||
let manifests = &*manifests;
|
let manifests = &*manifests.read();
|
||||||
let meshes = <Skel::Body as BodySpec>::bone_meshes(&key, &*manifests);
|
let meshes = <Skel::Body as BodySpec>::bone_meshes(&key, manifests);
|
||||||
|
|
||||||
// Then, set up meshing context.
|
// Then, set up meshing context.
|
||||||
let mut greedy = FigureModel::make_greedy();
|
let mut greedy = FigureModel::make_greedy();
|
||||||
@ -522,12 +516,9 @@ where
|
|||||||
{
|
{
|
||||||
// Check for reloaded manifests
|
// Check for reloaded manifests
|
||||||
// TODO: maybe do this in a different function, maintain?
|
// TODO: maybe do this in a different function, maintain?
|
||||||
if self.manifest_indicator.reloaded() {
|
if self.manifests.reloaded() {
|
||||||
col_lights.atlas.clear();
|
col_lights.atlas.clear();
|
||||||
self.models.clear();
|
self.models.clear();
|
||||||
if let Err(err) = <Skel::Body as BodySpec>::reload(Arc::make_mut(&mut self.manifests)) {
|
|
||||||
tracing::warn!(?err, "Hot reload failed.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// TODO: Don't hard-code this.
|
// TODO: Don't hard-code this.
|
||||||
if tick % 60 == 0 {
|
if tick % 60 == 0 {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use super::cache::FigureKey;
|
use super::cache::FigureKey;
|
||||||
use common::{
|
use common::{
|
||||||
assets::{self, watch::ReloadIndicator, Asset, AssetWith, Ron},
|
assets::{self, AssetExt, AssetHandle, DotVoxAsset, Ron},
|
||||||
comp::{
|
comp::{
|
||||||
biped_large::{self, BodyType as BLBodyType, Species as BLSpecies},
|
biped_large::{self, BodyType as BLBodyType, Species as BLSpecies},
|
||||||
bird_medium::{self, BodyType as BMBodyType, Species as BMSpecies},
|
bird_medium::{self, BodyType as BMBodyType, Species as BMSpecies},
|
||||||
@ -18,10 +18,8 @@ use common::{
|
|||||||
},
|
},
|
||||||
figure::{DynaUnionizer, MatSegment, Material, Segment},
|
figure::{DynaUnionizer, MatSegment, Material, Segment},
|
||||||
};
|
};
|
||||||
use dot_vox::DotVoxData;
|
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::sync::Arc;
|
|
||||||
use tracing::{error, warn};
|
use tracing::{error, warn};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
@ -29,29 +27,29 @@ pub type BoneMeshes = (Segment, Vec3<f32>);
|
|||||||
|
|
||||||
fn load_segment(mesh_name: &str) -> Segment {
|
fn load_segment(mesh_name: &str) -> Segment {
|
||||||
let full_specifier: String = ["voxygen.voxel.", mesh_name].concat();
|
let full_specifier: String = ["voxygen.voxel.", mesh_name].concat();
|
||||||
Segment::from(DotVoxData::load_expect(full_specifier.as_str()).as_ref())
|
Segment::from(&DotVoxAsset::load_expect(&full_specifier).read().0)
|
||||||
}
|
}
|
||||||
fn graceful_load_vox(mesh_name: &str) -> Arc<DotVoxData> {
|
fn graceful_load_vox(mesh_name: &str) -> AssetHandle<DotVoxAsset> {
|
||||||
let full_specifier: String = ["voxygen.voxel.", mesh_name].concat();
|
let full_specifier: String = ["voxygen.voxel.", mesh_name].concat();
|
||||||
match DotVoxData::load(full_specifier.as_str()) {
|
match DotVoxAsset::load(&full_specifier) {
|
||||||
Ok(dot_vox) => dot_vox,
|
Ok(dot_vox) => dot_vox,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
error!(?full_specifier, "Could not load vox file for figure");
|
error!(?full_specifier, "Could not load vox file for figure");
|
||||||
DotVoxData::load_expect("voxygen.voxel.not_found")
|
DotVoxAsset::load_expect("voxygen.voxel.not_found")
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn graceful_load_segment(mesh_name: &str) -> Segment {
|
fn graceful_load_segment(mesh_name: &str) -> Segment {
|
||||||
Segment::from(graceful_load_vox(mesh_name).as_ref())
|
Segment::from(&graceful_load_vox(mesh_name).read().0)
|
||||||
}
|
}
|
||||||
fn graceful_load_segment_flipped(mesh_name: &str) -> Segment {
|
fn graceful_load_segment_flipped(mesh_name: &str) -> Segment {
|
||||||
Segment::from_vox(graceful_load_vox(mesh_name).as_ref(), true)
|
Segment::from_vox(&graceful_load_vox(mesh_name).read().0, true)
|
||||||
}
|
}
|
||||||
fn graceful_load_mat_segment(mesh_name: &str) -> MatSegment {
|
fn graceful_load_mat_segment(mesh_name: &str) -> MatSegment {
|
||||||
MatSegment::from(graceful_load_vox(mesh_name).as_ref())
|
MatSegment::from(&graceful_load_vox(mesh_name).read().0)
|
||||||
}
|
}
|
||||||
fn graceful_load_mat_segment_flipped(mesh_name: &str) -> MatSegment {
|
fn graceful_load_mat_segment_flipped(mesh_name: &str) -> MatSegment {
|
||||||
MatSegment::from_vox(graceful_load_vox(mesh_name).as_ref(), true)
|
MatSegment::from_vox(&graceful_load_vox(mesh_name).read().0, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_mesh(mesh_name: &str, position: Vec3<f32>) -> BoneMeshes {
|
pub fn load_mesh(mesh_name: &str, position: Vec3<f32>) -> BoneMeshes {
|
||||||
@ -76,12 +74,8 @@ fn recolor_grey(rgb: Rgb<u8>, color: Rgb<u8>) -> Rgb<u8> {
|
|||||||
pub trait BodySpec: Sized {
|
pub trait BodySpec: Sized {
|
||||||
type Spec;
|
type Spec;
|
||||||
|
|
||||||
/// Initialize all the specifications for this Body and watch for changes.
|
/// Initialize all the specifications for this Body.
|
||||||
fn load_watched(indicator: &mut ReloadIndicator) -> Result<Self::Spec, assets::Error>;
|
fn load_spec() -> Result<AssetHandle<Self::Spec>, assets::Error>;
|
||||||
|
|
||||||
/// Reload all specifications for this Body (to be called if the reload
|
|
||||||
/// indicator is set).
|
|
||||||
fn reload(spec: &mut Self::Spec) -> Result<(), assets::Error>;
|
|
||||||
|
|
||||||
/// Mesh bones using the given spec, character state, and mesh generation
|
/// Mesh bones using the given spec, character state, and mesh generation
|
||||||
/// function.
|
/// function.
|
||||||
@ -104,23 +98,23 @@ macro_rules! make_vox_spec {
|
|||||||
) => {
|
) => {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct $Spec {
|
pub struct $Spec {
|
||||||
$( $field: AssetWith<Ron<$ty>, $asset_path>, )*
|
$( $field: AssetHandle<Ron<$ty>>, )*
|
||||||
|
}
|
||||||
|
|
||||||
|
impl assets::Compound for $Spec {
|
||||||
|
fn load<S: assets::source::Source>(_: &assets::AssetCache<S>, _: &str) -> Result<Self, assets::Error> {
|
||||||
|
Ok($Spec {
|
||||||
|
$( $field: AssetExt::load($asset_path)?, )*
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BodySpec for $body {
|
impl BodySpec for $body {
|
||||||
type Spec = $Spec;
|
type Spec = $Spec;
|
||||||
|
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn load_watched(indicator: &mut ReloadIndicator) -> Result<Self::Spec, assets::Error> {
|
fn load_spec() -> Result<AssetHandle<Self::Spec>, assets::Error> {
|
||||||
Ok(Self::Spec {
|
Self::Spec::load("")
|
||||||
$( $field: AssetWith::load_watched(indicator)?, )*
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
fn reload(spec: &mut Self::Spec) -> Result<(), assets::Error> {
|
|
||||||
$( spec.$field.reload()?; )*
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bone_meshes(
|
fn bone_meshes(
|
||||||
@ -375,95 +369,97 @@ make_vox_spec!(
|
|||||||
let hand = loadout.hand.as_deref();
|
let hand = loadout.hand.as_deref();
|
||||||
let foot = loadout.foot.as_deref();
|
let foot = loadout.foot.as_deref();
|
||||||
|
|
||||||
|
let color = &spec.color.read().0;
|
||||||
|
|
||||||
[
|
[
|
||||||
third_person.map(|_| {
|
third_person.map(|_| {
|
||||||
spec.head.asset.mesh_head(
|
spec.head.read().0.mesh_head(
|
||||||
body,
|
body,
|
||||||
&spec.color.asset,
|
color,
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
third_person.map(|loadout| {
|
third_person.map(|loadout| {
|
||||||
spec.armor_chest.asset.mesh_chest(
|
spec.armor_chest.read().0.mesh_chest(
|
||||||
body,
|
body,
|
||||||
&spec.color.asset,
|
color,
|
||||||
loadout.chest.as_deref(),
|
loadout.chest.as_deref(),
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
third_person.map(|loadout| {
|
third_person.map(|loadout| {
|
||||||
spec.armor_belt.asset.mesh_belt(
|
spec.armor_belt.read().0.mesh_belt(
|
||||||
body,
|
body,
|
||||||
&spec.color.asset,
|
color,
|
||||||
loadout.belt.as_deref(),
|
loadout.belt.as_deref(),
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
third_person.map(|loadout| {
|
third_person.map(|loadout| {
|
||||||
spec.armor_back.asset.mesh_back(
|
spec.armor_back.read().0.mesh_back(
|
||||||
body,
|
body,
|
||||||
&spec.color.asset,
|
color,
|
||||||
loadout.back.as_deref(),
|
loadout.back.as_deref(),
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
third_person.map(|loadout| {
|
third_person.map(|loadout| {
|
||||||
spec.armor_pants.asset.mesh_pants(
|
spec.armor_pants.read().0.mesh_pants(
|
||||||
body,
|
body,
|
||||||
&spec.color.asset,
|
color,
|
||||||
loadout.pants.as_deref(),
|
loadout.pants.as_deref(),
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
Some(spec.armor_hand.asset.mesh_left_hand(
|
Some(spec.armor_hand.read().0.mesh_left_hand(
|
||||||
body,
|
body,
|
||||||
&spec.color.asset,
|
color,
|
||||||
hand,
|
hand,
|
||||||
)),
|
)),
|
||||||
Some(spec.armor_hand.asset.mesh_right_hand(
|
Some(spec.armor_hand.read().0.mesh_right_hand(
|
||||||
body,
|
body,
|
||||||
&spec.color.asset,
|
color,
|
||||||
hand,
|
hand,
|
||||||
)),
|
)),
|
||||||
Some(spec.armor_foot.asset.mesh_left_foot(
|
Some(spec.armor_foot.read().0.mesh_left_foot(
|
||||||
body,
|
body,
|
||||||
&spec.color.asset,
|
color,
|
||||||
foot,
|
foot,
|
||||||
)),
|
)),
|
||||||
Some(spec.armor_foot.asset.mesh_right_foot(
|
Some(spec.armor_foot.read().0.mesh_right_foot(
|
||||||
body,
|
body,
|
||||||
&spec.color.asset,
|
color,
|
||||||
foot,
|
foot,
|
||||||
)),
|
)),
|
||||||
third_person.map(|loadout| {
|
third_person.map(|loadout| {
|
||||||
spec.armor_shoulder.asset.mesh_left_shoulder(
|
spec.armor_shoulder.read().0.mesh_left_shoulder(
|
||||||
body,
|
body,
|
||||||
&spec.color.asset,
|
color,
|
||||||
loadout.shoulder.as_deref(),
|
loadout.shoulder.as_deref(),
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
third_person.map(|loadout| {
|
third_person.map(|loadout| {
|
||||||
spec.armor_shoulder.asset.mesh_right_shoulder(
|
spec.armor_shoulder.read().0.mesh_right_shoulder(
|
||||||
body,
|
body,
|
||||||
&spec.color.asset,
|
color,
|
||||||
loadout.shoulder.as_deref(),
|
loadout.shoulder.as_deref(),
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
Some(spec.armor_glider.asset.mesh_glider(
|
Some(spec.armor_glider.read().0.mesh_glider(
|
||||||
body,
|
body,
|
||||||
&spec.color.asset,
|
color,
|
||||||
glider,
|
glider,
|
||||||
)),
|
)),
|
||||||
tool.and_then(|tool| tool.active.as_ref()).map(|tool| {
|
tool.and_then(|tool| tool.active.as_ref()).map(|tool| {
|
||||||
spec.main_weapon.asset.mesh_main_weapon(
|
spec.main_weapon.read().0.mesh_main_weapon(
|
||||||
tool,
|
tool,
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
tool.and_then(|tool| tool.second.as_ref()).map(|tool| {
|
tool.and_then(|tool| tool.second.as_ref()).map(|tool| {
|
||||||
spec.main_weapon.asset.mesh_main_weapon(
|
spec.main_weapon.read().0.mesh_main_weapon(
|
||||||
tool,
|
tool,
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
Some(spec.armor_lantern.asset.mesh_lantern(
|
Some(spec.armor_lantern.read().0.mesh_lantern(
|
||||||
body,
|
body,
|
||||||
&spec.color.asset,
|
color,
|
||||||
lantern,
|
lantern,
|
||||||
)),
|
)),
|
||||||
Some(mesh_hold()),
|
Some(mesh_hold()),
|
||||||
@ -1065,31 +1061,31 @@ make_vox_spec!(
|
|||||||
},
|
},
|
||||||
|FigureKey { body, .. }, spec| {
|
|FigureKey { body, .. }, spec| {
|
||||||
[
|
[
|
||||||
Some(spec.central.asset.mesh_head(
|
Some(spec.central.read().0.mesh_head(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_chest(
|
Some(spec.central.read().0.mesh_chest(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_foot_fl(
|
Some(spec.lateral.read().0.mesh_foot_fl(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_foot_fr(
|
Some(spec.lateral.read().0.mesh_foot_fr(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_foot_bl(
|
Some(spec.lateral.read().0.mesh_foot_bl(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_foot_br(
|
Some(spec.lateral.read().0.mesh_foot_br(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_tail(
|
Some(spec.central.read().0.mesh_tail(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
@ -1269,63 +1265,63 @@ make_vox_spec!(
|
|||||||
},
|
},
|
||||||
|FigureKey { body, .. }, spec| {
|
|FigureKey { body, .. }, spec| {
|
||||||
[
|
[
|
||||||
Some(spec.central.asset.mesh_head(
|
Some(spec.central.read().0.mesh_head(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_neck(
|
Some(spec.central.read().0.mesh_neck(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_jaw(
|
Some(spec.central.read().0.mesh_jaw(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_tail(
|
Some(spec.central.read().0.mesh_tail(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_torso_front(
|
Some(spec.central.read().0.mesh_torso_front(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_torso_back(
|
Some(spec.central.read().0.mesh_torso_back(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_ears(
|
Some(spec.central.read().0.mesh_ears(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_leg_fl(
|
Some(spec.lateral.read().0.mesh_leg_fl(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_leg_fr(
|
Some(spec.lateral.read().0.mesh_leg_fr(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_leg_bl(
|
Some(spec.lateral.read().0.mesh_leg_bl(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_leg_br(
|
Some(spec.lateral.read().0.mesh_leg_br(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_foot_fl(
|
Some(spec.lateral.read().0.mesh_foot_fl(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_foot_fr(
|
Some(spec.lateral.read().0.mesh_foot_fr(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_foot_bl(
|
Some(spec.lateral.read().0.mesh_foot_bl(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_foot_br(
|
Some(spec.lateral.read().0.mesh_foot_br(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
@ -1618,31 +1614,31 @@ make_vox_spec!(
|
|||||||
},
|
},
|
||||||
|FigureKey { body, .. }, spec| {
|
|FigureKey { body, .. }, spec| {
|
||||||
[
|
[
|
||||||
Some(spec.central.asset.mesh_head(
|
Some(spec.central.read().0.mesh_head(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_torso(
|
Some(spec.central.read().0.mesh_torso(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_tail(
|
Some(spec.central.read().0.mesh_tail(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_wing_l(
|
Some(spec.lateral.read().0.mesh_wing_l(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_wing_r(
|
Some(spec.lateral.read().0.mesh_wing_r(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_foot_l(
|
Some(spec.lateral.read().0.mesh_foot_l(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_foot_r(
|
Some(spec.lateral.read().0.mesh_foot_r(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
@ -1817,55 +1813,55 @@ make_vox_spec!(
|
|||||||
},
|
},
|
||||||
|FigureKey { body, .. }, spec| {
|
|FigureKey { body, .. }, spec| {
|
||||||
[
|
[
|
||||||
Some(spec.central.asset.mesh_head(
|
Some(spec.central.read().0.mesh_head(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_jaw(
|
Some(spec.central.read().0.mesh_jaw(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_neck(
|
Some(spec.central.read().0.mesh_neck(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_chest_front(
|
Some(spec.central.read().0.mesh_chest_front(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_chest_back(
|
Some(spec.central.read().0.mesh_chest_back(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_tail_front(
|
Some(spec.central.read().0.mesh_tail_front(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_tail_back(
|
Some(spec.central.read().0.mesh_tail_back(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_hand_l(
|
Some(spec.lateral.read().0.mesh_hand_l(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_hand_r(
|
Some(spec.lateral.read().0.mesh_hand_r(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_leg_l(
|
Some(spec.lateral.read().0.mesh_leg_l(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_leg_r(
|
Some(spec.lateral.read().0.mesh_leg_r(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_foot_l(
|
Some(spec.lateral.read().0.mesh_foot_l(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_foot_r(
|
Some(spec.lateral.read().0.mesh_foot_r(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
@ -2124,31 +2120,31 @@ make_vox_spec!(
|
|||||||
},
|
},
|
||||||
|FigureKey { body, .. }, spec| {
|
|FigureKey { body, .. }, spec| {
|
||||||
[
|
[
|
||||||
Some(spec.central.asset.mesh_head(
|
Some(spec.central.read().0.mesh_head(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_jaw(
|
Some(spec.central.read().0.mesh_jaw(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_chest_front(
|
Some(spec.central.read().0.mesh_chest_front(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_chest_back(
|
Some(spec.central.read().0.mesh_chest_back(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_tail(
|
Some(spec.central.read().0.mesh_tail(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_fin_l(
|
Some(spec.lateral.read().0.mesh_fin_l(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_fin_r(
|
Some(spec.lateral.read().0.mesh_fin_r(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
@ -2316,19 +2312,19 @@ make_vox_spec!(
|
|||||||
},
|
},
|
||||||
|FigureKey { body, .. }, spec| {
|
|FigureKey { body, .. }, spec| {
|
||||||
[
|
[
|
||||||
Some(spec.central.asset.mesh_chest(
|
Some(spec.central.read().0.mesh_chest(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_tail(
|
Some(spec.central.read().0.mesh_tail(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_fin_l(
|
Some(spec.lateral.read().0.mesh_fin_l(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_fin_r(
|
Some(spec.lateral.read().0.mesh_fin_r(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
@ -2465,63 +2461,63 @@ make_vox_spec!(
|
|||||||
},
|
},
|
||||||
|FigureKey { body, .. }, spec| {
|
|FigureKey { body, .. }, spec| {
|
||||||
[
|
[
|
||||||
Some(spec.central.asset.mesh_head_upper(
|
Some(spec.central.read().0.mesh_head_upper(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_head_lower(
|
Some(spec.central.read().0.mesh_head_lower(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_jaw(
|
Some(spec.central.read().0.mesh_jaw(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_chest_front(
|
Some(spec.central.read().0.mesh_chest_front(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_chest_rear(
|
Some(spec.central.read().0.mesh_chest_rear(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_tail_front(
|
Some(spec.central.read().0.mesh_tail_front(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_tail_rear(
|
Some(spec.central.read().0.mesh_tail_rear(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_wing_in_l(
|
Some(spec.lateral.read().0.mesh_wing_in_l(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_wing_in_r(
|
Some(spec.lateral.read().0.mesh_wing_in_r(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_wing_out_l(
|
Some(spec.lateral.read().0.mesh_wing_out_l(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_wing_out_r(
|
Some(spec.lateral.read().0.mesh_wing_out_r(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_foot_fl(
|
Some(spec.lateral.read().0.mesh_foot_fl(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_foot_fr(
|
Some(spec.lateral.read().0.mesh_foot_fr(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_foot_bl(
|
Some(spec.lateral.read().0.mesh_foot_bl(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_foot_br(
|
Some(spec.lateral.read().0.mesh_foot_br(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
@ -2883,63 +2879,63 @@ make_vox_spec!(
|
|||||||
},
|
},
|
||||||
|FigureKey { body, .. }, spec| {
|
|FigureKey { body, .. }, spec| {
|
||||||
[
|
[
|
||||||
Some(spec.central.asset.mesh_head(
|
Some(spec.central.read().0.mesh_head(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_jaw(
|
Some(spec.central.read().0.mesh_jaw(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_torso_upper(
|
Some(spec.central.read().0.mesh_torso_upper(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_torso_lower(
|
Some(spec.central.read().0.mesh_torso_lower(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_tail(
|
Some(spec.central.read().0.mesh_tail(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_main(
|
Some(spec.central.read().0.mesh_main(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_second(
|
Some(spec.central.read().0.mesh_second(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_shoulder_l(
|
Some(spec.lateral.read().0.mesh_shoulder_l(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_shoulder_r(
|
Some(spec.lateral.read().0.mesh_shoulder_r(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_hand_l(
|
Some(spec.lateral.read().0.mesh_hand_l(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_hand_r(
|
Some(spec.lateral.read().0.mesh_hand_r(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_leg_l(
|
Some(spec.lateral.read().0.mesh_leg_l(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_leg_r(
|
Some(spec.lateral.read().0.mesh_leg_r(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_foot_l(
|
Some(spec.lateral.read().0.mesh_foot_l(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_foot_r(
|
Some(spec.lateral.read().0.mesh_foot_r(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
@ -3235,51 +3231,51 @@ make_vox_spec!(
|
|||||||
},
|
},
|
||||||
|FigureKey { body, .. }, spec| {
|
|FigureKey { body, .. }, spec| {
|
||||||
[
|
[
|
||||||
Some(spec.central.asset.mesh_head(
|
Some(spec.central.read().0.mesh_head(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_jaw(
|
Some(spec.central.read().0.mesh_jaw(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_torso_upper(
|
Some(spec.central.read().0.mesh_torso_upper(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_torso_lower(
|
Some(spec.central.read().0.mesh_torso_lower(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_shoulder_l(
|
Some(spec.lateral.read().0.mesh_shoulder_l(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_shoulder_r(
|
Some(spec.lateral.read().0.mesh_shoulder_r(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_hand_l(
|
Some(spec.lateral.read().0.mesh_hand_l(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_hand_r(
|
Some(spec.lateral.read().0.mesh_hand_r(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_leg_l(
|
Some(spec.lateral.read().0.mesh_leg_l(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_leg_r(
|
Some(spec.lateral.read().0.mesh_leg_r(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_foot_l(
|
Some(spec.lateral.read().0.mesh_foot_l(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_foot_r(
|
Some(spec.lateral.read().0.mesh_foot_r(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
@ -3529,43 +3525,43 @@ make_vox_spec!(
|
|||||||
},
|
},
|
||||||
|FigureKey { body, .. }, spec| {
|
|FigureKey { body, .. }, spec| {
|
||||||
[
|
[
|
||||||
Some(spec.central.asset.mesh_head_upper(
|
Some(spec.central.read().0.mesh_head_upper(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_head_lower(
|
Some(spec.central.read().0.mesh_head_lower(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_jaw(
|
Some(spec.central.read().0.mesh_jaw(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_chest(
|
Some(spec.central.read().0.mesh_chest(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_tail_front(
|
Some(spec.central.read().0.mesh_tail_front(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.central.asset.mesh_tail_rear(
|
Some(spec.central.read().0.mesh_tail_rear(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_foot_fl(
|
Some(spec.lateral.read().0.mesh_foot_fl(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_foot_fr(
|
Some(spec.lateral.read().0.mesh_foot_fr(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_foot_bl(
|
Some(spec.lateral.read().0.mesh_foot_bl(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
Some(spec.lateral.asset.mesh_foot_br(
|
Some(spec.lateral.read().0.mesh_foot_br(
|
||||||
body.species,
|
body.species,
|
||||||
body.body_type,
|
body.body_type,
|
||||||
)),
|
)),
|
||||||
@ -3765,7 +3761,7 @@ make_vox_spec!(
|
|||||||
},
|
},
|
||||||
|FigureKey { body, .. }, spec| {
|
|FigureKey { body, .. }, spec| {
|
||||||
[
|
[
|
||||||
Some(spec.central.asset.mesh_bone0(
|
Some(spec.central.read().0.mesh_bone0(
|
||||||
body,
|
body,
|
||||||
)),
|
)),
|
||||||
None,
|
None,
|
||||||
|
@ -7,7 +7,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
assets::Asset,
|
assets::{AssetExt, DotVoxAsset},
|
||||||
comp::{item::Reagent, object, Body, CharacterState, Ori, Pos, Shockwave},
|
comp::{item::Reagent, object, Body, CharacterState, Ori, Pos, Shockwave},
|
||||||
figure::Segment,
|
figure::Segment,
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
@ -18,7 +18,6 @@ use common::{
|
|||||||
terrain::TerrainChunk,
|
terrain::TerrainChunk,
|
||||||
vol::{RectRasterableVol, SizedVol},
|
vol::{RectRasterableVol, SizedVol},
|
||||||
};
|
};
|
||||||
use dot_vox::DotVoxData;
|
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use specs::{Join, WorldExt};
|
use specs::{Join, WorldExt};
|
||||||
@ -686,7 +685,7 @@ fn default_cache(renderer: &mut Renderer) -> HashMap<&'static str, Model<Particl
|
|||||||
let mut model_cache = HashMap::new();
|
let mut model_cache = HashMap::new();
|
||||||
|
|
||||||
model_cache.entry(DEFAULT_MODEL_KEY).or_insert_with(|| {
|
model_cache.entry(DEFAULT_MODEL_KEY).or_insert_with(|| {
|
||||||
let vox = DotVoxData::load_expect(DEFAULT_MODEL_KEY);
|
let vox = DotVoxAsset::load_expect(DEFAULT_MODEL_KEY);
|
||||||
|
|
||||||
// NOTE: If we add texturing we may eventually try to share it among all
|
// NOTE: If we add texturing we may eventually try to share it among all
|
||||||
// particles in a single atlas.
|
// particles in a single atlas.
|
||||||
@ -695,7 +694,7 @@ fn default_cache(renderer: &mut Renderer) -> HashMap<&'static str, Model<Particl
|
|||||||
guillotiere::Size::new(i32::from(max_texture_size), i32::from(max_texture_size));
|
guillotiere::Size::new(i32::from(max_texture_size), i32::from(max_texture_size));
|
||||||
let mut greedy = GreedyMesh::new(max_size);
|
let mut greedy = GreedyMesh::new(max_size);
|
||||||
|
|
||||||
let segment = Segment::from(vox.as_ref());
|
let segment = Segment::from(&vox.read().0);
|
||||||
let segment_size = segment.size();
|
let segment_size = segment.size();
|
||||||
let mut mesh =
|
let mut mesh =
|
||||||
Meshable::<ParticlePipeline, &mut GreedyMesh>::generate_mesh(segment, &mut greedy).0;
|
Meshable::<ParticlePipeline, &mut GreedyMesh>::generate_mesh(segment, &mut greedy).0;
|
||||||
|
@ -13,7 +13,7 @@ use crate::{
|
|||||||
|
|
||||||
use super::{math, LodData, SceneData};
|
use super::{math, LodData, SceneData};
|
||||||
use common::{
|
use common::{
|
||||||
assets::{Asset, Ron},
|
assets::{self, AssetExt, DotVoxAsset},
|
||||||
figure::Segment,
|
figure::Segment,
|
||||||
span,
|
span,
|
||||||
spiral::Spiral2d,
|
spiral::Spiral2d,
|
||||||
@ -23,11 +23,9 @@ use common::{
|
|||||||
};
|
};
|
||||||
use core::{f32, fmt::Debug, i32, marker::PhantomData, time::Duration};
|
use core::{f32, fmt::Debug, i32, marker::PhantomData, time::Duration};
|
||||||
use crossbeam::channel;
|
use crossbeam::channel;
|
||||||
use dot_vox::DotVoxData;
|
|
||||||
use enum_iterator::IntoEnumIterator;
|
use enum_iterator::IntoEnumIterator;
|
||||||
use guillotiere::AtlasAllocator;
|
use guillotiere::AtlasAllocator;
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use image::DynamicImage;
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
@ -125,7 +123,14 @@ struct SpriteConfig<Model> {
|
|||||||
/// Configuration data for all sprite models.
|
/// Configuration data for all sprite models.
|
||||||
///
|
///
|
||||||
/// NOTE: Model is an asset path to the appropriate sprite .vox model.
|
/// NOTE: Model is an asset path to the appropriate sprite .vox model.
|
||||||
type SpriteSpec = sprite::sprite_kind::PureCases<Option<SpriteConfig<String>>>;
|
#[derive(Deserialize)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
struct SpriteSpec(sprite::sprite_kind::PureCases<Option<SpriteConfig<String>>>);
|
||||||
|
|
||||||
|
impl assets::Asset for SpriteSpec {
|
||||||
|
const EXTENSION: &'static str = "ron";
|
||||||
|
type Loader = assets::RonLoader;
|
||||||
|
}
|
||||||
|
|
||||||
/// Function executed by worker threads dedicated to chunk meshing.
|
/// Function executed by worker threads dedicated to chunk meshing.
|
||||||
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
|
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
|
||||||
@ -177,7 +182,7 @@ fn mesh_worker<V: BaseVol<Vox = Block> + RectRasterableVol + ReadVol + Debug + '
|
|||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(cfg) = sprite.elim_case_pure(&sprite_config) {
|
if let Some(cfg) = sprite.elim_case_pure(&sprite_config.0) {
|
||||||
let seed = wpos.x as u64 * 3
|
let seed = wpos.x as u64 * 3
|
||||||
+ wpos.y as u64 * 7
|
+ wpos.y as u64 * 7
|
||||||
+ wpos.x as u64 * wpos.y as u64; // Awful PRNG
|
+ wpos.x as u64 * wpos.y as u64; // Awful PRNG
|
||||||
@ -227,6 +232,9 @@ struct SpriteData {
|
|||||||
|
|
||||||
pub struct Terrain<V: RectRasterableVol = TerrainChunk> {
|
pub struct Terrain<V: RectRasterableVol = TerrainChunk> {
|
||||||
atlas: AtlasAllocator,
|
atlas: AtlasAllocator,
|
||||||
|
/// FIXME: This could possibly become an `AssetHandle<SpriteSpec>`, to get
|
||||||
|
/// hot-reloading for free, but I am not sure if sudden changes of this
|
||||||
|
/// value would break something
|
||||||
sprite_config: Arc<SpriteSpec>,
|
sprite_config: Arc<SpriteSpec>,
|
||||||
chunks: HashMap<Vec2<i32>, TerrainChunkData>,
|
chunks: HashMap<Vec2<i32>, TerrainChunkData>,
|
||||||
/// Temporary storage for dead chunks that might still be shadowing chunks
|
/// Temporary storage for dead chunks that might still be shadowing chunks
|
||||||
@ -268,8 +276,7 @@ impl<V: RectRasterableVol> Terrain<V> {
|
|||||||
#[allow(clippy::float_cmp)] // TODO: Pending review in #587
|
#[allow(clippy::float_cmp)] // TODO: Pending review in #587
|
||||||
pub fn new(renderer: &mut Renderer) -> Self {
|
pub fn new(renderer: &mut Renderer) -> Self {
|
||||||
// Load all the sprite config data.
|
// Load all the sprite config data.
|
||||||
let sprite_config = Ron::<SpriteSpec>::load("voxygen.voxel.sprite_manifest")
|
let sprite_config = Arc::<SpriteSpec>::load_expect("voxygen.voxel.sprite_manifest").cloned();
|
||||||
.expect("Failed to find sprite model data!");
|
|
||||||
|
|
||||||
// Create a new mpsc (Multiple Produced, Single Consumer) pair for communicating
|
// Create a new mpsc (Multiple Produced, Single Consumer) pair for communicating
|
||||||
// with worker threads that are meshing chunks.
|
// with worker threads that are meshing chunks.
|
||||||
@ -287,7 +294,7 @@ impl<V: RectRasterableVol> Terrain<V> {
|
|||||||
// NOTE: Tracks the start vertex of the next model to be meshed.
|
// NOTE: Tracks the start vertex of the next model to be meshed.
|
||||||
|
|
||||||
let sprite_data: HashMap<(SpriteKind, usize), _> = SpriteKind::into_enum_iter()
|
let sprite_data: HashMap<(SpriteKind, usize), _> = SpriteKind::into_enum_iter()
|
||||||
.filter_map(|kind| Some((kind, kind.elim_case_pure(&sprite_config_).as_ref()?)))
|
.filter_map(|kind| Some((kind, kind.elim_case_pure(&sprite_config_.0).as_ref()?)))
|
||||||
.flat_map(|(kind, sprite_config)| {
|
.flat_map(|(kind, sprite_config)| {
|
||||||
let wind_sway = sprite_config.wind_sway;
|
let wind_sway = sprite_config.wind_sway;
|
||||||
sprite_config.variations.iter().enumerate().map(
|
sprite_config.variations.iter().enumerate().map(
|
||||||
@ -302,9 +309,11 @@ impl<V: RectRasterableVol> Terrain<V> {
|
|||||||
let scaled = [1.0, 0.8, 0.6, 0.4, 0.2];
|
let scaled = [1.0, 0.8, 0.6, 0.4, 0.2];
|
||||||
let offset = Vec3::from(*offset);
|
let offset = Vec3::from(*offset);
|
||||||
let lod_axes = Vec3::from(*lod_axes);
|
let lod_axes = Vec3::from(*lod_axes);
|
||||||
let model = DotVoxData::load_expect(model);
|
let model = DotVoxAsset::load_expect(model);
|
||||||
let zero = Vec3::zero();
|
let zero = Vec3::zero();
|
||||||
let model_size = model
|
let model_size = model
|
||||||
|
.read()
|
||||||
|
.0
|
||||||
.models
|
.models
|
||||||
.first()
|
.first()
|
||||||
.map(
|
.map(
|
||||||
@ -344,7 +353,7 @@ impl<V: RectRasterableVol> Terrain<V> {
|
|||||||
// interesting return value, but updates the mesh.
|
// interesting return value, but updates the mesh.
|
||||||
let mut opaque_mesh = Mesh::new();
|
let mut opaque_mesh = Mesh::new();
|
||||||
Meshable::<SpritePipeline, &mut GreedyMesh>::generate_mesh(
|
Meshable::<SpritePipeline, &mut GreedyMesh>::generate_mesh(
|
||||||
Segment::from(model.as_ref()).scaled_by(lod_scale),
|
Segment::from(&model.read().0).scaled_by(lod_scale),
|
||||||
(
|
(
|
||||||
greedy,
|
greedy,
|
||||||
&mut opaque_mesh,
|
&mut opaque_mesh,
|
||||||
@ -403,7 +412,7 @@ impl<V: RectRasterableVol> Terrain<V> {
|
|||||||
sprite_col_lights,
|
sprite_col_lights,
|
||||||
waves: renderer
|
waves: renderer
|
||||||
.create_texture(
|
.create_texture(
|
||||||
&DynamicImage::load_expect("voxygen.texture.waves"),
|
&assets::Image::load_expect("voxygen.texture.waves").read().0,
|
||||||
Some(gfx::texture::FilterMethod::Trilinear),
|
Some(gfx::texture::FilterMethod::Trilinear),
|
||||||
Some(gfx::texture::WrapMode::Tile),
|
Some(gfx::texture::WrapMode::Tile),
|
||||||
None,
|
None,
|
||||||
@ -1154,7 +1163,7 @@ impl<V: RectRasterableVol> Terrain<V> {
|
|||||||
for (kind, instances) in (&chunk.sprite_instances).into_iter() {
|
for (kind, instances) in (&chunk.sprite_instances).into_iter() {
|
||||||
let SpriteData { model, locals, .. } = if kind
|
let SpriteData { model, locals, .. } = if kind
|
||||||
.0
|
.0
|
||||||
.elim_case_pure(&self.sprite_config)
|
.elim_case_pure(&self.sprite_config.0)
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|config| config.wind_sway >= 0.4)
|
.map(|config| config.wind_sway >= 0.4)
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
|
@ -13,7 +13,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use client::{self, Client};
|
use client::{self, Client};
|
||||||
use common::{
|
use common::{
|
||||||
assets::Asset,
|
assets::AssetExt,
|
||||||
comp,
|
comp,
|
||||||
comp::{ChatMsg, ChatType, InventoryUpdateEvent, Pos, Vel},
|
comp::{ChatMsg, ChatType, InventoryUpdateEvent, Pos, Vel},
|
||||||
consts::{MAX_MOUNT_RANGE, MAX_PICKUP_RANGE},
|
consts::{MAX_MOUNT_RANGE, MAX_PICKUP_RANGE},
|
||||||
@ -29,7 +29,7 @@ use common::{
|
|||||||
use common_net::msg::PresenceKind;
|
use common_net::msg::PresenceKind;
|
||||||
use ordered_float::OrderedFloat;
|
use ordered_float::OrderedFloat;
|
||||||
use specs::{Join, WorldExt};
|
use specs::{Join, WorldExt};
|
||||||
use std::{cell::RefCell, rc::Rc, sync::Arc, time::Duration};
|
use std::{cell::RefCell, rc::Rc, time::Duration};
|
||||||
use tracing::{error, info};
|
use tracing::{error, info};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
@ -48,7 +48,6 @@ pub struct SessionState {
|
|||||||
key_state: KeyState,
|
key_state: KeyState,
|
||||||
inputs: comp::ControllerInputs,
|
inputs: comp::ControllerInputs,
|
||||||
selected_block: Block,
|
selected_block: Block,
|
||||||
i18n: std::sync::Arc<Localization>,
|
|
||||||
walk_forward_dir: Vec2<f32>,
|
walk_forward_dir: Vec2<f32>,
|
||||||
walk_right_dir: Vec2<f32>,
|
walk_right_dir: Vec2<f32>,
|
||||||
freefly_vel: Vec3<f32>,
|
freefly_vel: Vec3<f32>,
|
||||||
@ -75,10 +74,6 @@ impl SessionState {
|
|||||||
.camera_mut()
|
.camera_mut()
|
||||||
.set_fov_deg(global_state.settings.graphics.fov);
|
.set_fov_deg(global_state.settings.graphics.fov);
|
||||||
let hud = Hud::new(global_state, &client.borrow());
|
let hud = Hud::new(global_state, &client.borrow());
|
||||||
let i18n = Localization::load_expect(&i18n_asset_key(
|
|
||||||
&global_state.settings.language.selected_language,
|
|
||||||
));
|
|
||||||
|
|
||||||
let walk_forward_dir = scene.camera().forward_xy();
|
let walk_forward_dir = scene.camera().forward_xy();
|
||||||
let walk_right_dir = scene.camera().right_xy();
|
let walk_right_dir = scene.camera().right_xy();
|
||||||
|
|
||||||
@ -89,7 +84,6 @@ impl SessionState {
|
|||||||
inputs: comp::ControllerInputs::default(),
|
inputs: comp::ControllerInputs::default(),
|
||||||
hud,
|
hud,
|
||||||
selected_block: Block::new(BlockKind::Misc, Rgb::broadcast(255)),
|
selected_block: Block::new(BlockKind::Misc, Rgb::broadcast(255)),
|
||||||
i18n,
|
|
||||||
walk_forward_dir,
|
walk_forward_dir,
|
||||||
walk_right_dir,
|
walk_right_dir,
|
||||||
freefly_vel: Vec3::zero(),
|
freefly_vel: Vec3::zero(),
|
||||||
@ -125,24 +119,27 @@ impl SessionState {
|
|||||||
self.hud.new_message(m);
|
self.hud.new_message(m);
|
||||||
},
|
},
|
||||||
client::Event::InventoryUpdated(inv_event) => {
|
client::Event::InventoryUpdated(inv_event) => {
|
||||||
let sfx_trigger_item = self
|
let sfx_triggers = self
|
||||||
.scene
|
.scene
|
||||||
.sfx_mgr
|
.sfx_mgr
|
||||||
.triggers
|
.triggers
|
||||||
.get_key_value(&SfxEvent::from(&inv_event));
|
.read();
|
||||||
|
|
||||||
|
let sfx_trigger_item = sfx_triggers.get_key_value(&SfxEvent::from(&inv_event));
|
||||||
global_state.audio.emit_sfx_item(sfx_trigger_item);
|
global_state.audio.emit_sfx_item(sfx_trigger_item);
|
||||||
|
|
||||||
|
let i18n = global_state.i18n.read();
|
||||||
|
|
||||||
match inv_event {
|
match inv_event {
|
||||||
InventoryUpdateEvent::CollectFailed => {
|
InventoryUpdateEvent::CollectFailed => {
|
||||||
self.hud.new_message(ChatMsg {
|
self.hud.new_message(ChatMsg {
|
||||||
message: self.i18n.get("hud.chat.loot_fail").to_string(),
|
message: i18n.get("hud.chat.loot_fail").to_string(),
|
||||||
chat_type: ChatType::CommandError,
|
chat_type: ChatType::CommandError,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
InventoryUpdateEvent::Collected(item) => {
|
InventoryUpdateEvent::Collected(item) => {
|
||||||
self.hud.new_message(ChatMsg {
|
self.hud.new_message(ChatMsg {
|
||||||
message: self
|
message: i18n
|
||||||
.i18n
|
|
||||||
.get("hud.chat.loot_msg")
|
.get("hud.chat.loot_msg")
|
||||||
.replace("{item}", item.name()),
|
.replace("{item}", item.name()),
|
||||||
chat_type: ChatType::Loot,
|
chat_type: ChatType::Loot,
|
||||||
@ -153,10 +150,11 @@ impl SessionState {
|
|||||||
},
|
},
|
||||||
client::Event::Disconnect => return Ok(TickAction::Disconnect),
|
client::Event::Disconnect => return Ok(TickAction::Disconnect),
|
||||||
client::Event::DisconnectionNotification(time) => {
|
client::Event::DisconnectionNotification(time) => {
|
||||||
|
let i18n = global_state.i18n.read();
|
||||||
|
|
||||||
let message = match time {
|
let message = match time {
|
||||||
0 => String::from(self.i18n.get("hud.chat.goodbye")),
|
0 => String::from(i18n.get("hud.chat.goodbye")),
|
||||||
_ => self
|
_ => i18n
|
||||||
.i18n
|
|
||||||
.get("hud.chat.connection_lost")
|
.get("hud.chat.connection_lost")
|
||||||
.replace("{time}", time.to_string().as_str()),
|
.replace("{time}", time.to_string().as_str()),
|
||||||
};
|
};
|
||||||
@ -169,7 +167,7 @@ impl SessionState {
|
|||||||
client::Event::Kicked(reason) => {
|
client::Event::Kicked(reason) => {
|
||||||
global_state.info_message = Some(format!(
|
global_state.info_message = Some(format!(
|
||||||
"{}: {}",
|
"{}: {}",
|
||||||
self.i18n.get("main.login.kicked").to_string(),
|
global_state.i18n.read().get("main.login.kicked").to_string(),
|
||||||
reason
|
reason
|
||||||
));
|
));
|
||||||
return Ok(TickAction::Disconnect);
|
return Ok(TickAction::Disconnect);
|
||||||
@ -212,10 +210,6 @@ impl PlayState for SessionState {
|
|||||||
fn tick(&mut self, global_state: &mut GlobalState, events: Vec<Event>) -> PlayStateResult {
|
fn tick(&mut self, global_state: &mut GlobalState, events: Vec<Event>) -> PlayStateResult {
|
||||||
span!(_guard, "tick", "<Session as PlayState>::tick");
|
span!(_guard, "tick", "<Session as PlayState>::tick");
|
||||||
// TODO: let mut client = self.client.borrow_mut();
|
// TODO: let mut client = self.client.borrow_mut();
|
||||||
// NOTE: Not strictly necessary, but useful for hotloading translation changes.
|
|
||||||
self.i18n = Localization::load_expect(&i18n_asset_key(
|
|
||||||
&global_state.settings.language.selected_language,
|
|
||||||
));
|
|
||||||
|
|
||||||
// TODO: can this be a method on the session or are there borrowcheck issues?
|
// TODO: can this be a method on the session or are there borrowcheck issues?
|
||||||
let (client_presence, client_registered) = {
|
let (client_presence, client_registered) = {
|
||||||
@ -681,7 +675,7 @@ impl PlayState for SessionState {
|
|||||||
Ok(TickAction::Disconnect) => return PlayStateResult::Pop, // Go to main menu
|
Ok(TickAction::Disconnect) => return PlayStateResult::Pop, // Go to main menu
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
global_state.info_message =
|
global_state.info_message =
|
||||||
Some(self.i18n.get("common.connection_lost").to_owned());
|
Some(global_state.i18n.read().get("common.connection_lost").to_owned());
|
||||||
error!("[session] Failed to tick the scene: {:?}", err);
|
error!("[session] Failed to tick the scene: {:?}", err);
|
||||||
|
|
||||||
return PlayStateResult::Pop;
|
return PlayStateResult::Pop;
|
||||||
@ -758,9 +752,9 @@ impl PlayState for SessionState {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Look for changes in the localization files
|
// Look for changes in the localization files
|
||||||
if global_state.localization_watcher.reloaded() {
|
if global_state.i18n.reloaded() {
|
||||||
hud_events.push(HudEvent::ChangeLanguage(Box::new(
|
hud_events.push(HudEvent::ChangeLanguage(Box::new(
|
||||||
self.i18n.metadata.clone(),
|
global_state.i18n.read().metadata.clone(),
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1016,13 +1010,11 @@ impl PlayState for SessionState {
|
|||||||
HudEvent::ChangeLanguage(new_language) => {
|
HudEvent::ChangeLanguage(new_language) => {
|
||||||
global_state.settings.language.selected_language =
|
global_state.settings.language.selected_language =
|
||||||
new_language.language_identifier;
|
new_language.language_identifier;
|
||||||
self.i18n = Localization::load_watched(
|
global_state.i18n = Localization::load_expect(
|
||||||
&i18n_asset_key(&global_state.settings.language.selected_language),
|
&i18n_asset_key(&global_state.settings.language.selected_language),
|
||||||
&mut global_state.localization_watcher,
|
);
|
||||||
)
|
global_state.i18n.read().log_missing_entries();
|
||||||
.unwrap();
|
self.hud.update_fonts(&global_state.i18n.read());
|
||||||
self.i18n.log_missing_entries();
|
|
||||||
self.hud.update_language(Arc::clone(&self.i18n));
|
|
||||||
},
|
},
|
||||||
HudEvent::ChangeFullscreenMode(new_fullscreen_settings) => {
|
HudEvent::ChangeFullscreenMode(new_fullscreen_settings) => {
|
||||||
global_state
|
global_state
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::i18n;
|
use crate::{i18n, ui::ice::RawFont};
|
||||||
use common::assets::Asset;
|
use common::assets::{self, AssetExt};
|
||||||
|
|
||||||
pub struct Font {
|
pub struct Font {
|
||||||
metadata: i18n::Font,
|
metadata: i18n::Font,
|
||||||
@ -8,11 +8,13 @@ pub struct Font {
|
|||||||
|
|
||||||
impl Font {
|
impl Font {
|
||||||
#[allow(clippy::needless_return)] // TODO: Pending review in #587
|
#[allow(clippy::needless_return)] // TODO: Pending review in #587
|
||||||
pub fn new(font: &i18n::Font, ui: &mut crate::ui::Ui) -> Self {
|
fn new(font: &i18n::Font, ui: &mut crate::ui::Ui) -> Result<Self, assets::Error> {
|
||||||
Self {
|
let raw_font = RawFont::load(&font.asset_key)?.cloned();
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
metadata: font.clone(),
|
metadata: font.clone(),
|
||||||
conrod_id: ui.new_font(crate::ui::ice::RawFont::load_expect(&font.asset_key)),
|
conrod_id: ui.new_font(raw_font),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Scale input size to final UI size
|
/// Scale input size to final UI size
|
||||||
@ -27,9 +29,9 @@ macro_rules! conrod_fonts {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Fonts {
|
impl Fonts {
|
||||||
pub fn load(fonts: &i18n::Fonts, ui: &mut crate::ui::Ui) -> Result<Self, common::assets::Error> {
|
pub fn load(fonts: &i18n::Fonts, ui: &mut crate::ui::Ui) -> Result<Self, assets::Error> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
$( $name: Font::new(fonts.get(stringify!($name)).unwrap(), ui),)*
|
$( $name: Font::new(fonts.get(stringify!($name)).unwrap(), ui)?, )*
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -47,11 +49,13 @@ pub struct IcedFont {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl IcedFont {
|
impl IcedFont {
|
||||||
pub fn new(font: &i18n::Font, ui: &mut crate::ui::ice::IcedUi) -> Self {
|
fn new(font: &i18n::Font, ui: &mut crate::ui::ice::IcedUi) -> Result<Self, assets::Error> {
|
||||||
Self {
|
let raw_font = RawFont::load(&font.asset_key)?.cloned();
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
metadata: font.clone(),
|
metadata: font.clone(),
|
||||||
id: ui.add_font((*crate::ui::ice::RawFont::load_expect(&font.asset_key)).clone()),
|
id: ui.add_font(raw_font),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Scale input size to final UI size
|
/// Scale input size to final UI size
|
||||||
@ -67,9 +71,9 @@ macro_rules! iced_fonts {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl IcedFonts {
|
impl IcedFonts {
|
||||||
pub fn load(fonts: &i18n::Fonts, ui: &mut crate::ui::ice::IcedUi) -> Result<Self, common::assets::Error> {
|
pub fn load(fonts: &i18n::Fonts, ui: &mut crate::ui::ice::IcedUi) -> Result<Self, assets::Error> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
$( $name: IcedFont::new(fonts.get(stringify!($name)).unwrap(), ui),)*
|
$( $name: IcedFont::new(fonts.get(stringify!($name)).unwrap(), ui)?, )*
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,9 @@ use crate::{
|
|||||||
render::{Renderer, Texture},
|
render::{Renderer, Texture},
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
use common::assets::{self, AssetExt};
|
||||||
use glyph_brush::GlyphBrushBuilder;
|
use glyph_brush::GlyphBrushBuilder;
|
||||||
use std::cell::{RefCell, RefMut};
|
use std::{borrow::Cow, cell::{RefCell, RefMut}};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
// Multiplied by current window size
|
// Multiplied by current window size
|
||||||
@ -20,6 +21,24 @@ type GlyphBrush = glyph_brush::GlyphBrush<(Aabr<f32>, Aabr<f32>), ()>;
|
|||||||
// TODO: might not need pub
|
// TODO: might not need pub
|
||||||
pub type Font = glyph_brush::ab_glyph::FontArc;
|
pub type Font = glyph_brush::ab_glyph::FontArc;
|
||||||
|
|
||||||
|
struct FontAsset(Font);
|
||||||
|
struct FontLoader;
|
||||||
|
impl assets::Loader<FontAsset> for FontLoader {
|
||||||
|
fn load(data: Cow<[u8]>, _: &str) -> Result<FontAsset, assets::BoxedError> {
|
||||||
|
let font = Font::try_from_vec(data.into_owned())?;
|
||||||
|
Ok(FontAsset(font))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl assets::Asset for FontAsset {
|
||||||
|
const EXTENSION: &'static str = "ttf";
|
||||||
|
type Loader = FontLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_font(specifier: &str) -> Font {
|
||||||
|
FontAsset::load_expect(specifier).read().0.clone()
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Default)]
|
#[derive(Clone, Copy, Default)]
|
||||||
pub struct FontId(pub(super) glyph_brush::FontId);
|
pub struct FontId(pub(super) glyph_brush::FontId);
|
||||||
|
|
||||||
@ -121,16 +140,14 @@ impl Cache {
|
|||||||
// TODO: use font type instead of raw vec once we convert to full iced
|
// TODO: use font type instead of raw vec once we convert to full iced
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RawFont(pub Vec<u8>);
|
pub struct RawFont(pub Vec<u8>);
|
||||||
impl common::assets::Asset for RawFont {
|
|
||||||
const ENDINGS: &'static [&'static str] = &["ttf"];
|
|
||||||
|
|
||||||
fn parse(
|
impl From<Vec<u8>> for RawFont {
|
||||||
mut buf_reader: std::io::BufReader<std::fs::File>,
|
fn from(raw: Vec<u8>) -> RawFont {
|
||||||
_specifier: &str,
|
RawFont(raw)
|
||||||
) -> Result<Self, common::assets::Error> {
|
|
||||||
use std::io::Read;
|
|
||||||
let mut buf = Vec::new();
|
|
||||||
buf_reader.read_to_end(&mut buf)?;
|
|
||||||
Ok(Self(buf))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl assets::Asset for RawFont {
|
||||||
|
const EXTENSION: &'static str = "ttf";
|
||||||
|
type Loader = assets::LoadFrom<Vec<u8>, assets::BytesLoader>;
|
||||||
|
}
|
||||||
|
@ -4,7 +4,7 @@ pub mod component;
|
|||||||
mod renderer;
|
mod renderer;
|
||||||
pub mod widget;
|
pub mod widget;
|
||||||
|
|
||||||
pub use cache::{Font, FontId, RawFont};
|
pub use cache::{Font, FontId, RawFont, load_font};
|
||||||
pub use graphic::{Id, Rotation};
|
pub use graphic::{Id, Rotation};
|
||||||
pub use iced::Event;
|
pub use iced::Event;
|
||||||
pub use iced_winit::conversion::window_event;
|
pub use iced_winit::conversion::window_event;
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
use super::{Graphic, SampleStrat, Transform};
|
use super::{Graphic, SampleStrat, Transform};
|
||||||
use common::{
|
use common::{
|
||||||
assets::{Asset, Error},
|
assets::{self, AssetExt, DotVoxAsset, Error},
|
||||||
figure::Segment,
|
figure::Segment,
|
||||||
};
|
};
|
||||||
use dot_vox::DotVoxData;
|
|
||||||
use image::DynamicImage;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
@ -24,7 +22,8 @@ impl<'a> GraphicCreator<'a> for ImageGraphic {
|
|||||||
type Specifier = &'a str;
|
type Specifier = &'a str;
|
||||||
|
|
||||||
fn new_graphic(specifier: Self::Specifier) -> Result<Graphic, Error> {
|
fn new_graphic(specifier: Self::Specifier) -> Result<Graphic, Error> {
|
||||||
Ok(Graphic::Image(DynamicImage::load(specifier)?, None))
|
let image = assets::Image::load(specifier)?.read().to_image();
|
||||||
|
Ok(Graphic::Image(image, None))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,8 +36,8 @@ pub enum VoxelSs9Graphic {}
|
|||||||
pub enum VoxelPixArtGraphic {}
|
pub enum VoxelPixArtGraphic {}
|
||||||
|
|
||||||
fn load_segment(specifier: &str) -> Result<Arc<Segment>, Error> {
|
fn load_segment(specifier: &str) -> Result<Arc<Segment>, Error> {
|
||||||
let dot_vox = DotVoxData::load(specifier)?;
|
let dot_vox = DotVoxAsset::load(specifier)?;
|
||||||
let seg = dot_vox.as_ref().into();
|
let seg = Segment::from(&dot_vox.read().0);
|
||||||
Ok(Arc::new(seg))
|
Ok(Arc::new(seg))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ use conrod_core::{
|
|||||||
use core::{convert::TryInto, f32, f64, ops::Range};
|
use core::{convert::TryInto, f32, f64, ops::Range};
|
||||||
use graphic::TexId;
|
use graphic::TexId;
|
||||||
use hashbrown::hash_map::Entry;
|
use hashbrown::hash_map::Entry;
|
||||||
use std::{sync::Arc, time::Duration};
|
use std::time::Duration;
|
||||||
use tracing::{error, warn};
|
use tracing::{error, warn};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
@ -215,8 +215,8 @@ impl Ui {
|
|||||||
self.image_map.replace(id, (graphic_id, Rotation::None));
|
self.image_map.replace(id, (graphic_id, Rotation::None));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_font(&mut self, font: Arc<crate::ui::ice::RawFont>) -> font::Id {
|
pub fn new_font(&mut self, font: crate::ui::ice::RawFont) -> font::Id {
|
||||||
let font = text::Font::from_bytes(font.0.clone()).unwrap();
|
let font = text::Font::from_bytes(font.0).unwrap();
|
||||||
self.ui.fonts.insert(font)
|
self.ui.fonts.insert(font)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ const H: usize = 480;
|
|||||||
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
|
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
|
||||||
fn main() {
|
fn main() {
|
||||||
let seed = 1337;
|
let seed = 1337;
|
||||||
let (ref index, ref colors) = Index::new(seed);
|
let (ref index, colors) = Index::new(seed);
|
||||||
|
|
||||||
let mut win =
|
let mut win =
|
||||||
minifb::Window::new("Settlement Viewer", W, H, minifb::WindowOptions::default()).unwrap();
|
minifb::Window::new("Settlement Viewer", W, H, minifb::WindowOptions::default()).unwrap();
|
||||||
@ -17,6 +17,7 @@ fn main() {
|
|||||||
|
|
||||||
let mut focus = Vec2::<f32>::zero();
|
let mut focus = Vec2::<f32>::zero();
|
||||||
let mut zoom = 1.0;
|
let mut zoom = 1.0;
|
||||||
|
let colors = &**colors.read();
|
||||||
let index = IndexRef { colors, index };
|
let index = IndexRef { colors, index };
|
||||||
|
|
||||||
while win.is_open() {
|
while win.is_open() {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::{site::Site, Colors};
|
use crate::{site::Site, Colors};
|
||||||
use common::{
|
use common::{
|
||||||
assets::{watch::ReloadIndicator, Asset, Ron},
|
assets::{AssetExt, AssetHandle},
|
||||||
store::Store,
|
store::Store,
|
||||||
};
|
};
|
||||||
use core::ops::Deref;
|
use core::ops::Deref;
|
||||||
@ -14,7 +14,6 @@ pub struct Index {
|
|||||||
pub time: f32,
|
pub time: f32,
|
||||||
pub noise: Noise,
|
pub noise: Noise,
|
||||||
pub sites: Store<Site>,
|
pub sites: Store<Site>,
|
||||||
indicator: ReloadIndicator,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An owned reference to indexed data.
|
/// An owned reference to indexed data.
|
||||||
@ -26,6 +25,10 @@ pub struct Index {
|
|||||||
pub struct IndexOwned {
|
pub struct IndexOwned {
|
||||||
colors: Arc<Colors>,
|
colors: Arc<Colors>,
|
||||||
index: Arc<Index>,
|
index: Arc<Index>,
|
||||||
|
|
||||||
|
/// Stored separatly so `colors` is only updated when `reload_colors_if_changed`
|
||||||
|
/// is called
|
||||||
|
colors_handle: AssetHandle<Arc<Colors>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for IndexOwned {
|
impl Deref for IndexOwned {
|
||||||
@ -51,10 +54,8 @@ impl<'a> Deref for IndexRef<'a> {
|
|||||||
|
|
||||||
impl Index {
|
impl Index {
|
||||||
/// NOTE: Panics if the color manifest cannot be loaded.
|
/// NOTE: Panics if the color manifest cannot be loaded.
|
||||||
pub fn new(seed: u32) -> (Self, Arc<Colors>) {
|
pub fn new(seed: u32) -> (Self, AssetHandle<Arc<Colors>>) {
|
||||||
let mut indicator = ReloadIndicator::new();
|
let colors = Arc::<Colors>::load_expect(WORLD_COLORS_MANIFEST);
|
||||||
let colors = Ron::<Colors>::load_watched(WORLD_COLORS_MANIFEST, &mut indicator)
|
|
||||||
.expect("Could not load world colors!");
|
|
||||||
|
|
||||||
(
|
(
|
||||||
Self {
|
Self {
|
||||||
@ -62,7 +63,6 @@ impl Index {
|
|||||||
time: 0.0,
|
time: 0.0,
|
||||||
noise: Noise::new(seed),
|
noise: Noise::new(seed),
|
||||||
sites: Store::default(),
|
sites: Store::default(),
|
||||||
indicator,
|
|
||||||
},
|
},
|
||||||
colors,
|
colors,
|
||||||
)
|
)
|
||||||
@ -70,10 +70,11 @@ impl Index {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl IndexOwned {
|
impl IndexOwned {
|
||||||
pub fn new(index: Index, colors: Arc<Colors>) -> Self {
|
pub fn new(index: Index, colors: AssetHandle<Arc<Colors>>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
index: Arc::new(index),
|
index: Arc::new(index),
|
||||||
colors,
|
colors: colors.cloned(),
|
||||||
|
colors_handle: colors,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,9 +89,9 @@ impl IndexOwned {
|
|||||||
&mut self,
|
&mut self,
|
||||||
reload: impl FnOnce(&mut Self) -> R,
|
reload: impl FnOnce(&mut Self) -> R,
|
||||||
) -> Option<R> {
|
) -> Option<R> {
|
||||||
self.indicator.reloaded().then(move || {
|
self.colors_handle.reloaded().then(move || {
|
||||||
// We know the asset was loaded before, so load_expect should be fine.
|
// Reload the color from the asse handle, which is updated automatically
|
||||||
self.colors = Ron::<Colors>::load_expect(WORLD_COLORS_MANIFEST);
|
self.colors = self.colors_handle.cloned();
|
||||||
reload(self)
|
reload(self)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ use crate::{
|
|||||||
Canvas, IndexRef,
|
Canvas, IndexRef,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
assets::Asset,
|
assets::AssetExt,
|
||||||
comp,
|
comp,
|
||||||
generation::{ChunkSupplement, EntityInfo},
|
generation::{ChunkSupplement, EntityInfo},
|
||||||
lottery::Lottery,
|
lottery::Lottery,
|
||||||
@ -182,8 +182,8 @@ pub fn apply_caves_to(canvas: &mut Canvas) {
|
|||||||
.chance(wpos2d.into(), 0.001 * difficulty.powf(1.5))
|
.chance(wpos2d.into(), 0.001 * difficulty.powf(1.5))
|
||||||
&& cave_base < surface_z as i32 - 25
|
&& cave_base < surface_z as i32 - 25
|
||||||
{
|
{
|
||||||
let kind = *Lottery::<SpriteKind>::load_expect("common.cave_scatter")
|
let lottery = Lottery::<SpriteKind>::load_expect("common.cave_scatter").read();
|
||||||
.choose_seeded(RandomField::new(info.index().seed + 1).get(wpos2d.into()));
|
let kind = *lottery.choose_seeded(RandomField::new(info.index().seed + 1).get(wpos2d.into()));
|
||||||
canvas.map(Vec3::new(wpos2d.x, wpos2d.y, cave_base), |block| {
|
canvas.map(Vec3::new(wpos2d.x, wpos2d.y, cave_base), |block| {
|
||||||
block.with_sprite(kind)
|
block.with_sprite(kind)
|
||||||
});
|
});
|
||||||
|
@ -6,27 +6,28 @@ use crate::{
|
|||||||
Canvas, CONFIG,
|
Canvas, CONFIG,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
terrain::{structure::Structure, Block, BlockKind},
|
assets::AssetHandle,
|
||||||
|
terrain::{Structure, StructuresGroup, Block, BlockKind},
|
||||||
vol::ReadVol,
|
vol::ReadVol,
|
||||||
};
|
};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use std::{f32, sync::Arc};
|
use std::f32;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref OAKS: Vec<Arc<Structure>> = Structure::load_group("oaks");
|
static ref OAKS: AssetHandle<StructuresGroup> = Structure::load_group("oaks");
|
||||||
pub static ref OAK_STUMPS: Vec<Arc<Structure>> = Structure::load_group("oak_stumps");
|
static ref OAK_STUMPS: AssetHandle<StructuresGroup> = Structure::load_group("oak_stumps");
|
||||||
pub static ref PINES: Vec<Arc<Structure>> = Structure::load_group("pines");
|
static ref PINES: AssetHandle<StructuresGroup> = Structure::load_group("pines");
|
||||||
pub static ref PALMS: Vec<Arc<Structure>> = Structure::load_group("palms");
|
static ref PALMS: AssetHandle<StructuresGroup> = Structure::load_group("palms");
|
||||||
pub static ref ACACIAS: Vec<Arc<Structure>> = Structure::load_group("acacias");
|
static ref ACACIAS: AssetHandle<StructuresGroup> = Structure::load_group("acacias");
|
||||||
pub static ref BAOBABS: Vec<Arc<Structure>> = Structure::load_group("baobabs");
|
static ref BAOBABS: AssetHandle<StructuresGroup> = Structure::load_group("baobabs");
|
||||||
pub static ref FRUIT_TREES: Vec<Arc<Structure>> = Structure::load_group("fruit_trees");
|
static ref FRUIT_TREES: AssetHandle<StructuresGroup> = Structure::load_group("fruit_trees");
|
||||||
pub static ref BIRCHES: Vec<Arc<Structure>> = Structure::load_group("birch");
|
static ref BIRCHES: AssetHandle<StructuresGroup> = Structure::load_group("birch");
|
||||||
pub static ref MANGROVE_TREES: Vec<Arc<Structure>> = Structure::load_group("mangrove_trees");
|
static ref MANGROVE_TREES: AssetHandle<StructuresGroup> = Structure::load_group("mangrove_trees");
|
||||||
pub static ref QUIRKY: Vec<Arc<Structure>> = Structure::load_group("quirky");
|
static ref QUIRKY: AssetHandle<StructuresGroup> = Structure::load_group("quirky");
|
||||||
pub static ref QUIRKY_DRY: Vec<Arc<Structure>> = Structure::load_group("quirky_dry");
|
static ref QUIRKY_DRY: AssetHandle<StructuresGroup> = Structure::load_group("quirky_dry");
|
||||||
pub static ref SWAMP_TREES: Vec<Arc<Structure>> = Structure::load_group("swamp_trees");
|
static ref SWAMP_TREES: AssetHandle<StructuresGroup> = Structure::load_group("swamp_trees");
|
||||||
}
|
}
|
||||||
|
|
||||||
static MODEL_RAND: RandomPerm = RandomPerm::new(0xDB21C052);
|
static MODEL_RAND: RandomPerm = RandomPerm::new(0xDB21C052);
|
||||||
@ -37,7 +38,7 @@ static QUIRKY_RAND: RandomPerm = RandomPerm::new(0xA634460F);
|
|||||||
pub fn apply_trees_to(canvas: &mut Canvas) {
|
pub fn apply_trees_to(canvas: &mut Canvas) {
|
||||||
struct Tree {
|
struct Tree {
|
||||||
pos: Vec3<i32>,
|
pos: Vec3<i32>,
|
||||||
model: Arc<Structure>,
|
model: Structure,
|
||||||
seed: u32,
|
seed: u32,
|
||||||
units: (Vec2<i32>, Vec2<i32>),
|
units: (Vec2<i32>, Vec2<i32>),
|
||||||
}
|
}
|
||||||
@ -72,34 +73,34 @@ pub fn apply_trees_to(canvas: &mut Canvas) {
|
|||||||
Some(Tree {
|
Some(Tree {
|
||||||
pos: Vec3::new(tree_wpos.x, tree_wpos.y, col.alt as i32),
|
pos: Vec3::new(tree_wpos.x, tree_wpos.y, col.alt as i32),
|
||||||
model: {
|
model: {
|
||||||
let models: &'static [_] = if is_quirky {
|
let models: AssetHandle<_> = if is_quirky {
|
||||||
if col.temp > CONFIG.desert_temp {
|
if col.temp > CONFIG.desert_temp {
|
||||||
&QUIRKY_DRY
|
*QUIRKY_DRY
|
||||||
} else {
|
} else {
|
||||||
&QUIRKY
|
*QUIRKY
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match col.forest_kind {
|
match col.forest_kind {
|
||||||
ForestKind::Oak if QUIRKY_RAND.chance(seed + 1, 1.0 / 16.0) => {
|
ForestKind::Oak if QUIRKY_RAND.chance(seed + 1, 1.0 / 16.0) => {
|
||||||
&OAK_STUMPS
|
*OAK_STUMPS
|
||||||
},
|
},
|
||||||
ForestKind::Oak if QUIRKY_RAND.chance(seed + 2, 1.0 / 20.0) => {
|
ForestKind::Oak if QUIRKY_RAND.chance(seed + 2, 1.0 / 20.0) => {
|
||||||
&FRUIT_TREES
|
*FRUIT_TREES
|
||||||
},
|
},
|
||||||
ForestKind::Palm => &PALMS,
|
ForestKind::Palm => *PALMS,
|
||||||
ForestKind::Acacia => &ACACIAS,
|
ForestKind::Acacia => *ACACIAS,
|
||||||
ForestKind::Baobab => &BAOBABS,
|
ForestKind::Baobab => *BAOBABS,
|
||||||
ForestKind::Oak => &OAKS,
|
ForestKind::Oak => *OAKS,
|
||||||
ForestKind::Pine => &PINES,
|
ForestKind::Pine => *PINES,
|
||||||
ForestKind::Birch => &BIRCHES,
|
ForestKind::Birch => *BIRCHES,
|
||||||
ForestKind::Mangrove => &MANGROVE_TREES,
|
ForestKind::Mangrove => *MANGROVE_TREES,
|
||||||
ForestKind::Swamp => &SWAMP_TREES,
|
ForestKind::Swamp => *SWAMP_TREES,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Arc::clone(
|
|
||||||
&models[(MODEL_RAND.get(seed.wrapping_mul(17)) / 13) as usize
|
let models = models.read();
|
||||||
% models.len()],
|
models[(MODEL_RAND.get(seed.wrapping_mul(17)) / 13) as usize % models.len()]
|
||||||
)
|
.clone()
|
||||||
},
|
},
|
||||||
seed,
|
seed,
|
||||||
units: UNIT_CHOOSER.get(seed),
|
units: UNIT_CHOOSER.get(seed),
|
||||||
|
@ -41,6 +41,7 @@ use crate::{
|
|||||||
util::{Grid, Sampler},
|
util::{Grid, Sampler},
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
|
assets,
|
||||||
generation::{ChunkSupplement, EntityInfo},
|
generation::{ChunkSupplement, EntityInfo},
|
||||||
terrain::{Block, BlockKind, SpriteKind, TerrainChunk, TerrainChunkMeta, TerrainChunkSize},
|
terrain::{Block, BlockKind, SpriteKind, TerrainChunk, TerrainChunkMeta, TerrainChunkSize},
|
||||||
vol::{ReadVol, RectVolSize, WriteVol},
|
vol::{ReadVol, RectVolSize, WriteVol},
|
||||||
@ -70,6 +71,11 @@ pub struct Colors {
|
|||||||
pub site: site::Colors,
|
pub site: site::Colors,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl assets::Asset for Colors {
|
||||||
|
const EXTENSION: &'static str = "ron";
|
||||||
|
type Loader = assets::RonLoader;
|
||||||
|
}
|
||||||
|
|
||||||
impl World {
|
impl World {
|
||||||
pub fn generate(seed: u32, opts: sim::WorldOpts) -> (Self, IndexOwned) {
|
pub fn generate(seed: u32, opts: sim::WorldOpts) -> (Self, IndexOwned) {
|
||||||
// NOTE: Generating index first in order to quickly fail if the color manifest
|
// NOTE: Generating index first in order to quickly fail if the color manifest
|
||||||
|
@ -35,7 +35,7 @@ use crate::{
|
|||||||
IndexRef, CONFIG,
|
IndexRef, CONFIG,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
assets,
|
assets::{self, AssetExt},
|
||||||
grid::Grid,
|
grid::Grid,
|
||||||
store::Id,
|
store::Id,
|
||||||
terrain::{
|
terrain::{
|
||||||
@ -220,7 +220,7 @@ pub enum WorldFileError {
|
|||||||
/// invalidation or make sure that the map is synchronized with updates to
|
/// invalidation or make sure that the map is synchronized with updates to
|
||||||
/// noise-rs, changes to other parameters, etc.
|
/// noise-rs, changes to other parameters, etc.
|
||||||
///
|
///
|
||||||
/// The map is verisoned to enable format detection between versions of Veloren,
|
/// The map is versioned to enable format detection between versions of Veloren,
|
||||||
/// so that when we update the map format we don't break existing maps (or at
|
/// so that when we update the map format we don't break existing maps (or at
|
||||||
/// least, we will try hard not to break maps between versions; if we can't
|
/// least, we will try hard not to break maps between versions; if we can't
|
||||||
/// avoid it, we can at least give a reasonable error message).
|
/// avoid it, we can at least give a reasonable error message).
|
||||||
@ -245,6 +245,11 @@ pub enum WorldFile {
|
|||||||
Veloren0_7_0(WorldMap_0_7_0) = 1,
|
Veloren0_7_0(WorldMap_0_7_0) = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl assets::Asset for WorldFile {
|
||||||
|
const EXTENSION: &'static str = "bin";
|
||||||
|
type Loader = assets::BincodeLoader;
|
||||||
|
}
|
||||||
|
|
||||||
/// Data for the most recent map type. Update this when you add a new map
|
/// Data for the most recent map type. Update this when you add a new map
|
||||||
/// version.
|
/// version.
|
||||||
pub type ModernMap = WorldMap_0_7_0;
|
pub type ModernMap = WorldMap_0_7_0;
|
||||||
@ -409,26 +414,21 @@ impl WorldSim {
|
|||||||
map.into_modern()
|
map.into_modern()
|
||||||
},
|
},
|
||||||
FileOpts::LoadAsset(ref specifier) => {
|
FileOpts::LoadAsset(ref specifier) => {
|
||||||
let reader = match assets::load_file(specifier, &["bin"]) {
|
match WorldFile::load_owned(&specifier) {
|
||||||
Ok(reader) => reader,
|
Ok(map) => map.into_modern(),
|
||||||
Err(e) => {
|
Err(err) => {
|
||||||
warn!(?e, ?specifier, "Couldn't read asset specifier for maps",);
|
match err {
|
||||||
|
assets::Error::Io(e) => {
|
||||||
|
warn!(?e, ?specifier, "Couldn't read asset specifier for maps");
|
||||||
|
}
|
||||||
|
assets::Error::Conversion(e) => {
|
||||||
|
warn!(?e, "Couldn't parse modern map. Maybe you meant to try a legacy load?");
|
||||||
|
}
|
||||||
|
assets::Error::NoDefaultValue => unreachable!(),
|
||||||
|
}
|
||||||
return None;
|
return None;
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
let map: WorldFile = match bincode::deserialize_from(reader) {
|
|
||||||
Ok(map) => map,
|
|
||||||
Err(e) => {
|
|
||||||
warn!(
|
|
||||||
?e,
|
|
||||||
"Couldn't parse modern map. Maybe you meant to try a legacy load?"
|
|
||||||
);
|
|
||||||
return None;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
map.into_modern()
|
|
||||||
},
|
},
|
||||||
FileOpts::Generate | FileOpts::Save => return None,
|
FileOpts::Generate | FileOpts::Save => return None,
|
||||||
};
|
};
|
||||||
|
@ -8,13 +8,13 @@ use crate::{
|
|||||||
IndexRef,
|
IndexRef,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
assets::Asset,
|
assets::{AssetExt, AssetHandle},
|
||||||
astar::Astar,
|
astar::Astar,
|
||||||
comp::{self},
|
comp::{self},
|
||||||
generation::{ChunkSupplement, EntityInfo},
|
generation::{ChunkSupplement, EntityInfo},
|
||||||
lottery::Lottery,
|
lottery::Lottery,
|
||||||
store::{Id, Store},
|
store::{Id, Store},
|
||||||
terrain::{Block, BlockKind, SpriteKind, Structure, TerrainChunkSize},
|
terrain::{Block, BlockKind, SpriteKind, Structure, StructuresGroup, TerrainChunkSize},
|
||||||
vol::{BaseVol, ReadVol, RectSizedVol, RectVolSize, WriteVol},
|
vol::{BaseVol, ReadVol, RectSizedVol, RectVolSize, WriteVol},
|
||||||
};
|
};
|
||||||
use core::{f32, hash::BuildHasherDefault};
|
use core::{f32, hash::BuildHasherDefault};
|
||||||
@ -22,7 +22,6 @@ use fxhash::FxHasher64;
|
|||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::sync::Arc;
|
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
pub struct Dungeon {
|
pub struct Dungeon {
|
||||||
@ -111,11 +110,12 @@ impl Dungeon {
|
|||||||
vol: &mut (impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol),
|
vol: &mut (impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol),
|
||||||
) {
|
) {
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref ENTRANCES: Vec<Arc<Structure>> =
|
pub static ref ENTRANCES: AssetHandle<StructuresGroup> =
|
||||||
Structure::load_group("dungeon_entrances");
|
Structure::load_group("dungeon_entrances");
|
||||||
}
|
}
|
||||||
|
|
||||||
let entrance = &ENTRANCES[self.seed as usize % ENTRANCES.len()];
|
let entrances = ENTRANCES.read();
|
||||||
|
let entrance = &entrances[self.seed as usize % entrances.len()];
|
||||||
|
|
||||||
for y in 0..vol.size_xy().y as i32 {
|
for y in 0..vol.size_xy().y as i32 {
|
||||||
for x in 0..vol.size_xy().x as i32 {
|
for x in 0..vol.size_xy().x as i32 {
|
||||||
@ -581,6 +581,7 @@ impl Floor {
|
|||||||
"common.loot_tables.loot_table_armor_misc",
|
"common.loot_tables.loot_table_armor_misc",
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
let chosen = chosen.read();
|
||||||
let chosen = chosen.choose();
|
let chosen = chosen.choose();
|
||||||
//let is_giant =
|
//let is_giant =
|
||||||
// RandomField::new(room.seed.wrapping_add(1)).chance(Vec3::from(tile_pos),
|
// RandomField::new(room.seed.wrapping_add(1)).chance(Vec3::from(tile_pos),
|
||||||
@ -741,6 +742,7 @@ impl Floor {
|
|||||||
"common.loot_tables.loot_table_armor_misc",
|
"common.loot_tables.loot_table_armor_misc",
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
let chosen = chosen.read();
|
||||||
let chosen = chosen.choose();
|
let chosen = chosen.choose();
|
||||||
let entity = match room.difficulty {
|
let entity = match room.difficulty {
|
||||||
0 => vec![
|
0 => vec![
|
||||||
@ -923,6 +925,7 @@ impl Floor {
|
|||||||
"common.loot_tables.loot_table_armor_misc",
|
"common.loot_tables.loot_table_armor_misc",
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
let chosen = chosen.read();
|
||||||
let chosen = chosen.choose();
|
let chosen = chosen.choose();
|
||||||
let entity = match room.difficulty {
|
let entity = match room.difficulty {
|
||||||
0 => vec![
|
0 => vec![
|
||||||
|
Loading…
Reference in New Issue
Block a user