#![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 { 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); } } // ---- win check (placeholder — becomes staircase in Phase 1) ---- if game.check_win() { std::process::exit(0); } // ---- 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); } } }