1use std::marker::PhantomData;
2
3use dotzuki_engine::palette::{PaletteProvider, PaletteTrait, SgbColor, SgbPaletteEntry};
4use dotzuki_engine::render::Rgba;
5
6pub trait ColorIndex: Copy + Clone + core::fmt::Debug + PartialEq + Eq {
15 const MAX: usize;
17
18 fn from_u8(val: u8) -> Self;
20
21 fn to_index(self) -> usize;
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30#[repr(u8)]
31pub enum GbColor {
32 White = 0,
33 LightGray = 1,
34 DarkGray = 2,
35 Black = 3,
36}
37
38impl ColorIndex for GbColor {
39 const MAX: usize = 4;
40
41 fn from_u8(val: u8) -> Self {
42 match val & 0x03 {
43 0 => GbColor::White,
44 1 => GbColor::LightGray,
45 2 => GbColor::DarkGray,
46 _ => GbColor::Black,
47 }
48 }
49
50 fn to_index(self) -> usize {
51 self as usize
52 }
53}
54
55impl GbColor {
56 pub const ALL: [GbColor; 4] = [
57 GbColor::White,
58 GbColor::LightGray,
59 GbColor::DarkGray,
60 GbColor::Black,
61 ];
62
63 pub fn from_u8(val: u8) -> Self {
64 <Self as ColorIndex>::from_u8(val)
65 }
66}
67
68#[derive(Debug, Clone, Copy, PartialEq, Eq)]
74pub struct GbaColor(pub u8);
75
76impl ColorIndex for GbaColor {
77 const MAX: usize = 16;
78
79 fn from_u8(val: u8) -> Self {
80 GbaColor(val & 0x0F)
81 }
82
83 fn to_index(self) -> usize {
84 self.0 as usize
85 }
86}
87
88#[derive(Debug, Clone, Copy, PartialEq, Eq)]
101pub struct Palette<C: ColorIndex = GbColor> {
102 pub colors: [Rgba; 16],
104 pub count: u8,
106 #[doc(hidden)]
107 pub _phantom: PhantomData<C>,
108}
109
110impl<C: ColorIndex> Palette<C> {
111 pub fn new(colors: &[Rgba]) -> Self {
115 let mut arr = [Rgba::BLACK; 16];
116 let count = colors.len().min(16);
117 arr[..count].copy_from_slice(&colors[..count]);
118 Self {
119 colors: arr,
120 count: count as u8,
121 _phantom: PhantomData,
122 }
123 }
124
125 pub fn color(&self, index: C) -> Rgba {
127 self.colors[index.to_index()]
128 }
129}
130
131impl Palette<GbColor> {
132 pub fn from_bgp_register(bgp: u8, base_palette: &Self) -> Self {
137 let mut arr = [Rgba::BLACK; 16];
138 for i in 0..4 {
139 let shade = (bgp >> (i * 2)) & 0x03;
140 arr[i] = base_palette.colors[shade as usize];
141 }
142 Self {
143 colors: arr,
144 count: 4,
145 _phantom: PhantomData,
146 }
147 }
148}
149
150impl Palette<GbaColor> {
151 pub fn from_gba_palette(colors: [Rgba; 16]) -> Self {
153 Self {
154 colors,
155 count: 16,
156 _phantom: PhantomData,
157 }
158 }
159}
160
161pub const DMG_PALETTE: Palette = Palette {
166 colors: [
167 Rgba::rgb(0x9B, 0xBC, 0x0F), Rgba::rgb(0x8B, 0xAC, 0x0F), Rgba::rgb(0x30, 0x62, 0x30), Rgba::rgb(0x0F, 0x38, 0x0F), Rgba::BLACK, Rgba::BLACK, Rgba::BLACK, Rgba::BLACK,
172 Rgba::BLACK, Rgba::BLACK, Rgba::BLACK, Rgba::BLACK,
173 Rgba::BLACK, Rgba::BLACK, Rgba::BLACK, Rgba::BLACK,
174 ],
175 count: 4,
176 _phantom: PhantomData,
177};
178
179pub const GRAYSCALE_PALETTE: Palette = Palette {
180 colors: [
181 Rgba::rgb(0xFF, 0xFF, 0xFF),
182 Rgba::rgb(0xAA, 0xAA, 0xAA),
183 Rgba::rgb(0x55, 0x55, 0x55),
184 Rgba::rgb(0x00, 0x00, 0x00),
185 Rgba::BLACK, Rgba::BLACK, Rgba::BLACK, Rgba::BLACK,
186 Rgba::BLACK, Rgba::BLACK, Rgba::BLACK, Rgba::BLACK,
187 Rgba::BLACK, Rgba::BLACK, Rgba::BLACK, Rgba::BLACK,
188 ],
189 count: 4,
190 _phantom: PhantomData,
191};
192
193pub const GRAYSCALE_SPRITE_PALETTE: Palette = Palette {
194 colors: [
195 Rgba::TRANSPARENT,
196 Rgba::rgb(0xAA, 0xAA, 0xAA),
197 Rgba::rgb(0x55, 0x55, 0x55),
198 Rgba::rgb(0x00, 0x00, 0x00),
199 Rgba::BLACK, Rgba::BLACK, Rgba::BLACK, Rgba::BLACK,
200 Rgba::BLACK, Rgba::BLACK, Rgba::BLACK, Rgba::BLACK,
201 Rgba::BLACK, Rgba::BLACK, Rgba::BLACK, Rgba::BLACK,
202 ],
203 count: 4,
204 _phantom: PhantomData,
205};
206
207pub const POCKET_SPRITE_PALETTE: Palette = Palette {
208 colors: [
209 Rgba::TRANSPARENT,
210 Rgba::rgb(0x8B, 0x95, 0x6D),
211 Rgba::rgb(0x4D, 0x53, 0x3C),
212 Rgba::rgb(0x1F, 0x1F, 0x1F),
213 Rgba::BLACK, Rgba::BLACK, Rgba::BLACK, Rgba::BLACK,
214 Rgba::BLACK, Rgba::BLACK, Rgba::BLACK, Rgba::BLACK,
215 Rgba::BLACK, Rgba::BLACK, Rgba::BLACK, Rgba::BLACK,
216 ],
217 count: 4,
218 _phantom: PhantomData,
219};
220
221pub const POCKET_PALETTE: Palette = Palette {
222 colors: [
223 Rgba::rgb(0xC4, 0xCF, 0xA1), Rgba::rgb(0x8B, 0x95, 0x6D), Rgba::rgb(0x4D, 0x53, 0x3C), Rgba::rgb(0x1F, 0x1F, 0x1F), Rgba::BLACK, Rgba::BLACK, Rgba::BLACK, Rgba::BLACK,
228 Rgba::BLACK, Rgba::BLACK, Rgba::BLACK, Rgba::BLACK,
229 Rgba::BLACK, Rgba::BLACK, Rgba::BLACK, Rgba::BLACK,
230 ],
231 count: 4,
232 _phantom: PhantomData,
233};
234
235pub const HP_BAR_GREEN_PALETTE: Palette = Palette {
236 colors: [
237 Rgba::rgb(0xFF, 0xFF, 0xFF),
238 Rgba::rgb(0xAA, 0xAA, 0xAA),
239 Rgba::rgb(0x00, 0xC8, 0x00),
240 Rgba::rgb(0x00, 0x00, 0x00),
241 Rgba::BLACK, Rgba::BLACK, Rgba::BLACK, Rgba::BLACK,
242 Rgba::BLACK, Rgba::BLACK, Rgba::BLACK, Rgba::BLACK,
243 Rgba::BLACK, Rgba::BLACK, Rgba::BLACK, Rgba::BLACK,
244 ],
245 count: 4,
246 _phantom: PhantomData,
247};
248
249pub const HP_BAR_YELLOW_PALETTE: Palette = Palette {
250 colors: [
251 Rgba::rgb(0xFF, 0xFF, 0xFF),
252 Rgba::rgb(0xAA, 0xAA, 0xAA),
253 Rgba::rgb(0xE8, 0xA8, 0x00),
254 Rgba::rgb(0x00, 0x00, 0x00),
255 Rgba::BLACK, Rgba::BLACK, Rgba::BLACK, Rgba::BLACK,
256 Rgba::BLACK, Rgba::BLACK, Rgba::BLACK, Rgba::BLACK,
257 Rgba::BLACK, Rgba::BLACK, Rgba::BLACK, Rgba::BLACK,
258 ],
259 count: 4,
260 _phantom: PhantomData,
261};
262
263pub const HP_BAR_RED_PALETTE: Palette = Palette {
264 colors: [
265 Rgba::rgb(0xFF, 0xFF, 0xFF),
266 Rgba::rgb(0xAA, 0xAA, 0xAA),
267 Rgba::rgb(0xD8, 0x20, 0x00),
268 Rgba::rgb(0x00, 0x00, 0x00),
269 Rgba::BLACK, Rgba::BLACK, Rgba::BLACK, Rgba::BLACK,
270 Rgba::BLACK, Rgba::BLACK, Rgba::BLACK, Rgba::BLACK,
271 Rgba::BLACK, Rgba::BLACK, Rgba::BLACK, Rgba::BLACK,
272 ],
273 count: 4,
274 _phantom: PhantomData,
275};
276
277pub const DEFAULT_BGP: u8 = 0b11100100; pub const DEFAULT_OBP0: u8 = 0b11010000; pub const DEFAULT_OBP1: u8 = 0b11100100; #[derive(Debug, Clone)]
286pub struct PaletteState {
287 pub base: Palette,
288 pub bgp: u8,
289 pub obp0: u8,
290 pub obp1: u8,
291}
292
293impl PaletteState {
294 pub fn new(base: Palette) -> Self {
295 Self {
296 base,
297 bgp: DEFAULT_BGP,
298 obp0: DEFAULT_OBP0,
299 obp1: DEFAULT_OBP1,
300 }
301 }
302
303 pub fn bg_palette(&self) -> Palette {
304 Palette::from_bgp_register(self.bgp, &self.base)
305 }
306
307 pub fn obj_palette0(&self) -> Palette {
308 let mut pal = Palette::from_bgp_register(self.obp0, &self.base);
309 pal.colors[0] = Rgba::TRANSPARENT;
310 pal
311 }
312
313 pub fn obj_palette1(&self) -> Palette {
314 let mut pal = Palette::from_bgp_register(self.obp1, &self.base);
315 pal.colors[0] = Rgba::TRANSPARENT;
316 pal
317 }
318
319 pub fn white_out(&mut self) {
320 self.bgp = 0x00;
321 self.obp0 = 0x00;
322 self.obp1 = 0x00;
323 }
324
325 pub fn reset_normal(&mut self) {
326 self.bgp = DEFAULT_BGP;
327 self.obp0 = DEFAULT_OBP0;
328 }
329}
330
331impl Default for PaletteState {
332 fn default() -> Self {
333 Self::new(DMG_PALETTE)
334 }
335}
336
337#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
347#[repr(u8)]
348pub enum SgbPaletteId {
349 Route = 0x00,
350 HomeTown = 0x01,
351 Viridian = 0x02,
352 Pewter = 0x03,
353 Cerulean = 0x04,
354 Lavender = 0x05,
355 Vermilion = 0x06,
356 Celadon = 0x07,
357 Fuchsia = 0x08,
358 Cinnabar = 0x09,
359 Indigo = 0x0A,
360 Saffron = 0x0B,
361 TownMap = 0x0C,
362 Logo1 = 0x0D,
363 Logo2 = 0x0E,
364 Pal0F = 0x0F,
365 PaleMon = 0x10,
366 BlueMon = 0x11,
367 RedMon = 0x12,
368 CyanMon = 0x13,
369 PurpleMon = 0x14,
370 BrownMon = 0x15,
371 GreenMon = 0x16,
372 PinkMon = 0x17,
373 YellowMon = 0x18,
374 GrayMon = 0x19,
375 Slots1 = 0x1A,
376 Slots2 = 0x1B,
377 Slots3 = 0x1C,
378 Slots4 = 0x1D,
379 Black = 0x1E,
380 GreenBar = 0x1F,
381 YellowBar = 0x20,
382 RedBar = 0x21,
383 Badge = 0x22,
384 Cave = 0x23,
385 CompanyLogo = 0x24,
386}
387
388impl PaletteTrait for SgbPaletteId {}
389
390pub const NUM_SGB_PALS: usize = 0x25;
392
393impl SgbPaletteId {
394 pub fn from_u8(val: u8) -> Option<Self> {
395 if val < NUM_SGB_PALS as u8 {
396 Some(unsafe { core::mem::transmute(val) })
397 } else {
398 None
399 }
400 }
401
402 pub fn as_u8(self) -> u8 {
403 self as u8
404 }
405}
406
407#[derive(Debug, Clone, Copy, PartialEq, Eq)]
409#[repr(u8)]
410pub enum SetPalCommand {
411 BattleBlack = 0x00,
412 Battle = 0x01,
413 TownMap = 0x02,
414 StatusScreen = 0x03,
415 Dex = 0x04,
416 Slots = 0x05,
417 TitleScreen = 0x06,
418 MonsterIntro = 0x07,
419 Generic = 0x08,
420 Overworld = 0x09,
421 PartyMenu = 0x0A,
422 WholeScreen = 0x0B,
423 CompanyLogoIntro = 0x0C,
424 TrainerCard = 0x0D,
425}
426
427pub const SET_PAL_PARTY_MENU_HP_BARS: u8 = 0xFC;
429pub const SET_PAL_DEFAULT: u8 = 0xFF;
431
432impl SetPalCommand {
433 pub fn from_u8(val: u8) -> Option<Self> {
434 match val {
435 0x00 => Some(Self::BattleBlack),
436 0x01 => Some(Self::Battle),
437 0x02 => Some(Self::TownMap),
438 0x03 => Some(Self::StatusScreen),
439 0x04 => Some(Self::Dex),
440 0x05 => Some(Self::Slots),
441 0x06 => Some(Self::TitleScreen),
442 0x07 => Some(Self::MonsterIntro),
443 0x08 => Some(Self::Generic),
444 0x09 => Some(Self::Overworld),
445 0x0A => Some(Self::PartyMenu),
446 0x0B => Some(Self::WholeScreen),
447 0x0C => Some(Self::CompanyLogoIntro),
448 0x0D => Some(Self::TrainerCard),
449 _ => None,
450 }
451 }
452
453 pub fn as_u8(self) -> u8 {
454 self as u8
455 }
456}
457
458pub fn sgb_color_to_rgba(color: &SgbColor) -> Rgba {
465 let r8 = (color.r << 3) | (color.r >> 2);
466 let g8 = (color.g << 3) | (color.g >> 2);
467 let b8 = (color.b << 3) | (color.b >> 2);
468 Rgba::rgb(r8, g8, b8)
469}
470
471pub fn sgb_entry_to_palette(entry: &SgbPaletteEntry) -> Palette {
473 Palette::new(&[
474 sgb_color_to_rgba(&entry[0]),
475 sgb_color_to_rgba(&entry[1]),
476 sgb_color_to_rgba(&entry[2]),
477 sgb_color_to_rgba(&entry[3]),
478 ])
479}
480
481#[derive(Debug, Clone, Copy, PartialEq, Eq)]
491pub enum PaletteMode {
492 Dmg,
493 Sgb,
494 Grayscale,
495 Pocket,
496}
497
498pub struct ColorPaletteState<'a, P: PaletteTrait = SgbPaletteId> {
505 pub dmg: PaletteState,
507 pub mode: PaletteMode,
509 pub is_red: bool,
511 pub palette_provider: &'a dyn PaletteProvider<P>,
513 pub sgb_bg_palette: P,
515 pub sgb_obj0_palette: P,
517 pub sgb_obj1_palette: P,
519 pub default_command: SetPalCommand,
521}
522
523impl<'a, P: PaletteTrait> ColorPaletteState<'a, P> {
524 pub fn new(
525 mode: PaletteMode,
526 is_red: bool,
527 palette_provider: &'a dyn PaletteProvider<P>,
528 bg: P,
529 obj0: P,
530 obj1: P,
531 ) -> Self {
532 Self {
533 dmg: PaletteState::default(),
534 mode,
535 is_red,
536 palette_provider,
537 sgb_bg_palette: bg,
538 sgb_obj0_palette: obj0,
539 sgb_obj1_palette: obj1,
540 default_command: SetPalCommand::Generic,
541 }
542 }
543
544 pub fn bg_palette(&self) -> Palette {
546 match self.mode {
547 PaletteMode::Sgb => {
548 sgb_entry_to_palette(&self.palette_provider.sgb_palette_data(
549 self.sgb_bg_palette,
550 self.is_red,
551 ))
552 }
553 PaletteMode::Dmg => self.dmg.bg_palette(),
554 PaletteMode::Grayscale => {
555 let mut state = self.dmg.clone();
556 state.base = GRAYSCALE_PALETTE;
557 state.bg_palette()
558 }
559 PaletteMode::Pocket => {
560 let mut state = self.dmg.clone();
561 state.base = POCKET_PALETTE;
562 state.bg_palette()
563 }
564 }
565 }
566
567 pub fn obj_palette0(&self) -> Palette {
569 match self.mode {
570 PaletteMode::Sgb => {
571 sgb_entry_to_palette(&self.palette_provider.sgb_palette_data(
572 self.sgb_obj0_palette,
573 self.is_red,
574 ))
575 }
576 PaletteMode::Dmg => self.dmg.obj_palette0(),
577 PaletteMode::Grayscale => {
578 let mut state = self.dmg.clone();
579 state.base = GRAYSCALE_PALETTE;
580 state.obj_palette0()
581 }
582 PaletteMode::Pocket => {
583 let mut state = self.dmg.clone();
584 state.base = POCKET_PALETTE;
585 state.obj_palette0()
586 }
587 }
588 }
589
590 pub fn obj_palette1(&self) -> Palette {
592 match self.mode {
593 PaletteMode::Sgb => {
594 sgb_entry_to_palette(&self.palette_provider.sgb_palette_data(
595 self.sgb_obj1_palette,
596 self.is_red,
597 ))
598 }
599 PaletteMode::Dmg => self.dmg.obj_palette1(),
600 PaletteMode::Grayscale => {
601 let mut state = self.dmg.clone();
602 state.base = GRAYSCALE_PALETTE;
603 state.obj_palette1()
604 }
605 PaletteMode::Pocket => {
606 let mut state = self.dmg.clone();
607 state.base = POCKET_PALETTE;
608 state.obj_palette1()
609 }
610 }
611 }
612
613 pub fn set_overworld_palette(&mut self, tileset: u8, map_id: u8, last_map: u8) {
615 let pal = self
616 .palette_provider
617 .overworld_palette_for(tileset, map_id, last_map);
618 self.sgb_bg_palette = pal;
619 self.default_command = SetPalCommand::Overworld;
620 }
621
622 pub fn set_battle_palette(&mut self, player_pal: P, enemy_pal: P, player_hp_bar_color: u8) {
628 self.sgb_bg_palette = self.palette_provider.hp_bar_to_palette_id(player_hp_bar_color);
635 self.sgb_obj0_palette = player_pal;
636 self.sgb_obj1_palette = enemy_pal;
637 self.default_command = SetPalCommand::Battle;
638 }
639}
640
641impl<'a> ColorPaletteState<'a, SgbPaletteId> {
642 pub fn new_classic(
645 mode: PaletteMode,
646 is_red: bool,
647 palette_provider: &'a dyn PaletteProvider<SgbPaletteId>,
648 ) -> Self {
649 Self::new(
650 mode,
651 is_red,
652 palette_provider,
653 SgbPaletteId::Route,
654 SgbPaletteId::Route,
655 SgbPaletteId::Route,
656 )
657 }
658}
659
660#[cfg(test)]
661mod tests {
662 use super::*;
663
664 #[test]
665 fn gb_color_from_u8() {
666 assert_eq!(GbColor::from_u8(0), GbColor::White);
667 assert_eq!(GbColor::from_u8(1), GbColor::LightGray);
668 assert_eq!(GbColor::from_u8(2), GbColor::DarkGray);
669 assert_eq!(GbColor::from_u8(3), GbColor::Black);
670 assert_eq!(GbColor::from_u8(4), GbColor::White);
672 assert_eq!(GbColor::from_u8(7), GbColor::Black);
673 }
674
675 #[test]
676 fn gb_color_to_index() {
677 assert_eq!(GbColor::White.to_index(), 0);
678 assert_eq!(GbColor::LightGray.to_index(), 1);
679 assert_eq!(GbColor::DarkGray.to_index(), 2);
680 assert_eq!(GbColor::Black.to_index(), 3);
681 }
682
683 #[test]
684 fn gba_color_from_u8() {
685 assert_eq!(GbaColor::from_u8(0).0, 0);
686 assert_eq!(GbaColor::from_u8(15).0, 15);
687 assert_eq!(GbaColor::from_u8(16).0, 0);
689 assert_eq!(GbaColor::from_u8(31).0, 15);
690 assert_eq!(GbaColor::from_u8(0xFF).0, 15);
691 }
692
693 #[test]
694 fn gba_color_to_index() {
695 assert_eq!(GbaColor(0).to_index(), 0);
696 assert_eq!(GbaColor(7).to_index(), 7);
697 assert_eq!(GbaColor(15).to_index(), 15);
698 }
699
700 #[test]
701 fn palette_color_lookup() {
702 let pal = DMG_PALETTE;
703 assert_eq!(pal.color(GbColor::White), Rgba::rgb(0x9B, 0xBC, 0x0F));
704 assert_eq!(pal.color(GbColor::Black), Rgba::rgb(0x0F, 0x38, 0x0F));
705 }
706
707 #[test]
708 fn palette_new_from_slice() {
709 let colors = [
710 Rgba::rgb(0xFF, 0x00, 0x00),
711 Rgba::rgb(0x00, 0xFF, 0x00),
712 Rgba::rgb(0x00, 0x00, 0xFF),
713 Rgba::TRANSPARENT,
714 ];
715 let pal: Palette = Palette::new(&colors);
716 assert_eq!(pal.count, 4);
717 assert_eq!(pal.color(GbColor::White), Rgba::rgb(0xFF, 0x00, 0x00));
718 assert_eq!(pal.color(GbColor::LightGray), Rgba::rgb(0x00, 0xFF, 0x00));
719 assert_eq!(pal.color(GbColor::DarkGray), Rgba::rgb(0x00, 0x00, 0xFF));
720 assert_eq!(pal.color(GbColor::Black), Rgba::TRANSPARENT);
721 }
722
723 #[test]
724 fn palette_from_bgp_register() {
725 let base = GRAYSCALE_PALETTE;
726 let pal = Palette::from_bgp_register(DEFAULT_BGP, &base);
728 assert_eq!(pal.color(GbColor::White), Rgba::rgb(0xFF, 0xFF, 0xFF));
729 assert_eq!(pal.color(GbColor::LightGray), Rgba::rgb(0xAA, 0xAA, 0xAA));
730 assert_eq!(pal.color(GbColor::DarkGray), Rgba::rgb(0x55, 0x55, 0x55));
731 assert_eq!(pal.color(GbColor::Black), Rgba::rgb(0x00, 0x00, 0x00));
732
733 let pal = Palette::from_bgp_register(0x00, &base);
735 assert_eq!(pal.color(GbColor::White), Rgba::rgb(0xFF, 0xFF, 0xFF));
736 assert_eq!(pal.color(GbColor::Black), Rgba::rgb(0xFF, 0xFF, 0xFF));
737 }
738
739 #[test]
740 fn gba_palette_from_array() {
741 let mut colors = [Rgba::BLACK; 16];
742 colors[0] = Rgba::rgb(0xFF, 0x00, 0x00);
743 colors[15] = Rgba::rgb(0x00, 0x00, 0xFF);
744 let pal = Palette::<GbaColor>::from_gba_palette(colors);
745 assert_eq!(pal.count, 16);
746 assert_eq!(pal.color(GbaColor(0)), Rgba::rgb(0xFF, 0x00, 0x00));
747 assert_eq!(pal.color(GbaColor(15)), Rgba::rgb(0x00, 0x00, 0xFF));
748 }
749
750 #[test]
751 fn palette_state_defaults() {
752 let state = PaletteState::default();
753 assert_eq!(state.bgp, DEFAULT_BGP);
754 assert_eq!(state.obp0, DEFAULT_OBP0);
755 assert_eq!(state.obp1, DEFAULT_OBP1);
756 }
757
758 #[test]
759 fn palette_state_white_out() {
760 let mut state = PaletteState::default();
761 state.white_out();
762 assert_eq!(state.bgp, 0x00);
763 assert_eq!(state.obp0, 0x00);
764 }
765
766 #[test]
767 fn dmg_palette_const_is_valid() {
768 assert_eq!(DMG_PALETTE.count, 4);
769 assert_eq!(DMG_PALETTE.color(GbColor::White), Rgba::rgb(0x9B, 0xBC, 0x0F));
770 }
771
772 #[test]
773 fn grayscale_palette_const_is_valid() {
774 assert_eq!(GRAYSCALE_PALETTE.count, 4);
775 assert_eq!(GRAYSCALE_PALETTE.color(GbColor::White), Rgba::rgb(0xFF, 0xFF, 0xFF));
776 }
777
778 #[test]
779 fn palette_copy_and_eq() {
780 let a = DMG_PALETTE;
781 let b = a; assert_eq!(a, b);
783 }
784
785 #[test]
786 fn color_palette_state_is_generic_over_custom_palette() {
787 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
790 enum TestPalette {
791 Field,
792 HpBar,
793 }
794 impl PaletteTrait for TestPalette {}
795
796 struct TestProvider;
797 impl PaletteProvider<TestPalette> for TestProvider {
798 fn bg_palette(&self, _: TestPalette) -> [u8; 4] {
799 [0; 4]
800 }
801 fn obj_palette0(&self, _: TestPalette) -> [u8; 4] {
802 [0; 4]
803 }
804 fn obj_palette1(&self, _: TestPalette) -> [u8; 4] {
805 [0; 4]
806 }
807 fn overworld_palette_for(&self, _: u8, _: u8, _: u8) -> TestPalette {
808 TestPalette::Field
809 }
810 fn monster_palette(&self, _: u8) -> TestPalette {
811 TestPalette::Field
812 }
813 fn hp_bar_to_palette_id(&self, _: u8) -> TestPalette {
814 TestPalette::HpBar
815 }
816 }
817
818 let mut state = ColorPaletteState::new(
819 PaletteMode::Sgb,
820 true,
821 &TestProvider,
822 TestPalette::Field,
823 TestPalette::Field,
824 TestPalette::Field,
825 );
826 state.set_overworld_palette(1, 2, 0);
827 assert_eq!(state.sgb_bg_palette, TestPalette::Field);
828 assert_eq!(state.default_command, SetPalCommand::Overworld);
829
830 state.set_battle_palette(TestPalette::Field, TestPalette::Field, 1);
831 assert_eq!(state.sgb_bg_palette, TestPalette::HpBar);
832 assert_eq!(state.default_command, SetPalCommand::Battle);
833
834 let pal = state.bg_palette();
836 assert_eq!(pal.color(GbColor::White), Rgba::rgb(0, 0, 0));
837 }
838}