Skip to main content

shuyi_ui/
basic.rs

1//! [`BasicData`]: a ready-made [`GameData`] for a game on the short path
2//! (ADR-0036). It reads the files every such game has from its bundle,
3//! through the format registry ([`BasicData::formats`]): the English
4//! screen text, the keymap, the palette with the engine's colours, the
5//! font atlas and every picture. A part of the game's own rides along in
6//! [`BasicData::own`]. It has no sounds, no voices (`()`), no settings of
7//! the game's own (`()`) and one palette. A game that needs more writes
8//! its own `GameData`, as before.
9
10use crate::prelude::*;
11use alloc::collections::BTreeMap;
12use core::fmt::Debug;
13
14use shuyi_assets::ContentError;
15use shuyi_assets::audio::AudioManifest;
16use shuyi_assets::bundle::Bundle;
17use shuyi_assets::font::{FontAtlasDef, FontAtlasRon};
18use shuyi_assets::format::Formats;
19use shuyi_assets::image::ImageTable;
20use shuyi_assets::keymap::{ActionSet, KeymapDef, KeymapRon};
21use shuyi_assets::lang::{English, Lang};
22use shuyi_assets::palette::{PaletteDef, PaletteRon};
23use shuyi_assets::ron::Ron;
24
25use crate::ctx::GameData;
26use crate::frame::{ConsoleSpec, EngineColor};
27use crate::text::check_key_placeholders;
28
29/// The English screen text by key, in the bundle.
30pub const ENGLISH_RON: &str = "en.ron";
31/// The keymap file, in the bundle.
32pub const KEYMAP_RON: &str = "keymap.ron";
33/// The palette, in the bundle: at least the engine's colours.
34pub const PALETTE_RON: &str = "palette.ron";
35/// The font atlas's description, in the bundle.
36pub const FONT_RON: &str = "font/atlas.ron";
37/// The font atlas's picture, beside [`FONT_RON`].
38pub const FONT_PNG: &str = "font/atlas.png";
39
40/// The glyphs the font must have: printable ASCII, and the box and shape
41/// glyphs the engine's widgets draw.
42pub const BASIC_GLYPHS: &str = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~·…─│┌┐└┘├┤┬┴┼═║╔╗╚╝▓▲▼◄►";
43
44/// How a game reads its own part from its bundle, with every problem of
45/// every file.
46pub type Own<T> = fn(&Bundle) -> Result<T, Vec<ContentError>>;
47
48/// A game's data on the short path: what the engine reads from the
49/// standard files, the files themselves and the game's own part `T`. Its
50/// actions are `A` (most simply `UiAction`), its colours the engine's.
51#[derive(Debug)]
52pub struct BasicData<A: ActionSet, T: Debug + 'static = ()> {
53    lang: Lang,
54    keymap: KeymapDef<A>,
55    audio: AudioManifest,
56    palette: PaletteDef,
57    console: ConsoleSpec,
58    /// The font atlas.
59    pub font: FontAtlasDef,
60    /// Every picture of the bundle but the font's.
61    pub images: ImageTable,
62    /// The bundle the data was read from: the pictures' bytes.
63    pub bundle: Bundle,
64    /// The game's own part.
65    pub own: T,
66}
67
68/// What `loaded` gives, or `None` with its problems added to `errors`.
69fn gather<T>(errors: &mut Vec<ContentError>, loaded: Result<T, Vec<ContentError>>) -> Option<T> {
70    loaded.map_err(|e| errors.extend(e)).ok()
71}
72
73impl<A: ActionSet, T: Debug + 'static> BasicData<A, T> {
74    /// The formats it reads with: the engine's standard ones, the palette
75    /// with the engine's colours and the font atlas with
76    /// [`BASIC_GLYPHS`] in their place, the keymap of `A` and the English.
77    pub fn formats() -> Formats {
78        let mut formats = Formats::standard();
79        let colors: Vec<&str> = EngineColor::ALL.iter().map(|c| c.name()).collect();
80        // Each replaces the standard one of its kind, or is the first of
81        // its kind: none is refused.
82        let _ = formats.replace(PaletteRon::requiring(colors));
83        let _ = formats.replace(FontAtlasRon::requiring(BASIC_GLYPHS));
84        let _ = formats.register(KeymapRon::<A>::new());
85        let _ = formats.register(Ron::<English>::new());
86        formats
87    }
88
89    /// The data in `bundle`, on `console`, with the game's own part read
90    /// by `own`; or every problem of every file at once, each naming its
91    /// file.
92    pub fn load(
93        bundle: Bundle,
94        console: ConsoleSpec,
95        own: Own<T>,
96    ) -> Result<Self, Vec<ContentError>> {
97        let formats = Self::formats();
98        let mut errors = Vec::new();
99        let english = gather(
100            &mut errors,
101            formats
102                .load::<English>(&bundle, ENGLISH_RON)
103                .and_then(|english| {
104                    check_key_placeholders::<A>(&bundle, ENGLISH_RON, &english.0).map(|()| english)
105                }),
106        );
107        let keymap = gather(
108            &mut errors,
109            formats.load::<KeymapDef<A>>(&bundle, KEYMAP_RON),
110        );
111        let palette = gather(
112            &mut errors,
113            formats.load::<PaletteDef>(&bundle, PALETTE_RON),
114        );
115        let font = gather(&mut errors, formats.load::<FontAtlasDef>(&bundle, FONT_RON));
116        let images = gather(
117            &mut errors,
118            ImageTable::load_with(&bundle, &formats, &[FONT_PNG]),
119        );
120        let own = gather(&mut errors, own(&bundle));
121        match (english, keymap, palette, font, images, own) {
122            (Some(english), Some(keymap), Some(palette), Some(font), Some(images), Some(own)) => {
123                Ok(Self {
124                    lang: Lang::new(english.0, BTreeMap::new()),
125                    keymap,
126                    audio: AudioManifest::default(),
127                    palette,
128                    console,
129                    font,
130                    images,
131                    bundle,
132                    own,
133                })
134            }
135            _ => Err(errors),
136        }
137    }
138}
139
140impl<A: ActionSet, T: Debug + 'static> GameData for BasicData<A, T> {
141    type Action = A;
142    type Colors = EngineColor;
143    type Voice = ();
144    type Settings = ();
145
146    fn lang(&self) -> &Lang {
147        &self.lang
148    }
149
150    fn keymap_def(&self) -> &KeymapDef<A> {
151        &self.keymap
152    }
153
154    fn audio_manifest(&self) -> &AudioManifest {
155        &self.audio
156    }
157
158    fn palette_def(&self) -> &PaletteDef {
159        &self.palette
160    }
161
162    fn console(&self) -> ConsoleSpec {
163        self.console
164    }
165}
166
167#[cfg(test)]
168mod tests;