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:
1 parent
dd16c684e5
commit
ea0623fbe3
11 files changed
+413
-85
No files matched your search
+243
-54
@@ -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;
|
||||
|
||||
+57
-12
@@ -83,6 +83,8 @@ pub struct SfxEvent {
|
||||
pub pos: Option<(f32, f32)>, // world position (for distance-based volume), None = UI
|
||||
}
|
||||
pub const MANA_MAX: f32 = 400.0;
|
||||
pub const SPRING_MANA: f32 = 0.6; // mana per second of each claimed mana spring
|
||||
pub const SPRING_MAX: f32 = 150.0; // more mana maximum per claimed spring
|
||||
pub const CARRY_MAX: i32 = 300;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
|
||||
@@ -241,6 +243,8 @@ pub struct Ent {
|
||||
pub side: u8, // 0 player, 2 and 3 rival masters (heroes: see side())
|
||||
#[serde(default)]
|
||||
pub target: Option<u8>, // attackers: side whose core (or gate) they march on
|
||||
#[serde(default)]
|
||||
pub chained: bool, // a lone creature chained in a sealed cave: joins when the cave is claimed
|
||||
}
|
||||
|
||||
fn yes() -> bool {
|
||||
@@ -295,6 +299,7 @@ impl Ent {
|
||||
pay_t: 0.0,
|
||||
side: 0,
|
||||
target: None,
|
||||
chained: false,
|
||||
}
|
||||
}
|
||||
pub fn tile(&self) -> usize {
|
||||
@@ -305,7 +310,7 @@ impl Ent {
|
||||
}
|
||||
// the player's creatures (not imps, heroes or the rivals' units)
|
||||
pub fn creature(&self) -> bool {
|
||||
!self.hero && self.side == 0 && self.kind != EK::Worker
|
||||
!self.hero && self.side == 0 && self.kind != EK::Worker && !self.chained
|
||||
}
|
||||
// 0 player, 1 humans, 2 and 3 rival masters
|
||||
pub fn side(&self) -> u8 {
|
||||
@@ -334,6 +339,9 @@ pub enum Goal {
|
||||
Loot(u32), // take treasure worth this much from knight vaults
|
||||
Towers, // raze all knight towers
|
||||
Survive(u32), // beat this many waves
|
||||
Rivals, // destroy the soul cores of all rival masters
|
||||
Springs(u32), // claim this many mana springs
|
||||
Freed(u32), // free this many chained creatures
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
@@ -424,7 +432,11 @@ pub struct Game {
|
||||
#[serde(default)]
|
||||
pub rivals: Vec<crate::rival::Rival>, // AI dungeon masters
|
||||
#[serde(default = "yes")]
|
||||
pub gate_on: bool, // free game: the humans' gate exists // tiles next to fighting heroes: knight ground there cannot be claimed
|
||||
pub gate_on: bool, // free game: the humans' gate exists
|
||||
#[serde(skip)]
|
||||
pub springs: usize, // claimed mana springs (recounted)
|
||||
#[serde(default)]
|
||||
pub freed: u32, // chained creatures freed // tiles next to fighting heroes: knight ground there cannot be claimed
|
||||
#[serde(skip)]
|
||||
pub hints: bool, // show hint messages (setting)
|
||||
#[serde(skip, default = "hint_times")]
|
||||
@@ -568,6 +580,8 @@ impl Game {
|
||||
mission: None,
|
||||
rivals: Vec::new(),
|
||||
gate_on: true,
|
||||
springs: 0,
|
||||
freed: 0,
|
||||
};
|
||||
let (cx, cy) = g.map.core;
|
||||
// the soul core starts surrounded by a one tile wide ring of treasury
|
||||
@@ -613,6 +627,9 @@ impl Game {
|
||||
Goal::Loot(n) => self.loot >= n as f32,
|
||||
Goal::Towers => !self.towers.iter().any(|t| t.hero),
|
||||
Goal::Survive(n) => self.wave >= n && !self.ents.iter().any(|e| e.alive && e.active_hero() && !e.guard && e.patrol.is_empty()),
|
||||
Goal::Rivals => self.rivals.iter().all(|r| !r.alive),
|
||||
Goal::Springs(n) => self.springs >= n as usize,
|
||||
Goal::Freed(n) => self.freed >= n,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -694,6 +711,7 @@ impl Game {
|
||||
|
||||
pub fn recount(&mut self) {
|
||||
let mut c = [0usize; 8];
|
||||
self.springs = self.map.t.iter().filter(|t| t.kind == Kind::ManaSpring && t.owner == Owner::Player).count();
|
||||
for t in &self.map.t {
|
||||
if t.owner == Owner::Player {
|
||||
if let Some(r) = room_index(t.kind) {
|
||||
@@ -704,6 +722,10 @@ impl Game {
|
||||
self.counts = c;
|
||||
}
|
||||
|
||||
pub fn mana_max(&self) -> f32 {
|
||||
MANA_MAX + SPRING_MAX * self.springs as f32
|
||||
}
|
||||
|
||||
pub fn gold_cap(&self) -> f32 {
|
||||
CORE_STORE + TREASURY_TILE * self.counts[0] as f32
|
||||
}
|
||||
@@ -907,7 +929,7 @@ impl Game {
|
||||
fn step(&mut self, dt: f32) {
|
||||
self.time += dt;
|
||||
self.recount();
|
||||
self.mana = (self.mana + 1.0 * dt).min(MANA_MAX);
|
||||
self.mana = (self.mana + (1.0 + SPRING_MANA * self.springs as f32) * dt).min(self.mana_max());
|
||||
self.core_hp = (self.core_hp + 0.5 * dt).min(CORE_HP);
|
||||
for t in self.map.t.iter_mut() {
|
||||
if t.kind == Kind::Farm {
|
||||
@@ -1532,7 +1554,23 @@ impl Game {
|
||||
self.msg("msg.level_up", vec![format!("@{}", s.name), e.level.to_string()]);
|
||||
}
|
||||
}
|
||||
if e.hero && e.captive != 0 {
|
||||
if e.chained {
|
||||
// waits in chains until the player's imps claim its cave
|
||||
e.moving = false;
|
||||
let (x, y) = xy(e.tile());
|
||||
let reached = std::iter::once((0, 0)).chain(DIR8.iter().copied()).any(|(dx, dy)| self.map.get(x + dx, y + dy).map_or(false, |t| t.owner == Owner::Player));
|
||||
if reached {
|
||||
e.chained = false;
|
||||
e.settled = false;
|
||||
e.job = Job::Register;
|
||||
e.timer = 1.0;
|
||||
e.happy = 1.0;
|
||||
self.freed += 1;
|
||||
self.fx(7, e.x, e.y, 0.0, 0.0, 1.2, 0xff44ddff);
|
||||
self.sfx(Sfx::Convert, e.x, e.y);
|
||||
self.msg("msg.creature_freed", vec![format!("@{}", st(e.kind).name)]);
|
||||
}
|
||||
} else if e.hero && e.captive != 0 {
|
||||
self.captive_update(e, dt);
|
||||
} else if e.rival() {
|
||||
self.rival_ai(e, dt);
|
||||
@@ -1667,7 +1705,7 @@ impl Game {
|
||||
let mut bd = radius * radius;
|
||||
for o in &self.ents {
|
||||
// every other side is an enemy
|
||||
if !o.alive || o.held || o.side() == e.side() || o.captive != 0 {
|
||||
if !o.alive || o.held || o.side() == e.side() || o.captive != 0 || o.chained {
|
||||
continue;
|
||||
}
|
||||
let d = (o.x - e.x).powi(2) + (o.y - e.y).powi(2);
|
||||
@@ -2118,6 +2156,11 @@ impl Game {
|
||||
self.sfx(Sfx::Payday, x, y);
|
||||
self.msg("msg.vault_taken", vec![t.gold.to_string()]);
|
||||
}
|
||||
if t.kind == Kind::ManaSpring {
|
||||
self.fx(7, x, y, 0.0, 0.0, 1.2, 0xffffe060);
|
||||
self.sfx(Sfx::Magic, x, y);
|
||||
self.msg("msg.spring_taken", vec![]);
|
||||
}
|
||||
if t.kind == Kind::Shard {
|
||||
self.shards_taken += 1;
|
||||
self.fx(7, x, y, 0.0, 0.0, 1.2, 0xff6060ff);
|
||||
@@ -2251,7 +2294,7 @@ impl Game {
|
||||
fn claimable(&self, i: usize, me: u32) -> bool {
|
||||
let t = &self.map.t[i];
|
||||
let ok = match t.owner {
|
||||
Owner::None => matches!(t.kind, Kind::Floor | Kind::Portal | Kind::Bridge | Kind::LavaBridge | Kind::Shard),
|
||||
Owner::None => matches!(t.kind, Kind::Floor | Kind::Portal | Kind::Bridge | Kind::LavaBridge | Kind::Shard | Kind::Treasury | Kind::ManaSpring),
|
||||
// knight ground (also vaults and shard chambers), but not while heroes fight there
|
||||
// knight and rival ground (also vaults and shard chambers), but not while enemies fight there
|
||||
Owner::Hero | Owner::Rival1 | Owner::Rival2 => {
|
||||
@@ -2459,15 +2502,17 @@ impl Game {
|
||||
e.working = true;
|
||||
e.timer += dt * self.help_speed(t);
|
||||
// knight ground takes longer
|
||||
let knight = self.map.t[t].owner == Owner::Hero;
|
||||
if e.timer >= if knight { 3.0 } else { 1.0 } {
|
||||
let prev = self.map.t[t].owner;
|
||||
let knight = prev == Owner::Hero;
|
||||
if e.timer >= if prev != Owner::None { 3.0 } else { 1.0 } {
|
||||
self.unreserve(me);
|
||||
e.job = Job::None;
|
||||
let (tx, ty) = xy(t);
|
||||
if knight {
|
||||
self.take_knight_tile(t);
|
||||
self.alarm(tx as f32 + 0.5, ty as f32 + 0.5, e.id);
|
||||
}
|
||||
// vaults, shards and springs (of the knights, rivals or nobody)
|
||||
self.take_knight_tile(t);
|
||||
if let (Kind::Portal, Some(pk)) = (self.map.t[t].kind, self.map.portal_at(tx, ty)) {
|
||||
let (px, py) = self.map.portals[pk];
|
||||
for y in 0..3 {
|
||||
@@ -2827,7 +2872,7 @@ impl Game {
|
||||
}
|
||||
e.working = true;
|
||||
let rate = if e.kind == EK::Mage { 1.2 } else { 0.5 };
|
||||
self.mana = (self.mana + rate * dt).min(MANA_MAX);
|
||||
self.mana = (self.mana + rate * dt).min(self.mana_max());
|
||||
e.xp += 3.0 * dt;
|
||||
e.timer -= dt;
|
||||
if e.timer <= 0.0 {
|
||||
@@ -2843,7 +2888,7 @@ impl Game {
|
||||
}
|
||||
e.working = true;
|
||||
let rate = if e.kind == EK::Priestess { 1.6 } else { 0.9 };
|
||||
self.mana = (self.mana + rate * dt).min(MANA_MAX);
|
||||
self.mana = (self.mana + rate * dt).min(self.mana_max());
|
||||
e.happy = (e.happy + 0.01 * dt).min(1.0);
|
||||
e.xp += 1.0 * dt;
|
||||
e.timer -= dt;
|
||||
@@ -3781,7 +3826,7 @@ impl Game {
|
||||
for (j, e) in self.ents.iter().enumerate() {
|
||||
// knight towers shoot the player's units, the player's towers shoot heroes
|
||||
// knight towers shoot everybody but the humans, the player's towers everybody else
|
||||
let enemy = e.captive == 0 && if t.hero { e.side() != 1 } else { e.side() != 0 };
|
||||
let enemy = e.captive == 0 && !e.chained && if t.hero { e.side() != 1 } else { e.side() != 0 };
|
||||
if !e.alive || e.held || !enemy {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1382,6 +1382,7 @@ impl Gfx {
|
||||
Kind::Shard => T_HEROFLOOR + v,
|
||||
Kind::Aerie => T_AERIE + v,
|
||||
Kind::Steel => T_STEEL + v,
|
||||
Kind::ManaSpring => T_PORTALFLOOR + v,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -148,7 +148,7 @@ fn mission_game(i: usize, progress: &campaign::Progress) -> (Game, Cam) {
|
||||
(g, cam)
|
||||
}
|
||||
|
||||
const PROGRESS_KEY: &str = "dc.campaign";
|
||||
const PROGRESS_KEY: &str = "dc.campaign30"; // new key: the 30-mission order differs from the old one
|
||||
|
||||
fn store_progress(p: &campaign::Progress) {
|
||||
platform::storage_set(PROGRESS_KEY, &serde_json::to_string(p).unwrap_or_default());
|
||||
|
||||
+4
-1
@@ -59,6 +59,7 @@ pub enum Kind {
|
||||
Diamond, // diamond vein: very rare, very valuable
|
||||
Aerie, // room for flying creatures (their beds)
|
||||
Steel, // steel wall: hardly anyone gets through
|
||||
ManaSpring, // crystal spring: once claimed, more mana and a higher mana maximum
|
||||
}
|
||||
|
||||
// options for the map generator (campaign missions)
|
||||
@@ -87,7 +88,7 @@ pub struct River {
|
||||
}
|
||||
|
||||
// order matters for the packed savegame codec: only append new kinds
|
||||
const KINDS: [Kind; 25] = [
|
||||
const KINDS: [Kind; 26] = [
|
||||
Kind::Rock,
|
||||
Kind::Earth,
|
||||
Kind::Gold,
|
||||
@@ -113,6 +114,7 @@ const KINDS: [Kind; 25] = [
|
||||
Kind::Diamond,
|
||||
Kind::Aerie,
|
||||
Kind::Steel,
|
||||
Kind::ManaSpring,
|
||||
];
|
||||
|
||||
// treasures (veins, treasury content, imp loads): 0 gold, 1 silver, 2 ruby, 3 diamond
|
||||
@@ -207,6 +209,7 @@ impl Tile {
|
||||
| Kind::LavaBridge
|
||||
| Kind::Shard
|
||||
| Kind::Aerie
|
||||
| Kind::ManaSpring
|
||||
)
|
||||
}
|
||||
// river tiles (open, but nobody can walk there)
|
||||
|
||||
@@ -1174,5 +1174,8 @@ pub fn goal_text(g: Goal) -> String {
|
||||
Goal::Loot(n) => trf("goal.loot", &[n.to_string()]),
|
||||
Goal::Towers => tr("goal.towers"),
|
||||
Goal::Survive(n) => trf("goal.survive", &[n.to_string()]),
|
||||
Goal::Rivals => tr("goal.rivals"),
|
||||
Goal::Springs(n) => trf("goal.springs", &[n.to_string()]),
|
||||
Goal::Freed(n) => trf("goal.freed", &[n.to_string()]),
|
||||
}
|
||||
}
|
||||
@@ -97,10 +97,32 @@ pub fn draw_props_solid(g: &Game, cam: &Cam, t: f32) {
|
||||
Kind::Temple if (x + z) % 2 == 0 && h % 3 == 0 => candle_stand(c + vec3(((h >> 4) % 5) as f32 * 0.1 - 0.2, 0.0, ((h >> 8) % 5) as f32 * 0.1 - 0.2), l),
|
||||
Kind::Library if z % 2 == 0 => shelf(c, h, l),
|
||||
Kind::Library if h % 3 == 0 => lectern(c, l),
|
||||
Kind::ManaSpring => spring_basin(c, l),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
// creatures in chains: an iron post with chains to the creature
|
||||
for e in g.ents.iter().filter(|e| e.alive && e.chained) {
|
||||
if (e.x as i32) < x0 || (e.x as i32) > x1 || (e.y as i32) < z0 || (e.y as i32) > z1 {
|
||||
continue;
|
||||
}
|
||||
let post = vec3(e.x.floor() + 0.5, 0.0, e.y.floor() + 0.15);
|
||||
cube(post + vec3(0.0, 0.35, 0.0), vec3(0.08, 0.7, 0.08), rgb(70, 70, 78, 0.6));
|
||||
for n in 0..6 {
|
||||
let f = n as f32 / 6.0;
|
||||
let side = if n % 2 == 0 { 0.08 } else { -0.08 };
|
||||
let p = post.lerp(vec3(e.x + side, 0.25, e.y), f) + vec3(0.0, 0.3 * (1.0 - f) + 0.05 * (f * 3.14).sin(), 0.0);
|
||||
cube(p, vec3(0.05, 0.05, 0.05), rgb(120, 120, 130, 0.6));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// a stone basin around a mana spring
|
||||
fn spring_basin(c: Vec3, l: f32) {
|
||||
for (dx, dz, sx, sz) in [(0.0, -0.38, 0.8, 0.1), (0.0, 0.38, 0.8, 0.1), (-0.38, 0.0, 0.1, 0.8), (0.38, 0.0, 0.1, 0.8)] {
|
||||
cube(c + vec3(dx, 0.08, dz), vec3(sx, 0.16, sz), rgb(90, 95, 110, l));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw_props_glow(g: &Game, cam: &Cam, t: f32) {
|
||||
@@ -161,6 +183,17 @@ pub fn draw_props_glow(g: &Game, cam: &Cam, t: f32) {
|
||||
}
|
||||
}
|
||||
}
|
||||
Kind::ManaSpring => {
|
||||
// rising mana: a pulsing pool and motes that drift up
|
||||
let f = 0.8 + 0.2 * (t * 2.0 + k).sin();
|
||||
draw_cube(c + vec3(0.0, 0.1, 0.0), vec3(0.66, 0.04, 0.66), None, Color::new(0.3, 0.8, 1.0, 0.85 * f));
|
||||
for n in 0..5 {
|
||||
let a = n as f32 * 1.26 + k;
|
||||
let y = ((t * 0.5 + n as f32 * 0.2) % 1.0) * 1.2;
|
||||
draw_sphere(c + vec3(a.cos() * 0.2, 0.15 + y, a.sin() * 0.2), 0.05 * (1.2 - y), None, Color::new(0.5, 0.9, 1.0, 0.9));
|
||||
}
|
||||
draw_sphere(c + vec3(0.0, 0.4, 0.0), 0.3 * f, None, Color::new(0.3, 0.7, 1.0, 0.15));
|
||||
}
|
||||
Kind::Lair if tile.bed != 0 => {
|
||||
// the flame priestess sleeps on embers
|
||||
if let Some(EK::Priestess) = u.beds.get(&tile.bed) {
|
||||
|
||||
@@ -1805,6 +1805,7 @@ pub fn minimap_color(tile: &Tile) -> [u8; 4] {
|
||||
Kind::Shard => [255, 60, 120, 255],
|
||||
Kind::Aerie => [170, 140, 90, 255],
|
||||
Kind::Steel => [150, 160, 180, 255],
|
||||
Kind::ManaSpring => [90, 230, 255, 255],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -452,7 +452,7 @@ impl Ui {
|
||||
}
|
||||
let mut segs: Vec<Vec<Part>> = vec![
|
||||
gold_seg,
|
||||
vec![txt(tr("hud.mana"), MANA), Part::Gap, Part::Bar(g.mana / MANA_MAX, MANA), Part::Gap, txt(format!("{:<3}", g.mana as i32), TXT)],
|
||||
vec![txt(tr("hud.mana"), MANA), Part::Gap, Part::Bar(g.mana / g.mana_max(), MANA), Part::Gap, txt(format!("{:<3}", g.mana as i32), TXT)],
|
||||
vec![txt(tr("hud.core"), RED), Part::Gap, Part::Bar(g.core_hp / CORE_HP, RED)],
|
||||
vec![txt(tr("hud.creatures"), TXT), Part::Gap, txt(format!("{}/{}", g.creature_count(), cap), TXT)],
|
||||
vec![txt(tr("hud.workers"), TXT), Part::Gap, txt(g.worker_count().to_string(), TXT)],
|
||||
@@ -699,6 +699,9 @@ impl Ui {
|
||||
Goal::Loot(n) => format!(" {}/{}", (g.loot as u32).min(n), n),
|
||||
Goal::Towers => trf("goal.left", &[g.towers.iter().filter(|t| t.hero).count().to_string()]),
|
||||
Goal::Survive(n) => format!(" {}/{}", g.wave.min(n), n),
|
||||
Goal::Rivals => trf("goal.left", &[g.rivals.iter().filter(|r| r.alive).count().to_string()]),
|
||||
Goal::Springs(n) => format!(" {}/{}", g.springs.min(n as usize), n),
|
||||
Goal::Freed(n) => format!(" {}/{}", g.freed.min(n), n),
|
||||
};
|
||||
let text = format!("{} {}{}", if done { "\u{221a}" } else { "\u{25ba}" }, crate::menu::goal_text(goal), progress);
|
||||
for l in wrap(&text, gcols as usize - 4) {
|
||||
|
||||
Reference in new issue
Block a user