Co-op matches of editor campaigns

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-05 17:39:38 +01:00
1 parent 0966e3318f
commit abe01ff23e
13 files changed
+394 -44

No files matched your search

+58 -3
View File
@@ -619,13 +619,68 @@ pub fn run_map_seed(run_seed: u32, mission: usize) -> u32 {
// a mission of an editor campaign: its painted map or a random map (rolled from the run's
// seed), the goals it can actually have, and its block scripts
pub fn start_editor_mission(doc: &crate::campaign_doc::MissionDoc, i: usize, run_seed: u32) -> Game {
editor_mission_mode(doc, i, run_seed, false)
}
// the same mission for a co-op match: the other master seats (2, 3, 4) belong to the players, so
// rival masters of the map are left out (like the built-in co-op missions) and so is the Rivals goal
pub fn start_editor_coop_mission(doc: &crate::campaign_doc::MissionDoc, i: usize, run_seed: u32) -> Game {
editor_mission_mode(doc, i, run_seed, true)
}
// editor missions that can be played in co-op: the master areas need a map of at least 72x72,
// and a goal other than beating rival masters
pub fn is_editor_coop_mission(doc: &crate::campaign_doc::MissionDoc) -> bool {
doc.size.0 >= 72 && doc.size.1 >= 72 && crate::campaign_doc::parse_goals(&doc.goals).iter().any(|g| *g != Goal::Rivals)
}
pub fn editor_coop_indexes(doc: &crate::campaign_doc::CampaignDoc) -> Vec<usize> {
doc.missions.iter().enumerate().filter(|(_, m)| is_editor_coop_mission(m)).map(|(i, _)| i).collect()
}
pub fn next_editor_coop_mission(doc: &crate::campaign_doc::CampaignDoc, i: usize, dir: i32) -> usize {
let list = editor_coop_indexes(doc);
if list.is_empty() {
return 0;
}
let pos = list.iter().position(|m| *m == i).unwrap_or(0);
list[((pos as i32 + dir).rem_euclid(list.len() as i32)) as usize]
}
// a painted map without its rival masters: their creatures go, their tiles become unowned
fn remove_rivals(g: &mut Game) {
let sides: Vec<u8> = g.rivals.iter().map(|r| r.side).collect();
if sides.is_empty() {
return;
}
g.rivals.clear();
g.ents.retain(|e| e.hero || !sides.contains(&e.side));
let owners: Vec<Owner> = sides.iter().map(|&s| Owner::of_side(s)).collect();
for t in g.map.t.iter_mut() {
if owners.contains(&t.owner) {
t.owner = Owner::None;
if t.kind == Kind::Core {
t.kind = Kind::Floor;
}
}
}
g.recount();
}
fn editor_mission_mode(doc: &crate::campaign_doc::MissionDoc, i: usize, run_seed: u32, coop: bool) -> Game {
use crate::campaign_doc::MapDoc;
let generated = || start_mission_def(&generated_def(doc, run_map_seed(run_seed, i)), i, None, coop);
let mut g = match &doc.map {
MapDoc::Fixed { game } => match load_fixed_map(game) {
Some(g) => g,
None => start_mission_def(&generated_def(doc, run_map_seed(run_seed, i)), i, None, false),
Some(mut g) => {
if coop {
remove_rivals(&mut g);
}
g
}
None => generated(),
},
MapDoc::Random => start_mission_def(&generated_def(doc, run_map_seed(run_seed, i)), i, None, false),
MapDoc::Random => generated(),
};
g.msgs.clear();
g.state = 0;