Manual AI seats for matches: host toggles empty seats, co-op allies fight heroes
This commit is contained in:
1 parent
3492eacd59
commit
ee7b76fa6a
10 files changed
+308
-43
No files matched your search
+35
-6
@@ -1308,12 +1308,15 @@ impl Game {
|
||||
side == self.me_side || self.masters.iter().any(|m| m.side == side)
|
||||
}
|
||||
|
||||
fn is_hostile_master(&self, side: u8) -> bool {
|
||||
self.coop && Owner::MASTER_SIDES.contains(&self.me_side) && Owner::MASTER_SIDES.contains(&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_hostile_master(e.side())
|
||||
e.alive && !e.held && e.captive == 0 && !e.chained && e.side() != self.me_side && !self.is_coop_ally_side(e.side())
|
||||
}
|
||||
|
||||
pub fn is_rival_side(&self, side: u8) -> bool {
|
||||
@@ -1420,7 +1423,10 @@ impl Game {
|
||||
#[allow(dead_code)] // used by the tests; the *Multiplayer* menu (step D) will call it
|
||||
pub fn start_masters(&mut self, sides: &[u8], local: u8) -> bool {
|
||||
let sides: Vec<u8> = sides.iter().copied().filter(|s| Owner::MASTER_SIDES.contains(s)).collect();
|
||||
if sides.len() < 2 || !sides.contains(&0) || !sides.contains(&local) {
|
||||
if sides.is_empty() || !sides.contains(&0) || !sides.contains(&local) {
|
||||
return false;
|
||||
}
|
||||
if sides.len() < 2 && !self.coop {
|
||||
return false;
|
||||
}
|
||||
let (w, h) = (mw() as i32, mh() as i32);
|
||||
@@ -2362,8 +2368,8 @@ impl Game {
|
||||
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_allies = self.coop && Owner::MASTER_SIDES.contains(&e.side()) && Owner::MASTER_SIDES.contains(&o.side());
|
||||
if !o.alive || o.held || o.side() == e.side() || coop_allies || o.captive != 0 || o.chained || o.invis > 0.0 {
|
||||
let coop_ally = self.is_coop_ally_side(o.side());
|
||||
if !o.alive || o.held || o.side() == e.side() || coop_ally || o.captive != 0 || o.chained || o.invis > 0.0 {
|
||||
continue;
|
||||
}
|
||||
let d = (o.x - e.x).powi(2) + (o.y - e.y).powi(2);
|
||||
@@ -6295,5 +6301,28 @@ mod tests {
|
||||
assert!(seen);
|
||||
assert!(g.ents.iter().filter(|e| e.kind == EK::Chainbreaker).count() <= 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_coop_ai_seat_fights_beside_the_host_not_against_him() {
|
||||
let mut g = Game::new(11, 96, 128);
|
||||
g.init_fog();
|
||||
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");
|
||||
|
||||
g.ents.clear();
|
||||
let (cx, cy) = g.map.core;
|
||||
let local = g.spawn(EK::Kobold, false, cx as f32 + 3.5, cy as f32 + 3.5, 1);
|
||||
let ally = g.spawn(EK::Kobold, false, cx as f32 + 4.5, cy as f32 + 3.5, 1);
|
||||
if let Some(e) = g.ents.iter_mut().find(|e| e.id == ally) {
|
||||
e.side = 3;
|
||||
}
|
||||
|
||||
let local = g.ents.iter().find(|e| e.id == local).unwrap().clone();
|
||||
let ally = g.ents.iter().find(|e| e.id == ally).unwrap().clone();
|
||||
assert!(!g.is_hostile(&ally));
|
||||
assert_eq!(g.nearest_enemy(&local, 5.0), None, "the allied AI creature is no target for the local master");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user