diff --git a/src/gfx.rs b/src/gfx.rs index e2fa9c2..463fd99 100644 --- a/src/gfx.rs +++ b/src/gfx.rs @@ -91,6 +91,8 @@ pub const T_GRASS: u32 = 204; // 4 variants: grass above ground pub const I_WALL: u32 = 208; // wall tool: bricks rising from the floor pub const I_RAM: u32 = 209; // battering ram tool pub const I_CATAPULT: u32 = 210; // catapult tool +pub const T_SNOW: u32 = 211; // 4 variants: snow on the land above ground (winter) +pub const T_PUMPKIN: u32 = 215; // a carved pumpkin (Halloween) pub struct Gfx { pub atlas: Texture2D, @@ -914,6 +916,25 @@ fn gen_atlas() -> Image { Some(if r % 5 == 0 { c(14, 40, 30) } else if r % 7 == 0 { c(52, 96, 62) } else if v == 0 { c(26, 64, 44) } else { c(32, 72, 40) }) }); } + // snow above ground in winter + for v in 0..4u32 { + cv.cell(T_SNOW + v, |x, y| { + let r = hash(x, y, 450 + v); + Some(if r % 9 == 0 { c(214, 222, 236) } else if r % 23 == 0 { c(150, 170, 120) } else { c(236, 240, 248) }) + }); + } + // a carved pumpkin: orange ribs, a grinning face + cv.cell(T_PUMPKIN, |x, y| { + let eye = (y == 5 || y == 6) && (x == 4 || x == 5 || x == 10 || x == 11); + let mouth = (y == 10 && (3..=12).contains(&x)) || (y == 11 && (5..=10).contains(&x) && x % 2 == 0); + if eye || mouth { + Some(c(255, 230, 90)) + } else if x % 4 == 0 { + Some(c(190, 90, 20)) + } else { + Some(c(240, 128, 30)) + } + }); // grass above ground for v in 0..4u32 { cv.cell(T_GRASS + v, |x, y| { diff --git a/src/main.rs b/src/main.rs index 89b1c91..1f90f04 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1893,6 +1893,17 @@ app.pending_briefing = briefing_of(&campaign, i); } } app.game.smooth_display(dt); + // winter: snow falls on the land above ground near the camera + if render3d::season() == 2 && !app.game.map.sky.is_empty() && app.game.parts.len() < 3000 { + let (cx, cz) = (app.cam.tx, app.cam.tz); + for _ in 0..3 { + let x = cx + (rand::gen_range(-1.0f32, 1.0)) * app.cam.zoom * 0.8; + let z = cz + (rand::gen_range(-1.0f32, 1.0)) * app.cam.zoom * 0.8; + if inb(x as i32, z as i32) && app.game.map.sky(map::idx(x as i32, z as i32)) { + app.game.parts.push(particles::Part { kind: particles::EMBER, x, y: z, h: 4.0, vx: rand::gen_range(-0.2f32, 0.2), vy: 0.0, vh: -0.6, t: 0.0, max: 6.0, col: particles::abgr(245, 248, 255), size: 0.04 }); + } + } + } let hear = app.cam.zoom * 1.2; let (right, _) = app.cam.ground_axes(); for ev in app.game.events.drain(..) { diff --git a/src/render3d.rs b/src/render3d.rs index 0ce655a..17b8860 100644 --- a/src/render3d.rs +++ b/src/render3d.rs @@ -440,6 +440,24 @@ impl MB { } } +// the season of the real date: 0 none, 1 Halloween (20 Oct - 5 Nov), 2 winter (Dec - Feb) +pub fn season() -> u8 { + thread_local! { + static SEASON: std::cell::Cell = const { std::cell::Cell::new(255) }; + } + SEASON.with(|s| { + if s.get() == 255 { + let (_, m, d, _, _) = crate::cloud::civil(macroquad::miniquad::date::now() as i64); + s.set(match (m, d) { + (10, 20..=31) | (11, 1..=5) => 1, + (12, _) | (1, _) | (2, _) => 2, + _ => 0, + }); + } + s.get() + }) +} + // the researched level of the room on tile t (1: none) fn room_level_at(map: &Map, t: &Tile) -> u8 { if map.upgrades.is_empty() { @@ -693,7 +711,7 @@ fn build_chunk(map: &Map, amb: &[f32], glow: &[f32], cx: i32, cz: i32, atlas: &T let mut tex = Gfx::tile_tex(t.kind, t.owner, t.var, t.reinforced); // open land above ground is grass (until a master claims it) if map.sky(idx(x, z)) && t.kind == Kind::Floor && t.owner == Owner::None { - tex = T_GRASS + (t.var as u32 & 3); + tex = if season() == 2 { T_SNOW } else { T_GRASS } + (t.var as u32 & 3); } let (ua, ub) = cell_uv(tex); let (xf, zf) = (x as f32, z as f32); @@ -889,6 +907,15 @@ fn build_chunk(map: &Map, amb: &[f32], glow: &[f32], cx: i32, cz: i32, atlas: &T } // room decor let dc = col(lava_lit(Vec3::splat(amb_at(amb, xf + 0.5, zf + 0.5)), lava_at(glow, xf + 0.5, zf + 0.5)), 1.0); + // Halloween: now and then a glowing pumpkin on the dungeon's plain floor + if season() == 1 && t.kind == Kind::Floor && t.owner == Owner::Player { + let hsh = (x as u32).wrapping_mul(2246822519) ^ (z as u32).wrapping_mul(3266489917); + if (hsh >> 7) % 37 == 0 { + let (cx, cz) = (xf + 0.3 + ((hsh >> 3) % 4) as f32 * 0.1, zf + 0.35); + let pc = col(Vec3::splat(1.4), 1.0); + mb.boxy(vec3(cx - 0.14, 0.0, cz - 0.14), vec3(cx + 0.14, 0.24, cz + 0.14), T_PUMPKIN, pc, pc); + } + } if t.plant() { // a giant mushroom or a tree (marked for felling: a yellow tint) let (cx, cz) = (xf + 0.5, zf + 0.5); @@ -908,7 +935,8 @@ fn build_chunk(map: &Map, amb: &[f32], glow: &[f32], cx: i32, cz: i32, atlas: &T let tc = tint(Vec3::splat(a.max(0.3))); mb.boxy(vec3(cx - 0.12, 0.0, cz - 0.12), vec3(cx + 0.12, 0.9 * s, cz + 0.12), T_BARK, tc, tc); let lc = tint(Vec3::splat(a.max(0.3) * 0.9)); - let top = tint(Vec3::splat(a.max(0.3) * 1.1)); + // snow on the crowns in winter + let top = if season() == 2 { col(Vec3::splat(a.max(0.3) * 2.2), 1.0) } else { tint(Vec3::splat(a.max(0.3) * 1.1)) }; let leaves = T_LEAVES + (t.var as u32 & 1); mb.boxy(vec3(cx - 0.48, 0.75 * s, cz - 0.48), vec3(cx + 0.48, 1.55 * s, cz + 0.48), leaves, lc, top); mb.boxy(vec3(cx - 0.3, 1.55 * s, cz - 0.3), vec3(cx + 0.3, 1.95 * s, cz + 0.3), leaves, lc, top);