diff --git a/src/game.rs b/src/game.rs index 85bf03e..2a7ea53 100644 --- a/src/game.rs +++ b/src/game.rs @@ -25,6 +25,8 @@ pub struct Game { pub ui: UiLayer, pub cam_x: i32, pub cam_y: i32, + pub cam_offset_x: i32, + pub cam_offset_y: i32, pub running: bool, pub tick: u64, pub fixed_dt: Duration, @@ -56,6 +58,8 @@ impl Game { ui: UiLayer::new(), cam_x: 100, cam_y: 100, + cam_offset_x: 0, + cam_offset_y: 0, running: true, tick: 0, fixed_dt: Duration::from_millis(16), @@ -239,8 +243,8 @@ impl Game { let vw = renderer.viewport_w(); let vh = renderer.viewport_h(); let (px, py) = self.player.center(&self.entities); - self.cam_x = px as i32 - (vw as i32 / 2); - self.cam_y = py as i32 - (vh as i32 / 2); + self.cam_x = px as i32 - (vw as i32 / 2) + self.cam_offset_x; + self.cam_y = py as i32 - (vh as i32 / 2) + self.cam_offset_y; self.build_ui(vw, vh); @@ -520,12 +524,16 @@ impl Game { } pub fn descend(&mut self) { - if let Some(e) = self.player.entity(&self.entities) { - let foot_x = e.cx as i32; - let foot_y = (e.cy + e.half_h).ceil() as i32; - if self.grid.get(foot_x, foot_y).material != MaterialId::Stairs { - return; + let can_descend = match self.player.entity(&self.entities) { + Some(e) => { + let foot_x = e.cx as i32; + let foot_y = (e.cy + e.half_h).ceil() as i32; + self.grid.get(foot_x, foot_y).material == MaterialId::Stairs } + None => false, + }; + if !can_descend { + return; } self.depth += 1; self.grid = Grid::new(); @@ -546,10 +554,18 @@ impl Game { let item = self.player.inventory[index].clone(); let name = item.name(); if item.is_weapon() { + if let Some(old) = self.player.weapon.take() { + self.player.inventory.push(old); + } self.player.weapon = Some(item); + self.player.inventory.remove(index); self.ui.add_message(&format!("Equipped {}", name)); } else if item.is_armor() { + if let Some(old) = self.player.armor.take() { + self.player.inventory.push(old); + } self.player.armor = Some(item); + self.player.inventory.remove(index); self.ui.add_message(&format!("Equipped {}", name)); } else if item.is_consumable() { let heal = item.heal_amount(); @@ -752,11 +768,16 @@ impl Game { let jump_phase = tick % 60; if jump_phase == 0 && dist < 40.0 { let dir_x = dx / dist; - let _dir_y = dy / dist; + let dir_y = dy / dist; let jump_power = 0.8 + (1.0 - dist / 40.0).min(0.5) * 0.5; if let Some(e) = self.entities.all_mut().get_mut(idx) { e.set_horizontal_vel(dir_x * jump_power); - e.set_vertical_vel(-jump_power * 0.8); + let vy = if dy < -1.0 { + -jump_power * 0.8 + } else { + -jump_power * 0.6 + }; + e.set_vertical_vel(vy + dir_y * jump_power * 0.3); } } else if jump_phase == 30 { if let Some(e) = self.entities.all_mut().get_mut(idx) { @@ -768,6 +789,14 @@ impl Game { fn update_combat(&mut self) { let player_id = self.player.entity_id; + let player_alive = self + .entities + .get(player_id) + .map(|e| e.alive) + .unwrap_or(false); + if !player_alive { + return; + } let player_center = self.player.center(&self.entities); let player_half_w = self .entities @@ -1196,7 +1225,10 @@ impl Game { substeps: u32, ) { let grid = &self.grid; - let e = self.entities.all_mut().get_mut(idx).unwrap(); + let e = match self.entities.all_mut().get_mut(idx) { + Some(e) => e, + None => return, + }; let alive = e.alive; let bodies = &mut e.bodies; let constraints = &e.constraints; diff --git a/src/main.rs b/src/main.rs index d195563..d4d5db2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -229,6 +229,9 @@ fn run_gpu_mode(title: &str) { } => { input.on_key_event(key_event.physical_key, key_event.state); } + WindowEvent::Focused(false) => { + input.clear_keys(); + } _ => {} }, Event::AboutToWait => { @@ -296,16 +299,16 @@ fn run_gpu_mode(title: &str) { } if input.cam_left { - game.cam_x -= 3; + game.cam_offset_x -= 3; } if input.cam_right { - game.cam_x += 3; + game.cam_offset_x += 3; } if input.cam_up { - game.cam_y -= 3; + game.cam_offset_y -= 3; } if input.cam_down { - game.cam_y += 3; + game.cam_offset_y += 3; } if let Some(brush_id) = input.paint { @@ -344,8 +347,8 @@ fn run_gpu_mode(title: &str) { } let (px, py) = game.player.center(&game.entities); - game.cam_x = px as i32 - (vw as i32 / 2); - game.cam_y = py as i32 - (vh as i32 / 2); + game.cam_x = px as i32 - (vw as i32 / 2) + game.cam_offset_x; + game.cam_y = py as i32 - (vh as i32 / 2) + game.cam_offset_y; game.build_ui(vw, vh); diff --git a/src/physics/projectile.rs b/src/physics/projectile.rs index 6b23e6d..aa2124a 100644 --- a/src/physics/projectile.rs +++ b/src/physics/projectile.rs @@ -143,7 +143,7 @@ impl Projectile { } } } - if entity.id != self.owner && entity.id != 0 { + if entity.id != self.owner { let before = entity.health; entity.take_damage(self.total_damage()); ui.add_damage_number( @@ -155,7 +155,7 @@ impl Projectile { return; } - if entity.id == self.owner || entity.id == 0 { + if entity.id == self.owner { return; } diff --git a/src/render/capture.rs b/src/render/capture.rs index df48b39..8bd1322 100644 --- a/src/render/capture.rs +++ b/src/render/capture.rs @@ -86,7 +86,7 @@ fn background_color(wx: i32, wy: i32, vy: i32, view_h: i32) -> [u8; 3] { let base_g = (10.0 + t * 25.0) as u8; let base_b = (25.0 + t * 35.0) as u8; - let hash = ((wx.wrapping_mul(73856093)) ^ (wy.wrapping_mul(19349663))).abs(); + let hash = ((wx.wrapping_mul(73856093)) ^ (wy.wrapping_mul(19349663))).wrapping_abs(); if hash % 80 == 0 { let brightness = (60 + (hash % 120) as u8).min(255); return [brightness, brightness, brightness + 20]; diff --git a/src/render/graphics.rs b/src/render/graphics.rs index 34b4e77..d1b25be 100644 --- a/src/render/graphics.rs +++ b/src/render/graphics.rs @@ -28,7 +28,7 @@ fn background_color(wx: i32, wy: i32, vy: i32, view_h: i32) -> [u8; 4] { let base_g = (10.0 + t * 25.0) as u8; let base_b = (25.0 + t * 35.0) as u8; - let hash = ((wx.wrapping_mul(73856093)) ^ (wy.wrapping_mul(19349663))).abs(); + let hash = ((wx.wrapping_mul(73856093)) ^ (wy.wrapping_mul(19349663))).wrapping_abs(); if hash % 80 == 0 { let brightness = (60 + (hash % 120) as u8).min(255); return [brightness, brightness, brightness + 20, 255]; diff --git a/src/render/lighting.rs b/src/render/lighting.rs index fdd705f..e3804fc 100644 --- a/src/render/lighting.rs +++ b/src/render/lighting.rs @@ -121,7 +121,7 @@ pub fn compute_lighting( let mut grid_light = LightGrid::new(view_w, view_h); grid_light.clear(ambient); - let sources = gather_sources(grid); + let sources = gather_sources_in_range(grid, cam_x, cam_y, view_w, view_h, 30); let cap = sources.len().min(32); let radius_limit = 30u32; diff --git a/src/render/vulkan.rs b/src/render/vulkan.rs index 892a5e3..93fa870 100644 --- a/src/render/vulkan.rs +++ b/src/render/vulkan.rs @@ -32,7 +32,7 @@ fn background_color(wx: i32, wy: i32, vy: i32, view_h: i32) -> [u8; 4] { let base_g = (10.0 + t * 25.0) as u8; let base_b = (25.0 + t * 35.0) as u8; - let hash = ((wx.wrapping_mul(73856093)) ^ (wy.wrapping_mul(19349663))).abs(); + let hash = ((wx.wrapping_mul(73856093)) ^ (wy.wrapping_mul(19349663))).wrapping_abs(); if hash % 80 == 0 { let brightness = (60 + (hash % 120) as u8).min(255); return [brightness, brightness, brightness + 20, 255]; diff --git a/src/render/window_input.rs b/src/render/window_input.rs index 01ff876..9a9d659 100644 --- a/src/render/window_input.rs +++ b/src/render/window_input.rs @@ -80,6 +80,10 @@ impl WindowInput { } } + pub fn clear_keys(&mut self) { + self.down_keys.clear(); + } + pub fn update(&mut self) { let keys = &self.down_keys; diff --git a/src/ui/mod.rs b/src/ui/mod.rs index b4d561d..b4facf0 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -183,7 +183,7 @@ impl UiLayer { }; for i in 0..width { let x = screen_x + i; - let ch = if i < filled { 'โ–ˆ' } else { 'โ–‘' }; + let ch = if i < filled { '#' } else { '-' }; let fg = if i < filled { color } else { [80, 80, 80] }; self.set(x, screen_y, ch, fg, [0, 0, 0]); } @@ -212,19 +212,19 @@ impl UiLayer { let icon_x = sx + (e.half_w as i32 * UI_SCALE) + 1; let mut icon_y = sy - (e.half_h as i32 * UI_SCALE) - 7; if e.on_fire { - self.set(icon_x, icon_y, '๐Ÿ”ฅ', [255, 100, 20], [0, 0, 0]); + self.set(icon_x, icon_y, 'F', [255, 100, 20], [0, 0, 0]); icon_y += 1; } if e.poisoned { - self.set(icon_x, icon_y, 'โ˜ ', [80, 255, 80], [0, 0, 0]); + self.set(icon_x, icon_y, 'P', [80, 255, 80], [0, 0, 0]); icon_y += 1; } if e.frozen { - self.set(icon_x, icon_y, 'โ„', [120, 220, 255], [0, 0, 0]); + self.set(icon_x, icon_y, 'I', [120, 220, 255], [0, 0, 0]); icon_y += 1; } if e.bleeding { - self.set(icon_x, icon_y, 'โœš', [255, 40, 40], [0, 0, 0]); + self.set(icon_x, icon_y, 'B', [255, 40, 40], [0, 0, 0]); } } } @@ -286,7 +286,7 @@ impl UiLayer { [40, 40, 50] }; let bg = [20, 20, 30]; - self.set(start_x + dx, start_y + dy, 'ยท', fg, bg); + self.set(start_x + dx, start_y + dy, '.', fg, bg); } } for e in entities { @@ -308,20 +308,20 @@ impl UiLayer { } } for dx in 0..size { - self.set(start_x + dx, start_y - 1, 'โ”€', [80, 80, 100], [0, 0, 0]); - self.set(start_x + dx, start_y + size, 'โ”€', [80, 80, 100], [0, 0, 0]); + self.set(start_x + dx, start_y - 1, '-', [80, 80, 100], [0, 0, 0]); + self.set(start_x + dx, start_y + size, '-', [80, 80, 100], [0, 0, 0]); } for dy in 0..size { - self.set(start_x - 1, start_y + dy, 'โ”‚', [80, 80, 100], [0, 0, 0]); - self.set(start_x + size, start_y + dy, 'โ”‚', [80, 80, 100], [0, 0, 0]); + self.set(start_x - 1, start_y + dy, '|', [80, 80, 100], [0, 0, 0]); + self.set(start_x + size, start_y + dy, '|', [80, 80, 100], [0, 0, 0]); } - self.set(start_x - 1, start_y - 1, 'โ”Œ', [80, 80, 100], [0, 0, 0]); - self.set(start_x + size, start_y - 1, 'โ”', [80, 80, 100], [0, 0, 0]); - self.set(start_x - 1, start_y + size, 'โ””', [80, 80, 100], [0, 0, 0]); + self.set(start_x - 1, start_y - 1, '+', [80, 80, 100], [0, 0, 0]); + self.set(start_x + size, start_y - 1, '+', [80, 80, 100], [0, 0, 0]); + self.set(start_x - 1, start_y + size, '+', [80, 80, 100], [0, 0, 0]); self.set( start_x + size, start_y + size, - 'โ”˜', + '+', [80, 80, 100], [0, 0, 0], ); @@ -351,23 +351,23 @@ impl UiLayer { } for x in 0..w { if (start_x + x) % 2 == 0 { - self.set_alpha(start_x + x, start_y, 'ยท', border, bg, border_alpha); - self.set_alpha(start_x + x, start_y + h - 1, 'ยท', border, bg, border_alpha); + self.set_alpha(start_x + x, start_y, '.', border, bg, border_alpha); + self.set_alpha(start_x + x, start_y + h - 1, '.', border, bg, border_alpha); } } for y in 0..h { if (start_y + y) % 2 == 0 { - self.set_alpha(start_x, start_y + y, 'ยท', border, bg, border_alpha); - self.set_alpha(start_x + w - 1, start_y + y, 'ยท', border, bg, border_alpha); + self.set_alpha(start_x, start_y + y, '.', border, bg, border_alpha); + self.set_alpha(start_x + w - 1, start_y + y, '.', border, bg, border_alpha); } } - self.set_alpha(start_x, start_y, 'โ—†', border, bg, border_alpha); - self.set_alpha(start_x + w - 1, start_y, 'โ—†', border, bg, border_alpha); - self.set_alpha(start_x, start_y + h - 1, 'โ—†', border, bg, border_alpha); + self.set_alpha(start_x, start_y, '*', border, bg, border_alpha); + self.set_alpha(start_x + w - 1, start_y, '*', border, bg, border_alpha); + self.set_alpha(start_x, start_y + h - 1, '*', border, bg, border_alpha); self.set_alpha( start_x + w - 1, start_y + h - 1, - 'โ—†', + '*', border, bg, border_alpha, @@ -389,7 +389,7 @@ impl UiLayer { }; self.draw_text(start_x + 2, start_y + 7, "HP", fg, 255); for i in 0..32 { - let ch = if i < hp_filled { 'โ–ˆ' } else { 'โ–‘' }; + let ch = if i < hp_filled { '#' } else { '-' }; let c = if i < hp_filled { bar_color } else { @@ -404,7 +404,7 @@ impl UiLayer { let xp_filled = (xp_ratio * 32.0).round() as i32; self.draw_text(start_x + 2, start_y + 19, "XP", fg, 255); for i in 0..32 { - let ch = if i < xp_filled { 'โ–ˆ' } else { 'โ–‘' }; + let ch = if i < xp_filled { '#' } else { '-' }; let c = if i < xp_filled { [80, 160, 240] } else { @@ -431,16 +431,16 @@ impl UiLayer { let status = if let Some(p) = player { let mut s = String::new(); if p.on_fire { - s.push('๐Ÿ”ฅ'); + s.push_str("[FIRE] "); } if p.poisoned { - s.push('โ˜ '); + s.push_str("[PSN] "); } if p.frozen { - s.push('โ„'); + s.push_str("[ICE] "); } if p.bleeding { - s.push('โœš'); + s.push_str("[BLD] "); } s } else { @@ -508,7 +508,7 @@ impl UiLayer { } for x in 0..screen_w { if x % 2 == 0 { - self.set_alpha(x as i32, y_top - 1, 'ยท', border, bg, 220); + self.set_alpha(x as i32, y_top - 1, '.', border, bg, 220); } } @@ -524,7 +524,7 @@ impl UiLayer { [255, 60, 60] }; for i in 0..28 { - let ch = if i < hp_filled { 'โ–ˆ' } else { 'โ–‘' }; + let ch = if i < hp_filled { '#' } else { '-' }; let c = if i < hp_filled { bar_fg } else { [70, 70, 90] }; self.set(10 + i, y_row1 + 4, ch, c, bg); } @@ -533,7 +533,7 @@ impl UiLayer { } let brush_color = brush_color(brush); - self.set(0, y_row2 + 4, 'โ– ', brush_color, bg); + self.set(0, y_row2 + 4, '#', brush_color, bg); let brush_text = format!(" {}", brush_name); self.draw_text(1, y_row2, &brush_text, [200, 200, 140], 255); @@ -573,6 +573,9 @@ impl UiLayer { let mut yy = y; let messages: Vec<(String, u32)> = self.messages.iter().rev().take(8).cloned().collect(); for (msg, life) in messages { + if yy < 0 { + break; + } let fade = (life as f32 / 300.0).clamp(0.3, 1.0); let fg = [ (200.0 * fade) as u8, @@ -732,6 +735,18 @@ fn char_bitmap(c: char) -> Option<[u8; 5]> { '?' => Some([0b111, 0b001, 0b011, 0b000, 0b010]), '!' => Some([0b010, 0b010, 0b010, 0b000, 0b010]), '.' => Some([0b000, 0b000, 0b000, 0b000, 0b010]), + ',' => Some([0b000, 0b000, 0b000, 0b010, 0b100]), + '\'' => Some([0b010, 0b010, 0b000, 0b000, 0b000]), + '+' => Some([0b000, 0b010, 0b111, 0b010, 0b000]), + '=' => Some([0b000, 0b111, 0b000, 0b111, 0b000]), + '>' => Some([0b100, 0b010, 0b001, 0b010, 0b100]), + '<' => Some([0b001, 0b010, 0b100, 0b010, 0b001]), + '*' => Some([0b000, 0b101, 0b010, 0b101, 0b000]), + '%' => Some([0b101, 0b001, 0b010, 0b100, 0b101]), + '#' => Some([0b101, 0b111, 0b101, 0b111, 0b101]), + '|' => Some([0b010, 0b010, 0b010, 0b010, 0b010]), + '@' => Some([0b111, 0b101, 0b111, 0b101, 0b111]), + '_' => Some([0b000, 0b000, 0b000, 0b000, 0b111]), _ => None, } }