Skip to main content

yog_book/renderer/
overlay.rs

1//! What's drawn around the widgets: sprites from the sheets before them,
2//! and after them what yog-ui doesn't draw — items, blocks, entities, the
3//! game font's text, mods' pictures.
4
5use yog_gfx::{Color, GfxContext, Rect, Vec2};
6
7use super::geometry::{
8    BLOCK_PITCH, ENTITY_TURN, ENTITY_YAW, MULTIBLOCK_SWAY, MULTIBLOCK_YAW, SLOT_YAW,
9};
10use super::{CRAFTING_SHEET, recipe::normalize_item_id};
11
12#[derive(Clone, PartialEq)]
13pub(crate) enum SpriteSheet {
14    Book,
15    Crafting,
16    /// Any image source (a recipe layout's).
17    Image(String),
18}
19
20/// A part of a sheet drawn before yog-ui.
21#[derive(Clone)]
22pub(crate) struct BgSprite {
23    pub sheet: SpriteSheet,
24    /// Source rect in pixels on the sheet.
25    pub src: Rect,
26    /// Destination on screen.
27    pub dst: Rect,
28}
29
30/// Draws a mod's picture (`BookPage::Drawn`): `(ctx, drawer, data, [x, y, w, h])`.
31pub type PageDrawer<'a> = &'a mut dyn FnMut(&GfxContext, &str, &yog_data::Value, [f32; 4]);
32
33/// Something drawn over the widgets, in its box on screen.
34#[derive(Clone)]
35pub(crate) enum OverlayCmd {
36    /// A mod's picture: its drawer (`BookPage::Drawn`) draws `data` in the box.
37    Drawn {
38        drawer: String,
39        data: yog_data::Value,
40        at: Rect,
41    },
42    /// One of `items` at a time, a new one every second (an ingredient's
43    /// alternatives, its tags' items among them).
44    McItems { items: Vec<String>, at: Rect },
45    /// Blocks in 3D: slowly turning (a multiblock on show), else at a
46    /// slot's angle (a station).
47    Blocks {
48        blocks: Vec<((i32, i32, i32), String)>,
49        at: Rect,
50        turning: bool,
51    },
52    /// A living entity standing in the box.
53    Entity { entity: String, at: Rect },
54    /// The game font's text (the nameplate's title and subtitle).
55    /// Text in the game's font, at the book's scale.
56    McText {
57        text: String,
58        at: Vec2,
59        color: Color,
60        scale: f32,
61    },
62    /// An item's icon.
63    McItem { item_id: String, at: Rect },
64    /// A texture drawn whole (16×16) — an icon naming a texture rather
65    /// than an item, as Patchouli's `BookIcon.from` reads one.
66    McTexture { path: String, at: Rect },
67}
68
69impl OverlayCmd {
70    /// A book author's icon (a category's, an entry's): a `.png` is a
71    /// texture, anything else an item (Patchouli's `BookIcon.from`).
72    pub(crate) fn icon(icon: &str, at: Rect) -> OverlayCmd {
73        if icon.ends_with(yog_ids::res::PNG) {
74            OverlayCmd::McTexture {
75                path: icon.to_string(),
76                at,
77            }
78        } else {
79            OverlayCmd::McItem {
80                item_id: icon.to_string(),
81                at,
82            }
83        }
84    }
85
86    /// It moves by itself: the host should keep redrawing.
87    pub(crate) fn animated(&self) -> bool {
88        match self {
89            OverlayCmd::Entity { .. } | OverlayCmd::Blocks { .. } => true,
90            OverlayCmd::McItems { items, .. } => items.iter().filter(|i| !is_tag(i)).count() > 1,
91            _ => false,
92        }
93    }
94}
95
96/// An ingredient's tag (`#ns:path`), not an item.
97fn is_tag(item: &str) -> bool {
98    item.starts_with(yog_ids::sep::TAG_SHOWN)
99}
100
101/// The sprites, onto `book_sheet` / the crafting sheet / their own images.
102pub(crate) fn draw_sprites(ctx: &GfxContext, sprites: &[BgSprite], book_sheet: &str) {
103    let d2 = ctx.draw2d();
104    for spr in sprites {
105        let sheet = match &spr.sheet {
106            SpriteSheet::Book => book_sheet,
107            SpriteSheet::Crafting => CRAFTING_SHEET,
108            SpriteSheet::Image(source) => source.as_str(),
109        };
110        d2.image_region(sheet, spr.dst, Some(spr.src), Color::WHITE);
111    }
112}
113
114/// The overlays drawn in 3D or by mods, `t` seconds into the book being open.
115pub(crate) fn draw_scenes(
116    ctx: &GfxContext,
117    overlays: &[OverlayCmd],
118    t: f32,
119    draw: super::PageDrawer<'_>,
120) {
121    let d2 = ctx.draw2d();
122    for ov in overlays {
123        match ov {
124            OverlayCmd::Drawn { drawer, data, at } => {
125                draw(ctx, drawer, data, [at.x, at.y, at.w, at.h])
126            }
127            OverlayCmd::Entity { entity, at } => {
128                d2.entity(entity, *at, ENTITY_YAW + t * ENTITY_TURN, 0.0)
129            }
130            OverlayCmd::Blocks {
131                blocks,
132                at,
133                turning,
134            } => {
135                let list: Vec<((i32, i32, i32), &str)> =
136                    blocks.iter().map(|(p, b)| (*p, b.as_str())).collect();
137                let yaw = if *turning {
138                    MULTIBLOCK_YAW + t * MULTIBLOCK_SWAY
139                } else {
140                    SLOT_YAW
141                };
142                d2.blocks(&list, *at, yaw, BLOCK_PITCH);
143            }
144            _ => {}
145        }
146    }
147}
148
149/// The item stacks and the game font's text — last: item rendering changes
150/// GL state. An ingredient's alternatives turn every `second`.
151pub(crate) fn draw_items(ctx: &GfxContext, overlays: &[OverlayCmd], second: usize) {
152    let d2 = ctx.draw2d();
153    for ov in overlays {
154        match ov {
155            OverlayCmd::McItems { items, at } => {
156                let shown: Vec<&String> = items.iter().filter(|i| !is_tag(i)).collect();
157                if !shown.is_empty() {
158                    let item = shown[second % shown.len()];
159                    d2.item(
160                        &normalize_item_id(item),
161                        Vec2::new(at.x, at.y),
162                        at.w.min(at.h),
163                    );
164                }
165            }
166            OverlayCmd::McItem { item_id, at } => d2.item(
167                &normalize_item_id(item_id),
168                Vec2::new(at.x, at.y),
169                at.w.min(at.h),
170            ),
171            OverlayCmd::McText {
172                text,
173                at,
174                color,
175                scale,
176            } => d2.text_scaled(
177                text,
178                *at,
179                &yog_gfx::draw2d::TextStyle {
180                    font: "",
181                    scale: *scale,
182                    color: *color,
183                    shadow: false,
184                },
185            ),
186            // A texture of its own, not an atlas region: drawn whole into the box.
187            OverlayCmd::McTexture { path, at } => d2.image(path, *at),
188            _ => {}
189        }
190    }
191}