Files
dungeoncraft/src/main.rs
T
2026-11-02 22:24:34 +01:00

276 lines
10 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// DungeonCraft – Dungeon-Aufbau & Verteidigung im Browser (Rust + macroquad, WASM).
mod game;
mod gfx;
mod map;
mod render;
mod sound;
mod ui;
use game::Game;
use gfx::Gfx;
use macroquad::prelude::*;
use map::{idx, inb, MH, MW};
use render::{draw_world, Overlay, View, ZOOMS};
use sound::SoundBank;
use ui::{hover_info, Tool, Ui, UiAction, TOOLS};
fn conf() -> Conf {
Conf { window_title: "DungeonCraft".into(), window_width: 1280, window_height: 800, high_dpi: false, ..Default::default() }
}
struct Input {
ldown: Option<(i32, i32)>, // Start des Auswahlrechtecks
rdown: Option<(f32, f32)>, // Startpunkt rechte Maustaste
rlast: (f32, f32),
rdrag: bool,
minimap_drag: bool,
}
fn new_game(seed: u32) -> (Game, View) {
let g = Game::new(seed);
let (cx, cy) = g.map.core;
let zoom = 32.0;
let view = View { cam_x: cx as f32 + 1.5 - screen_width() / zoom * 0.5, cam_y: cy as f32 + 1.5 - screen_height() / zoom * 0.5, zoom };
(g, view)
}
fn clamp_cam(v: &mut View) {
let vw = screen_width() / v.zoom;
let vh = screen_height() / v.zoom;
v.cam_x = v.cam_x.clamp(-vw * 0.5, MW as f32 - vw * 0.5);
v.cam_y = v.cam_y.clamp(-vh * 0.5, MH as f32 - vh * 0.5);
}
fn center_on(v: &mut View, wx: f32, wy: f32) {
v.cam_x = wx - screen_width() / v.zoom * 0.5;
v.cam_y = wy - screen_height() / v.zoom * 0.5;
}
#[macroquad::main(conf)]
async fn main() {
let gfx = Gfx::new();
let mut sounds = SoundBank::new();
sounds.generate_all().await;
let mut seed = (miniquad::date::now() * 1000.0) as u32 | 1;
let (mut game, mut view) = new_game(seed);
let mut ui = Ui::new();
let mut inp = Input { ldown: None, rdown: None, rlast: (0.0, 0.0), rdrag: false, minimap_drag: false };
loop {
let dt = get_frame_time();
let now = get_time();
let (mx, my) = mouse_position();
let over_ui = ui.over_ui(mx, my);
let (wx, wy) = view.world(mx, my);
let hover_tile = (wx.floor() as i32, wy.floor() as i32);
// ---------------- Tastatur
for (i, k) in [
KeyCode::Key1,
KeyCode::Key2,
KeyCode::Key3,
KeyCode::Key4,
KeyCode::Key5,
KeyCode::Key6,
KeyCode::Key7,
KeyCode::Key8,
KeyCode::Key9,
KeyCode::Key0,
KeyCode::Q,
KeyCode::E,
]
.iter()
.enumerate()
{
if is_key_pressed(*k) {
ui.tool = TOOLS[i];
inp.ldown = None;
}
}
if is_key_pressed(KeyCode::H) || (is_key_pressed(KeyCode::Escape) && ui.show_help) {
ui.show_help = !ui.show_help;
}
if is_key_pressed(KeyCode::Escape) {
inp.ldown = None;
}
if (is_key_pressed(KeyCode::P) || is_key_pressed(KeyCode::Space)) && game.state == 0 {
game.paused = !game.paused;
}
if is_key_pressed(KeyCode::M) {
sounds.muted = !sounds.muted;
}
if is_key_pressed(KeyCode::Equal) || is_key_pressed(KeyCode::KpAdd) {
sounds.change_volume(0.1);
}
if is_key_pressed(KeyCode::Minus) || is_key_pressed(KeyCode::KpSubtract) {
sounds.change_volume(-0.1);
}
if is_key_pressed(KeyCode::N) && game.state != 0 {
seed = seed.wrapping_mul(1103515245).wrapping_add(12345) | 1;
(game, view) = new_game(seed);
ui.show_help = false;
}
// ---------------- Kamera
let pan = 900.0 * dt / view.zoom;
if is_key_down(KeyCode::W) || is_key_down(KeyCode::Up) {
view.cam_y -= pan;
}
if is_key_down(KeyCode::S) || is_key_down(KeyCode::Down) {
view.cam_y += pan;
}
if is_key_down(KeyCode::A) || is_key_down(KeyCode::Left) {
view.cam_x -= pan;
}
if is_key_down(KeyCode::D) || is_key_down(KeyCode::Right) {
view.cam_x += pan;
}
// Bildschirmrand
let edge = 6.0;
if !ui.show_help && mx >= 0.0 && my >= 0.0 && mx < screen_width() && my < screen_height() {
if mx < edge {
view.cam_x -= pan;
} else if mx > screen_width() - edge {
view.cam_x += pan;
}
if my < edge {
view.cam_y -= pan;
} else if my > screen_height() - edge {
view.cam_y += pan;
}
}
// Zoom
let (_, wheel) = mouse_wheel();
if wheel != 0.0 && !over_ui {
let zi = ZOOMS.iter().position(|&z| z >= view.zoom).unwrap_or(2) as i32;
let ni = (zi + if wheel > 0.0 { 1 } else { -1 }).clamp(0, ZOOMS.len() as i32 - 1) as usize;
if ZOOMS[ni] != view.zoom {
view.zoom = ZOOMS[ni];
view.cam_x = wx - mx / view.zoom;
view.cam_y = wy - my / view.zoom;
}
}
// Rechte Maustaste: ziehen = verschieben, klicken = absetzen
if is_mouse_button_pressed(MouseButton::Right) {
inp.rdown = Some((mx, my));
inp.rlast = (mx, my);
inp.rdrag = false;
}
if let Some((sx, sy)) = inp.rdown {
if is_mouse_button_down(MouseButton::Right) {
if (mx - sx).abs() + (my - sy).abs() > 5.0 {
inp.rdrag = true;
}
if inp.rdrag {
view.cam_x -= (mx - inp.rlast.0) / view.zoom;
view.cam_y -= (my - inp.rlast.1) / view.zoom;
}
inp.rlast = (mx, my);
}
}
if is_mouse_button_released(MouseButton::Right) {
if !inp.rdrag && !over_ui && game.state == 0 {
if inp.ldown.is_some() {
inp.ldown = None;
} else {
game.drop(wx, wy);
}
}
inp.rdown = None;
}
// ---------------- Linke Maustaste
if is_mouse_button_pressed(MouseButton::Left) {
match ui.click(mx, my, &game) {
UiAction::SelectTool(t) => {
ui.tool = t;
game.sfx_ui(game::Sfx::Click);
}
UiAction::MinimapJump(x, y) => {
center_on(&mut view, x, y);
inp.minimap_drag = true;
}
UiAction::CloseHelp => ui.show_help = false,
UiAction::NewGame => {
seed = seed.wrapping_mul(1103515245).wrapping_add(12345) | 1;
(game, view) = new_game(seed);
ui.show_help = false;
}
UiAction::Consumed => {}
UiAction::None => {
if game.state == 0 && inb(hover_tile.0, hover_tile.1) {
match ui.tool {
Tool::Dig | Tool::Room(_) | Tool::Sell => inp.ldown = Some(hover_tile),
Tool::Hand => {
game.pickup(wx, wy);
}
Tool::Flag => game.set_flag(hover_tile.0, hover_tile.1),
Tool::Lightning => game.lightning(wx, wy),
Tool::Heal => game.heal(wx, wy),
Tool::Summon => game.summon(wx, wy),
}
}
}
}
}
if inp.minimap_drag {
if is_mouse_button_down(MouseButton::Left) && ui.minimap_contains(mx, my) {
let (x, y) = ui.minimap_world(mx, my);
center_on(&mut view, x, y);
} else if !is_mouse_button_down(MouseButton::Left) {
inp.minimap_drag = false;
}
}
if is_mouse_button_released(MouseButton::Left) {
if let Some((sx, sy)) = inp.ldown.take() {
let (ex, ey) = (hover_tile.0.clamp(0, MW as i32 - 1), hover_tile.1.clamp(0, MH as i32 - 1));
match ui.tool {
Tool::Dig => {
let on = !game.map.t[idx(sx, sy)].marked;
game.mark_rect(sx, sy, ex, ey, on);
}
Tool::Room(r) => game.build_rect(r, sx, sy, ex, ey),
Tool::Sell => game.sell_rect(sx, sy, ex, ey),
_ => {}
}
}
}
clamp_cam(&mut view);
// ---------------- Update
game.update(dt);
for ev in game.events.drain(..) {
sounds.play(ev, &view, now);
}
for m in game.msgs.drain(..) {
ui.log.push((m, now));
}
// ---------------- Zeichnen
clear_background(Color::from_rgba(8, 6, 8, 255));
let (hover_color, radius) = match ui.tool {
Tool::Dig => (Color::new(1.0, 0.85, 0.3, 1.0), None),
Tool::Hand => (Color::new(0.9, 0.75, 0.6, 1.0), None),
Tool::Room(_) => (Color::new(0.4, 0.85, 1.0, 1.0), None),
Tool::Sell => (Color::new(1.0, 0.5, 0.3, 1.0), None),
Tool::Flag => (Color::new(1.0, 0.3, 0.3, 1.0), None),
Tool::Lightning => (Color::new(1.0, 1.0, 0.6, 1.0), Some((wx, wy, 1.6))),
Tool::Heal => (Color::new(0.4, 1.0, 0.5, 1.0), Some((wx, wy, 2.55))),
Tool::Summon => (Color::new(0.8, 0.5, 1.0, 1.0), None),
};
let ov = Overlay {
hover: if over_ui || !inb(hover_tile.0, hover_tile.1) { None } else { Some(hover_tile) },
hover_color,
rect: inp.ldown.map(|(sx, sy)| (sx, sy, hover_tile.0.clamp(0, MW as i32 - 1), hover_tile.1.clamp(0, MH as i32 - 1))),
rect_color: hover_color,
radius: if over_ui { None } else { radius },
};
draw_world(&game, &gfx, &view, now, &ov);
let info = if over_ui { Vec::new() } else { hover_info(&game, wx, wy) };
ui.draw(&game, &gfx, &view, &sounds, &info, now);
next_frame().await
}
}