summaryrefslogtreecommitdiff
path: root/graphics
diff options
context:
space:
mode:
authorFreya Murphy <freya@freyacat.org>2025-11-07 14:21:03 -0500
committerFreya Murphy <freya@freyacat.org>2025-11-07 14:21:03 -0500
commit85fa878e5927fe23e8e54e924df688b39215b5bf (patch)
tree46692a9f05f181a6c878f21d7c4b97a3430a4910 /graphics
parentgraphics: new UI! (diff)
downloadDungeonCrawl-85fa878e5927fe23e8e54e924df688b39215b5bf.tar.gz
DungeonCrawl-85fa878e5927fe23e8e54e924df688b39215b5bf.tar.bz2
DungeonCrawl-85fa878e5927fe23e8e54e924df688b39215b5bf.zip
graphics: refactor to get rid of casting hell, use u16 when possible
Diffstat (limited to 'graphics')
-rw-r--r--graphics/src/render.rs318
1 files changed, 177 insertions, 141 deletions
diff --git a/graphics/src/render.rs b/graphics/src/render.rs
index 87716de..1d79b13 100644
--- a/graphics/src/render.rs
+++ b/graphics/src/render.rs
@@ -1,8 +1,8 @@
use std::ops::Div;
use dungeon::{
- Direction, Dungeon, Entity, EntityKind, FPos, Floor, Item, MAP_SIZE,
- PLAYER_INVENTORY_SIZE, Player, Pos, Tile,
+ Direction, Dungeon, Entity, EntityKind, Floor, Item, MAP_SIZE, PLAYER_INVENTORY_SIZE,
+ Player, Pos, Tile,
};
use raylib::{
RaylibHandle, RaylibThread,
@@ -19,14 +19,60 @@ macro_rules! downcast {
};
}
+macro_rules! rect {
+ {$x:expr, $y:expr, $w:expr, $h:expr $(,)?} => {
+ ::raylib::math::Rectangle {
+ x: ($x) as f32,
+ y: ($y) as f32,
+ width: ($w) as f32,
+ height: ($h) as f32,
+ }
+ };
+}
+
+macro_rules! vec2 {
+ {$x:expr, $y:expr $(,)?} => {
+ ::raylib::math::Vector2 {
+ x: ($x) as f32,
+ y: ($y) as f32,
+ }
+ };
+}
+
/// The baseline size of all ingame sprites and tile textures
-const BASE_TILE_SIZE: i32 = 32;
+const BASE_TILE_SIZE: u16 = 32;
/// The height of the wall (offset between tile layers)
-const WALL_HEIGHT: i32 = 13;
+const WALL_HEIGHT: u16 = 13;
/// The (prefered) view distance of the game
-const VIEW_DISTANCE: i32 = 5;
+const VIEW_DISTANCE: u16 = 5;
+
+// Tile atlas.bmp textures
+const ATLAS_WALL_SIDE: (u16, u16) = (0, 0);
+const ATLAS_FLOOR_FULL: (u16, u16) = (1, 0);
+const ATLAS_FLOOR_EMPTY: (u16, u16) = (2, 0);
+const ATLAS_WALL_TOP: (u16, u16) = (3, 0);
+const ATLAS_WALL_EDGE: (u16, u16) = (0, 1);
+
+// Entity atlas.bmp textures
+const ATLAS_PLAYER: (u16, u16) = (0, 2);
+
+// UI atlas.bmp textures
+const ATLAS_INV_CONTAINER: (u16, u16) = (1, 1);
+const ATLAS_HEART_ICON: (u16, u16) = (2, 1);
+const ATLAS_DAMAGE_ICON: (u16, u16) = (3, 1);
+
+// Misc atlas.bmp textures
+const ATLAS_ERROR: (u16, u16) = (3, 3);
+
+/// Full source rec for any texture
+const FULL_SOURCE_REC: Rectangle = rect! {
+ 0.0,
+ 0.0,
+ BASE_TILE_SIZE,
+ BASE_TILE_SIZE,
+};
#[derive(Debug)]
struct Textures {
@@ -49,32 +95,6 @@ impl Textures {
}
}
-// Tile atlas.bmp textures
-const ATLAS_WALL_SIDE: (i32, i32) = (0, 0);
-const ATLAS_FLOOR_FULL: (i32, i32) = (1, 0);
-const ATLAS_FLOOR_EMPTY: (i32, i32) = (2, 0);
-const ATLAS_WALL_TOP: (i32, i32) = (3, 0);
-const ATLAS_WALL_EDGE: (i32, i32) = (0, 1);
-
-// Entity atlas.bmp textures
-const ATLAS_PLAYER: (i32, i32) = (0, 2);
-
-// UI atlas.bmp textures
-const ATLAS_INV_CONTAINER: (i32, i32) = (1, 1);
-const ATLAS_HEART_ICON: (i32, i32) = (2, 1);
-const ATLAS_DAMAGE_ICON: (i32, i32) = (3, 1);
-
-// Misc atlas.bmp textures
-const ATLAS_ERROR: (i32, i32) = (3, 3);
-
-/// Full source rec for any texture
-const FULL_SOURCE_REC: Rectangle = Rectangle {
- x: 0.0,
- y: 0.0,
- width: BASE_TILE_SIZE as f32,
- height: BASE_TILE_SIZE as f32,
-};
-
#[derive(Debug)]
pub struct Renderer {
/* Persistant Render Data */
@@ -88,20 +108,20 @@ pub struct Renderer {
tiles_hash: Option<u64>,
/* Per Frame Caculated Data */
/// Current tile size we are rendering
- tile_size: i32,
+ tile_size: u16,
/// Last known FPS
fps: u32,
/// Render width of the current frame
- width: i32,
+ width: u16,
/// Render height of the current frame
- height: i32,
+ height: u16,
}
impl Renderer {
pub fn new(handle: &mut RaylibHandle, thread: &RaylibThread) -> crate::Result<Self> {
// Load resources
let textures = Textures::new(handle, thread)?;
// Load render textures
- let tex_size = MAP_SIZE as u32 * BASE_TILE_SIZE as u32;
+ let tex_size = (MAP_SIZE * BASE_TILE_SIZE) as u32;
let tilemap_fg = handle.load_render_texture(thread, tex_size, tex_size)?;
let tilemap_bg = handle.load_render_texture(thread, tex_size, tex_size)?;
@@ -137,14 +157,14 @@ impl Renderer {
// Get last known fps
self.fps = handle.get_fps();
// Get size of framebuffer
- self.width = handle.get_render_width();
- self.height = handle.get_render_height();
+ self.width = downcast!(handle.get_render_width(), u16);
+ self.height = downcast!(handle.get_render_height(), u16);
// Get size (in pixels) to draw each tile
self.tile_size = {
let size = self.width.min(self.height);
let dist = VIEW_DISTANCE * 2 + 1;
let pixels = size.div(dist).max(BASE_TILE_SIZE);
- 1 << (i32::BITS - pixels.leading_zeros())
+ 1 << (u16::BITS - pixels.leading_zeros())
};
}
@@ -153,16 +173,16 @@ impl Renderer {
let cpos = dungeon.camera();
let width = self.width;
let height = self.height;
- let ui_height = self.get_ui_height() as f32;
+ let ui_height = self.get_ui_height();
Camera2D {
target: Vector2::from(cpos.xy())
- .scale_by(self.tile_size as f32)
- .max(Vector2::new(width as f32 / 2.0, height as f32 / 2.0))
- .min(Vector2::new(
- (MAP_SIZE as i32 * self.tile_size) as f32 - (width as f32 / 2.0),
- (MAP_SIZE as i32 * self.tile_size) as f32 - (height as f32 / 2.0),
- )),
- offset: Vector2::new(width as f32 / 2.0, height as f32 / 2.0 + ui_height),
+ .scale_by(self.tile_size.into())
+ .max(vec2! {width/2, height/2})
+ .min(vec2! {
+ (MAP_SIZE * self.tile_size) - (width/2),
+ (MAP_SIZE * self.tile_size) - (height/2),
+ }),
+ offset: vec2! {width/2, height/2 + ui_height},
rotation: 0.0,
zoom: 1.0,
}
@@ -204,13 +224,12 @@ impl Renderer {
where
R: RaylibDraw + RaylibTextureModeExt,
{
- let size = BASE_TILE_SIZE as f32;
+ let size = BASE_TILE_SIZE;
let mut rt = r.begin_texture_mode(t, &mut self.tilemap_fg);
rt.clear_background(Color::BLANK);
for pos in Pos::values() {
- let (fx, fy) = FPos::from(pos).xy();
- let (xs, ys) = (fx * size, fy * size);
+ let (xs, ys) = (pos.x() * size, pos.y() * size);
// fg layer only draws a top walls
if floor.get(pos) != Tile::Wall {
@@ -243,7 +262,7 @@ impl Renderer {
where
R: RaylibDraw + RaylibTextureModeExt,
{
- let size = BASE_TILE_SIZE as f32;
+ let size = BASE_TILE_SIZE;
let mut rt = r.begin_texture_mode(t, &mut self.tilemap_bg);
rt.clear_background(Color::BLACK);
@@ -256,14 +275,7 @@ impl Renderer {
Tile::Air if (x + y) % 2 == 1 => ATLAS_FLOOR_EMPTY,
_ => ATLAS_ERROR,
};
- rt.draw_atlas(
- &self.textures.atlas,
- idx,
- x as f32 * size,
- y as f32 * size,
- size,
- 0.0,
- );
+ rt.draw_atlas(&self.textures.atlas, idx, x * size, y * size, size, 0.0);
}
}
@@ -272,9 +284,7 @@ impl Renderer {
where
R: RaylibDraw,
{
- let size = self.tile_size as f32;
- let offset = WALL_HEIGHT as f32;
- r.draw_tilemap(&self.tilemap_fg, size, offset);
+ r.draw_tilemap(&self.tilemap_fg, self.tile_size, WALL_HEIGHT);
}
/// Draw dungeon tiles (background layer)
@@ -282,9 +292,7 @@ impl Renderer {
where
R: RaylibDraw,
{
- let size = self.tile_size as f32;
- let offset = 0.0;
- r.draw_tilemap(&self.tilemap_bg, size, offset);
+ r.draw_tilemap(&self.tilemap_bg, self.tile_size, 0);
}
/// Draws the entities on the map
@@ -314,17 +322,17 @@ impl Renderer {
}
/// Returns the known UI height for this frame
- fn get_ui_height(&self) -> i32 {
+ fn get_ui_height(&self) -> u16 {
self.tile_size
}
/// Returns the padding used between ui elements
- fn get_ui_padding(&self) -> i32 {
+ fn get_ui_padding(&self) -> u16 {
self.get_ui_height() / 10
}
/// Returns the font size for the UI
- fn get_ui_font_size(&self) -> i32 {
+ fn get_ui_font_size(&self) -> u16 {
self.get_ui_height() / 4
}
@@ -334,7 +342,7 @@ impl Renderer {
R: RaylibDraw,
{
// Draw UI base rect
- r.draw_rectangle(0, 0, self.width, self.get_ui_height(), Color::BLACK);
+ r.draw_rectangle_ext(0, 0, self.width, self.get_ui_height(), Color::BLACK);
// Draw core ui components
self.draw_minimap(r, dungeon);
@@ -361,19 +369,16 @@ impl Renderer {
// Draw minimap in top left of UI bar
let minimap_x = text_x + self.get_ui_font_size() + padding;
let size = self.get_ui_height() - padding * 2;
- r.draw_rectangle(minimap_x, y, size, size, Color::DARKGRAY);
-
- let size_u16 = downcast!(size, u16);
- let group_size = (MAP_SIZE / size_u16).max(1);
+ let group_size = (MAP_SIZE / size).max(1);
let groups = MAP_SIZE / group_size;
- let dot_size = size_u16 / groups;
+ let dot_size = size / groups;
let mut draw_dot = |x_idx: u16, y_idx: u16, color| {
- r.draw_rectangle(
- minimap_x + (x_idx * dot_size) as i32,
- padding + (y_idx * dot_size) as i32,
- dot_size as i32,
- dot_size as i32,
+ r.draw_rectangle_ext(
+ minimap_x + x_idx * dot_size,
+ padding + y_idx * dot_size,
+ dot_size,
+ dot_size,
color,
);
};
@@ -409,7 +414,7 @@ impl Renderer {
}
/// Draws vertical text
- fn draw_vertical_text<R>(&self, r: &mut R, x: i32, y: i32, text: &str, color: Color)
+ fn draw_vertical_text<R>(&self, r: &mut R, x: u16, y: u16, text: &str, color: Color)
where
R: RaylibDraw,
{
@@ -418,11 +423,11 @@ impl Renderer {
let spacing = font_size + padding;
let mut byte_off = 0;
for (idx_usize, char) in text.chars().enumerate() {
- let idx = downcast!(idx_usize, i32);
+ let idx = downcast!(idx_usize, u16);
let byte_len = char.len_utf8();
let str = &text[byte_off..byte_off + byte_len];
let char_y = y + idx * spacing;
- r.draw_text(str, x, char_y, font_size, color);
+ r.draw_text_ext(str, x, char_y, font_size, color);
byte_off += byte_len;
}
}
@@ -433,7 +438,7 @@ impl Renderer {
where
R: RaylibDraw,
{
- let slots = downcast!(PLAYER_INVENTORY_SIZE, i32);
+ let slots = downcast!(PLAYER_INVENTORY_SIZE, u16);
let padding = self.get_ui_padding();
let font_size = self.get_ui_font_size();
let ui_height = self.get_ui_height();
@@ -458,27 +463,27 @@ impl Renderer {
break;
}
- let slot_x = slots_x + slot_len * downcast!(idx, i32);
- let size = (ui_height - padding * 2) as f32;
+ let slot_x = slots_x + slot_len * downcast!(idx, u16);
+ let size = ui_height - padding * 2;
// Draw slot container
r.draw_inv_atlas(
&self.textures.atlas,
ATLAS_INV_CONTAINER,
- slot_x as f32,
- padding as f32,
+ slot_x,
+ padding,
size,
0.0,
);
if let Some(item) = player.inventory.get(idx) {
let tex = self.textures.item_texture(item);
- let item_padding = padding as f32 * 3.0;
- let dest_rec = Rectangle {
- x: slot_x as f32 + item_padding / 2.0,
- y: padding as f32 + item_padding / 2.0,
- width: size - item_padding,
- height: size - item_padding,
+ let item_padding = padding * 3;
+ let dest_rec = rect! {
+ slot_x + item_padding / 2,
+ padding+ item_padding / 2,
+ size - item_padding,
+ size - item_padding,
};
r.draw_texture_pro(
tex,
@@ -514,12 +519,12 @@ impl Renderer {
r.draw_inv_atlas(
&self.textures.atlas,
ATLAS_HEART_ICON,
- icon_x as f32,
- heart_y as f32,
- icon_width as f32,
+ icon_x,
+ heart_y,
+ icon_width,
0.0,
);
- r.draw_text(
+ r.draw_text_ext(
&format!("x{health:02}"),
text_x,
heart_y + padding,
@@ -532,12 +537,12 @@ impl Renderer {
r.draw_inv_atlas(
&self.textures.atlas,
ATLAS_DAMAGE_ICON,
- icon_x as f32,
- damage_y as f32,
- icon_width as f32,
+ icon_x,
+ damage_y,
+ icon_width,
0.0,
);
- r.draw_text(
+ r.draw_text_ext(
&format!("x{damage:02}"),
text_x,
damage_y + padding,
@@ -563,7 +568,7 @@ impl Renderer {
R: RaylibDraw,
{
let fps_str = format!("{}", self.fps);
- r.draw_text(&fps_str, 10, 10, 30, Color::YELLOW);
+ r.draw_text_ext(&fps_str, 10, 10, 30, Color::YELLOW);
}
}
@@ -633,70 +638,101 @@ where
fn draw_atlas(
&mut self,
tex: &Texture2D,
- (ax, ay): (i32, i32),
- x: f32,
- y: f32,
- size: f32,
- rotation: f32,
+ (ax, ay): (u16, u16),
+ x: impl Into<f32>,
+ y: impl Into<f32>,
+ size: impl Into<f32>,
+ rotation: impl Into<f32>,
) {
- let source_rec = Rectangle {
- x: (ax * BASE_TILE_SIZE) as f32,
- y: (ay * BASE_TILE_SIZE) as f32,
- width: BASE_TILE_SIZE as f32,
- height: BASE_TILE_SIZE as f32,
+ let size_into = size.into();
+ let source_rec = rect! {
+ ax * BASE_TILE_SIZE,
+ ay * BASE_TILE_SIZE,
+ BASE_TILE_SIZE,
+ BASE_TILE_SIZE,
};
- let dest_rec = Rectangle {
- x,
- y,
- width: size,
- height: size,
+ let dest_rec = rect! {
+ x.into(),
+ y.into(),
+ size_into,
+ size_into,
};
- let origin = Vector2 {
- x: dest_rec.width / 2.0,
- y: dest_rec.height / 2.0,
+ let origin = vec2! {
+ dest_rec.width / 2.0,
+ dest_rec.height / 2.0,
};
- self.draw_texture_pro(tex, source_rec, dest_rec, origin, rotation, Color::WHITE);
+ self.draw_texture_pro(
+ tex,
+ source_rec,
+ dest_rec,
+ origin,
+ rotation.into(),
+ Color::WHITE,
+ );
}
/// Draw in INV element from an atlas
fn draw_inv_atlas(
&mut self,
tex: &Texture2D,
- (ax, ay): (i32, i32),
- x: f32,
- y: f32,
- size: f32,
- rotation: f32,
+ (ax, ay): (u16, u16),
+ x: impl Into<f32>,
+ y: impl Into<f32>,
+ size: impl Into<f32>,
+ rotation: impl Into<f32>,
) {
+ let size_into = size.into();
self.draw_atlas(
tex,
(ax, ay),
- x + size / 2.0,
- y + size / 2.0,
- size,
+ x.into() + size_into / 2.0,
+ y.into() + size_into / 2.0,
+ size_into,
rotation,
);
}
/// Draw dungeon tiles helper function
- fn draw_tilemap(&mut self, tex: &RenderTexture2D, size: f32, offset: f32) {
- let scale = size / BASE_TILE_SIZE as f32;
- let width = tex.width() as f32;
- let height = tex.height() as f32;
- let source_rec = Rectangle {
- x: 0.0,
- y: 0.0,
+ fn draw_tilemap(&mut self, tex: &RenderTexture2D, size: u16, offset: u16) {
+ let scale = size / BASE_TILE_SIZE;
+ let width = downcast!(tex.width(), u16);
+ let height = downcast!(tex.height(), u16);
+ let source_rec = rect! {
+ 0,
+ 0,
width,
- height: -height,
+ -height.cast_signed(),
};
- let dest_rec = Rectangle {
- x: 0.0,
- y: -(offset * scale),
- width: width * scale,
- height: height * scale,
+ let dest_rec = rect! {
+ 0,
+ -(offset * scale).cast_signed(),
+ width * scale,
+ height * scale,
};
let origin = Vector2::zero();
self.draw_texture_pro(tex, source_rec, dest_rec, origin, 0.0, Color::WHITE);
}
+
+ fn draw_text_ext(
+ &mut self,
+ text: &str,
+ x: impl Into<i32>,
+ y: impl Into<i32>,
+ font_size: impl Into<i32>,
+ color: Color,
+ ) {
+ self.draw_text(text, x.into(), y.into(), font_size.into(), color);
+ }
+
+ fn draw_rectangle_ext(
+ &mut self,
+ x: impl Into<i32>,
+ y: impl Into<i32>,
+ width: impl Into<i32>,
+ height: impl Into<i32>,
+ color: Color,
+ ) {
+ self.draw_rectangle(x.into(), y.into(), width.into(), height.into(), color);
+ }
}
impl<T> RaylibDrawExt for T where T: RaylibDraw {}