diff --git a/agb/examples/object_text_render.rs b/agb/examples/object_text_render.rs index 8bc1729f..e05c0041 100644 --- a/agb/examples/object_text_render.rs +++ b/agb/examples/object_text_render.rs @@ -4,7 +4,7 @@ use agb::{ display::{ object::{ - font::{ObjectTextRender, TextAlignment}, + font::{ChangeColour, ObjectTextRender, TextAlignment}, PaletteVram, Size, }, palette16::Palette16, @@ -30,14 +30,17 @@ fn main(mut gba: agb::Gba) -> ! { loop { let mut palette = [0x0; 16]; palette[1] = 0xFF_FF; + palette[2] = 0x00_FF; let palette = Palette16::new(palette); let palette = PaletteVram::new(&palette).unwrap(); let mut wr = ObjectTextRender::new(&FONT, Size::S16x16, palette); + let player_name = "You"; let _ = writeln!( wr, - "Woah! Hey there! I have a bunch of text I want to show you. However, you will find that the amount of text I can display is limited. Who'd have thought! Good thing that my text system supports scrolling! It only took around 20 jank versions to get here!" - + "Woah!{change2} {player_name}! {change1}Hey there! I have a bunch of text I want to show you. However, you will find that the amount of text I can display is limited. Who'd have thought! Good thing that my text system supports scrolling! It only took around 20 jank versions to get here!", + change2 = ChangeColour::new(2), + change1 = ChangeColour::new(1), ); let vblank = agb::interrupt::VBlank::get(); @@ -64,9 +67,7 @@ fn main(mut gba: agb::Gba) -> ! { if frame % 4 == 0 { line_done = !wr.next_letter_group(); } - if line_done - && input.is_just_pressed(Button::A) - { + if line_done && input.is_just_pressed(Button::A) { line_done = false; wr.pop_line(); } diff --git a/agb/src/display/object/font.rs b/agb/src/display/object/font.rs index 9cea9054..417e71ea 100644 --- a/agb/src/display/object/font.rs +++ b/agb/src/display/object/font.rs @@ -1,4 +1,4 @@ -use core::fmt::Write; +use core::fmt::{Display, Write}; use agb_fixnum::Vector2D; use alloc::{collections::VecDeque, vec::Vec}; @@ -97,6 +97,37 @@ fn is_private_use(c: char) -> bool { ('\u{E000}'..'\u{F8FF}').contains(&c) } +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct ChangeColour(u8); + +impl ChangeColour { + #[must_use] + pub fn new(colour: u32) -> Self { + assert!(colour < 16, "paletted colour must be valid (0..=15)"); + + Self(colour as u8) + } + + fn try_from_char(c: char) -> Option { + let c = c as u32; + if c >= 0xE000 && c < 0xE000 + 16 { + Some(ChangeColour::new(c - 0xE000)) + } else { + None + } + } + + fn to_char(self) -> char { + char::from_u32(self.0 as u32 + 0xE000).unwrap() + } +} + +impl Display for ChangeColour { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_char(self.to_char()) + } +} + impl BufferedRender<'_> { fn input_character(&mut self, character: char) { if !is_private_use(character) { diff --git a/agb/src/display/object/font/preprocess.rs b/agb/src/display/object/font/preprocess.rs index ca1e95e5..9955bf51 100644 --- a/agb/src/display/object/font/preprocess.rs +++ b/agb/src/display/object/font/preprocess.rs @@ -1,6 +1,4 @@ -use core::num::NonZeroU8; - -use alloc::{collections::VecDeque, vec::Vec}; +use alloc::collections::VecDeque; use crate::display::Font; diff --git a/agb/src/display/object/font/renderer.rs b/agb/src/display/object/font/renderer.rs index d08ee8e0..b59b6549 100644 --- a/agb/src/display/object/font/renderer.rs +++ b/agb/src/display/object/font/renderer.rs @@ -3,6 +3,8 @@ use crate::display::{ Font, }; +use super::ChangeColour; + struct WorkingLetter { dynamic: DynamicSprite, // where to render the letter from x_min to x_max @@ -73,6 +75,11 @@ impl WordRender { #[must_use] pub(crate) fn render_char(&mut self, font: &Font, c: char) -> Option { + if let Some(next_colour) = ChangeColour::try_from_char(c) { + self.colour = next_colour.0 as usize; + return None; + } + let font_letter: &crate::display::FontLetter = font.letter(c); // uses more than the sprite can hold