1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
mod game;
mod renderer;
mod texture;
use game::{Game, GameState};
use minifb::{Key, Window, WindowOptions};
use renderer::Renderer;
use std::time::Instant;
const SCREEN_W: usize = 640;
const SCREEN_H: usize = 480;
fn main() {
let mut game = Game::new();
let renderer = Renderer::new(SCREEN_W, SCREEN_H);
let mut window = Window::new(
"Dungeon Crawler",
SCREEN_W,
SCREEN_H,
WindowOptions::default(),
)
.expect("Failed to create window");
// Try to centre on screen (best-effort; not all backends support this).
window.set_position(640, 240);
let mut framebuffer = vec![0u32; SCREEN_W * SCREEN_H];
let mut last = Instant::now();
let frame_budget = std::time::Duration::from_micros(16600); // ~60 fps
// Track previous key state for edge-triggered actions.
let mut prev_w = false;
let mut prev_s = false;
let mut prev_a = false;
let mut prev_d = false;
let mut prev_q = false;
let mut prev_e = false;
let mut prev_space = false;
let mut prev_z = false;
let mut prev_x = false;
let mut prev_c = false;
let mut prev_1 = false;
let mut prev_2 = false;
let mut prev_3 = false;
let mut prev_4 = false;
let mut prev_any = false;
while window.is_open() && !window.is_key_down(Key::Escape) {
let frame_start = Instant::now();
let dt = (frame_start - last).as_secs_f64().min(0.1);
last = frame_start;
// ---- read current key states ----
let cur_w = window.is_key_down(Key::W);
let cur_s = window.is_key_down(Key::S);
let cur_a = window.is_key_down(Key::A);
let cur_d = window.is_key_down(Key::D);
let cur_q = window.is_key_down(Key::Q);
let cur_e = window.is_key_down(Key::E);
let cur_space = window.is_key_down(Key::Space);
let cur_z = window.is_key_down(Key::Z);
let cur_x = window.is_key_down(Key::X);
let cur_c = window.is_key_down(Key::C);
let cur_1 = window.is_key_down(Key::Key1);
let cur_2 = window.is_key_down(Key::Key2);
let cur_3 = window.is_key_down(Key::Key3);
let cur_4 = window.is_key_down(Key::Key4);
let cur_any = cur_w
|| cur_s
|| cur_a
|| cur_d
|| cur_q
|| cur_e
|| cur_space
|| cur_z
|| cur_x
|| cur_c
|| cur_1
|| cur_2
|| cur_3
|| cur_4
|| window.is_key_down(Key::Enter)
|| window.is_key_down(Key::Escape);
// ---- input ----
let was_showing_controls = game.show_controls;
if game.show_controls {
if cur_any && !prev_any {
game.show_controls = false;
}
}
// Only process game input if controls were NOT showing at the start
// of this frame (prevents the dismissing key from triggering an action).
if !was_showing_controls && game.can_act() {
if cur_w && !prev_w {
game.move_forward();
} else if cur_s && !prev_s {
game.move_backward();
} else if cur_a && !prev_a {
game.strafe_left();
} else if cur_d && !prev_d {
game.strafe_right();
} else if cur_q && !prev_q {
game.turn_left();
} else if cur_e && !prev_e {
game.turn_right();
} else if cur_space && !prev_space {
if game.on_exit() {
game.descend();
} else {
game.attack();
}
} else if cur_z && !prev_z {
game.use_item();
} else if cur_x && !prev_x {
game.discard_item();
} else if cur_c && !prev_c {
game.skip_turn();
} else if cur_1 && !prev_1 {
game.select_slot(0);
} else if cur_2 && !prev_2 {
game.select_slot(1);
} else if cur_3 && !prev_3 {
game.select_slot(2);
} else if cur_4 && !prev_4 {
game.select_slot(3);
}
}
// Update previous states for next frame.
prev_w = cur_w;
prev_s = cur_s;
prev_a = cur_a;
prev_d = cur_d;
prev_q = cur_q;
prev_e = cur_e;
prev_space = cur_space;
prev_z = cur_z;
prev_x = cur_x;
prev_c = cur_c;
prev_1 = cur_1;
prev_2 = cur_2;
prev_3 = cur_3;
prev_4 = cur_4;
prev_any = cur_any;
// ---- advance animations / state machine ----
game.update_animation(dt);
// ---- death check ----
if game.state == GameState::GameOver {
// Minimum 30 frames (~0.5s) before input is accepted.
if game.death_frames >= 30 && cur_any {
std::process::exit(0);
}
}
// ---- descend prompt ----
// Show the prompt persistently while the player stands on the exit.
if game.on_exit() && game.state == GameState::WaitingForInput {
game.set_message("Press Space to descend...");
}
// ---- render ----
renderer.render(&game, &mut framebuffer);
window
.update_with_buffer(&framebuffer, SCREEN_W, SCREEN_H)
.unwrap();
// Sleep the remainder of the frame budget (saves CPU).
let elapsed = frame_start.elapsed();
if elapsed < frame_budget {
std::thread::sleep(frame_budget - elapsed);
}
}
}
|