diff --git a/src/game.rs b/src/game.rs index 3ac0ee1..539f827 100644 --- a/src/game.rs +++ b/src/game.rs @@ -40,6 +40,9 @@ pub struct Game { pub score: u32, pub depth: u32, pub fps: f32, + pub inventory_open: bool, + pub inventory_mouse_x: i32, + pub inventory_mouse_y: i32, } impl Game { @@ -73,6 +76,9 @@ impl Game { score: 0, depth: 1, fps: 0.0, + inventory_open: false, + inventory_mouse_x: 0, + inventory_mouse_y: 0, } } @@ -425,8 +431,10 @@ impl Game { let player_alive = player.as_ref().map(|e| e.alive).unwrap_or(false); let ui_w = (vw as i32) * crate::ui::UI_SCALE; let ui_h = (vh as i32) * crate::ui::UI_SCALE; - self.ui - .draw_character_panel(1, 1, player.as_ref(), &self.player); + if !self.inventory_open { + self.ui + .draw_character_panel(1, 1, player.as_ref(), &self.player); + } self.ui.draw_hud( ui_w as usize, ui_h as usize, @@ -461,6 +469,15 @@ impl Game { self.cam_x, self.cam_y, ); + if self.inventory_open { + self.ui.draw_inventory_overlay( + ui_w as usize, + ui_h as usize, + &self.player, + self.inventory_mouse_x, + self.inventory_mouse_y, + ); + } if !player_alive { self.ui .draw_death_screen(ui_w as usize, ui_h as usize, self.kills, self.score); diff --git a/src/main.rs b/src/main.rs index c43d238..239fe3f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -304,7 +304,7 @@ fn run_gpu_mode(title: &str) { game.player.stop_horizontal(&mut game.entities); } - if input.shoot_mouse + if (input.shoot_mouse && !game.inventory_open) || input.shoot_left || input.shoot_right || input.shoot_up @@ -348,6 +348,89 @@ fn run_gpu_mode(title: &str) { game.drop_item(0); } + if input.inventory_toggle { + game.inventory_open = !game.inventory_open; + } + + let ui_scale = verbatim::ui::UI_SCALE as f64; + let cell_px = 8.0_f64; + let ui_cell_px = cell_px / ui_scale; + game.inventory_mouse_x = (input.mouse_x / ui_cell_px) as i32; + game.inventory_mouse_y = (input.mouse_y / ui_cell_px) as i32; + + if game.inventory_open { + if let Some((mx, my)) = &input.inventory_click { + let mouse_ui_x = (*mx / ui_cell_px) as i32; + let mouse_ui_y = (*my / ui_cell_px) as i32; + let vw = renderer.grid_w(); + let vh = renderer.grid_h(); + let ui_w = (vw as i32) * verbatim::ui::UI_SCALE; + let ui_h = (vh as i32) * verbatim::ui::UI_SCALE; + + let cols = 4i32; + let rows = 2i32; + let slot_w = 8i32; + let slot_h = 6i32; + let gap = 2i32; + let panel_w = cols * slot_w + (cols + 1) * gap + 4; + let panel_h = rows * slot_h + (rows + 1) * gap + 20; + let px_start = (ui_w - panel_w) / 2; + let py_start = (ui_h - panel_h) / 2; + + for row in 0..rows { + for col in 0..cols { + let idx = (row * cols + col) as usize; + let sx = px_start + gap + col * (slot_w + gap) + 2; + let sy = py_start + 10 + row * (slot_h + gap) + gap; + if mouse_ui_x >= sx + && mouse_ui_x < sx + slot_w + && mouse_ui_y >= sy + && mouse_ui_y < sy + slot_h + { + if idx < game.player.inventory.len() { + game.use_item(idx); + } + } + } + } + } + if let Some((mx, my)) = &input.inventory_right_click { + let mouse_ui_x = (*mx / ui_cell_px) as i32; + let mouse_ui_y = (*my / ui_cell_px) as i32; + let vw = renderer.grid_w(); + let vh = renderer.grid_h(); + let ui_w = (vw as i32) * verbatim::ui::UI_SCALE; + let ui_h = (vh as i32) * verbatim::ui::UI_SCALE; + + let cols = 4i32; + let rows = 2i32; + let slot_w = 8i32; + let slot_h = 6i32; + let gap = 2i32; + let panel_w = cols * slot_w + (cols + 1) * gap + 4; + let panel_h = rows * slot_h + (rows + 1) * gap + 20; + let px_start = (ui_w - panel_w) / 2; + let py_start = (ui_h - panel_h) / 2; + + for row in 0..rows { + for col in 0..cols { + let idx = (row * cols + col) as usize; + let sx = px_start + gap + col * (slot_w + gap) + 2; + let sy = py_start + 10 + row * (slot_h + gap) + gap; + if mouse_ui_x >= sx + && mouse_ui_x < sx + slot_w + && mouse_ui_y >= sy + && mouse_ui_y < sy + slot_h + { + if idx < game.player.inventory.len() { + game.drop_item(idx); + } + } + } + } + } + } + { let (px, _py) = game.player.center(&game.entities); let player_screen_x = ((px as i32 - game.cam_x) as f64) * 8.0 + 4.0; diff --git a/src/render/window_input.rs b/src/render/window_input.rs index f8c749c..92fe640 100644 --- a/src/render/window_input.rs +++ b/src/render/window_input.rs @@ -23,8 +23,14 @@ pub struct WindowInput { pub mouse_y: f64, pub mouse_left: bool, pub mouse_left_was_down: bool, + pub mouse_right: bool, + pub mouse_right_was_down: bool, pub shoot_mouse: bool, + pub inventory_toggle: bool, + pub inventory_click: Option<(f64, f64)>, + pub inventory_right_click: Option<(f64, f64)>, jump_was_down: bool, + inventory_tab_was_down: bool, shoot_left_was_down: bool, shoot_right_was_down: bool, shoot_up_was_down: bool, @@ -60,8 +66,14 @@ impl WindowInput { mouse_y: 0.0, mouse_left: false, mouse_left_was_down: false, + mouse_right: false, + mouse_right_was_down: false, shoot_mouse: false, + inventory_toggle: false, + inventory_click: None, + inventory_right_click: None, jump_was_down: false, + inventory_tab_was_down: false, shoot_left_was_down: false, shoot_right_was_down: false, shoot_up_was_down: false, @@ -93,6 +105,7 @@ impl WindowInput { pub fn clear_keys(&mut self) { self.down_keys.clear(); self.mouse_left = false; + self.mouse_right = false; } pub fn on_mouse_move(&mut self, x: f64, y: f64) { @@ -112,6 +125,12 @@ impl WindowInput { (winit::event::MouseButton::Left, winit::event::ElementState::Released) => { self.mouse_left = false; } + (winit::event::MouseButton::Right, winit::event::ElementState::Pressed) => { + self.mouse_right = true; + } + (winit::event::MouseButton::Right, winit::event::ElementState::Released) => { + self.mouse_right = false; + } _ => {} } } @@ -164,6 +183,20 @@ impl WindowInput { self.shoot_mouse = self.mouse_left && !self.mouse_left_was_down; self.mouse_left_was_down = self.mouse_left; + let now_tab = keys.contains(&KeyCode::Tab); + self.inventory_toggle = now_tab && !self.inventory_tab_was_down; + self.inventory_tab_was_down = now_tab; + + self.inventory_click = None; + self.inventory_right_click = None; + if self.mouse_left && !self.mouse_left_was_down { + self.inventory_click = Some((self.mouse_x, self.mouse_y)); + } + if self.mouse_right && !self.mouse_right_was_down { + self.inventory_right_click = Some((self.mouse_x, self.mouse_y)); + } + self.mouse_right_was_down = self.mouse_right; + self.cam_left = keys.contains(&KeyCode::KeyY); self.cam_right = keys.contains(&KeyCode::KeyU); self.cam_up = keys.contains(&KeyCode::KeyI); diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 676d6d8..0be7bf8 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -464,6 +464,197 @@ impl UiLayer { } } + pub fn draw_inventory_overlay( + &mut self, + screen_w: usize, + screen_h: usize, + player_state: &crate::entity::player::Player, + mouse_ui_x: i32, + mouse_ui_y: i32, + ) -> Vec<(usize, i32, i32)> { + let bg = [15u8, 18, 28]; + let border = [160u8, 180, 255]; + let title_col = [220u8, 230, 255]; + let dim = [120u8, 130, 160]; + let slot_bg = [30u8, 36, 50]; + let slot_border = [80u8, 100, 140]; + let hover_border = [255u8, 220, 100]; + + let cols = 4i32; + let rows = 2i32; + let slot_w = 8i32; + let slot_h = 6i32; + let gap = 2i32; + let panel_w = cols * slot_w + (cols + 1) * gap + 4; + let panel_h = rows * slot_h + (rows + 1) * gap + 20; + + let px_start = (screen_w as i32 - panel_w) / 2; + let py_start = (screen_h as i32 - panel_h) / 2; + + for y in 0..panel_h { + for x in 0..panel_w { + self.set_alpha(px_start + x, py_start + y, ' ', [0, 0, 0], bg, 200); + } + } + for x in 0..panel_w { + if x % 2 == 0 { + self.set_alpha(px_start + x, py_start, '.', border, bg, 240); + self.set_alpha(px_start + x, py_start + panel_h - 1, '.', border, bg, 240); + } + } + for y in 0..panel_h { + if y % 2 == 0 { + self.set_alpha(px_start, py_start + y, '.', border, bg, 240); + self.set_alpha(px_start + panel_w - 1, py_start + y, '.', border, bg, 240); + } + } + self.set_alpha(px_start, py_start, '*', border, bg, 240); + self.set_alpha(px_start + panel_w - 1, py_start, '*', border, bg, 240); + self.set_alpha(px_start, py_start + panel_h - 1, '*', border, bg, 240); + self.set_alpha( + px_start + panel_w - 1, + py_start + panel_h - 1, + '*', + border, + bg, + 240, + ); + + let title = "INVENTORY"; + self.draw_text(px_start + 4, py_start + 3, title, title_col, 255); + let count_text = format!("({}/{} items)", player_state.inventory.len(), cols * rows); + self.draw_text( + px_start + 4 + Self::text_width(title) + 2, + py_start + 3, + &count_text, + dim, + 200, + ); + + let mut hover_slot: Option = None; + let mut slots: Vec<(usize, i32, i32)> = Vec::new(); + + for row in 0..rows { + for col in 0..cols { + let idx = (row * cols + col) as usize; + let sx = px_start + gap + col * (slot_w + gap) + 2; + let sy = py_start + 10 + row * (slot_h + gap) + gap; + + for y in 0..slot_h { + for x in 0..slot_w { + self.set_alpha(sx + x, sy + y, ' ', [0, 0, 0], slot_bg, 200); + } + } + let sb = if mouse_ui_x >= sx + && mouse_ui_x < sx + slot_w + && mouse_ui_y >= sy + && mouse_ui_y < sy + slot_h + { + hover_slot = Some(idx); + hover_border + } else { + slot_border + }; + for x in 0..slot_w { + self.set_alpha(sx + x, sy, '.', sb, slot_bg, 240); + self.set_alpha(sx + x, sy + slot_h - 1, '.', sb, slot_bg, 240); + } + for y in 0..slot_h { + self.set_alpha(sx, sy + y, '.', sb, slot_bg, 240); + self.set_alpha(sx + slot_w - 1, sy + y, '.', sb, slot_bg, 240); + } + + if idx < player_state.inventory.len() { + let item = &player_state.inventory[idx]; + let [ch1, ch2] = item.display_glyph(); + let col = item.color(); + let cx = sx + slot_w / 2 - 1; + let cy = sy + slot_h / 2 - 1; + self.set(cx, cy, ch1, col, slot_bg); + self.set( + cx + 1, + cy, + ch2, + [col[0] / 2, col[1] / 2, col[2] / 2], + slot_bg, + ); + + let label = item.name(); + let label_w = Self::text_width(label) as i32; + let lx = sx + (slot_w - label_w) / 2; + let ly = sy + slot_h - 1; + if lx >= sx && lx + label_w <= sx + slot_w { + self.draw_text(lx, ly, label, dim, 200); + } + + if hover_slot == Some(idx) { + let action = if item.is_consumable() { + "L-click: Use" + } else if item.is_weapon() { + if player_state + .weapon + .as_ref() + .map(|w| w.typ == item.typ) + .unwrap_or(false) + { + "Equipped" + } else { + "L-click: Equip" + } + } else if item.is_armor() { + if player_state + .armor + .as_ref() + .map(|a| a.typ == item.typ) + .unwrap_or(false) + { + "Equipped" + } else { + "L-click: Equip" + } + } else { + "L-click: Use" + }; + self.draw_text(sx, sy + slot_h, action, hover_border, 240); + } + } else if hover_slot == Some(idx) { + self.draw_text(sx + 1, sy + slot_h / 2, "empty", dim, 200); + } + + slots.push((idx, sx, sy)); + } + } + + if player_state.weapon.is_some() || player_state.armor.is_some() { + let eq_y = py_start + panel_h - 6; + self.draw_text(px_start + 4, eq_y, "EQ:", dim, 200); + let mut eq_x = px_start + 8; + if let Some(ref w) = player_state.weapon { + let [c1, c2] = w.display_glyph(); + let col = w.color(); + self.set(eq_x, eq_y, c1, col, bg); + self.set(eq_x + 1, eq_y, c2, [col[0] / 2, col[1] / 2, col[2] / 2], bg); + eq_x += 4; + } + if let Some(ref a) = player_state.armor { + let [c1, c2] = a.display_glyph(); + let col = a.color(); + self.set(eq_x, eq_y, c1, col, bg); + self.set(eq_x + 1, eq_y, c2, [col[0] / 2, col[1] / 2, col[2] / 2], bg); + } + } + + self.draw_text( + px_start + 4, + py_start + panel_h - 3, + "Tab: close L-click: use R-click: drop", + dim, + 200, + ); + + slots + } + pub fn draw_hud( &mut self, screen_w: usize,