freenukum 0.3.5

A clone of the 1991 DOS game Duke Nukem 1
Documentation
use super::hero::{Firepower, Inventory, InventoryItem};
use super::text;
use crate::rendering::{MovePositionRenderer, Renderer, TileIndex};
use crate::{
    Result, BORDER_GREY_START, FONT_HEIGHT, FONT_WIDTH, HALFTILE_HEIGHT,
    HALFTILE_WIDTH, MAX_LIFE, OBJECT_ACCESS_CARD, OBJECT_BOOT,
    OBJECT_CLAMP, OBJECT_GLOVE, OBJECT_GUN, OBJECT_HEALTH,
    OBJECT_KEY_BLUE, OBJECT_KEY_GREEN, OBJECT_KEY_PINK, OBJECT_KEY_RED,
    OBJECT_NONHEALTH, OBJECT_SHOT, SCORE_DIGITS, TILE_HEIGHT, TILE_WIDTH,
    WINDOW_HEIGHT, WINDOW_WIDTH,
};
use sdl2::rect::Point;

pub struct Borders {}

#[rustfmt::skip]
const BORDERS:
[i32; (2 * WINDOW_HEIGHT / TILE_HEIGHT) as usize * (2 * WINDOW_WIDTH / TILE_WIDTH) as usize] =
[
     4,-1, 2,-1, 2,-1, 2,-1, 2,-1, 2,-1, 2,-1, 2,-1, 2,-1, 2,-1,
     2,-1, 2,-1, 2,-1, 2,-1, 5,-1, 8,-1,38,-1,39,-1, 8,-1, 9,-1,

    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,

     0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
    -1,-1,-1,-1,-1,-1,-1,-1, 1,-1,-1,-1,-1,-1,-1,-1,-1,-1,10,-1,

    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,

     0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
    -1,-1,-1,-1,-1,-1,-1,-1, 1,-1,-1,-1,-1,-1,-1,-1,-1,-1,10,-1,

    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
    -1,-1,-1,-1,-1,-1,-1,-1,14,-1, 8,-1,36,-1,37,-1, 8,-1,15,-1,

     0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,

    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
    -1,-1,-1,-1,-1,-1,-1,-1, 1,-1,-1,-1,-1,-1,-1,-1,-1,-1,10,-1,

     0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
    -1,-1,-1,-1,-1,-1,-1,-1, 1,-1,-1,-1,-1,-1,-1,-1,-1,-1,10,-1,

    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,

     0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
    -1,-1,-1,-1,-1,-1,-1,-1,14,-1, 8,33,-1,34,-1,35,-1, 8,15,-1,

    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,

     0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
    -1,-1,-1,-1,-1,-1,-1,-1, 1,-1,-1,-1,-1,-1,-1,-1,-1,-1,10,-1,

    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,

     0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
    -1,-1,-1,-1,-1,-1,-1,-1, 1,-1,-1,-1,-1,-1,-1,-1,-1,-1,10,-1,

    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,

     0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
    -1,-1,-1,-1,-1,-1,-1,-1,14,-1, 8,30,-1,31,-1,32,-1, 8,15,-1,

    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,

     0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
    -1,-1,-1,-1,-1,-1,-1,-1, 1,-1,-1,-1,-1,-1,-1,-1,-1,-1,10,-1,

    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,

     0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
    -1,-1,-1,-1,-1,-1,-1,-1, 1,-1,-1,-1,-1,-1,-1,-1,-1,-1,10,-1,

    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,

     7,-1, 3,-1, 3,-1, 3, -1,3,-1, 3,26,-1,27,-1,28,-1,29,-1, 3,
     3,-1, 3,-1, 3,-1, 3,-1, 6,-1, 8,-1, 8,-1, 8,-1, 8,-1,11,-1,

    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,

    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
];

impl Borders {
    fn render_tile(
        &self,
        x: i32,
        y: i32,
        renderer: &mut dyn Renderer,
        tile: TileIndex,
    ) -> Result<()> {
        renderer.place_tile(tile, Point::new(x, y))?;
        Ok(())
    }

    fn render_iter<I>(
        &self,
        columns: u8,
        grid_x: u16,
        grid_y: u16,
        renderer: &mut dyn Renderer,
        borders: I,
    ) -> Result<()>
    where
        I: Iterator<Item = Option<usize>>,
    {
        for (i, border) in borders.enumerate() {
            if let Some(border) = border {
                self.render_tile(
                    (i as i32 % columns as i32) * (grid_x as i32),
                    (i as i32 / columns as i32) * (grid_y as i32),
                    renderer,
                    border,
                )?;
            }
        }
        Ok(())
    }

    pub fn render(&self, renderer: &mut dyn Renderer) -> Result<()> {
        self.render_iter(
            (2 * WINDOW_WIDTH / TILE_WIDTH) as u8,
            HALFTILE_WIDTH as u16,
            HALFTILE_HEIGHT as u16,
            renderer,
            BORDERS.iter().map(|i| {
                if *i < 0 {
                    None
                } else {
                    Some(*i as usize + BORDER_GREY_START)
                }
            }),
        )
    }

    pub fn render_life(
        &self,
        health: u8,
        renderer: &mut dyn Renderer,
    ) -> Result<()> {
        let health = std::cmp::min(health as usize, MAX_LIFE);
        let iter = (0..MAX_LIFE).map(|i| {
            if i < health {
                Some(OBJECT_HEALTH)
            } else {
                Some(OBJECT_NONHEALTH)
            }
        });
        let mut move_renderer = MovePositionRenderer {
            offset_x: 30 * HALFTILE_WIDTH as i32,
            offset_y: (15 * HALFTILE_HEIGHT as i32) / 2,
            upstream: renderer,
        };
        self.render_iter(
            MAX_LIFE as u8,
            HALFTILE_WIDTH as u16,
            HALFTILE_HEIGHT as u16,
            &mut move_renderer,
            iter,
        )
    }

    pub fn render_score(
        &self,
        score: u128,
        renderer: &mut dyn Renderer,
    ) -> Result<()> {
        let score = std::cmp::min(99999999, score);
        let score_string =
            format!("{0:0width$}", score, width = SCORE_DIGITS);

        let mut position_renderer = MovePositionRenderer {
            offset_x: 30 * FONT_WIDTH as i32,
            offset_y: 3 * FONT_HEIGHT as i32,
            upstream: renderer,
        };

        text::render(&mut position_renderer, &score_string)
    }

    pub fn render_firepower(
        &self,
        firepower: &Firepower,
        renderer: &mut dyn Renderer,
    ) -> Result<()> {
        const GUN: Option<usize> = Some(OBJECT_GUN);
        const SHOT: Option<usize> = Some(OBJECT_SHOT);

        let shots = firepower.num_shots();

        let shot0 = if shots > 0 { SHOT } else { None };
        let shot1 = if shots > 1 { SHOT } else { None };
        let shot2 = if shots > 2 { SHOT } else { None };
        let shot3 = if shots > 3 { SHOT } else { None };

        #[rustfmt::skip]
        let tiles = vec![
            None,  None, None,  GUN,  None,  None, None,  None,
            None,  None, None,  None, None,  None, None,  None,
            shot0, None, shot1, None, shot2, None, shot3, None,
            None,  None, None,  None, None,  None, None,  None,
        ];

        let mut move_renderer = MovePositionRenderer {
            offset_x: 15 * TILE_WIDTH as i32,
            offset_y: 6 * TILE_HEIGHT as i32,
            upstream: renderer,
        };
        self.render_iter(
            MAX_LIFE as u8,
            HALFTILE_WIDTH as u16,
            HALFTILE_HEIGHT as u16,
            &mut move_renderer,
            tiles.into_iter(),
        )
    }

    pub fn render_inventory(
        &self,
        inventory: &Inventory,
        renderer: &mut dyn Renderer,
    ) -> Result<()> {
        let red_key = if inventory.is_set(InventoryItem::KeyRed) {
            Some(OBJECT_KEY_RED)
        } else {
            None
        };
        let green_key = if inventory.is_set(InventoryItem::KeyGreen) {
            Some(OBJECT_KEY_GREEN)
        } else {
            None
        };
        let blue_key = if inventory.is_set(InventoryItem::KeyBlue) {
            Some(OBJECT_KEY_BLUE)
        } else {
            None
        };
        let pink_key = if inventory.is_set(InventoryItem::KeyPink) {
            Some(OBJECT_KEY_PINK)
        } else {
            None
        };
        let boot = if inventory.is_set(InventoryItem::Boot) {
            Some(OBJECT_BOOT)
        } else {
            None
        };
        let glove = if inventory.is_set(InventoryItem::Glove) {
            Some(OBJECT_GLOVE)
        } else {
            None
        };
        let clamp = if inventory.is_set(InventoryItem::Clamp) {
            Some(OBJECT_CLAMP)
        } else {
            None
        };
        let access_card = if inventory.is_set(InventoryItem::AccessCard) {
            Some(OBJECT_ACCESS_CARD)
        } else {
            None
        };

        #[rustfmt::skip]
        let tiles = vec![
            red_key, green_key, blue_key, pink_key,
            boot, glove, clamp, access_card,
        ];

        let mut move_renderer = MovePositionRenderer {
            offset_x: 15 * TILE_WIDTH as i32,
            offset_y: 9 * TILE_HEIGHT as i32,
            upstream: renderer,
        };
        self.render_iter(
            4,
            TILE_WIDTH as u16,
            TILE_HEIGHT as u16,
            &mut move_renderer,
            tiles.into_iter(),
        )
    }
}