diff --git a/docs/screenshots/title-battle.png b/docs/screenshots/title-battle.png index 937f091..6efd33b 100644 Binary files a/docs/screenshots/title-battle.png and b/docs/screenshots/title-battle.png differ diff --git a/src/demo.rs b/src/demo.rs index 243b3e6..8db6e4e 100644 --- a/src/demo.rs +++ b/src/demo.rs @@ -2,7 +2,8 @@ // the knights fight on a tongue of rock between a lava river (north) and an underground river // (south). The dungeon side has a wall with a wooden, an iron and a magic door, rooms behind it // (treasury, forge, library, lair, ...), a rune forge tower, a magic tower and a mana spring; all -// seven traps lie on the knights' way; the knights have their towers and the hero gate. Both sides +// seven traps lie on the knights' way; the knights have their towers, and their gate stands above +// ground in a sunny forest east of a cliff, from where they march in through two cave mouths. Both sides // get balanced reinforcements, destroyed towers, doors and traps come back after a while, and now // and then the dungeon casts a spell. Nothing here is ever saved. @@ -31,6 +32,7 @@ pub struct Demo { } const WALL_X: i32 = RIVER_X - 8; // the dungeon's wall with its three doors +const SURF_X: i32 = 55; // from here east the land lies above ground (the knights come from there) const CREATURES: [(EK, u32); 12] = [(EK::Kobold, 4), (EK::Spider, 3), (EK::Mage, 2), (EK::Ogre, 2), (EK::Jailer, 1), (EK::Priestess, 1), (EK::Bat, 2), (EK::Gargoyle, 1), (EK::Owl, 1), (EK::Wyrm, 1), (EK::Cogling, 1), (EK::Revenant, 1)]; const KNIGHTS: [(EK, u32); 8] = [(EK::Knight, 5), (EK::Archer, 3), (EK::Priest, 2), (EK::Dwarf, 2), (EK::Paladin, 1), (EK::Giant, 1), (EK::Fairy, 1), (EK::BattleMage, 1)]; @@ -183,8 +185,58 @@ impl Game { let spring = idx(WALL_X - 3, BRIDGE_Y + 1); g.map.t[spring].kind = Kind::ManaSpring; g.map.t[spring].owner = Owner::Player; - // the hero gate on the knights' side - let (gx, gy) = (RIVER_X + 11, BRIDGE_Y); + // above ground in the east: grass, a forest at the edges, a cliff with two cave mouths + let mouth = |y: i32| (BRIDGE_Y - 4..=BRIDGE_Y - 1).contains(&y) || (BRIDGE_Y + 2..=BRIDGE_Y + 6).contains(&y); + for y in 1..H as i32 - 1 { + for x in SURF_X - 1..W as i32 - 1 { + let i = idx(x, y); + let border = y < 2 || y >= H as i32 - 2 || x >= W as i32 - 2; + if x < SURF_X + 1 { + // the cliff (or a cave mouth: knight ground) + if !border { + let t = &mut g.map.t[i]; + t.kind = if mouth(y) { Kind::Floor } else { Kind::Rock }; + t.owner = if mouth(y) { Owner::Hero } else { Owner::None }; + t.reinforced = false; + t.gold = 0; + } + continue; + } + g.map.set_sky(i, true); + if border { + continue; + } + let t = &mut g.map.t[i]; + t.owner = Owner::None; + t.gold = 0; + t.reinforced = false; + if t.kind == Kind::Rock && rng.f() < 0.6 { + continue; // a few rocks stay as outcrops + } + // trees along the north and south edges and in the far east; the march route stays open + let dy = (y - (BRIDGE_Y + 1)).abs(); + let h = ((x * 73 + y * 151) as u32).wrapping_mul(2654435761) >> 24; + let wood = dy > 9 || x > W as i32 - 7; + t.kind = if wood && h % 3 != 0 { Kind::Tree } else { Kind::Floor }; + t.hp = if t.kind == Kind::Tree { crate::wood::TREE_HP } else { 0.0 }; + } + } + // the knight cave runs on to the cave mouths + for y in BRIDGE_Y - 4..=BRIDGE_Y + 6 { + if !mouth(y) { + continue; + } + let mut x = SURF_X - 1; + while x > RIVER_X + 3 && !g.map.t[idx(x, y)].walkable() { + let t = &mut g.map.t[idx(x, y)]; + t.kind = Kind::Floor; + t.owner = Owner::Hero; + t.reinforced = false; + x -= 1; + } + } + // the hero gate on the knights' side, in the forest + let (gx, gy) = (SURF_X + 6, BRIDGE_Y); for y in gy..gy + 3 { for x in gx..gx + 3 { let t = &mut g.map.t[idx(x, y)]; @@ -220,8 +272,9 @@ impl Game { g.demo_spawn(true); g.demo_spawn(false); } - let mut cam = Cam::new(RIVER_X as f32 + 0.5, BRIDGE_Y as f32 + 1.5); - cam.zoom = 16.0; + // the camera between the battle on the tongue and the forest the knights come from + let mut cam = Cam::new(RIVER_X as f32 + 11.5, BRIDGE_Y as f32 + 1.5); + cam.zoom = 21.0; (g, cam) } diff --git a/src/gfx.rs b/src/gfx.rs index ce72e44..bb820d6 100644 --- a/src/gfx.rs +++ b/src/gfx.rs @@ -910,14 +910,16 @@ fn gen_atlas() -> Image { for v in 0..2u32 { cv.cell(T_LEAVES + v, |x, y| { let r = hash(x, y, 430 + v); - Some(if r % 5 == 0 { c(30, 70, 30) } else if r % 7 == 0 { c(110, 160, 60) } else if v == 0 { c(56, 110, 44) } else { c(70, 120, 40) }) + // dark, bluish green: clearly apart from the light meadow + 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) }) }); } // grass above ground for v in 0..4u32 { cv.cell(T_GRASS + v, |x, y| { let r = hash(x, y, 440 + v); - Some(if r % 11 == 0 { c(120, 170, 70) } else if r % 6 == 0 { c(50, 96, 40) } else if r % 23 == 0 { c(200, 190, 90) } else { c(74, 126, 52) }) + // a light, yellowish meadow with flowers + Some(if r % 11 == 0 { c(176, 200, 96) } else if r % 6 == 0 { c(112, 150, 62) } else if r % 23 == 0 { c(236, 220, 110) } else if r % 37 == 0 { c(230, 236, 240) } else { c(140, 176, 78) }) }); } // obsidian: cooled lava, dark glass with a few glowing seams diff --git a/src/surface.rs b/src/surface.rs index 0525a77..e18029c 100644 --- a/src/surface.rs +++ b/src/surface.rs @@ -64,6 +64,14 @@ impl Game { continue; } self.map.set_sky(i, true); + // no lava under the open sky: rivers up here carry water + let tl = &mut self.map.t[i]; + match tl.kind { + Kind::Lava | Kind::Obsidian => tl.kind = Kind::Water, + Kind::LavaBridge => tl.kind = Kind::Bridge, + Kind::Drawbridge => tl.res &= !DB_LAVA, + _ => {} + } if keep { continue; } @@ -195,4 +203,22 @@ mod tests { // the heroes find their way from the gate to the core assert!(g.map.hero_path_to_core(idx(gx + 1, gy + 1)).is_some()); } + + #[test] + fn no_lava_under_the_open_sky() { + let mut g = Game::new(31, 128, 96); + // a lava river across the whole map + for x in 2..126 { + for y in 40..44 { + g.map.t[idx(x, y)].kind = Kind::Lava; + } + } + g.add_surface(31, 0.5, 3); + for i in 0..g.map.t.len() { + if g.map.sky(i) { + assert!(!matches!(g.map.t[i].kind, Kind::Lava | Kind::LavaBridge), "lava above ground"); + } + } + assert!(g.map.t.iter().any(|t| t.kind == Kind::Lava), "the lava below stays"); + } }