#![allow(clippy::indexing_slicing)]
use crate::math::{SCREENHEIGHT, SCREENWIDTH};
use crate::types::WeaponType;
use super::Renderer;
#[derive(Clone, Copy, Debug, Default)]
pub struct HudFrame {
pub health: i32,
pub armor: i32,
pub ammo: [i32; 4],
pub ammo_active: Option<usize>,
}
impl HudFrame {
pub fn from_stats(
health: i32,
armor: i32,
ammo: [i32; 4],
ready_weapon: WeaponType,
) -> Self {
Self {
health,
armor,
ammo,
ammo_active: weapon_ammo_type(ready_weapon),
}
}
}
impl Renderer {
pub fn draw_hud(&mut self, hud: HudFrame) {
let screen = &mut self.screen;
let health = hud.health.max(0) as u32;
draw_icon(screen, 4, SCREENHEIGHT - 12, &ICON_CROSS, 176); draw_number(screen, 14, SCREENHEIGHT - 11, health, 3);
let icons: [&[u8; 25]; 4] = [&ICON_BULLET, &ICON_SHELL, &ICON_CELL, &ICON_ROCKET];
let x_base = SCREENWIDTH - 56;
let y_num = SCREENHEIGHT - 8;
for (i, icon) in icons.iter().enumerate() {
let x = x_base + i * 14;
let is_active = hud.ammo_active == Some(i);
let color = if is_active { 4u8 } else { 96 };
draw_icon5x5(screen, x + 1, y_num - 6, icon, color);
let val = hud.ammo.get(i).copied().unwrap_or(0).max(0) as u32;
draw_small_number(screen, x, y_num, val, 3, color);
}
if hud.armor > 0 {
let armor = hud.armor as u32;
draw_icon(screen, 4, SCREENHEIGHT - 22, &ICON_SHIELD, 192); draw_number(screen, 14, SCREENHEIGHT - 21, armor, 3);
}
}
}
fn weapon_ammo_type(weapon: WeaponType) -> Option<usize> {
match weapon {
WeaponType::Fist | WeaponType::Chainsaw => None,
WeaponType::Pistol | WeaponType::Chaingun => Some(0), WeaponType::Shotgun | WeaponType::SuperShotgun => Some(1), WeaponType::PlasmaRifle | WeaponType::Bfg => Some(2), WeaponType::RocketLauncher => Some(3), }
}
#[rustfmt::skip]
const ICON_CROSS: [u8; 49] = [
0,0,0,1,0,0,0,
0,0,0,1,0,0,0,
0,0,0,1,0,0,0,
1,1,1,1,1,1,1,
0,0,0,1,0,0,0,
0,0,0,1,0,0,0,
0,0,0,1,0,0,0,
];
#[rustfmt::skip]
const ICON_SHIELD: [u8; 49] = [
0,1,1,1,1,1,0,
1,1,1,1,1,1,1,
1,1,0,1,0,1,1,
1,1,1,1,1,1,1,
0,1,1,1,1,1,0,
0,0,1,1,1,0,0,
0,0,0,1,0,0,0,
];
#[rustfmt::skip]
const ICON_BULLET: [u8; 25] = [
0,0,1,0,0,
0,1,1,1,0,
0,1,1,1,0,
0,1,1,1,0,
0,1,1,1,0,
];
#[rustfmt::skip]
const ICON_SHELL: [u8; 25] = [
0,1,1,0,0,
1,1,1,1,0,
1,1,1,1,0,
1,1,1,1,0,
0,1,1,0,0,
];
#[rustfmt::skip]
const ICON_CELL: [u8; 25] = [
1,1,1,1,1,
1,0,1,0,1,
1,1,1,1,1,
1,0,1,0,1,
1,1,1,1,1,
];
#[rustfmt::skip]
const ICON_ROCKET: [u8; 25] = [
0,0,1,0,0,
0,1,1,1,0,
0,1,1,1,0,
1,1,1,1,1,
0,1,0,1,0,
];
#[rustfmt::skip]
const TINY_DIGITS: [[u8; 15]; 10] = [
[1,1,1, 1,0,1, 1,0,1, 1,0,1, 1,1,1], [0,1,0, 1,1,0, 0,1,0, 0,1,0, 1,1,1], [1,1,1, 0,0,1, 1,1,1, 1,0,0, 1,1,1], [1,1,1, 0,0,1, 1,1,1, 0,0,1, 1,1,1], [1,0,1, 1,0,1, 1,1,1, 0,0,1, 0,0,1], [1,1,1, 1,0,0, 1,1,1, 0,0,1, 1,1,1], [1,1,1, 1,0,0, 1,1,1, 1,0,1, 1,1,1], [1,1,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1], [1,1,1, 1,0,1, 1,1,1, 1,0,1, 1,1,1], [1,1,1, 1,0,1, 1,1,1, 0,0,1, 1,1,1], ];
#[rustfmt::skip]
const BIG_DIGITS: [[u8; 28]; 10] = [
[1,1,1,0, 1,0,1,0, 1,0,1,0, 1,0,1,0, 1,0,1,0, 1,0,1,0, 1,1,1,0],
[0,1,0,0, 1,1,0,0, 0,1,0,0, 0,1,0,0, 0,1,0,0, 0,1,0,0, 1,1,1,0],
[1,1,1,0, 0,0,1,0, 0,0,1,0, 1,1,1,0, 1,0,0,0, 1,0,0,0, 1,1,1,0],
[1,1,1,0, 0,0,1,0, 0,0,1,0, 1,1,1,0, 0,0,1,0, 0,0,1,0, 1,1,1,0],
[1,0,1,0, 1,0,1,0, 1,0,1,0, 1,1,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0],
[1,1,1,0, 1,0,0,0, 1,0,0,0, 1,1,1,0, 0,0,1,0, 0,0,1,0, 1,1,1,0],
[1,1,1,0, 1,0,0,0, 1,0,0,0, 1,1,1,0, 1,0,1,0, 1,0,1,0, 1,1,1,0],
[1,1,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0],
[1,1,1,0, 1,0,1,0, 1,0,1,0, 1,1,1,0, 1,0,1,0, 1,0,1,0, 1,1,1,0],
[1,1,1,0, 1,0,1,0, 1,0,1,0, 1,1,1,0, 0,0,1,0, 0,0,1,0, 1,1,1,0],
];
fn draw_icon5x5(screen: &mut [u8], x: usize, y: usize, icon: &[u8; 25], color: u8) {
stamp(screen, x, y, 5, 5, icon, color);
}
fn draw_icon(screen: &mut [u8], x: usize, y: usize, icon: &[u8; 49], color: u8) {
stamp(screen, x, y, 7, 7, icon, color);
}
#[inline]
fn stamp(screen: &mut [u8], x: usize, y: usize, w: usize, h: usize, icon: &[u8], color: u8) {
for py in 0..h {
for px in 0..w {
if icon[py * w + px] != 0 {
let sx = x + px;
let sy = y + py;
if sx < SCREENWIDTH && sy < SCREENHEIGHT {
screen[sy * SCREENWIDTH + sx] = color;
}
}
}
}
}
fn draw_small_number(screen: &mut [u8], x: usize, y: usize, value: u32, digits: usize, color: u8) {
let positions = decompose(value, digits);
for (d, &pos) in positions.iter().enumerate().take(digits) {
let digit = pos as usize;
if digit < TINY_DIGITS.len() {
stamp(screen, x + d * 4, y, 3, 5, &TINY_DIGITS[digit], color);
}
}
}
fn draw_number(screen: &mut [u8], x: usize, y: usize, value: u32, digits: usize) {
let positions = decompose(value, digits);
for (d, &pos) in positions.iter().enumerate().take(digits) {
let digit = pos as usize;
if digit < BIG_DIGITS.len() {
stamp(screen, x + d * 5, y, 4, 7, &BIG_DIGITS[digit], 4);
}
}
}
fn decompose(value: u32, digits: usize) -> [u32; 10] {
let mut out = [0u32; 10];
let mut val = value;
for i in 0..digits.min(out.len()) {
out[digits - 1 - i] = val % 10;
val /= 10;
}
out
}