diff options
| author | Freya Murphy <freya@freyacat.org> | 2025-10-31 11:04:13 -0400 |
|---|---|---|
| committer | Freya Murphy <freya@freyacat.org> | 2025-10-31 11:04:13 -0400 |
| commit | d330827da265fdb6bb51ae35704509b1a2723f28 (patch) | |
| tree | 479c31cc7bca23b1412cb98cb070c50507044351 /graphics | |
| parent | Add LICENSE file (diff) | |
| download | DungeonCrawl-d330827da265fdb6bb51ae35704509b1a2723f28.tar.gz DungeonCrawl-d330827da265fdb6bb51ae35704509b1a2723f28.tar.bz2 DungeonCrawl-d330827da265fdb6bb51ae35704509b1a2723f28.zip | |
graphics: add health/inventory ui rendering
Diffstat (limited to 'graphics')
| -rw-r--r-- | graphics/src/assets.rs | 26 | ||||
| -rw-r--r-- | graphics/src/render.rs | 141 |
2 files changed, 154 insertions, 13 deletions
diff --git a/graphics/src/assets.rs b/graphics/src/assets.rs index e607fdf..bb989a3 100644 --- a/graphics/src/assets.rs +++ b/graphics/src/assets.rs @@ -1,6 +1,7 @@ //! The `assets` crate stores all audio and image assets that need to be //! loaded during runtime +use dungeon::Item; use raylib::{RaylibHandle, RaylibThread, audio::RaylibAudio, texture::Texture2D}; #[expect(dead_code)] @@ -50,6 +51,8 @@ pub(crate) enum AtlasTexture { WallEdgeSouth, WallEdgeWest, Player, + InvTopLayer, + InvBottomLayer, Error, } impl AtlasTexture { @@ -66,6 +69,8 @@ impl AtlasTexture { Self::WallEdgeSouth => (2, 1), Self::WallEdgeWest => (3, 1), Self::Player => (0, 2), + Self::InvTopLayer => (1, 2), + Self::InvBottomLayer => (2, 2), Self::Error => (3, 3), } } @@ -87,6 +92,10 @@ impl AtlasTexture { #[derive(Debug)] pub(crate) struct ImageData { pub(crate) atlas: Texture2D, + pub(crate) heart_full: Texture2D, + pub(crate) heart_half: Texture2D, + pub(crate) heart_empty: Texture2D, + pub(crate) error: Texture2D, } impl ImageData { pub(crate) fn load( @@ -94,7 +103,22 @@ impl ImageData { thread: &RaylibThread, ) -> crate::Result<Self> { let atlas = handle.load_texture(thread, "assets/atlas.bmp")?; + let heart_full = handle.load_texture(thread, "assets/heart_full.bmp")?; + let heart_half = handle.load_texture(thread, "assets/heart_half.bmp")?; + let heart_empty = handle.load_texture(thread, "assets/heart_empty.bmp")?; + let error = handle.load_texture(thread, "assets/error.bmp")?; - Ok(Self { atlas }) + Ok(Self { + atlas, + heart_full, + heart_half, + heart_empty, + error, + }) + } + + pub(crate) fn get_item_texture(&self, _item: &Item) -> &Texture2D { + // TODO: make item textures + &self.error } } diff --git a/graphics/src/render.rs b/graphics/src/render.rs index b6af7c7..499301c 100644 --- a/graphics/src/render.rs +++ b/graphics/src/render.rs @@ -2,12 +2,12 @@ //! the game, with each frame represented by a `Renderer` and //! frame specific information in `FrameInfo`. -/// The (prefered) view distance of the game -const VIEW_DISTANCE: i32 = 5; - use std::{cell::RefCell, ops::Div, rc::Rc}; -use dungeon::{Direction, Dungeon, Entity, EntityKind, FPos, Floor, MAP_SIZE, Pos, Tile}; +use dungeon::{ + Direction, Dungeon, Entity, EntityKind, FPos, Floor, MAP_SIZE, PLAYER_FULL_HEALTH, + Player, Pos, Tile, +}; use raylib::{ RaylibThread, camera::Camera2D, @@ -22,6 +22,17 @@ use raylib::{ use crate::assets::{AtlasTexture, BASE_TILE_SIZE, ImageData, WALL_HEIGHT}; +/// The (prefered) view distance of the game +const VIEW_DISTANCE: i32 = 5; + +/// 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, +}; + /// The `FrameInfo` struct stores information used during a single frame #[derive(Clone, Copy, Debug)] struct FrameInfo { @@ -285,12 +296,118 @@ where } /// Draws player HP, inventory, and floor number - pub fn draw_ui(&mut self, _dungeon: &Dungeon) { + pub fn draw_ui(&mut self, dungeon: &Dungeon) { + // Draw core ui components + self.draw_health(&dungeon.player); + self.draw_inventory(&dungeon.player); + + // Draw debug info #[cfg(feature = "debug")] - // Draw fps (debug only) + self.draw_debug_ui(dungeon); + } + + fn draw_health(&mut self, player: &Player) { + let health = player.entity.health.unwrap_or(0); + let hearts = PLAYER_FULL_HEALTH.div_ceil(2); + let mut full_hearts = health / 2; + let mut half_hearts = health % 2; + let mut empty_hearts = hearts.saturating_sub(full_hearts + half_hearts); + + let size = self.info.tile_size.div(2).max(BASE_TILE_SIZE); + let y = BASE_TILE_SIZE; + let mut x = BASE_TILE_SIZE; + loop { + let tex = if full_hearts > 0 { + full_hearts -= 1; + &self.state.image.heart_full + } else if half_hearts > 0 { + half_hearts -= 1; + &self.state.image.heart_half + } else if empty_hearts > 0 { + empty_hearts -= 1; + &self.state.image.heart_empty + } else { + break; + }; + + let dest_rec = Rectangle { + x: x as f32, + y: y as f32, + width: size as f32, + height: size as f32, + }; + self.handle.draw_texture_pro( + tex, + FULL_SOURCE_REC, + dest_rec, + Vector2::zero(), + 0.0, + Color::WHITE, + ); + x += size; + } + } + + fn draw_inventory(&mut self, player: &Player) { + let len = i32::try_from(player.inventory.len()).unwrap_or(0); + + // size of the inv blocks + let size = self.info.tile_size.div(2).max(BASE_TILE_SIZE); + + // position of the inv blocks + let y = self.info.height - size; + let mut x = self.info.width / 2 - (size * len / 2); + + // size of font for number index + let font_size = size / 3; + let font_offset = font_size * 2; + + for (idx, item) in player.inventory.iter().enumerate() { + let dest_rec = Rectangle { + x: x as f32, + y: y as f32, + width: size as f32, + height: size as f32, + }; + self.draw_ui_atlas( + AtlasTexture::InvBottomLayer, + x as f32, + y as f32, + size as f32, + ); + let tex = self.state.image.get_item_texture(item); + self.handle.draw_texture_pro( + tex, + FULL_SOURCE_REC, + dest_rec, + Vector2::zero(), + 0.0, + Color::WHITE, + ); + self.draw_ui_atlas(AtlasTexture::InvTopLayer, x as f32, y as f32, size as f32); + self.handle.draw_text( + &format!("{}", idx + 1), + x + font_offset + font_offset / 5, + y + font_offset, + font_size, + Color::WHITE, + ); + x += size; + } + } + + /// Draws debug information ontop of other UI elements + /// Called by default if `debug` featue is enabled + pub fn draw_debug_ui(&mut self, _dungeon: &Dungeon) { self.draw_fps(); } + /// Draw FPS counter (debug) + fn draw_fps(&mut self) { + let fps_str = format!("{}", self.info.fps); + self.handle.draw_text(&fps_str, 10, 10, 30, Color::YELLOW); + } + /// Draws the entities on the map fn draw_entities(&mut self, dungeon: &Dungeon) { self.draw_entity(&dungeon.player.entity); @@ -324,6 +441,12 @@ where self.handle.draw_tilemap(tex, size, offset); } + /// Draw an atlas texture index (with ui offset applied) + fn draw_ui_atlas(&mut self, tex: AtlasTexture, x: f32, y: f32, size: f32) { + let half_size = size / 2.0; + self.draw_atlas(tex, x + half_size, y + half_size, size, 0.0); + } + /// Draw an atlas texture index fn draw_atlas(&mut self, tex: AtlasTexture, x: f32, y: f32, size: f32, rotation: f32) { let source_rec = Rectangle { @@ -351,12 +474,6 @@ where Color::WHITE, ); } - - /// Draw FPS counter - fn draw_fps(&mut self) { - let fps_str = format!("{}", self.info.fps); - self.handle.draw_text(&fps_str, 10, 10, 30, Color::YELLOW); - } } trait Vector2Ext { |