darkomen 0.5.0

Warhammer: Dark Omen library and CLI in Rust
Documentation
use bevy_app::prelude::*;
use bevy_asset::{io::Reader, prelude::*, AssetLoader, LoadContext};
use bevy_reflect::prelude::*;
use derive_more::derive::{Display, Error, From};
use serde::{Deserialize, Serialize};

use crate::battle_tabletop::*;

use super::army::*;

pub struct BattleTabletopAssetPlugin;

impl Plugin for BattleTabletopAssetPlugin {
    fn build(&self, app: &mut App) {
        if !app.is_plugin_added::<ArmyAssetPlugin>() {
            app.add_plugins(ArmyAssetPlugin);
        }

        app.init_asset::<BattleTabletopAsset>()
            .init_asset_loader::<BattleTabletopAssetLoader>();
        #[cfg(feature = "bevy_reflect")]
        app.register_asset_reflect::<BattleTabletopAsset>();
    }
}

#[derive(Asset, Clone)]
#[cfg_attr(feature = "debug", derive(Debug))]
#[cfg_attr(not(feature = "bevy_reflect"), derive(TypePath))]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(all(feature = "bevy_reflect", feature = "debug"), reflect(Debug))]
pub struct BattleTabletopAsset {
    source: BattleTabletop,

    pub army1: Option<Handle<ArmyAsset>>,
    pub army2: Option<Handle<ArmyAsset>>,
}

impl BattleTabletopAsset {
    #[inline(always)]
    pub fn get(&self) -> &BattleTabletop {
        &self.source
    }

    #[inline(always)]
    pub fn objectives(&self) -> &[Objective] {
        &self.source.objectives
    }

    #[inline(always)]
    pub fn obstacles(&self) -> &[Obstacle] {
        &self.source.obstacles
    }

    #[inline(always)]
    pub fn regions(&self) -> &[Region] {
        &self.source.regions
    }

    #[inline(always)]
    pub fn nodes(&self) -> &[Node] {
        &self.source.nodes
    }
}

#[derive(Clone, Default, TypePath)]
#[cfg_attr(feature = "debug", derive(Debug))]
pub struct BattleTabletopAssetLoader;

#[derive(Clone, Copy, Default, Deserialize, Reflect, Serialize)]
#[cfg_attr(feature = "debug", derive(Debug))]
#[reflect(Default, Deserialize, Serialize)]
#[cfg_attr(all(feature = "bevy_reflect", feature = "debug"), reflect(Debug))]
pub struct BattleTabletopAssetLoaderSettings {
    pub load_army1: bool,
    pub army1_loader_settings: Option<ArmyAssetLoaderSettings>,
    pub load_army2: bool,
    pub army2_loader_settings: Option<ArmyAssetLoaderSettings>,
}

/// Possible errors that can be produced by [BattleTabletopAssetLoader].
#[non_exhaustive]
#[derive(Debug, Display, Error, From)]
pub enum BattleTabletopAssetLoaderError {
    /// An [IO](std::io) error.
    #[display("could not load asset: {_0}")]
    Io(std::io::Error),
    /// A [DecodeError] error.
    #[display("could not decode battle tabletop: {_0}")]
    DecodeError(DecodeError),
}

impl AssetLoader for BattleTabletopAssetLoader {
    type Asset = BattleTabletopAsset;
    type Settings = BattleTabletopAssetLoaderSettings;
    type Error = BattleTabletopAssetLoaderError;
    async fn load(
        &self,
        reader: &mut dyn Reader,
        settings: &Self::Settings,
        load_context: &mut LoadContext<'_>,
    ) -> Result<Self::Asset, Self::Error> {
        let parent_path = load_context
            .path()
            .path()
            .parent()
            .expect("parent path should exist")
            .to_path_buf();

        let mut bytes = Vec::new();
        reader.read_to_end(&mut bytes).await?;
        let reader = std::io::Cursor::new(bytes);

        let mut decoder = Decoder::new(reader);

        let btb = decoder.decode()?;

        Ok(BattleTabletopAsset {
            source: btb.clone(),
            army1: if settings.load_army1 {
                let mut b = load_context.loader();
                if let Some(ref s) = settings.army1_loader_settings {
                    let s = *s;
                    b = b.with_settings(move |settings| {
                        *settings = s;
                    });
                }
                Some(b.load(parent_path.join(btb.army1_file_stem).with_extension("ARM")))
            } else {
                None
            },
            army2: if settings.load_army2 {
                let mut b = load_context.loader();
                if let Some(ref s) = settings.army2_loader_settings {
                    let s = *s;
                    b = b.with_settings(move |settings| {
                        *settings = s;
                    });
                }
                Some(b.load(parent_path.join(btb.army2_file_stem).with_extension("ARM")))
            } else {
                None
            },
        })
    }

    fn extensions(&self) -> &[&str] {
        &["BTB", "btb"]
    }
}