Versus teams, gold for allies, fallen hearts as spectators, multi-mission editor co-op
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
811c955bad
commit
fc9c49d4ca
16 files changed
+1896
-49
No files matched your search
+33
-17
@@ -617,6 +617,10 @@ pub struct Game {
|
||||
#[serde(skip)]
|
||||
pub coop: bool, // co-op match: the playable master sides are on one side against the heroes
|
||||
#[serde(skip)]
|
||||
pub teams: Vec<(u8, u8)>, // (side, team) of a versus match; a side without an entry is its own team
|
||||
#[serde(skip)]
|
||||
pub out_sides: Vec<u8>, // masters of a match whose soul core fell (their creatures fight on)
|
||||
#[serde(skip)]
|
||||
pub marks: Vec<Mark>, // short-lived markers for commands given by the masters
|
||||
#[serde(skip)]
|
||||
pub hints: bool, // show hint messages (setting)
|
||||
@@ -859,6 +863,8 @@ impl Game {
|
||||
ai_sides: Vec::new(),
|
||||
ai_difficulty: 1,
|
||||
coop: false,
|
||||
teams: Vec::new(),
|
||||
out_sides: Vec::new(),
|
||||
marks: Vec::new(),
|
||||
hint_t: hint_times(),
|
||||
reserve: 0.0,
|
||||
@@ -1367,15 +1373,8 @@ impl Game {
|
||||
side == self.me_side || self.masters.iter().any(|m| m.side == side)
|
||||
}
|
||||
|
||||
pub(crate) fn is_coop_ally_side(&self, side: u8) -> bool {
|
||||
self.coop
|
||||
&& Owner::MASTER_SIDES.contains(&self.local_side)
|
||||
&& Owner::MASTER_SIDES.contains(&side)
|
||||
&& (side == self.local_side || self.is_master(side) || self.ai_sides.iter().any(|&s| s == side))
|
||||
}
|
||||
|
||||
pub(crate) fn is_hostile(&self, e: &Ent) -> bool {
|
||||
e.alive && !e.held && e.captive == 0 && !e.chained && e.side() != self.me_side && !self.is_coop_ally_side(e.side())
|
||||
e.alive && !e.held && e.captive == 0 && !e.chained && e.side() != self.me_side && !self.allied(self.me_side, e.side())
|
||||
}
|
||||
|
||||
pub fn is_rival_side(&self, side: u8) -> bool {
|
||||
@@ -1576,6 +1575,10 @@ impl Game {
|
||||
}
|
||||
}
|
||||
for side in self.master_sides() {
|
||||
// a master whose heart fell has no household any more: no mana, no new creatures, no payday
|
||||
if self.is_out(side) {
|
||||
continue;
|
||||
}
|
||||
self.switch_to(side);
|
||||
self.economy(dt);
|
||||
}
|
||||
@@ -1647,9 +1650,15 @@ impl Game {
|
||||
self.update_rivals(dt);
|
||||
}
|
||||
self.update_dwarves(dt);
|
||||
// fallen hearts of a match are shared state: every client marks them in the same step
|
||||
self.update_fallen_masters();
|
||||
// the end of the game and the fog are about the master at this computer
|
||||
self.switch_to(local);
|
||||
self.check_end();
|
||||
if self.netplay.is_some() {
|
||||
self.check_match_end();
|
||||
} else {
|
||||
self.check_end();
|
||||
}
|
||||
self.update_fog(dt);
|
||||
}
|
||||
|
||||
@@ -2432,8 +2441,8 @@ impl Game {
|
||||
let mut best = None;
|
||||
let mut bd = radius * radius;
|
||||
for o in &self.ents {
|
||||
// every other side is an enemy; co-op master sides fight the heroes, not each other
|
||||
let coop_ally = self.is_coop_ally_side(o.side());
|
||||
// every other side is an enemy; co-op partners and team mates fight the heroes, not each other
|
||||
let coop_ally = self.allied(e.side(), o.side());
|
||||
if !o.alive || o.held || o.side() == e.side() || coop_ally || o.captive != 0 || o.chained || o.invis > 0.0 {
|
||||
continue;
|
||||
}
|
||||
@@ -2908,7 +2917,7 @@ impl Game {
|
||||
for e in &self.ents {
|
||||
// any enemy of the player (heroes and the rivals' units) keeps ground from being claimed.
|
||||
// In co-op matches, allied master sides do not block the player's own ground claims.
|
||||
if !e.alive || e.held || e.captive != 0 || e.side() == 0 || (self.coop && Owner::MASTER_SIDES.contains(&e.side())) {
|
||||
if !e.alive || e.held || e.captive != 0 || e.side() == 0 || (self.coop && Owner::MASTER_SIDES.contains(&e.side())) || self.allied(0, e.side()) {
|
||||
continue;
|
||||
}
|
||||
let (x, y) = (e.x as i32, e.y as i32);
|
||||
@@ -3824,11 +3833,13 @@ impl Game {
|
||||
let (fx, fy) = xy(f);
|
||||
let (fx, fy) = (fx as f32 + 0.5, fy as f32 + 0.5);
|
||||
// the flag next to a rival master's core: attack the core
|
||||
let rival_near = self.rivals.iter().filter(|r| r.alive).find(|r| {
|
||||
let rival_near = self.rivals.iter().filter(|r| r.alive && !self.allied(e.side(), r.side)).find(|r| {
|
||||
let (cx, cy) = r.centre();
|
||||
(fx - cx).powi(2) + (fy - cy).powi(2) <= 8.0 * 8.0
|
||||
});
|
||||
if let Some(side) = rival_near.map(|r| r.side) {
|
||||
// in a match the heart of an enemy master counts as well
|
||||
let master_near = self.enemy_master_near(e.side(), fx, fy);
|
||||
if let Some(side) = rival_near.map(|r| r.side).or(master_near) {
|
||||
e.target = Some(side);
|
||||
if self.march_on(e, dt) {
|
||||
return;
|
||||
@@ -4423,11 +4434,12 @@ impl Game {
|
||||
let cur = e.tile();
|
||||
if e.path.is_empty() && self.map.adj_core(cur) {
|
||||
e.working = true;
|
||||
let (cx, cy) = self.map.core;
|
||||
// the core next to it is the first master's (whoever is being processed in a match)
|
||||
let (cx, cy) = self.core_of_side(0);
|
||||
e.face = sgn(cx as f32 + 1.5 - e.x);
|
||||
if e.cd <= 0.0 {
|
||||
e.cd = st(e.kind).interval;
|
||||
self.core_hp -= e.atk();
|
||||
self.damage_master_core(0, e.atk());
|
||||
let fx = (e.x + cx as f32 + 1.5) * 0.5;
|
||||
let fy = (e.y + cy as f32 + 1.5) * 0.5;
|
||||
self.fx(2, fx, fy, 0.0, 0.0, 0.3, 0xff4040ff);
|
||||
@@ -5078,6 +5090,10 @@ impl Game {
|
||||
e.side() != 1
|
||||
} else if self.coop {
|
||||
e.side() == 1
|
||||
} else if !self.masters.is_empty() {
|
||||
// a match: a tower belongs to the master whose ground it stands on and spares his allies
|
||||
let owner = self.map.t.get(t.tile).map_or(0, |tl| crate::netplay::side_of_owner(tl.owner));
|
||||
!self.allied(owner, e.side())
|
||||
} else {
|
||||
e.side() != 0
|
||||
};
|
||||
@@ -6496,7 +6512,7 @@ mod tests {
|
||||
g.coop = true;
|
||||
assert!(g.start_masters(&[0, 2, 3], 2), "a co-op match may keep an empty side as AI");
|
||||
g.sync_ai_sides(&[3], 0);
|
||||
assert!(g.is_coop_ally_side(3), "side 3 is an allied master seat, not a rival");
|
||||
assert!(g.allied(g.me_side, 3), "side 3 is an allied master seat, not a rival");
|
||||
|
||||
g.ents.clear();
|
||||
let (cx, cy) = g.map.core;
|
||||
|
||||
Reference in new issue
Block a user