1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
//! Drawing utilities.

use fontdue::layout::{
    CoordinateSystem, HorizontalAlign, Layout, LayoutSettings, TextStyle, VerticalAlign, WrapStyle,
};
use fontdue::Font;
use fontdue_sdl2::FontTexture;
use sdl2::pixels::Color;
use sdl2::rect::Rect;
use sdl2::render::{Canvas, Texture};
use sdl2::video::Window;

/// The size at which tiles are rendered. The tileset consists of 16px tiles, so
/// this should be multiple of that to avoid artifacts.
pub const TILE_SIZE: u32 = 48;

/// Utility functions for drawing. This struct owns the main things required for
/// any drawing.
pub struct RenderingContext<'frame, 'texture_creator> {
    pub canvas: &'frame mut Canvas<Window>,
    pub font_texture: &'frame mut FontTexture<'texture_creator>,
    pub fonts: &'frame [Font],
}

impl RenderingContext<'_, '_> {
    /// Draws a box with some text in the top-left corner of the screen.
    pub fn draw_notification(&mut self, text: &str, nth: i32) {
        let (w, h) = (400 + 8 * 2, 32 + 6 * 2);
        let (x, y) = (10, 10 + nth * (h as i32 + 6));
        let mut layout = Layout::new(CoordinateSystem::PositiveYDown);
        layout.reset(&LayoutSettings {
            x: x as f32 + 6.0,
            y: y as f32 + 8.0,
            max_width: Some(w as f32 - 6.0 * 2.0),
            max_height: Some(h as f32 - 8.0 * 2.0),
            horizontal_align: HorizontalAlign::Left,
            vertical_align: VerticalAlign::Middle,
            wrap_style: WrapStyle::Word,
            ..Default::default()
        });
        let notification_text = TextStyle::with_user_data(text, 16.0, 1, Color::YELLOW);
        layout.append(self.fonts, &notification_text);
        self.canvas.set_draw_color(Color::RGB(0x44, 0x44, 0x44));
        let _ = self.canvas.fill_rect(Rect::new(x, y, w, h));
        self.canvas.set_draw_color(Color::BLACK);
        let _ = self.canvas.draw_rect(Rect::new(x, y, w, h));
        let _ = self
            .font_texture
            .draw_text(self.canvas, self.fonts, layout.glyphs());
    }

    /// Draws the sprite for the given tile index at the given position.
    pub fn draw_tile(
        &mut self,
        camera_point: (f32, f32),
        tile_x: f32,
        tile_y: f32,
        texture: &Texture,
        tile_index: u32,
    ) {
        let (cw, ch) = self.canvas.viewport().size();
        let x = (tile_x * TILE_SIZE as f32) as i32 - (camera_point.0 * TILE_SIZE as f32) as i32
            + (cw as i32 - TILE_SIZE as i32) / 2;
        let y = (tile_y * TILE_SIZE as f32) as i32 - (camera_point.1 * TILE_SIZE as f32) as i32
            + (ch as i32 - TILE_SIZE as i32) / 2;
        let dst_rect = Rect::new(x, y, TILE_SIZE, TILE_SIZE);

        let tileset_x = (tile_index % 49) as i32 * 17;
        let tileset_y = (tile_index / 49) as i32 * 17;
        let src_rect = Rect::new(tileset_x, tileset_y, 16, 16);

        let _ = self.canvas.copy(texture, src_rect, dst_rect);
    }

    /// Draws a rectangle around the given region (length unit: tiles) for
    /// visualizing areas on the map.
    pub fn draw_debug_region(
        &mut self,
        camera_point: (f32, f32),
        x: f32,
        y: f32,
        width: f32,
        height: f32,
        color: Color,
    ) {
        let (cw, ch) = self.canvas.viewport().size();
        let x = (x * TILE_SIZE as f32) as i32 - (camera_point.0 * TILE_SIZE as f32) as i32
            + (cw as i32 - TILE_SIZE as i32) / 2;
        let y = (y * TILE_SIZE as f32) as i32 - (camera_point.1 * TILE_SIZE as f32) as i32
            + (ch as i32 - TILE_SIZE as i32) / 2;
        let w = (width * TILE_SIZE as f32) as u32;
        let h = (height * TILE_SIZE as f32) as u32;
        let dst_rect = Rect::new(x, y, w, h);
        self.canvas.set_draw_color(color);
        let _ = self.canvas.draw_rect(dst_rect);
    }
}