Final missions above ground (M30, co-op M20), surface option for versus matches, docs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1ae41228ab
commit
f27dbea2f1
28 files changed
+111
-49
No files matched your search
+7
-4
@@ -358,16 +358,19 @@ pub fn missions() -> Vec<MissionDef> {
|
||||
.boss(Paladin, 10)],
|
||||
..base("m20", (160, 160), 2020, vec![Goal::Heart, Goal::Shards])
|
||||
});
|
||||
// the finale above ground: the dungeon in the rock, the eastern half a forest with the High
|
||||
// Marshal's fortress (the gate of the order) and both rival masters
|
||||
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)],
|
||||
rivers: vec![river(false, true, 0.3, 4, 2)],
|
||||
islands: vec![island((0.2, 0.2), false, true, 3000)],
|
||||
forts: vec![fort((0.8, 0.5), (27, 25), &[3], &[Arrow, Cannon, Magic, Arrow, Cannon, Magic, Arrow, Cannon], 3, 14, 4, 4, 3, 3000).heart().boss(Paladin, 10)],
|
||||
rivals: vec![(0.86, 0.15, 2, 2), (0.86, 0.85, 2, 3)],
|
||||
surface: 0.48,
|
||||
..base("m30", (176, 176), 3030, vec![Goal::Rivals, Goal::Heart])
|
||||
});
|
||||
v
|
||||
|
||||
@@ -91,7 +91,7 @@ pub fn coop_campaign() -> CampaignDoc {
|
||||
waves: sp.waves.0,
|
||||
wave_limit: sp.waves.1,
|
||||
first_wave: if sp.waves.0 { sp.waves.2 } else { 300.0 },
|
||||
gen: GenDoc { rivers: sp.gen.0, lava: sp.gen.1, forts: sp.gen.2, rivals: 0, difficulty: sp.gen.3, surface: false },
|
||||
gen: GenDoc { rivers: sp.gen.0, lava: sp.gen.1, forts: sp.gen.2, rivals: 0, difficulty: sp.gen.3, surface: i + 1 == specs.len() },
|
||||
next: (i + 1 < specs.len()).then(|| format!("c{:02}", i + 2)),
|
||||
pos: (40.0 + (i % 5) as f32 * 220.0, 40.0 + (i / 5) as f32 * 140.0),
|
||||
..MissionDoc::default()
|
||||
|
||||
+16
-2
@@ -90,6 +90,7 @@ pub struct StartInfo {
|
||||
pub teams: Vec<(u8, u8)>, // (side, team) of a versus match
|
||||
pub next: bool, // the next mission of a running editor campaign co-op match
|
||||
pub carry: Vec<(u8, crate::campaign::Carry)>, // what every side brings into that mission
|
||||
pub surface: bool, // a free match whose eastern half lies above ground
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
@@ -223,6 +224,18 @@ impl MatchClient {
|
||||
self.send_create(sides, map_w, map_h, difficulty, None);
|
||||
}
|
||||
|
||||
// a free match; `surface`: its eastern half lies above ground
|
||||
pub fn create_land(&mut self, sides: &[u8], map_w: usize, map_h: usize, difficulty: u8, surface: bool) {
|
||||
let n = self.outbox.len();
|
||||
self.send_create(sides, map_w, map_h, difficulty, None);
|
||||
if let (true, Some(text)) = (surface, self.outbox.get_mut(n)) {
|
||||
if let Ok(mut msg) = serde_json::from_str::<Value>(text) {
|
||||
msg["surface"] = json!(true);
|
||||
*text = msg.to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_coop(&mut self, mission: usize, sides: &[u8], map_w: usize, map_h: usize, difficulty: u8) {
|
||||
self.send_create(sides, map_w, map_h, difficulty, Some(mission));
|
||||
}
|
||||
@@ -550,7 +563,8 @@ impl MatchClient {
|
||||
// a resumed editor campaign run goes on like the next mission of a running one
|
||||
let resume = editor && v.get("resume").and_then(|x| x.as_bool()).unwrap_or(false);
|
||||
let carry: Vec<(u8, crate::campaign::Carry)> = if resume { v.get("carry").cloned().and_then(|c| serde_json::from_value(c).ok()).unwrap_or_default() } else { Vec::new() };
|
||||
self.start = Some(StartInfo { seed, map_w, map_h, difficulty, sides, local_side, mission, ai, doc, editor, teams, next: resume, carry });
|
||||
let surface = v.get("surface").and_then(|x| x.as_bool()).unwrap_or(false);
|
||||
self.start = Some(StartInfo { seed, map_w, map_h, difficulty, sides, local_side, mission, ai, doc, editor, teams, next: resume, carry, surface });
|
||||
self.error = None;
|
||||
self.status = None;
|
||||
}
|
||||
@@ -581,7 +595,7 @@ impl MatchClient {
|
||||
}
|
||||
r.ai = ai.clone();
|
||||
}
|
||||
self.start = Some(StartInfo { seed, map_w, map_h, difficulty, sides, local_side, mission: Some(mission), ai, doc: Some(doc), editor: true, teams, next: true, carry });
|
||||
self.start = Some(StartInfo { seed, map_w, map_h, difficulty, sides, local_side, mission: Some(mission), ai, doc: Some(doc), editor: true, teams, next: true, carry, surface: false });
|
||||
self.error = None;
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -349,7 +349,7 @@ fn account_action(a: MenuAction, cloud: &mut Cloud, menu: &mut Menu, app: &mut A
|
||||
}
|
||||
}
|
||||
MenuAction::MatchJoin(code) => app.client.join(&code),
|
||||
MenuAction::MatchCreate => app.client.create(&[0, 2, 3, 4], app.settings.map_w, app.settings.map_h, app.settings.difficulty),
|
||||
MenuAction::MatchCreate => app.client.create_land(&[0, 2, 3, 4], app.settings.map_w, app.settings.map_h, app.settings.difficulty, app.settings.surface),
|
||||
MenuAction::MatchCoopCreate(mission) => {
|
||||
let all = campaign::missions();
|
||||
if let Some(d) = all.get(mission) {
|
||||
@@ -692,7 +692,7 @@ impl App {
|
||||
let next = run_seed.is_some();
|
||||
let seed = run_seed.unwrap_or_else(|| next_seed(self.seed));
|
||||
let editor = doc.is_some();
|
||||
let info = StartInfo { seed, map_w, map_h, difficulty: self.settings.difficulty, sides: vec![0, 2], local_side: Some(0), mission: Some(mission), ai: vec![2], doc, editor, teams: Vec::new(), next, carry };
|
||||
let info = StartInfo { seed, map_w, map_h, difficulty: self.settings.difficulty, sides: vec![0, 2], local_side: Some(0), mission: Some(mission), ai: vec![2], doc, editor, teams: Vec::new(), next, carry, surface: false };
|
||||
if !self.start_match_game(info) {
|
||||
return false;
|
||||
}
|
||||
@@ -772,6 +772,10 @@ impl App {
|
||||
if !self.game.start_match(&info.sides, local) {
|
||||
return false;
|
||||
}
|
||||
if info.surface {
|
||||
// the same land on every client: built from the shared seed
|
||||
self.game.add_surface(info.seed, 0.5, 2 + (info.map_h / 64) as u32);
|
||||
}
|
||||
self.game.coop = false;
|
||||
self.game.set_teams(&info.teams);
|
||||
self.game.ai_difficulty = info.difficulty;
|
||||
|
||||
@@ -183,4 +183,16 @@ mod tests {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_finale_is_fought_above_ground() {
|
||||
let g = crate::campaign::start_mission(29, None);
|
||||
assert!(g.surface_share() > 0.3, "share {}", g.surface_share());
|
||||
assert_eq!(g.rivals.len(), 2);
|
||||
assert!(g.gate_hp > 0.0 && g.has_gate());
|
||||
let (gx, gy) = g.map.gate;
|
||||
assert!(g.map.sky(idx(gx + 1, gy + 1)), "the gate of the order stands in the forest");
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user