Fog of war: unexplored darkness until the dungeon's ground, creatures or towers come close
- explored tiles stay known (saved compactly as bits), enemies are only drawn while in sight; sight runs along open ground, walls block it - unexplored tiles are black blocks; units, towers, torches, furniture, portals, gate and rival cores in the dark are hidden; minimap and tile info too - the nearest portal is known from the start; the seer's eye opens a wide area for 20 seconds without remembering it Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7f4d5ca7b4
commit
240e89d9cb
11 files changed
+244
-17
No files matched your search
+47
-12
@@ -401,6 +401,12 @@ fn amb_at(f: &[f32], x: f32, z: f32) -> f32 {
|
||||
|
||||
const LIGHT_DIR: Vec3 = Vec3::new(0.45, 0.8, 0.35);
|
||||
|
||||
// fog of war: units in unexplored tiles, and enemies out of sight, are not drawn
|
||||
pub fn unit_hidden(g: &Game, e: &Ent) -> bool {
|
||||
let i = e.tile();
|
||||
!g.map.shown(i) || (e.side() != 0 && !g.map.in_sight(i))
|
||||
}
|
||||
|
||||
fn solid_at(map: &Map, x: i32, z: i32) -> bool {
|
||||
map.get(x, z).map_or(true, |t| t.solid())
|
||||
}
|
||||
@@ -494,6 +500,26 @@ fn build_chunk(map: &Map, amb: &[f32], cx: i32, cz: i32, atlas: &Texture2D, wall
|
||||
let tex = Gfx::tile_tex(t.kind, t.owner, t.var, t.reinforced);
|
||||
let (ua, ub) = cell_uv(tex);
|
||||
let (xf, zf) = (x as f32, z as f32);
|
||||
if !map.shown(idx(x, z)) {
|
||||
// fog of war: a black block, with black sides towards known open ground
|
||||
let black = col(Vec3::splat(0.0), 1.0);
|
||||
mb.quad([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)], [ua, ua, ua, ua], [black; 4], [Vec3::Y; 4]);
|
||||
for (d, &(dx, dz)) in DIR4.iter().enumerate() {
|
||||
let open = map.get(x + dx, z + dz).map_or(false, |o| !o.solid()) && map.shown(idx((x + dx).max(0), (z + dz).max(0)));
|
||||
if !open {
|
||||
continue;
|
||||
}
|
||||
let (p, q) = match d {
|
||||
0 => (vec3(xf + 1.0, 0.0, zf), vec3(xf, 0.0, zf)),
|
||||
1 => (vec3(xf + 1.0, 0.0, zf + 1.0), vec3(xf + 1.0, 0.0, zf)),
|
||||
2 => (vec3(xf, 0.0, zf + 1.0), vec3(xf + 1.0, 0.0, zf + 1.0)),
|
||||
_ => (vec3(xf, 0.0, zf), vec3(xf, 0.0, zf + 1.0)),
|
||||
};
|
||||
let out = vec3(dx as f32, 0.0, dz as f32);
|
||||
mb.quad([p + Vec3::Y * WALL_H, q + Vec3::Y * WALL_H, q - Vec3::Y * LIQ_D, p - Vec3::Y * LIQ_D], [ua, ua, ua, ua], [black; 4], [out; 4]);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if let Some(river) = t.river() {
|
||||
// ---- river bed: sunken surface, banks where the ground or a wall begins
|
||||
let lava = river == Kind::Lava;
|
||||
@@ -798,7 +824,8 @@ fn chunk_sig(map: &Map, cx: i32, cz: i32) -> u64 {
|
||||
Kind::Treasury => (t.gold / 50) as u64 | (t.res as u64) << 5,
|
||||
_ => 0,
|
||||
};
|
||||
(t.kind as u64) | (t.owner as u64) << 5 | (t.marked as u64) << 7 | (t.reinforced as u64) << 8 | extra << 9
|
||||
let shown = map.shown(idx(x, z)) as u64;
|
||||
(t.kind as u64) | (t.owner as u64) << 5 | (t.marked as u64) << 8 | (t.reinforced as u64) << 9 | shown << 10 | extra << 11
|
||||
}
|
||||
None => 0xff,
|
||||
};
|
||||
@@ -1021,7 +1048,7 @@ impl R3d {
|
||||
}
|
||||
// solid unlit objects (default material, depth tested)
|
||||
gl_use_default_material();
|
||||
for tr in &g.torches {
|
||||
for tr in g.torches.iter().filter(|tr| g.map.shown(tr.wall)) {
|
||||
let (x, z) = tr.pos();
|
||||
draw_cube(vec3(x, 0.55, z), vec3(0.07, 0.3, 0.07), None, Color::from_rgba(70, 45, 28, 255));
|
||||
}
|
||||
@@ -1046,7 +1073,7 @@ impl R3d {
|
||||
draw_mesh(k);
|
||||
}
|
||||
}
|
||||
for (k, tr) in g.torches.iter().enumerate() {
|
||||
for (k, tr) in g.torches.iter().enumerate().filter(|(_, tr)| g.map.shown(tr.wall)) {
|
||||
let (x, z) = tr.pos();
|
||||
let fl = 1.0 + 0.15 * (t * 11.0 + k as f32 * 2.3).sin();
|
||||
draw_sphere(vec3(x, 0.78, z), 0.09 * fl, None, Color::new(1.0, 0.55, 0.15, 0.9));
|
||||
@@ -1098,7 +1125,7 @@ impl R3d {
|
||||
let sw = SPRITE_TEX_W;
|
||||
let sh = SPRITE_TEX_H;
|
||||
for e in &g.ents {
|
||||
if !e.alive || e.held {
|
||||
if !e.alive || e.held || unit_hidden(g, e) {
|
||||
continue;
|
||||
}
|
||||
let w = sprite_size(e.kind);
|
||||
@@ -1253,7 +1280,7 @@ impl R3d {
|
||||
let cw = GW * small; // width of one small block character
|
||||
let px_per_tile = screen_height() / cam.zoom * d;
|
||||
for e in &g.ents {
|
||||
if !e.alive || e.held {
|
||||
if !e.alive || e.held || unit_hidden(g, e) {
|
||||
continue;
|
||||
}
|
||||
let big = matches!(e.kind, EK::Ogre | EK::Paladin | EK::Jailer | EK::Chainbreaker | EK::Wyrm | EK::Gargoyle);
|
||||
@@ -1512,7 +1539,7 @@ fn draw_dark_tower(tw: &Tower) {
|
||||
}
|
||||
|
||||
fn draw_towers_solid(g: &Game) {
|
||||
for tw in &g.towers {
|
||||
for tw in g.towers.iter().filter(|t| g.map.shown(t.tile)) {
|
||||
if !tw.hero && tw.kind != TowerKind::Rune {
|
||||
draw_dark_tower(tw);
|
||||
continue;
|
||||
@@ -1634,7 +1661,7 @@ fn draw_towers_solid(g: &Game) {
|
||||
}
|
||||
|
||||
fn draw_towers_glow(g: &Game, t: f32) {
|
||||
for (k, tw) in g.towers.iter().enumerate() {
|
||||
for (k, tw) in g.towers.iter().enumerate().filter(|(_, t)| g.map.shown(t.tile)) {
|
||||
if !tw.hero && tw.kind != TowerKind::Rune {
|
||||
// red glowing joints, arrow slits and the hellmouth's fire
|
||||
let (x, z) = tw.pos();
|
||||
@@ -1816,7 +1843,7 @@ fn draw_landmarks_solid(g: &Game, t: f32) {
|
||||
}
|
||||
}
|
||||
// rival masters' cores: obsidian pedestal (the orb glows in the glow pass)
|
||||
for rv in g.rivals.iter().filter(|r| r.alive) {
|
||||
for rv in g.rivals.iter().filter(|r| r.alive && g.map.shown(idx(r.core.0 + 1, r.core.1 + 1))) {
|
||||
let (rx, rz) = rv.centre();
|
||||
let c = vec3(rx, 0.0, rz);
|
||||
draw_cube(c + vec3(0.0, 0.08, 0.0), vec3(2.6, 0.16, 2.6), None, Color::from_rgba(24, 22, 30, 255));
|
||||
@@ -1828,6 +1855,9 @@ fn draw_landmarks_solid(g: &Game, t: f32) {
|
||||
// portals: a demon gate - an arch of stone blocks with bone ribs and horns on a stepped
|
||||
// platform (the vortex glows in the glow pass); grey and dead until claimed
|
||||
for (pk, &(px, py)) in g.map.portals.iter().enumerate() {
|
||||
if !g.map.shown(idx(px + 1, py + 1)) {
|
||||
continue;
|
||||
}
|
||||
let owned = g.map.portal_owned(pk);
|
||||
let pc = vec3(px as f32 + 1.5, 0.0, py as f32 + 1.5);
|
||||
let stone = |k: u8| if owned { Color::from_rgba(46 + k, 30 + k / 2, 58 + k, 255) } else { Color::from_rgba(56 + k, 54 + k, 60 + k, 255) };
|
||||
@@ -1867,7 +1897,9 @@ fn draw_landmarks_solid(g: &Game, t: f32) {
|
||||
}
|
||||
// gate pillars
|
||||
let (gx, gz) = g.gate_center();
|
||||
if g.gate_hp > 0.0 {
|
||||
if g.gate_hp > 0.0 && !g.map.shown(idx(gx as i32, gz as i32)) {
|
||||
// unexplored: not drawn
|
||||
} else if g.gate_hp > 0.0 {
|
||||
let stone = Color::from_rgba(150, 155, 172, 255);
|
||||
let dark = Color::from_rgba(112, 116, 132, 255);
|
||||
let gold = Color::from_rgba(230, 200, 100, 255);
|
||||
@@ -1968,7 +2000,7 @@ fn draw_landmarks_glow(g: &Game, t: f32, cam_r: Vec3) {
|
||||
}
|
||||
let _ = hot;
|
||||
// rival masters' cores in their colours
|
||||
for rv in g.rivals.iter().filter(|r| r.alive) {
|
||||
for rv in g.rivals.iter().filter(|r| r.alive && g.map.shown(idx(r.core.0 + 1, r.core.1 + 1))) {
|
||||
let (rx, rz) = rv.centre();
|
||||
let hp = (rv.hp / crate::rival::RIVAL_CORE_HP).clamp(0.0, 1.0);
|
||||
let col = if rv.side == 2 { vec3(0.3, 0.95, 0.4) } else { vec3(0.35, 0.55, 1.0) };
|
||||
@@ -1983,6 +2015,9 @@ fn draw_landmarks_glow(g: &Game, t: f32, cam_r: Vec3) {
|
||||
}
|
||||
// portals: runes on the arch and a vertical vortex inside it, sparks pouring out and falling
|
||||
for (pk, &(px, py)) in g.map.portals.iter().enumerate() {
|
||||
if !g.map.shown(idx(px + 1, py + 1)) {
|
||||
continue;
|
||||
}
|
||||
let owned = g.map.portal_owned(pk);
|
||||
let pc = vec3(px as f32 + 1.5, 0.0, py as f32 + 1.5);
|
||||
let arm = if owned { Color::new(0.8, 0.35, 1.0, 1.0) } else { Color::new(0.45, 0.42, 0.5, 1.0) };
|
||||
@@ -2025,7 +2060,7 @@ fn draw_landmarks_glow(g: &Game, t: f32, cam_r: Vec3) {
|
||||
}
|
||||
}
|
||||
// gate light
|
||||
if g.gate_hp > 0.0 {
|
||||
if g.gate_hp > 0.0 && g.map.shown(idx(g.gate_center().0 as i32, g.gate_center().1 as i32)) {
|
||||
let (gx, gz) = g.gate_center();
|
||||
let gc = vec3(gx, 1.0, gz);
|
||||
draw_sphere(gc, 0.45 + 0.04 * (t * 3.0).sin(), None, Color::new(0.95, 0.97, 1.0, 0.95));
|
||||
@@ -2048,7 +2083,7 @@ fn floor_y(g: &Game, x: f32, z: f32) -> f32 {
|
||||
|
||||
fn draw_shadows(g: &Game) {
|
||||
for e in &g.ents {
|
||||
if !e.alive || e.held {
|
||||
if !e.alive || e.held || unit_hidden(g, e) {
|
||||
continue;
|
||||
}
|
||||
let r = if matches!(e.kind, EK::Ogre | EK::Paladin | EK::Jailer | EK::Chainbreaker) { 0.32 } else { 0.24 };
|
||||
|
||||
Reference in new issue
Block a user