Dig marks and hover follow the real block shape; sprites no longer clipped by walls behind them
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5fd0d37c14
commit
c508e232b0
2 files changed
+66
-41
No files matched your search
@@ -16,10 +16,10 @@ An original dungeon management game (genre: dungeon building & defence) that run
|
|||||||
- Camera **freely rotatable 360° and tiltable**
|
- Camera **freely rotatable 360° and tiltable**
|
||||||
- Walls with an **irregular surface** (noise displacement of the wall faces, seamless between neighbours)
|
- Walls with an **irregular surface** (noise displacement of the wall faces, seamless between neighbours)
|
||||||
- Textures: **procedural pixel art** (tile atlas)
|
- Textures: **procedural pixel art** (tile atlas)
|
||||||
- Units: **pixel sprites as billboards** (always facing the camera)
|
- Units: **pixel sprites as billboards** (always facing the camera), pulled towards the camera in depth so walls behind a unit never cut into its sprite
|
||||||
- Lighting: **dark + torches** – dark base level; claimed areas and light sources (torches, core, portal, gate) are bright, flickering light
|
- Lighting: **dark + torches** – dark base level; claimed areas and light sources (torches, core, portal, gate) are bright, flickering light
|
||||||
- **Dig marking**: a marked block glows as a translucent yellow box with a yellow outline around all edges
|
- **Dig marking**: a marked block glows yellow along its **real irregular shape** (tinted top and open sides, outline following the displaced edges)
|
||||||
- **Hover**: the wall block under the mouse gets a faint version of that box (in the colour of the active tool); while dragging a dig rectangle, every block inside is tinted faintly; floor tiles get an outline
|
- **Hover**: the wall block under the mouse gets a faint version of that highlight (in the colour of the active tool); while dragging a dig rectangle, every block inside is tinted faintly; floor tiles get an outline
|
||||||
- **Tile outlines**: subtle dark lines around every tile (floors, wall tops, vertical wall edges), following the irregular surface; toggle with G (later in the graphics settings)
|
- **Tile outlines**: subtle dark lines around every tile (floors, wall tops, vertical wall edges), following the irregular surface; toggle with G (later in the graphics settings)
|
||||||
- The UI is a 2D layer on top, including a minimap
|
- The UI is a 2D layer on top, including a minimap
|
||||||
|
|
||||||
|
|||||||
+63
-38
@@ -415,39 +415,61 @@ const MARK_FILL: [u8; 4] = [255, 205, 50, 70];
|
|||||||
const MARK_LINE: [u8; 4] = [255, 220, 70, 235];
|
const MARK_LINE: [u8; 4] = [255, 220, 70, 235];
|
||||||
|
|
||||||
// Glowing yellow box around a block that is marked for digging
|
// Glowing yellow box around a block that is marked for digging
|
||||||
fn mark_block(mk: &mut MB, map: &Map, x: i32, z: i32) {
|
fn mark_block(mk: &mut MB, map: &Map, x: i32, z: i32, n: usize) {
|
||||||
block_box(mk, map, x, z, MARK_FILL, MARK_LINE, 0.06, 1.0);
|
block_shell(mk, map, x, z, n, MARK_FILL, MARK_LINE, 1.0, 0.03);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Translucent box around a wall block: fill on top and open sides, outline on all edges.
|
// Colored, slightly lifted copy of a displaced face (same grid and displacement as the wall)
|
||||||
// pad = how far the box sticks out, lw = line width factor
|
fn shell_face(mk: &mut MB, pos: impl Fn(f32, f32) -> Vec3, face_n: Vec3, n: usize, fill: [u8; 4], lift: f32) {
|
||||||
fn block_box(mk: &mut MB, map: &Map, x: i32, z: i32, fill: [u8; 4], line: [u8; 4], pad: f32, lw: f32) {
|
let m = n + 1;
|
||||||
let (x0, z0) = (x as f32 - pad, z as f32 - pad);
|
let mut p = Vec::with_capacity(m * m);
|
||||||
let (x1, z1) = (x as f32 + 1.0 + pad, z as f32 + 1.0 + pad);
|
for j in 0..m {
|
||||||
let top = WALL_H + 0.1 + pad;
|
for i in 0..m {
|
||||||
let c = [x0, z0, x1, z1];
|
let base = pos(i as f32 / n as f32, j as f32 / n as f32);
|
||||||
let p = |xx: f32, y: f32, zz: f32| vec3(xx, y, zz);
|
p.push(base + displace(base) + face_n * lift);
|
||||||
// top face
|
|
||||||
mk.quad([p(c[0], top, c[1]), p(c[2], top, c[1]), p(c[2], top, c[3]), p(c[0], top, c[3])], [Vec2::ZERO; 4], [fill; 4], [Vec3::Y; 4]);
|
|
||||||
let corners = [p(c[0], 0.0, c[1]), p(c[2], 0.0, c[1]), p(c[2], 0.0, c[3]), p(c[0], 0.0, c[3])];
|
|
||||||
// side faces towards open neighbours, outline on all edges
|
|
||||||
for k in 0..4 {
|
|
||||||
let a = corners[k];
|
|
||||||
let b = corners[(k + 1) % 4];
|
|
||||||
let (dx, dz) = DIR4[k];
|
|
||||||
let up = vec3(0.0, top, 0.0);
|
|
||||||
let out = vec3(dx as f32, 0.0, dz as f32);
|
|
||||||
if !solid_at(map, x + dx, z + dz) {
|
|
||||||
mk.quad([a, b, b + up, a + up], [Vec2::ZERO; 4], [fill; 4], [out; 4]);
|
|
||||||
mk.strip(a + vec3(0.0, 0.02, 0.0), b + vec3(0.0, 0.02, 0.0), out, 0.05 * lw, line);
|
|
||||||
}
|
}
|
||||||
mk.strip(a + up, b + up, Vec3::Y, 0.06 * lw, line);
|
}
|
||||||
// vertical edge at this corner: down to the floor only where a side is open,
|
let b = mk.v.len() as u16;
|
||||||
// otherwise just the part that sticks out above the neighbouring walls
|
for &q in &p {
|
||||||
|
mk.vert(q, Vec2::ZERO, fill, face_n);
|
||||||
|
}
|
||||||
|
for j in 0..n {
|
||||||
|
for i in 0..n {
|
||||||
|
let a = b + (j * m + i) as u16;
|
||||||
|
mk.i.extend_from_slice(&[a, a + 1, a + m as u16 + 1, a, a + m as u16 + 1, a + m as u16]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Highlight that follows the block's real (irregular) shape: tinted top and open sides,
|
||||||
|
// outline along the displaced edges. lw = line width factor, lift = distance off the surface.
|
||||||
|
fn block_shell(mk: &mut MB, map: &Map, x: i32, z: i32, n: usize, fill: [u8; 4], line: [u8; 4], lw: f32, lift: f32) {
|
||||||
|
let (xf, zf) = (x as f32, z as f32);
|
||||||
|
let up = Vec3::Y * lift;
|
||||||
|
shell_face(mk, |u, v| vec3(xf + u, WALL_H, zf + v), Vec3::Y, n, fill, lift);
|
||||||
|
let tops = [vec3(xf, WALL_H, zf), vec3(xf + 1.0, WALL_H, zf), vec3(xf + 1.0, WALL_H, zf + 1.0), vec3(xf, WALL_H, zf + 1.0)];
|
||||||
|
for k in 0..4 {
|
||||||
|
let (dx, dz) = DIR4[k];
|
||||||
|
let out = vec3(dx as f32, 0.0, dz as f32);
|
||||||
|
let (ta, tb) = (tops[k], tops[(k + 1) % 4]);
|
||||||
|
// top edge (lifted a bit outwards so it sits on the rim)
|
||||||
|
let rim: Vec<Vec3> = edge_pts(ta, tb, n).into_iter().map(|q| q + up + out * lift).collect();
|
||||||
|
mk.polyline(&rim, Vec3::Y, 0.06 * lw, line);
|
||||||
|
let open = !solid_at(map, x + dx, z + dz);
|
||||||
|
if open {
|
||||||
|
let (ga, gb) = (ta - Vec3::Y * WALL_H, tb - Vec3::Y * WALL_H);
|
||||||
|
shell_face(mk, |u, v| ga + (gb - ga) * u + vec3(0.0, WALL_H * (1.0 - v), 0.0), out, n, fill, lift);
|
||||||
|
let foot: Vec<Vec3> = edge_pts(ga, gb, n).into_iter().map(|q| q + out * lift + Vec3::Y * 0.02).collect();
|
||||||
|
mk.polyline(&foot, out, 0.05 * lw, line);
|
||||||
|
}
|
||||||
|
// vertical edge at corner ta: only where one of the two sides meeting there is open
|
||||||
let (pdx, pdz) = DIR4[(k + 3) % 4];
|
let (pdx, pdz) = DIR4[(k + 3) % 4];
|
||||||
let open_here = !solid_at(map, x + dx, z + dz) || !solid_at(map, x + pdx, z + pdz);
|
if open || !solid_at(map, x + pdx, z + pdz) {
|
||||||
let bottom = if open_here { 0.0 } else { WALL_H - 0.05 };
|
let g = ta - Vec3::Y * WALL_H;
|
||||||
mk.strip(a + vec3(0.0, bottom, 0.0), a + up, out, 0.05 * lw, line);
|
let pout = vec3(pdx as f32, 0.0, pdz as f32);
|
||||||
|
let col: Vec<Vec3> = edge_pts(g, ta, n).into_iter().map(|q| q + (out + pout) * lift).collect();
|
||||||
|
mk.polyline(&col, (out + pout).normalize(), 0.05 * lw, line);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -549,8 +571,9 @@ fn build_chunk(map: &Map, amb: &[f32], cx: i32, cz: i32, gold_fill: f32, atlas:
|
|||||||
if !solid_at(map, x + 1, z) {
|
if !solid_at(map, x + 1, z) {
|
||||||
ln.polyline(&edge_pts(tr, br, n), Vec3::Y, OUTLINE_W, OUTLINE_C);
|
ln.polyline(&edge_pts(tr, br, n), Vec3::Y, OUTLINE_W, OUTLINE_C);
|
||||||
}
|
}
|
||||||
if t.marked {
|
// vertex budget: one draw call holds at most 40000 vertices
|
||||||
mark_block(&mut mk, map, x, z);
|
if t.marked && mk.v.len() < 34000 {
|
||||||
|
mark_block(&mut mk, map, x, z, n);
|
||||||
}
|
}
|
||||||
for (d, &(dx, dz)) in DIR4.iter().enumerate() {
|
for (d, &(dx, dz)) in DIR4.iter().enumerate() {
|
||||||
if solid_at(map, x + dx, z + dz) {
|
if solid_at(map, x + dx, z + dz) {
|
||||||
@@ -881,7 +904,7 @@ impl R3d {
|
|||||||
for f in &g.fx {
|
for f in &g.fx {
|
||||||
draw_fx(f, t);
|
draw_fx(f, t);
|
||||||
}
|
}
|
||||||
draw_overlay(&g.map, ov);
|
draw_overlay(&g.map, ov, self.wall_sub);
|
||||||
gl_use_default_material();
|
gl_use_default_material();
|
||||||
set_default_camera();
|
set_default_camera();
|
||||||
|
|
||||||
@@ -923,7 +946,9 @@ impl R3d {
|
|||||||
}
|
}
|
||||||
let a = amb(e.x, e.y) * if sleeping { 0.6 } else { 1.0 };
|
let a = amb(e.x, e.y) * if sleeping { 0.6 } else { 1.0 };
|
||||||
let c = if white { [255, 255, 255, 255] } else { col(Vec3::splat(a), 1.0) };
|
let c = if white { [255, 255, 255, 255] } else { col(Vec3::splat(a), 1.0) };
|
||||||
let base = vec3(e.x, bob, e.y);
|
// pulled towards the camera: unchanged on screen (orthographic), but walls behind
|
||||||
|
// the unit no longer cut into the sprite
|
||||||
|
let base = vec3(e.x, bob, e.y) + toward * 0.45;
|
||||||
let (sr, cr) = rot.sin_cos();
|
let (sr, cr) = rot.sin_cos();
|
||||||
let corner = |dx: f32, dy: f32| {
|
let corner = |dx: f32, dy: f32| {
|
||||||
let x = dx * cr - dy * sr;
|
let x = dx * cr - dy * sr;
|
||||||
@@ -1198,8 +1223,8 @@ fn outline(x0: f32, z0: f32, x1: f32, z1: f32, y: f32, c: Color) {
|
|||||||
|
|
||||||
// Hover feedback: wall blocks get a faint version of the dig-mark box (in the tool colour),
|
// Hover feedback: wall blocks get a faint version of the dig-mark box (in the tool colour),
|
||||||
// floor tiles an outline. While dragging a dig rectangle, every block inside is tinted.
|
// floor tiles an outline. While dragging a dig rectangle, every block inside is tinted.
|
||||||
fn draw_overlay(map: &Map, ov: &Overlay) {
|
fn draw_overlay(map: &Map, ov: &Overlay, n: usize) {
|
||||||
const MAX_BOXES: usize = 400; // keeps the per-frame mesh within one draw call
|
const MAX_BOXES: usize = 150; // keeps the per-frame mesh within one draw call
|
||||||
let c = ov.color;
|
let c = ov.color;
|
||||||
let fill = [(c.r * 255.0) as u8, (c.g * 255.0) as u8, (c.b * 255.0) as u8, 28];
|
let fill = [(c.r * 255.0) as u8, (c.g * 255.0) as u8, (c.b * 255.0) as u8, 28];
|
||||||
let line = [(c.r * 255.0) as u8, (c.g * 255.0) as u8, (c.b * 255.0) as u8, 110];
|
let line = [(c.r * 255.0) as u8, (c.g * 255.0) as u8, (c.b * 255.0) as u8, 110];
|
||||||
@@ -1213,10 +1238,10 @@ fn draw_overlay(map: &Map, ov: &Overlay) {
|
|||||||
'outer: for z in az..=bz {
|
'outer: for z in az..=bz {
|
||||||
for x in ax..=bx {
|
for x in ax..=bx {
|
||||||
if map.get(x, z).map_or(false, |t| t.diggable()) {
|
if map.get(x, z).map_or(false, |t| t.diggable()) {
|
||||||
if boxes >= MAX_BOXES {
|
if boxes >= MAX_BOXES || mb.v.len() > 30000 {
|
||||||
break 'outer;
|
break 'outer;
|
||||||
}
|
}
|
||||||
block_box(&mut mb, map, x, z, fill, line, 0.08, 0.6);
|
block_shell(&mut mb, map, x, z, n, fill, line, 0.6, 0.05);
|
||||||
boxes += 1;
|
boxes += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1226,7 +1251,7 @@ fn draw_overlay(map: &Map, ov: &Overlay) {
|
|||||||
outline(ax as f32, az as f32, bx as f32 + 1.0, bz as f32 + 1.0, y, ov.color);
|
outline(ax as f32, az as f32, bx as f32 + 1.0, bz as f32 + 1.0, y, ov.color);
|
||||||
} else if let Some((x, z)) = ov.hover {
|
} else if let Some((x, z)) = ov.hover {
|
||||||
if solid(x, z) {
|
if solid(x, z) {
|
||||||
block_box(&mut mb, map, x, z, fill, line, 0.08, 0.6);
|
block_shell(&mut mb, map, x, z, n, fill, line, 0.6, 0.05);
|
||||||
} else {
|
} else {
|
||||||
outline(x as f32, z as f32, x as f32 + 1.0, z as f32 + 1.0, 0.04, ov.color);
|
outline(x as f32, z as f32, x as f32 + 1.0, z as f32 + 1.0, 0.04, ov.color);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user