use crate::fmt_utils::SkipDebug;
use crate::prelude::*;
use bevy::platform::collections::{HashMap, HashSet};
use bevy::prelude::*;
pub(crate) use compilation::{
RecompileLoadedYarnFilesEvent, YarnFilesBeingLoaded, YarnProjectConfigToLoad,
};
use std::fmt::Debug;
use std::iter;
mod compilation;
pub(crate) fn project_plugin(app: &mut App) {
app.add_plugins(compilation::project_compilation_plugin)
.add_message::<LoadYarnProjectEvent>();
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, SystemSet)]
pub(crate) struct CompilationSystemSet;
#[derive(Resource, Debug)]
pub struct YarnProject {
pub(crate) yarn_files: HashSet<Handle<YarnFile>>,
pub(crate) compilation: Compilation,
pub(crate) localizations: Option<Localizations>,
pub(crate) asset_server: SkipDebug<AssetServer>,
pub(crate) metadata: HashMap<LineId, Vec<String>>,
pub(crate) watching_for_changes: bool,
pub(crate) development_file_generation: DevelopmentFileGeneration,
}
impl YarnProject {
pub fn yarn_files(&self) -> impl Iterator<Item = &Handle<YarnFile>> {
self.yarn_files.iter()
}
pub fn compilation(&self) -> &Compilation {
&self.compilation
}
pub fn localizations(&self) -> Option<&Localizations> {
self.localizations.as_ref()
}
pub fn create_dialogue_runner(&self, commands: &mut Commands) -> DialogueRunner {
self.build_dialogue_runner(commands).build()
}
pub fn build_dialogue_runner(&self, commands: &mut Commands) -> DialogueRunnerBuilder {
DialogueRunnerBuilder::from_yarn_project(self, commands)
}
pub fn line_metadata(&self, line_id: &LineId) -> Option<&[String]> {
self.metadata.get(line_id).map(|v| v.as_slice())
}
pub fn headers_for_node(&self, node_name: &str) -> Option<HashMap<&str, Vec<&str>>> {
self.compilation
.program
.as_ref()
.unwrap()
.nodes
.get(node_name)?
.headers
.iter()
.fold(HashMap::default(), |mut map: HashMap<_, Vec<_>>, header| {
map.entry(header.key.as_str())
.or_default()
.push(header.value.as_str());
map
})
.into()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Message)]
pub struct LoadYarnProjectEvent {
pub(crate) localizations: Option<Localizations>,
pub(crate) yarn_files: HashSet<YarnFileSource>,
pub(crate) development_file_generation: DevelopmentFileGeneration,
}
#[cfg(not(any(target_arch = "wasm32", target_os = "android")))]
impl Default for LoadYarnProjectEvent {
fn default() -> Self {
Self {
localizations: None,
yarn_files: HashSet::from_iter([YarnFileSource::Folder(DEFAULT_ASSET_DIR.into())]),
development_file_generation: default(),
}
}
}
impl LoadYarnProjectEvent {
#[cfg(not(any(target_arch = "wasm32", target_os = "android")))]
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with_yarn_sources<T, U>(yarn_files: T) -> Self
where
T: IntoIterator<Item = U>,
U: Into<YarnFileSource>,
{
let yarn_files = yarn_files
.into_iter()
.map(|yarn_file| yarn_file.into())
.collect();
Self {
localizations: None,
yarn_files,
development_file_generation: default(),
}
}
#[must_use]
pub fn with_yarn_source(yarn_file_source: impl Into<YarnFileSource>) -> Self {
Self::with_yarn_sources(iter::once(yarn_file_source))
}
#[must_use]
pub fn add_yarn_source(mut self, yarn_file: impl Into<YarnFileSource>) -> Self {
self.yarn_files.insert(yarn_file.into());
self
}
#[must_use]
pub fn add_yarn_sources(
mut self,
yarn_files: impl IntoIterator<Item = impl Into<YarnFileSource>>,
) -> Self {
self.yarn_files
.extend(yarn_files.into_iter().map(|yarn_file| yarn_file.into()));
self
}
#[must_use]
pub fn with_localizations(mut self, localizations: impl Into<Option<Localizations>>) -> Self {
let localizations = localizations.into();
self.localizations = localizations;
self
}
#[cfg(not(any(target_arch = "wasm32", target_os = "android")))]
#[must_use]
pub fn with_development_file_generation(
mut self,
development_file_generation: DevelopmentFileGeneration,
) -> Self {
self.development_file_generation = development_file_generation;
self
}
}
impl<T, U> From<T> for LoadYarnProjectEvent
where
T: IntoIterator<Item = U>,
U: Into<YarnFileSource>,
{
fn from(yarn_files: T) -> Self {
Self::with_yarn_sources(yarn_files)
}
}
#[derive(Debug, Clone, Resource, Default)]
pub(crate) struct WatchingForChanges(pub(crate) bool);
pub(crate) const DEFAULT_ASSET_DIR: &str = "dialogue";