summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-07-11 14:01:49 +0200
committersalmonik <salmonik@tuta.io>2026-07-11 14:01:49 +0200
commit1e57b563280582c9bd1c385d4b44458d66885b8b (patch)
tree54c81a13b73511ff6419f5385e36e4ee3f680311
parentCreate AGENTS.md (diff)
downloaddungeon-1e57b563280582c9bd1c385d4b44458d66885b8b.tar.gz
dungeon-1e57b563280582c9bd1c385d4b44458d66885b8b.zip
end game screen, fix occlusion
-rw-r--r--src/game.rs38
-rw-r--r--src/main.rs5
-rw-r--r--src/renderer.rs69
3 files changed, 96 insertions, 16 deletions
diff --git a/src/game.rs b/src/game.rs
index a17526b..f66ca40 100644
--- a/src/game.rs
+++ b/src/game.rs
@@ -544,7 +544,11 @@ fn place_entities(
544 } 544 }
545 545
546 // --- hallucinated pickups --- 546 // --- hallucinated pickups ---
547 let fake_count = if depth >= 3 { rng.random_range(1..=2) } else { 0 }; 547 let fake_count = if depth >= 3 {
548 rng.random_range(1..=2)
549 } else {
550 0
551 };
548 for _ in 0..fake_count { 552 for _ in 0..fake_count {
549 if next >= candidates.len() { 553 if next >= candidates.len() {
550 break; 554 break;
@@ -673,6 +677,9 @@ pub struct Game {
673 677
674 // ---- controls overlay ---- 678 // ---- controls overlay ----
675 pub show_controls: bool, 679 pub show_controls: bool,
680
681 // ---- death screen ----
682 pub death_frames: u32,
676} 683}
677 684
678fn smoothstep(t: f64) -> f64 { 685fn smoothstep(t: f64) -> f64 {
@@ -724,6 +731,7 @@ impl Game {
724 flash_color: 0, 731 flash_color: 0,
725 flash_alpha: 0.0, 732 flash_alpha: 0.0,
726 show_controls: true, 733 show_controls: true,
734 death_frames: 0,
727 } 735 }
728 } 736 }
729 737
@@ -802,10 +810,11 @@ impl Game {
802 if done { 810 if done {
803 self.state = GameState::ResolvingTurn; 811 self.state = GameState::ResolvingTurn;
804 self.resolve_turn(); 812 self.resolve_turn();
805 self.state = GameState::WaitingForInput; 813 if self.state != GameState::GameOver {
814 self.state = GameState::WaitingForInput;
815 }
806 return true; 816 return true;
807 } 817 }
808 false
809 } 818 }
810 819
811 GameState::AnimatingEnemies => { 820 GameState::AnimatingEnemies => {
@@ -814,14 +823,22 @@ impl Game {
814 self.enemy_anim_t = 0.0; 823 self.enemy_anim_t = 0.0;
815 self.state = GameState::ResolvingTurn; 824 self.state = GameState::ResolvingTurn;
816 self.resolve_turn(); 825 self.resolve_turn();
817 self.state = GameState::WaitingForInput; 826 if self.state != GameState::GameOver {
827 self.state = GameState::WaitingForInput;
828 }
818 return true; 829 return true;
819 } 830 }
820 false
821 } 831 }
822 832
823 _ => false, 833 _ => {}
834 }
835
836 // Count frames since death so the death screen has a minimum display time.
837 if self.state == GameState::GameOver {
838 self.death_frames += 1;
824 } 839 }
840
841 false
825 } 842 }
826 843
827 /// Attempt to walk one tile in grid direction (dx, dy). 844 /// Attempt to walk one tile in grid direction (dx, dy).
@@ -1160,7 +1177,9 @@ impl Game {
1160 fn advance_turn(&mut self) { 1177 fn advance_turn(&mut self) {
1161 self.state = GameState::ResolvingTurn; 1178 self.state = GameState::ResolvingTurn;
1162 self.resolve_turn(); 1179 self.resolve_turn();
1163 self.state = GameState::WaitingForInput; 1180 if self.state != GameState::GameOver {
1181 self.state = GameState::WaitingForInput;
1182 }
1164 } 1183 }
1165 1184
1166 /// Apply all per-turn effects (weakness, heartbeat, HP drain, message decay). 1185 /// Apply all per-turn effects (weakness, heartbeat, HP drain, message decay).
@@ -1248,4 +1267,9 @@ impl Game {
1248 pub fn weakness_factor(&self) -> f64 { 1267 pub fn weakness_factor(&self) -> f64 {
1249 (self.weakness as f64 / MAX_WEAKNESS as f64) * 2.0 1268 (self.weakness as f64 / MAX_WEAKNESS as f64) * 2.0
1250 } 1269 }
1270
1271 /// Compute the final score.
1272 pub fn score(&self) -> u64 {
1273 (self.kills * 100) + (self.depth.pow(2) * 50) + (self.weapon_base_damage as u64 * 500)
1274 }
1251} 1275}
diff --git a/src/main.rs b/src/main.rs
index a59edcd..e82d939 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -150,7 +150,10 @@ fn main() {
150 150
151 // ---- death check ---- 151 // ---- death check ----
152 if game.state == GameState::GameOver { 152 if game.state == GameState::GameOver {
153 std::process::exit(0); 153 // Minimum 30 frames (~0.5s) before input is accepted.
154 if game.death_frames >= 30 && cur_any {
155 std::process::exit(0);
156 }
154 } 157 }
155 158
156 // ---- win check (placeholder — becomes staircase in Phase 1) ---- 159 // ---- win check (placeholder — becomes staircase in Phase 1) ----
diff --git a/src/renderer.rs b/src/renderer.rs
index 6eff9fa..aa87b54 100644
--- a/src/renderer.rs
+++ b/src/renderer.rs
@@ -1,4 +1,4 @@
1use crate::game::{Entity, Game, PickupType}; 1use crate::game::{Entity, Game, GameState, PickupType};
2use crate::texture::TEX_SIZE; 2use crate::texture::TEX_SIZE;
3 3
4// ---- simple 5×5 ASCII glyphs ---- 4// ---- simple 5×5 ASCII glyphs ----
@@ -287,6 +287,11 @@ impl Renderer {
287 } 287 }
288 } 288 }
289 } 289 }
290
291 // ---- death screen ----
292 if game.state == GameState::GameOver {
293 self.draw_death_screen(buffer, game);
294 }
290 } 295 }
291 296
292 // ---- entity sprites --------------------------------------------------- 297 // ---- entity sprites ---------------------------------------------------
@@ -350,13 +355,6 @@ impl Renderer {
350 let entity_h = (self.screen_h as f64 / cam_z * 0.4) as isize; 355 let entity_h = (self.screen_h as f64 / cam_z * 0.4) as isize;
351 let sy = (half_h - entity_h as f64 * 0.3) as isize; 356 let sy = (half_h - entity_h as f64 * 0.3) as isize;
352 357
353 // Occlusion: entity is visible only if its centre column is not behind a wall.
354 // Compare Euclidean distance to entity vs Euclidean distance to wall.
355 let col = sx.clamp(0, self.screen_w as isize - 1) as usize;
356 if cam_z >= wall_euclidean[col] {
357 continue;
358 }
359
360 let scale = (entity_h.max(6) as f64 / LETTER_H as f64).max(0.5); 358 let scale = (entity_h.max(6) as f64 / LETTER_H as f64).max(0.5);
361 let (ch, color) = entity_char_and_color(entity); 359 let (ch, color) = entity_char_and_color(entity);
362 360
@@ -585,6 +583,61 @@ impl Renderer {
585 let hint_w = hint.len() * (LETTER_W + 1) - 1; 583 let hint_w = hint.len() * (LETTER_W + 1) - 1;
586 self.draw_text(buffer, cx - hint_w / 2, y, hint, hint_color); 584 self.draw_text(buffer, cx - hint_w / 2, y, hint, hint_color);
587 } 585 }
586
587 fn draw_death_screen(&self, buffer: &mut [u32], game: &Game) {
588 // Full darken.
589 for py in 0..self.screen_h {
590 for px in 0..self.screen_w {
591 let idx = py * self.screen_w + px;
592 let c = buffer[idx];
593 let r = ((c >> 16) & 0xFF) as u32;
594 let g = ((c >> 8) & 0xFF) as u32;
595 let b = (c & 0xFF) as u32;
596 buffer[idx] = 0xFF00_0000 | ((r / 5) << 16) | ((g / 5) << 8) | (b / 5);
597 }
598 }
599
600 let cx = self.screen_w / 2;
601 let mut y = self.screen_h as usize / 2 - 40;
602
603 let title_color = 0xFF_CC3333;
604 let text_color = 0xFF_CCCCCC;
605 let score_color = 0xFF_CC8844;
606 let hint_color = 0xFF_666666;
607
608 let title = "YOU HAVE DIED";
609 let title_w = title.len() * (LETTER_W + 1) - 1;
610 self.draw_text(buffer, cx - title_w / 2, y, title, title_color);
611 y += LETTER_H + 12;
612
613 let msg = "YOU HAD BECOME YET ANOTHER";
614 let msg_w = msg.len() * (LETTER_W + 1) - 1;
615 self.draw_text(buffer, cx - msg_w / 2, y, msg, text_color);
616 y += LETTER_H + 4;
617
618 let msg2 = "LEGEND OF THIS DUNGEON.";
619 let msg2_w = msg2.len() * (LETTER_W + 1) - 1;
620 self.draw_text(buffer, cx - msg2_w / 2, y, msg2, text_color);
621 y += LETTER_H + 16;
622
623 let score = game.score();
624 let score_str = format!("SCORE: {}", score);
625 let score_w = score_str.len() * (LETTER_W + 1) - 1;
626 self.draw_text(buffer, cx - score_w / 2, y, &score_str, score_color);
627 y += LETTER_H + 4;
628
629 let kills_str = format!(
630 "KILLS: {} DEPTH: {} WPN:{}",
631 game.kills, game.depth, game.weapon_base_damage
632 );
633 let kills_w = kills_str.len() * (LETTER_W + 1) - 1;
634 self.draw_text(buffer, cx - kills_w / 2, y, &kills_str, text_color);
635 y += LETTER_H + 20;
636
637 let hint = "PRESS ANY KEY TO EXIT";
638 let hint_w = hint.len() * (LETTER_W + 1) - 1;
639 self.draw_text(buffer, cx - hint_w / 2, y, hint, hint_color);
640 }
588} 641}
589 642
590// ---- helpers ---- 643// ---- helpers ----