1use crate::theme::{ColorRGBA, Theme};
6
7pub fn rgba_to_migui(c: ColorRGBA) -> migui::Color {
13 migui::Color {
14 r: c.r,
15 g: c.g,
16 b: c.b,
17 a: c.a,
18 }
19}
20
21pub fn rgba_to_sdl2(c: ColorRGBA) -> sdl2::pixels::Color {
23 sdl2::pixels::Color::RGBA(c.r, c.g, c.b, c.a)
24}
25
26pub fn draw_health_bar(
33 gui: &mut migui::Migui,
34 x: f32,
35 y: f32,
36 width: f32,
37 height: f32,
38 current: f32,
39 max: f32,
40 theme: &Theme,
41) {
42 let ratio = (current / max).clamp(0.0, 1.0);
43
44 gui.panel(
46 migui::WidgetId::new("hp_bg"),
47 migui::Rect::new(x, y, width, height),
48 rgba_to_migui(theme.health_bar_bg),
49 );
50
51 let fill_width = width * ratio;
53 if fill_width > 0.0 {
54 gui.panel(
55 migui::WidgetId::new("hp_fill"),
56 migui::Rect::new(x, y, fill_width, height),
57 rgba_to_migui(theme.health_bar_fill),
58 );
59 }
60
61 if theme.border_width > 0.0 {
63 gui.panel(
64 migui::WidgetId::new("hp_border"),
65 migui::Rect::new(x, y, width, height),
66 rgba_to_migui(ColorRGBA::transparent()),
67 );
68 }
69
70 let label = format!("HP: {}/{}", current as i32, max as i32);
72 gui.label(
73 migui::WidgetId::new("hp_label"),
74 &label,
75 migui::Rect::new(x + 4.0, y + 2.0, width - 8.0, height),
76 );
77}
78
79pub fn draw_mana_bar(
81 gui: &mut migui::Migui,
82 x: f32,
83 y: f32,
84 width: f32,
85 height: f32,
86 current: f32,
87 max: f32,
88 theme: &Theme,
89) {
90 let ratio = (current / max).clamp(0.0, 1.0);
91
92 gui.panel(
93 migui::WidgetId::new("mp_bg"),
94 migui::Rect::new(x, y, width, height),
95 rgba_to_migui(theme.mana_bar_bg),
96 );
97
98 let fill_width = width * ratio;
99 if fill_width > 0.0 {
100 gui.panel(
101 migui::WidgetId::new("mp_fill"),
102 migui::Rect::new(x, y, fill_width, height),
103 rgba_to_migui(theme.mana_bar_fill),
104 );
105 }
106
107 let label = format!("MP: {}/{}", current as i32, max as i32);
108 gui.label(
109 migui::WidgetId::new("mp_label"),
110 &label,
111 migui::Rect::new(x + 4.0, y + 2.0, width - 8.0, height),
112 );
113}
114
115pub fn draw_xp_bar(
117 gui: &mut migui::Migui,
118 x: f32,
119 y: f32,
120 width: f32,
121 height: f32,
122 current: f32,
123 max: f32,
124 theme: &Theme,
125) {
126 let ratio = (current / max).clamp(0.0, 1.0);
127
128 gui.panel(
129 migui::WidgetId::new("xp_bg"),
130 migui::Rect::new(x, y, width, height),
131 rgba_to_migui(theme.xp_bar_bg),
132 );
133
134 let fill_width = width * ratio;
135 if fill_width > 0.0 {
136 gui.panel(
137 migui::WidgetId::new("xp_fill"),
138 migui::Rect::new(x, y, fill_width, height),
139 rgba_to_migui(theme.xp_bar_fill),
140 );
141 }
142
143 let label = format!("XP: {}/{}", current as i32, max as i32);
144 gui.label(
145 migui::WidgetId::new("xp_label"),
146 &label,
147 migui::Rect::new(x + 4.0, y + 2.0, width - 8.0, height),
148 );
149}
150
151pub fn draw_score(
153 gui: &mut migui::Migui,
154 x: f32,
155 y: f32,
156 score: i64,
157 theme: &Theme,
158) {
159 let label = format!("Score: {}", score);
160 gui.label(
161 migui::WidgetId::new("score"),
162 &label,
163 migui::Rect::new(x, y, 200.0, theme.font_size as f32 + 8.0),
164 );
165}
166
167pub fn draw_gold(
169 gui: &mut migui::Migui,
170 x: f32,
171 y: f32,
172 gold: i64,
173 theme: &Theme,
174) {
175 let label = format!("🪙 {}", gold);
176 gui.label(
177 migui::WidgetId::new("gold"),
178 &label,
179 migui::Rect::new(x, y, 150.0, theme.font_size as f32 + 8.0),
180 );
181}
182
183pub fn draw_timer(
185 gui: &mut migui::Migui,
186 x: f32,
187 y: f32,
188 seconds: f32,
189 theme: &Theme,
190) {
191 let mins = (seconds / 60.0) as i32;
192 let secs = (seconds % 60.0) as i32;
193 let label = format!("{:02}:{:02}", mins, secs);
194 gui.label(
195 migui::WidgetId::new("timer"),
196 &label,
197 migui::Rect::new(x, y, 80.0, theme.font_size as f32 + 8.0),
198 );
199}
200
201pub fn draw_full_hud(
203 gui: &mut migui::Migui,
204 hp: f32,
205 max_hp: f32,
206 mp: f32,
207 max_mp: f32,
208 xp: f32,
209 max_xp: f32,
210 score: i64,
211 theme: &Theme,
212) {
213 let bar_w = 200.0;
214 let bar_h = 18.0;
215 let x = 20.0;
216 let mut y = 20.0;
217
218 draw_health_bar(gui, x, y, bar_w, bar_h, hp, max_hp, theme);
219 y += bar_h + 4.0;
220
221 draw_mana_bar(gui, x, y, bar_w, bar_h, mp, max_mp, theme);
222 y += bar_h + 4.0;
223
224 draw_xp_bar(gui, x, y, bar_w, bar_h, xp, max_xp, theme);
225
226 draw_score(gui, 600.0, 20.0, score, theme);
227}
228
229pub fn draw_main_menu(
235 gui: &mut migui::Migui,
236 title: &str,
237 options: &[&str],
238 theme: &Theme,
239 hover_state: &mut i32,
240) -> i32 {
241 let screen_w = 800.0;
242 let screen_h = 600.0;
243
244 gui.panel(
246 migui::WidgetId::new("menu_bg"),
247 migui::Rect::new(0.0, 0.0, screen_w, screen_h),
248 rgba_to_migui(theme.menu_bg),
249 );
250
251 gui.label(
253 migui::WidgetId::new("menu_title"),
254 title,
255 migui::Rect::new(
256 screen_w / 2.0 - 150.0,
257 80.0,
258 300.0,
259 theme.font_size_title as f32 + 10.0,
260 ),
261 );
262
263 let item_h = 40.0;
265 let total_h = options.len() as f32 * (item_h + theme.spacing);
266 let mut y = (screen_h - total_h) / 2.0;
267
268 for (i, option) in options.iter().enumerate() {
269 let id = format!("menu_opt_{}", i);
270 let is_hovered = *hover_state == i as i32;
271
272 let bg = if is_hovered {
273 rgba_to_migui(theme.menu_item_hover)
274 } else {
275 rgba_to_migui(theme.menu_item_bg)
276 };
277
278 gui.panel(
279 migui::WidgetId::new(&format!("{}_bg", id)),
280 migui::Rect::new(screen_w / 2.0 - 120.0, y, 240.0, item_h),
281 bg,
282 );
283
284 if gui.button(
285 migui::WidgetId::new(&id),
286 migui::Rect::new(screen_w / 2.0 - 110.0, y + 4.0, 220.0, item_h - 8.0),
287 option,
288 ) {
289 return i as i32;
290 }
291
292 y += item_h + theme.spacing;
293 }
294
295 -1
296}
297
298pub fn draw_pause_menu(
300 gui: &mut migui::Migui,
301 theme: &Theme,
302 hover_state: &mut i32,
303) -> i32 {
304 draw_main_menu(
305 gui,
306 "PAUSA",
307 &["Continuar", "Opciones", "Guardar", "Salir"],
308 theme,
309 hover_state,
310 )
311}
312
313pub fn draw_game_over(
315 gui: &mut migui::Migui,
316 score: i64,
317 theme: &Theme,
318 hover_state: &mut i32,
319) -> i32 {
320 draw_main_menu(
321 gui,
322 &format!("GAME OVER\nScore: {}", score),
323 &["Reiniciar", "Menú Principal"],
324 theme,
325 hover_state,
326 )
327}
328
329pub fn draw_options_menu(
331 gui: &mut migui::Migui,
332 theme: &Theme,
333 volume: &mut f32,
334 fullscreen: &mut bool,
335 hover_state: &mut i32,
336) {
337 let screen_w = 800.0;
338 let screen_h = 600.0;
339
340 gui.panel(
341 migui::WidgetId::new("options_bg"),
342 migui::Rect::new(0.0, 0.0, screen_w, screen_h),
343 rgba_to_migui(theme.menu_bg),
344 );
345
346 gui.label(
347 migui::WidgetId::new("options_title"),
348 "OPCIONES",
349 migui::Rect::new(
350 screen_w / 2.0 - 100.0,
351 40.0,
352 200.0,
353 theme.font_size_title as f32 + 10.0,
354 ),
355 );
356
357 gui.label(
359 migui::WidgetId::new("vol_label"),
360 &format!("Volumen: {:.0}%", *volume * 100.0),
361 migui::Rect::new(200.0, 150.0, 200.0, 30.0),
362 );
363
364 *volume = gui.slider(
365 migui::WidgetId::new("vol_slider"),
366 *volume,
367 0.0,
368 1.0,
369 migui::Rect::new(420.0, 150.0, 200.0, 30.0),
370 );
371
372 gui.checkbox(
374 migui::WidgetId::new("fs_check"),
375 "Pantalla Completa",
376 fullscreen,
377 migui::Rect::new(200.0, 200.0, 300.0, 30.0),
378 );
379
380 if gui.button(
382 migui::WidgetId::new("options_back"),
383 migui::Rect::new(screen_w / 2.0 - 60.0, 400.0, 120.0, 40.0),
384 "Volver",
385 ) {
386 *hover_state = 99;
387 }
388}
389
390pub fn draw_dialog(
393 gui: &mut migui::Migui,
394 npc_name: &str,
395 text: &str,
396 x: f32,
397 y: f32,
398 width: f32,
399 height: f32,
400 theme: &Theme,
401) {
402 gui.panel(
404 migui::WidgetId::new("dialog_bg"),
405 migui::Rect::new(x, y, width, height),
406 rgba_to_migui(theme.dialog_bg),
407 );
408
409 if theme.border_width > 0.0 {
411 gui.panel(
412 migui::WidgetId::new("dialog_border"),
413 migui::Rect::new(x, y, width, height),
414 rgba_to_migui(ColorRGBA::transparent()),
415 );
416 }
417
418 gui.label(
420 migui::WidgetId::new("dialog_name"),
421 npc_name,
422 migui::Rect::new(x + 10.0, y + 4.0, width - 20.0, theme.font_size_small as f32 + 6.0),
423 );
424
425 gui.label(
427 migui::WidgetId::new("dialog_text"),
428 text,
429 migui::Rect::new(
430 x + 10.0,
431 y + theme.font_size_small as f32 + 12.0,
432 width - 20.0,
433 height - theme.font_size_small as f32 - 20.0,
434 ),
435 );
436}
437
438pub fn draw_message_box(
440 gui: &mut migui::Migui,
441 title: &str,
442 message: &str,
443 buttons: &[&str],
444 theme: &Theme,
445) -> i32 {
446 let screen_w = 800.0;
447 let screen_h = 600.0;
448 let box_w = 400.0;
449 let box_h = 200.0;
450 let x = (screen_w - box_w) / 2.0;
451 let y = (screen_h - box_h) / 2.0;
452
453 gui.panel(
455 migui::WidgetId::new("msg_bg"),
456 migui::Rect::new(0.0, 0.0, screen_w, screen_h),
457 rgba_to_migui(ColorRGBA { r: 0, g: 0, b: 0, a: 128 }),
458 );
459
460 gui.panel(
461 migui::WidgetId::new("msg_box"),
462 migui::Rect::new(x, y, box_w, box_h),
463 rgba_to_migui(theme.dialog_bg),
464 );
465
466 gui.label(
468 migui::WidgetId::new("msg_title"),
469 title,
470 migui::Rect::new(x + 10.0, y + 8.0, box_w - 20.0, theme.font_size as f32 + 6.0),
471 );
472
473 gui.label(
475 migui::WidgetId::new("msg_text"),
476 message,
477 migui::Rect::new(x + 10.0, y + 40.0, box_w - 20.0, 80.0),
478 );
479
480 let btn_w = 100.0;
482 let btn_h = 35.0;
483 let total_w = buttons.len() as f32 * (btn_w + 10.0);
484 let mut btn_x = x + (box_w - total_w) / 2.0;
485 let btn_y = y + box_h - 50.0;
486
487 for (i, label) in buttons.iter().enumerate() {
488 if gui.button(
489 migui::WidgetId::new(&format!("msg_btn_{}", i)),
490 migui::Rect::new(btn_x, btn_y, btn_w, btn_h),
491 label,
492 ) {
493 return i as i32;
494 }
495 btn_x += btn_w + 10.0;
496 }
497
498 -1
499}
500
501pub fn draw_inventory_slot(
507 gui: &mut migui::Migui,
508 x: f32,
509 y: f32,
510 size: f32,
511 item_name: Option<&str>,
512 count: i32,
513 is_selected: bool,
514 theme: &Theme,
515) {
516 let bg = if is_selected {
517 rgba_to_migui(theme.slot_hover)
518 } else {
519 rgba_to_migui(theme.slot_bg)
520 };
521
522 gui.panel(
523 migui::WidgetId::new(&format!("slot_{}_{}", x as i32, y as i32)),
524 migui::Rect::new(x, y, size, size),
525 bg,
526 );
527
528 if let Some(name) = item_name {
529 gui.label(
530 migui::WidgetId::new(&format!("slot_label_{}_{}", x as i32, y as i32)),
531 name,
532 migui::Rect::new(x + 2.0, y + 2.0, size - 4.0, size - 14.0),
533 );
534
535 if count > 1 {
536 gui.label(
537 migui::WidgetId::new(&format!("slot_count_{}_{}", x as i32, y as i32)),
538 &format!("x{}", count),
539 migui::Rect::new(
540 x + size - 30.0,
541 y + size - 14.0,
542 28.0,
543 theme.font_size_small as f32,
544 ),
545 );
546 }
547 }
548}
549
550pub fn draw_inventory_grid(
552 gui: &mut migui::Migui,
553 x: f32,
554 y: f32,
555 cols: usize,
556 rows: usize,
557 slot_size: f32,
558 spacing: f32,
559 items: &[Option<(String, i32)>],
560 selected_slot: &mut usize,
561 theme: &Theme,
562) -> Option<usize> {
563 let panel_w = cols as f32 * (slot_size + spacing) + theme.padding * 2.0;
565 let panel_h = rows as f32 * (slot_size + spacing) + theme.padding * 2.0 + 30.0;
566
567 gui.panel(
568 migui::WidgetId::new("inv_panel"),
569 migui::Rect::new(x, y, panel_w, panel_h),
570 rgba_to_migui(theme.dialog_bg),
571 );
572
573 gui.label(
574 migui::WidgetId::new("inv_title"),
575 "INVENTARIO",
576 migui::Rect::new(
577 x + theme.padding,
578 y + 4.0,
579 panel_w - theme.padding * 2.0,
580 theme.font_size as f32 + 6.0,
581 ),
582 );
583
584 let slot_y = y + theme.padding + 30.0;
585
586 for row in 0..rows {
587 for col in 0..cols {
588 let idx = row * cols + col;
589 let slot_x = x + theme.padding + col as f32 * (slot_size + spacing);
590 let slot_y = slot_y + row as f32 * (slot_size + spacing);
591
592 let item = items.get(idx).and_then(|i| i.as_ref());
593 let (item_name, count) = match item {
594 Some((name, c)) => (Some(name.as_str()), *c),
595 None => (None, 0),
596 };
597
598 let is_selected = idx == *selected_slot;
599
600 if gui.button(
601 migui::WidgetId::new(&format!("inv_slot_{}", idx)),
602 migui::Rect::new(slot_x, slot_y, slot_size, slot_size),
603 "",
604 ) {
605 *selected_slot = idx;
606 return Some(idx);
607 }
608
609 draw_inventory_slot(
610 gui,
611 slot_x,
612 slot_y,
613 slot_size,
614 item_name,
615 count,
616 is_selected,
617 theme,
618 );
619 }
620 }
621
622 None
623}
624
625pub fn draw_notification(
631 gui: &mut migui::Migui,
632 text: &str,
633 x: f32,
634 y: f32,
635 theme: &Theme,
636) {
637 let text_w = text.len() as f32 * theme.font_size as f32 * 0.5;
638 let h = theme.font_size as f32 + 16.0;
639
640 gui.panel(
641 migui::WidgetId::new("notif_bg"),
642 migui::Rect::new(x, y, text_w + 20.0, h),
643 rgba_to_migui(theme.menu_item_hover),
644 );
645
646 gui.label(
647 migui::WidgetId::new("notif_text"),
648 text,
649 migui::Rect::new(x + 10.0, y + 8.0, text_w, theme.font_size as f32),
650 );
651}
652
653pub fn draw_minimap(
659 gui: &mut migui::Migui,
660 x: f32,
661 y: f32,
662 size: f32,
663 player_px: f32,
664 player_py: f32,
665 world_w: f32,
666 world_h: f32,
667 theme: &Theme,
668) {
669 gui.panel(
671 migui::WidgetId::new("minimap_bg"),
672 migui::Rect::new(x, y, size, size),
673 rgba_to_migui(theme.slot_bg),
674 );
675
676 let dot_x = x + (player_px / world_w) * size;
678 let dot_y = y + (player_py / world_h) * size;
679 let dot_size = 6.0;
680
681 gui.panel(
682 migui::WidgetId::new("minimap_player"),
683 migui::Rect::new(dot_x - dot_size / 2.0, dot_y - dot_size / 2.0, dot_size, dot_size),
684 rgba_to_migui(theme.health_bar_fill),
685 );
686}
687
688pub fn draw_loading(
694 gui: &mut migui::Migui,
695 text: &str,
696 progress: f32,
697 theme: &Theme,
698) {
699 let screen_w = 800.0;
700 let screen_h = 600.0;
701
702 gui.panel(
703 migui::WidgetId::new("load_bg"),
704 migui::Rect::new(0.0, 0.0, screen_w, screen_h),
705 rgba_to_migui(theme.bg_color),
706 );
707
708 gui.label(
709 migui::WidgetId::new("load_text"),
710 text,
711 migui::Rect::new(
712 screen_w / 2.0 - 100.0,
713 250.0,
714 200.0,
715 theme.font_size as f32 + 8.0,
716 ),
717 );
718
719 let bar_w = 300.0;
720 let bar_h = 20.0;
721 let bar_x = (screen_w - bar_w) / 2.0;
722 let bar_y = 300.0;
723
724 gui.panel(
725 migui::WidgetId::new("load_bar_bg"),
726 migui::Rect::new(bar_x, bar_y, bar_w, bar_h),
727 rgba_to_migui(theme.slot_bg),
728 );
729
730 let fill_w = bar_w * progress.clamp(0.0, 1.0);
731 gui.panel(
732 migui::WidgetId::new("load_bar_fill"),
733 migui::Rect::new(bar_x, bar_y, fill_w, bar_h),
734 rgba_to_migui(theme.health_bar_fill),
735 );
736
737 let pct = format!("{:.0}%", progress * 100.0);
738 gui.label(
739 migui::WidgetId::new("load_pct"),
740 &pct,
741 migui::Rect::new(
742 screen_w / 2.0 - 20.0,
743 bar_y + bar_h + 8.0,
744 40.0,
745 theme.font_size_small as f32,
746 ),
747 );
748}
749
750#[cfg(test)]
751mod tests {
752 use super::*;
753
754 #[test]
755 fn test_color_conversion() {
756 let c = ColorRGBA::rgb(255, 128, 64);
757 let migui_c = rgba_to_migui(c);
758 assert_eq!(migui_c.r, 255);
759 assert_eq!(migui_c.g, 128);
760 assert_eq!(migui_c.b, 64);
761 }
762
763 #[test]
764 fn test_sdl2_color_conversion() {
765 let c = ColorRGBA::rgb(100, 150, 200);
766 let sdl2_c = rgba_to_sdl2(c);
767 assert_eq!(sdl2_c.r, 100);
768 assert_eq!(sdl2_c.g, 150);
769 assert_eq!(sdl2_c.b, 200);
770 }
771
772 #[test]
773 fn test_rgba_transparent() {
774 let c = ColorRGBA::transparent();
775 assert_eq!(c.a, 0);
776 }
777}