Campaign of 30 missions: six against the rival masters Mossgrim and Lady Nightwell, islands, mana springs, sealed caves with chained creatures

- missions interleaved into one story (EN/DE), new epilogue, map sizes grow to 176x176
- islands in water or lava with treasure and/or a mana spring; rivers at least four tiles wide
- sealed caves with treasure and a chained creature that joins once the cave is claimed
- new goals: rival cores, mana springs, freed creatures
- rival messages name the masters; new progress key for the new mission order

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-03 14:36:45 +01:00
1 parent dd16c684e5
commit ea0623fbe3
11 files changed
+413 -85

No files matched your search

+243 -54
View File
@@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize};
// From which mission (0-based) on each tool is available (same order as ui::TOOLS):
// dig, hand, treasury, lair, farm, training, library, sell, flag, lightning, heal, imp,
// torture, temple, arrow tower, cannon tower, magic tower, bridge, aerie, steel
pub const TOOL_UNLOCK: [usize; 20] = [0, 0, 0, 0, 0, 2, 4, 0, 0, 3, 4, 0, 6, 9, 3, 5, 9, 1, 6, 10];
pub const TOOL_UNLOCK: [usize; 20] = [0, 0, 0, 0, 0, 2, 5, 0, 0, 3, 5, 0, 8, 12, 3, 7, 12, 1, 8, 17];
pub struct FortDef {
pub at: (f32, f32), // centre as a fraction of the map size
@@ -45,6 +45,33 @@ pub struct MissionDef {
pub rivers: Vec<River>,
pub forts: Vec<FortDef>,
pub gift: Vec<(EK, u32)>, // creatures that join at the start
pub rivals: Vec<(f32, f32, u8, u8)>, // rival masters: position (fraction of the map), difficulty, side (2 Mossgrim, 3 Lady Nightwell)
pub islands: Vec<IslandDef>,
pub caves: Vec<CaveDef>,
}
// an island in a lake of water or lava, with treasure and/or a mana spring
pub struct IslandDef {
pub at: (f32, f32),
pub r: i32,
pub lava: bool,
pub spring: bool,
pub treasure: i32,
}
// a sealed cave: treasure and/or a lone creature in chains that joins when the cave is claimed
pub struct CaveDef {
pub at: (f32, f32),
pub chained: Option<(EK, u32)>,
pub treasure: i32,
}
fn island(at: (f32, f32), lava: bool, spring: bool, treasure: i32) -> IslandDef {
IslandDef { at, r: 2, lava, spring, treasure }
}
fn cave(at: (f32, f32), chained: Option<(EK, u32)>, treasure: i32) -> CaveDef {
CaveDef { at, chained, treasure }
}
// carried over from the last won mission: the strongest creatures and part of the gold
@@ -132,45 +159,58 @@ fn base(key: &'static str, size: (usize, usize), seed: u32, goals: Vec<Goal>) ->
rivers: Vec::new(),
forts: Vec::new(),
gift: Vec::new(),
rivals: Vec::new(),
islands: Vec::new(),
caves: Vec::new(),
}
}
// Rivers are at least four tiles wide: a ranged attack just reaches across.
pub fn missions() -> Vec<MissionDef> {
let mut v = Vec::new();
// ---- Act I: awakening
v.push(MissionDef { gold: 2500.0, forts: vec![fort((0.74, 0.5), (15, 13), &[3], &[], 1, 4, 0, 1, 1, 1200).shard()], ..base("m01", (64, 64), 101, vec![Goal::Shards]) });
v.push(MissionDef {
gold: 2500.0,
rivers: vec![river(false, true, 0.47, 3, 0)],
forts: vec![fort((0.8, 0.5), (15, 15), &[3], &[Arrow], 1, 4, 1, 1, 1, 1500).shard()],
..base("m02", (80, 64), 202, vec![Goal::Shards])
rivers: vec![river(false, true, 0.47, 4, 0)],
forts: vec![fort((0.82, 0.5), (15, 15), &[3], &[Arrow], 1, 4, 1, 1, 1, 1500).shard()],
..base("m02", (72, 64), 202, vec![Goal::Shards])
});
v.push(MissionDef { forts: vec![fort((0.74, 0.5), (19, 17), &[3, 0], &[Arrow, Arrow], 1, 6, 2, 1, 1, 1500)], ..base("m03", (80, 80), 303, vec![Goal::KillAll]) });
v.push(MissionDef { forts: vec![fort((0.74, 0.5), (19, 17), &[3, 0], &[Arrow, Arrow], 1, 6, 2, 1, 1, 1500)], ..base("m03", (80, 72), 303, vec![Goal::KillAll]) });
v.push(MissionDef { portals: 2, treasures: 1, waves: true, wave_limit: 4, first_wave: 270.0, gate: true, ..base("m04", (80, 72), 404, vec![Goal::Survive(4)]) });
v.push(MissionDef {
portals: 2,
treasures: 1,
waves: true,
wave_limit: 4,
first_wave: 270.0,
gate: true,
..base("m04", (80, 64), 404, vec![Goal::Survive(4)])
rivals: vec![(0.78, 0.5, 0, 2)],
islands: vec![island((0.5, 0.2), false, false, 1200)],
..base("m21", (80, 80), 2121, vec![Goal::Rivals])
});
v.push(MissionDef {
rivers: vec![river(false, true, 0.42, 3, 1)],
forts: vec![fort((0.76, 0.5), (21, 21), &[3, 2], &[Arrow, Arrow, Arrow], 1, 6, 2, 2, 4, 2000)],
..base("m05", (96, 80), 505, vec![Goal::Loot(6000)])
rivers: vec![river(false, true, 0.42, 4, 1)],
islands: vec![island((0.45, 0.8), false, false, 1500)],
forts: vec![fort((0.78, 0.5), (21, 21), &[3, 2], &[Arrow, Arrow, Arrow], 1, 6, 2, 2, 4, 2000)],
..base("m05", (88, 80), 505, vec![Goal::Loot(6000)])
});
// ---- Act II: the rivers
v.push(MissionDef {
caves: vec![cave((0.5, 0.22), Some((Ogre, 6)), 800), cave((0.56, 0.8), None, 1500)],
forts: vec![fort((0.82, 0.5), (17, 17), &[3], &[Arrow, Arrow], 1, 5, 1, 2, 1, 1200).shard()],
..base("m22", (88, 88), 2222, vec![Goal::Freed(1), Goal::Shards])
});
// ---- Act II: the rivers and the other masters
v.push(MissionDef {
rivers: vec![river(false, true, 0.52, 5, 1)],
forts: vec![fort((0.82, 0.5), (21, 19), &[3], &[Arrow, Cannon, Arrow, Cannon], 1, 8, 2, 2, 2, 1500).heart()],
..base("m06", (96, 80), 606, vec![Goal::Heart])
forts: vec![fort((0.84, 0.5), (21, 19), &[3], &[Arrow, Cannon, Arrow, Cannon], 1, 8, 2, 2, 2, 1500).heart()],
..base("m06", (96, 88), 606, vec![Goal::Heart])
});
v.push(MissionDef {
rivers: vec![river(true, true, 0.45, 3, 0)],
forts: vec![fort((0.79, 0.45), (19, 19), &[3], &[Arrow, Cannon], 2, 7, 2, 2, 2, 1800).shard()],
rivers: vec![river(true, true, 0.45, 4, 0)],
forts: vec![fort((0.8, 0.45), (19, 19), &[3], &[Arrow, Cannon], 2, 7, 2, 2, 2, 1800).shard()],
..base("m07", (96, 96), 707, vec![Goal::Shards])
});
v.push(MissionDef {
rivers: vec![river(false, true, 0.55, 4, 1)],
islands: vec![island((0.4, 0.2), false, false, 2000)],
rivals: vec![(0.8, 0.5, 1, 3)],
..base("m23", (104, 96), 2323, vec![Goal::Rivals])
});
v.push(MissionDef {
time_limit: 900.0,
gift: vec![(Ogre, 3), (Kobold, 3), (Kobold, 3), (Spider, 2)],
@@ -178,44 +218,48 @@ pub fn missions() -> Vec<MissionDef> {
fort((0.6, 0.27), (17, 15), &[3, 2], &[Arrow], 1, 4, 1, 2, 3, 1500),
fort((0.78, 0.72), (17, 15), &[3, 0], &[Arrow, Cannon], 1, 4, 1, 2, 3, 1500),
],
..base("m08", (96, 80), 808, vec![Goal::Loot(8000)])
..base("m08", (96, 96), 808, vec![Goal::Loot(8000)])
});
v.push(MissionDef {
rivers: vec![river(false, false, 0.5, 3, 2)],
rivers: vec![river(false, false, 0.5, 4, 2)],
forts: vec![
fort((0.76, 0.24), (19, 17), &[3, 2], &[Arrow, Cannon], 2, 7, 2, 2, 1, 1500).shard(),
fort((0.76, 0.76), (19, 17), &[3, 0], &[Arrow, Cannon], 2, 7, 2, 2, 1, 1500).shard(),
],
..base("m09", (112, 96), 909, vec![Goal::Shards])
});
v.push(MissionDef { gold: 4000.0, portals: 2, treasures: 1, waves: true, wave_limit: 6, first_wave: 240.0, gate: true, ..base("m10", (104, 96), 1010, vec![Goal::Survive(6)]) });
v.push(MissionDef {
islands: vec![island((0.42, 0.25), false, true, 0), island((0.6, 0.75), true, true, 800), island((0.74, 0.35), false, true, 1200)],
forts: vec![fort((0.86, 0.6), (15, 15), &[3], &[Arrow, Cannon], 2, 6, 2, 2, 1, 1200)],
..base("m24", (112, 104), 2424, vec![Goal::Springs(3)])
});
v.push(MissionDef { forts: vec![fort((0.72, 0.5), (25, 25), &[3, 0, 2], &[Arrow, Cannon, Magic, Arrow, Cannon], 2, 8, 4, 3, 2, 2000)], ..base("m11", (112, 104), 1111, vec![Goal::KillAll]) });
v.push(MissionDef {
gold: 4000.0,
portals: 2,
treasures: 1,
waves: true,
wave_limit: 6,
first_wave: 240.0,
gate: true,
..base("m10", (96, 80), 1010, vec![Goal::Survive(6)])
waves: true,
first_wave: 330.0,
rivers: vec![river(false, false, 0.62, 4, 2)],
rivals: vec![(0.8, 0.3, 1, 2)],
..base("m25", (120, 104), 2525, vec![Goal::Rivals])
});
v.push(MissionDef {
forts: vec![fort((0.72, 0.5), (25, 25), &[3, 0, 2], &[Arrow, Cannon, Magic, Arrow, Cannon], 2, 8, 4, 3, 2, 2000)],
..base("m11", (112, 96), 1111, vec![Goal::KillAll])
rivers: vec![river(false, true, 0.4, 4, 1), river(true, true, 0.62, 4, 1)],
forts: vec![fort((0.86, 0.5), (21, 25), &[3], &[Arrow, Cannon, Magic, Arrow], 2, 10, 2, 3, 2, 2000).heart().shard()],
..base("m12", (120, 112), 1212, vec![Goal::Heart, Goal::Shards])
});
// ---- Act III: the citadel and the masters
v.push(MissionDef {
rivers: vec![river(false, true, 0.4, 3, 1), river(true, true, 0.62, 3, 1)],
forts: vec![fort((0.85, 0.5), (21, 25), &[3], &[Arrow, Cannon, Magic, Arrow], 2, 10, 2, 3, 2, 2000).heart().shard()],
..base("m12", (112, 96), 1212, vec![Goal::Heart, Goal::Shards])
});
// ---- Act III: the citadel
v.push(MissionDef {
rivers: vec![river(false, false, 0.22, 3, 1)],
rivers: vec![river(false, false, 0.22, 4, 1)],
forts: vec![fort((0.75, 0.55), (27, 23), &[3, 0], &[Arrow, Cannon, Magic, Arrow, Cannon, Magic], 2, 12, 3, 3, 4, 3500)],
..base("m13", (112, 96), 1313, vec![Goal::Loot(13000)])
..base("m13", (120, 112), 1313, vec![Goal::Loot(13000)])
});
v.push(MissionDef { forts: vec![fort((0.7, 0.5), (25, 25), &[3, 1], &[Arrow, Cannon, Magic, Arrow, Cannon, Magic, Arrow, Cannon], 3, 8, 2, 3, 2, 2000)], ..base("m14", (128, 112), 1414, vec![Goal::Towers]) });
v.push(MissionDef {
forts: vec![fort((0.7, 0.5), (25, 25), &[3, 1], &[Arrow, Cannon, Magic, Arrow, Cannon, Magic, Arrow, Cannon], 3, 8, 2, 3, 2, 2000)],
..base("m14", (112, 112), 1414, vec![Goal::Towers])
rivers: vec![river(true, true, 0.62, 4, 1)],
islands: vec![island((0.45, 0.78), true, true, 2500)],
rivals: vec![(0.84, 0.5, 2, 3)],
..base("m26", (128, 120), 2626, vec![Goal::Rivals])
});
v.push(MissionDef {
portals: 0,
@@ -223,40 +267,61 @@ pub fn missions() -> Vec<MissionDef> {
gift: vec![(Ogre, 5), (Ogre, 4), (Mage, 5), (Mage, 4), (Spider, 4), (Kobold, 4), (Kobold, 4), (Mage, 3)],
rivers: vec![river(false, true, 0.42, 4, 1)],
forts: vec![fort((0.74, 0.5), (21, 21), &[3, 0, 2], &[Arrow, Cannon, Magic], 2, 10, 2, 3, 2, 2000)],
..base("m15", (112, 96), 1515, vec![Goal::KillAll])
..base("m15", (128, 120), 1515, vec![Goal::KillAll])
});
v.push(MissionDef {
waves: true,
first_wave: 360.0,
gate_defenders: true,
rivers: vec![river(true, true, 0.55, 3, 2)],
forts: vec![fort((0.82, 0.5), (23, 23), &[3], &[Arrow, Cannon, Magic, Arrow], 2, 10, 2, 3, 2, 2000).heart()],
..base("m16", (112, 96), 1616, vec![Goal::Heart])
rivers: vec![river(true, true, 0.55, 4, 2)],
forts: vec![fort((0.84, 0.5), (23, 23), &[3], &[Arrow, Cannon, Magic, Arrow], 2, 10, 2, 3, 2, 2000).heart()],
..base("m16", (128, 128), 1616, vec![Goal::Heart])
});
v.push(MissionDef {
caves: vec![cave((0.42, 0.2), Some((Chainbreaker, 4)), 1000), cave((0.45, 0.82), Some((Gargoyle, 6)), 1000), cave((0.66, 0.5), None, 3000)],
islands: vec![island((0.62, 0.2), false, true, 0), island((0.66, 0.82), true, true, 0)],
forts: vec![fort((0.86, 0.5), (17, 17), &[3], &[Arrow, Cannon, Magic], 3, 8, 2, 3, 1, 1500)],
..base("m27", (136, 128), 2727, vec![Goal::Freed(2), Goal::Springs(2)])
});
v.push(MissionDef {
portals: 2,
rivers: vec![river(false, true, 0.36, 3, 2), river(false, true, 0.56, 3, 2), river(false, false, 0.5, 3, 2)],
rivers: vec![river(false, true, 0.36, 4, 2), river(false, true, 0.56, 4, 2), river(false, false, 0.5, 4, 2)],
forts: vec![
fort((0.8, 0.24), (19, 17), &[3, 2], &[Arrow, Cannon, Magic], 2, 8, 2, 3, 2, 2000).shard(),
fort((0.8, 0.76), (19, 17), &[3, 0], &[Arrow, Cannon, Magic], 2, 8, 2, 3, 2, 2000).shard(),
fort((0.82, 0.24), (19, 17), &[3, 2], &[Arrow, Cannon, Magic], 2, 8, 2, 3, 2, 2000).shard(),
fort((0.82, 0.76), (19, 17), &[3, 0], &[Arrow, Cannon, Magic], 2, 8, 2, 3, 2, 2000).shard(),
],
..base("m17", (128, 112), 1717, vec![Goal::Shards, Goal::Loot(6000)])
..base("m17", (136, 136), 1717, vec![Goal::Shards, Goal::Loot(6000)])
});
v.push(MissionDef {
portals: 2,
rivers: vec![river(false, true, 0.6, 4, 2)],
islands: vec![island((0.45, 0.5), false, true, 2000)],
rivals: vec![(0.84, 0.25, 1, 2), (0.84, 0.75, 1, 3)],
..base("m28", (144, 136), 2828, vec![Goal::Rivals])
});
v.push(MissionDef {
portals: 2,
rivers: vec![river(true, true, 0.5, 4, 1)],
forts: vec![fort((0.82, 0.5), (25, 25), &[3], &[Arrow, Cannon, Magic, Arrow, Cannon, Magic], 3, 14, 3, 4, 3, 2500).heart().boss(Paladin, 5)],
..base("m18", (128, 112), 1818, vec![Goal::Heart])
forts: vec![fort((0.84, 0.5), (25, 25), &[3], &[Arrow, Cannon, Magic, Arrow, Cannon, Magic], 3, 14, 3, 4, 3, 2500).heart().boss(Paladin, 5)],
..base("m18", (144, 144), 1818, vec![Goal::Heart])
});
v.push(MissionDef {
portals: 2,
rivers: vec![river(false, true, 0.42, 3, 2), river(true, false, 0.5, 3, 2)],
rivers: vec![river(false, true, 0.42, 4, 2), river(true, false, 0.5, 4, 2)],
forts: vec![
fort((0.62, 0.22), (19, 17), &[3, 2], &[Arrow, Cannon, Magic], 3, 8, 2, 4, 1, 2500).shard(),
fort((0.86, 0.5), (19, 19), &[3], &[Arrow, Cannon, Magic, Arrow], 3, 10, 2, 4, 1, 2500).shard(),
fort((0.62, 0.8), (19, 17), &[3, 0], &[Arrow, Cannon, Magic], 3, 8, 2, 4, 1, 2500).shard(),
],
..base("m19", (144, 128), 1919, vec![Goal::Shards])
..base("m19", (152, 144), 1919, vec![Goal::Shards])
});
v.push(MissionDef {
portals: 2,
rivers: vec![river(true, true, 0.62, 4, 1)],
islands: vec![island((0.42, 0.22), true, true, 2500), island((0.45, 0.78), true, true, 2500)],
caves: vec![cave((0.5, 0.5), Some((Wyrm, 5)), 2000)],
forts: vec![fort((0.85, 0.5), (21, 21), &[3], &[Arrow, Cannon, Magic, Magic], 3, 12, 2, 4, 2, 2500).shard()],
..base("m29", (152, 152), 2929, vec![Goal::Shards, Goal::Springs(2)])
});
v.push(MissionDef {
portals: 3,
@@ -269,7 +334,19 @@ pub fn missions() -> Vec<MissionDef> {
.heart()
.shard()
.boss(Paladin, 10)],
..base("m20", (144, 144), 2020, vec![Goal::Heart, Goal::Shards])
..base("m20", (160, 160), 2020, vec![Goal::Heart, Goal::Shards])
});
v.push(MissionDef {
portals: 3,
gold: 6000.0,
gate: true,
waves: true,
first_wave: 420.0,
gate_defenders: true,
rivers: vec![river(false, true, 0.45, 4, 2), river(true, false, 0.5, 4, 2)],
islands: vec![island((0.3, 0.2), false, true, 3000)],
rivals: vec![(0.85, 0.22, 2, 2), (0.85, 0.78, 2, 3)],
..base("m30", (176, 176), 3030, vec![Goal::Rivals, Goal::Heart])
});
v
}
@@ -308,6 +385,91 @@ impl FortDef {
}
}
// keeps the core area, portals, gates and fortresses (rock is carved away)
fn free_for_terrain(t: &Tile) -> bool {
t.owner == Owner::None && !matches!(t.kind, Kind::Core | Kind::Portal | Kind::Gate | Kind::Tower | Kind::Shard)
}
// an island ringed by four tiles of water or lava
fn place_island(g: &mut Game, d: &IslandDef, w: usize, h: usize, seed: u32) {
let (cx, cy) = ((d.at.0 * w as f32) as i32, (d.at.1 * h as f32) as i32);
let outer = d.r + 4;
let mut rng = Rng(seed | 1);
for y in cy - outer..=cy + outer {
for x in cx - outer..=cx + outer {
if x < 2 || y < 2 || x >= w as i32 - 2 || y >= h as i32 - 2 {
continue;
}
let dist = (((x - cx).pow(2) + (y - cy).pow(2)) as f32).sqrt();
let t = &mut g.map.t[idx(x, y)];
if !free_for_terrain(t) || dist > outer as f32 + 0.4 {
continue;
}
t.gold = 0;
t.marked = false;
t.reinforced = false;
t.kind = if dist <= d.r as f32 + 0.4 {
Kind::Floor
} else if d.lava {
Kind::Lava
} else {
Kind::Water
};
}
}
if d.spring {
g.map.t[idx(cx, cy)].kind = Kind::ManaSpring;
}
if d.treasure > 0 {
let spots = [(cx - 1, cy), (cx + 1, cy), (cx, cy - 1), (cx, cy + 1)];
let per = d.treasure / spots.len() as i32;
for &(x, y) in &spots {
let t = &mut g.map.t[idx(x, y)];
t.kind = Kind::Treasury;
t.gold = per;
let r = rng.f();
t.res = if r < 0.15 { 3 } else if r < 0.4 { 2 } else if r < 0.6 { 1 } else { 0 };
}
}
}
// a small cave sealed by earth all around, with treasure and/or a creature in chains
fn place_cave(g: &mut Game, d: &CaveDef, w: usize, h: usize) {
let (cx, cy) = ((d.at.0 * w as f32) as i32, (d.at.1 * h as f32) as i32);
for y in cy - 3..=cy + 3 {
for x in cx - 3..=cx + 3 {
if x < 2 || y < 2 || x >= w as i32 - 2 || y >= h as i32 - 2 {
continue;
}
let t = &mut g.map.t[idx(x, y)];
if !free_for_terrain(t) {
continue;
}
let inside = (x - cx).abs() <= 2 && (y - cy).abs() <= 2;
t.kind = if inside { Kind::Floor } else { Kind::Earth };
t.gold = 0;
t.marked = false;
t.reinforced = false;
t.hp = 6.0;
}
}
if d.treasure > 0 {
let spots = [(cx - 2, cy - 2), (cx + 2, cy - 2), (cx - 2, cy + 2), (cx + 2, cy + 2)];
for &(x, y) in &spots {
let t = &mut g.map.t[idx(x, y)];
t.kind = Kind::Treasury;
t.gold = d.treasure / 4;
t.res = 2;
}
}
if let Some((kind, level)) = d.chained {
let id = g.spawn(kind, false, cx as f32 + 0.5, cy as f32 + 0.5, level);
if let Some(e) = g.ents.iter_mut().find(|e| e.id == id) {
e.chained = true;
}
}
}
// Builds the game of mission i (carry: what the last mission handed over)
pub fn start_mission(i: usize, carry: Option<&Carry>) -> Game {
let all = missions();
@@ -326,10 +488,22 @@ pub fn start_mission(i: usize, carry: Option<&Carry>) -> Game {
g.build_fortress(&f.spec(w, h));
shards += f.shard as u32;
}
for (k, isl) in d.islands.iter().enumerate() {
place_island(&mut g, isl, w, h, d.seed.wrapping_add(k as u32 * 131));
}
for c in &d.caves {
place_cave(&mut g, c, w, h);
}
for &(rx, ry, diff, side) in &d.rivals {
let x = ((rx * w as f32) as i32).clamp(10, w as i32 - 10);
let y = ((ry * h as f32) as i32).clamp(10, h as i32 - 10);
g.add_rival(side, x, y, diff);
}
let heart = d.gate || d.forts.iter().any(|f| f.heart);
if !heart {
g.gate_hp = 0.0;
}
g.gate_on = heart;
g.guard_treasures();
g.place_all_torches();
g.mission = Some(MissionState {
@@ -401,8 +575,23 @@ mod tests {
assert!(vaults as u32 >= *n, "{}: vaults hold {}", d.key, vaults);
}
Goal::Survive(_) => assert!(m.waves, "{}", d.key),
Goal::Rivals => assert!(!g.rivals.is_empty(), "{}", d.key),
Goal::Springs(n) => {
let springs = g.map.t.iter().filter(|t| t.kind == Kind::ManaSpring).count();
assert!(springs as u32 >= *n, "{}: {} springs", d.key, springs);
}
Goal::Freed(n) => {
let chained = g.ents.iter().filter(|e| e.alive && e.chained).count();
assert!(chained as u32 >= *n, "{}: {} chained", d.key, chained);
}
}
}
// rival masters stand with an intact core
for r in &g.rivals {
let t = &g.map.t[idx(r.core.0 + 1, r.core.1 + 1)];
assert!(t.kind == Kind::Core && t.owner == Owner::of_side(r.side), "{}: rival core broken", d.key);
println!(" rival {} at {:?}", r.side, r.core);
}
// wave missions: the heroes can reach the core
if m.waves {
let (gx, gy) = g.map.gate;