Title screen, pause menu, settings dialog and savegames
- title screen (continue/new/load/settings/help) and Esc pause menu - settings: language & sound, camera, graphics, game, rebindable keys; stored in localStorage and in every savegame (always applied on load) - savegames as JSON (.dcsave): download, load via file picker or drag & drop - autosave to localStorage every 2 minutes and when the tab is hidden/closed - configurable wall detail, light count, interface size, game speed, hints - audio behind a cargo feature so native tests run (cargo test --no-default-features) - savegame round-trip test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
fbf6f223fc
commit
fb7dab3683
17 files changed
+1742
-345
No files matched your search
+23
-6
@@ -8,6 +8,7 @@ use crate::map::*;
|
||||
use macroquad::miniquad::{BlendFactor, BlendState, BlendValue, Comparison, Equation, PipelineParams, UniformDesc, UniformType};
|
||||
use macroquad::models::{Mesh, Vertex};
|
||||
use macroquad::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub const WALL_H: f32 = 0.95;
|
||||
const CHUNK: i32 = 16;
|
||||
@@ -15,11 +16,11 @@ const NCH: i32 = MW as i32 / CHUNK;
|
||||
pub const MAX_LIGHTS: usize = 24;
|
||||
const ATLAS_W: f32 = 256.0;
|
||||
const ATLAS_H: f32 = 80.0;
|
||||
const WALL_SUB: usize = 3; // subdivisions per wall face edge
|
||||
const DISP_AMP: f32 = 0.11; // wall surface irregularity
|
||||
|
||||
// ------------------------------------------------------------------ camera
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct Cam {
|
||||
pub tx: f32, // look-at target on the ground (world x)
|
||||
pub tz: f32, // look-at target on the ground (world z = map y)
|
||||
@@ -411,7 +412,7 @@ fn edge_pts(a: Vec3, b: Vec3, n: usize) -> Vec<Vec3> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn build_chunk(map: &Map, amb: &[f32], cx: i32, cz: i32, gold_fill: f32, atlas: &Texture2D) -> (Option<Mesh>, Option<Mesh>, Option<Mesh>) {
|
||||
fn build_chunk(map: &Map, amb: &[f32], cx: i32, cz: i32, gold_fill: f32, atlas: &Texture2D, wall_sub: usize) -> (Option<Mesh>, Option<Mesh>, Option<Mesh>) {
|
||||
let mut mb = MB::new();
|
||||
let mut ov = MB::new();
|
||||
let mut ln = MB::new();
|
||||
@@ -485,7 +486,7 @@ fn build_chunk(map: &Map, amb: &[f32], cx: i32, cz: i32, gold_fill: f32, atlas:
|
||||
[dark; 4],
|
||||
[Vec3::Y; 4],
|
||||
);
|
||||
let n = WALL_SUB;
|
||||
let n = wall_sub;
|
||||
let top_amb = amb_at(amb, xf + 0.5, zf + 0.5) * 0.95 + 0.1;
|
||||
build_grid(
|
||||
&mut mb,
|
||||
@@ -615,6 +616,8 @@ pub struct R3d {
|
||||
glow: Material,
|
||||
chunks: Vec<Chunk>,
|
||||
pub outlines: bool, // tile outlines on/off
|
||||
wall_sub: usize, // wall face subdivisions (detail)
|
||||
pub max_lights: usize,
|
||||
}
|
||||
|
||||
fn rgba(u: u32) -> Color {
|
||||
@@ -645,7 +648,21 @@ fn chunk_sig(map: &Map, cx: i32, cz: i32, fill_bucket: u8) -> u64 {
|
||||
impl R3d {
|
||||
pub fn new() -> R3d {
|
||||
let chunks = (0..NCH * NCH).map(|_| Chunk { sig: 0, mesh: None, overlay: None, lines: None }).collect();
|
||||
R3d { lit: lit_material(), glow: glow_material(), chunks, outlines: true }
|
||||
R3d { lit: lit_material(), glow: glow_material(), chunks, outlines: true, wall_sub: 3, max_lights: MAX_LIGHTS }
|
||||
}
|
||||
|
||||
// Changing the wall detail rebuilds all chunks
|
||||
pub fn set_wall_detail(&mut self, sub: usize) {
|
||||
if sub != self.wall_sub {
|
||||
self.wall_sub = sub;
|
||||
self.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn invalidate(&mut self) {
|
||||
for c in self.chunks.iter_mut() {
|
||||
c.sig = 0;
|
||||
}
|
||||
}
|
||||
|
||||
fn update_chunks(&mut self, g: &Game, gfx: &Gfx) {
|
||||
@@ -658,7 +675,7 @@ impl R3d {
|
||||
let ch = &mut self.chunks[(cz * NCH + cx) as usize];
|
||||
if ch.sig != sig {
|
||||
let a = amb.get_or_insert_with(|| ambient_field(&g.map));
|
||||
let (m, o, l) = build_chunk(&g.map, a, cx, cz, bucket as f32 / 6.0, &gfx.atlas);
|
||||
let (m, o, l) = build_chunk(&g.map, a, cx, cz, bucket as f32 / 6.0, &gfx.atlas, self.wall_sub);
|
||||
ch.mesh = m;
|
||||
ch.overlay = o;
|
||||
ch.lines = l;
|
||||
@@ -714,7 +731,7 @@ impl R3d {
|
||||
}
|
||||
}
|
||||
l.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal));
|
||||
l.truncate(MAX_LIGHTS);
|
||||
l.truncate(self.max_lights.min(MAX_LIGHTS));
|
||||
let mut pos = vec![Vec4::ZERO; MAX_LIGHTS];
|
||||
let mut colr = vec![Vec4::ZERO; MAX_LIGHTS];
|
||||
for (k, (_, p, c)) in l.iter().enumerate() {
|
||||
|
||||
Reference in new issue
Block a user