diff --git a/docs/MULTIPLAYER.md b/docs/MULTIPLAYER.md index c36b5f3..20d59b2 100644 --- a/docs/MULTIPLAYER.md +++ b/docs/MULTIPLAYER.md @@ -10,6 +10,8 @@ Multiplayer is only available for users of a server (not in the standalone file) - The match **pauses while a player is missing** (connection lost); a player who **leaves** is replaced by an AI. - Before the start, the host manually marks a free seat as AI with `{"t":"set_ai","side":n,"ai":true}` and clears it again with `ai:false`; side 0 stays human. - The humans (hero waves of the Order) as a third party (can be switched off), chat, alliances/teams. + Every wave marches on the strongest master still standing (creatures, towers, gold) – player or + AI rival – and is measured against him; the other players learn whom it attacks. - Coop is "separate but helpful": own heart, gold, mana, research and creatures; allies never fight each other, their creatures help each other, gold can be sent, allied ground can be walked; a fallen heart only knocks out that player (who may keep watching); the allies win when all enemies have fallen. diff --git a/lang/cs.lang b/lang/cs.lang index ef0981f..d410602 100644 --- a/lang/cs.lang +++ b/lang/cs.lang @@ -707,6 +707,8 @@ msg.rival2_out = Lady Nightwell je poražena - její jádro duše se roztříšt msg.rival_unit_slain = Padl tvor soupeřícího mistra: {0}! +{1} zlata. msg.wave_on_rival1 = Vlna {0}: hrdinové ({1}) táhnou na zeleného mistra. msg.wave_on_rival2 = Vlna {0}: hrdinové ({1}) táhnou na modrou mistryni. +msg.wave_warn_on_master = Zvědové hlásí: vlna hrdinů {0} dorazí za 30 sekund a míří na pána {1}. +msg.wave_on_master = Vlna {0}: {1} hrdinů táhne na pána {2}. hud.rival1 = Zelený hud.rival2 = Modrá msg.creature_freed = Tvor přetrhl řetězy a přidal se k tobě: {0}! diff --git a/lang/da.lang b/lang/da.lang index 3348473..c29835f 100644 --- a/lang/da.lang +++ b/lang/da.lang @@ -706,6 +706,8 @@ msg.rival2_out = Lady Nightwell er besejret - hendes sjælekerne er splintret! msg.rival_unit_slain = En {0} fra en rivaliserende mester falder! +{1} guld. msg.wave_on_rival1 = Bølge {0}: {1} helte marcherer mod den grønne mester. msg.wave_on_rival2 = Bølge {0}: {1} helte marcherer mod den blå mester. +msg.wave_warn_on_master = Spejdere melder: heltebølge {0} kommer om 30 sekunder og går mod mester {1}. +msg.wave_on_master = Bølge {0}: {1} helte marcherer mod mester {2}. hud.rival1 = Grøn hud.rival2 = Blå msg.creature_freed = En {0} sprænger sine lænker og slutter sig til dig! diff --git a/lang/de.lang b/lang/de.lang index 04215b0..b1ddc83 100644 --- a/lang/de.lang +++ b/lang/de.lang @@ -706,6 +706,8 @@ msg.rival2_out = Lady Nightwell ist besiegt - ihr Seelenkern ist zersplittert! msg.rival_unit_slain = Ein {0} eines Rivalen fällt! +{1} Gold. msg.wave_on_rival1 = Welle {0}: {1} Helden ziehen gegen den grünen Meister. msg.wave_on_rival2 = Welle {0}: {1} Helden ziehen gegen den blauen Meister. +msg.wave_warn_on_master = Späher melden: Heldenwelle {0} kommt in 30 Sekunden – sie zieht gegen Meister {1}. +msg.wave_on_master = Welle {0}: {1} Helden ziehen gegen Meister {2}. hud.rival1 = Grün hud.rival2 = Blau msg.creature_freed = Ein {0} sprengt seine Ketten und schließt sich dir an! diff --git a/lang/el.lang b/lang/el.lang index 7ac7d99..4a90970 100644 --- a/lang/el.lang +++ b/lang/el.lang @@ -707,6 +707,8 @@ msg.rival2_out = Η Λαίδη Νάιτγουελ νικήθηκε - ο πυρή msg.rival_unit_slain = Πέφτει πλάσμα αντίπαλου άρχοντα: {0}! +{1} χρυσός. msg.wave_on_rival1 = Κύμα {0}: {1} ήρωες βαδίζουν κατά του πράσινου άρχοντα. msg.wave_on_rival2 = Κύμα {0}: {1} ήρωες βαδίζουν κατά της γαλάζιας άρχοντος. +msg.wave_warn_on_master = Οι ανιχνευτές αναφέρουν: το κύμα ηρώων {0} έρχεται σε 30 δευτερόλεπτα και κατευθύνεται στον άρχοντα {1}. +msg.wave_on_master = Κύμα {0}: {1} ήρωες βαδίζουν κατά του άρχοντα {2}. hud.rival1 = Πράσινος hud.rival2 = Γαλάζια msg.creature_freed = Σπάει τις αλυσίδες του και έρχεται μαζί σου: {0}! diff --git a/lang/en.lang b/lang/en.lang index 44fe35b..0586072 100644 --- a/lang/en.lang +++ b/lang/en.lang @@ -707,6 +707,8 @@ msg.rival2_out = Lady Nightwell is defeated - her soul core has shattered! msg.rival_unit_slain = A {0} of a rival master falls! +{1} gold. msg.wave_on_rival1 = Wave {0}: {1} heroes march on the green master. msg.wave_on_rival2 = Wave {0}: {1} heroes march on the blue master. +msg.wave_warn_on_master = Scouts report: hero wave {0} comes in 30 seconds – it heads for master {1}. +msg.wave_on_master = Wave {0}: {1} heroes march on master {2}. hud.rival1 = Green hud.rival2 = Blue msg.creature_freed = A {0} breaks its chains and joins you! diff --git a/lang/es.lang b/lang/es.lang index e0f880b..dccaaba 100644 --- a/lang/es.lang +++ b/lang/es.lang @@ -702,6 +702,8 @@ msg.rival2_out = Lady Nightwell ha sido derrotada: ¡su núcleo de alma se ha he msg.rival_unit_slain = ¡Cae un {0} de un señor rival! +{1} de oro. msg.wave_on_rival1 = Oleada {0}: {1} héroes marchan contra el señor verde. msg.wave_on_rival2 = Oleada {0}: {1} héroes marchan contra la señora azul. +msg.wave_warn_on_master = Los exploradores informan: la oleada {0} llega en 30 segundos y va a por el maestro {1}. +msg.wave_on_master = Oleada {0}: {1} héroes marchan contra el maestro {2}. hud.rival1 = Verde hud.rival2 = Azul msg.creature_freed = ¡Un {0} rompe sus cadenas y se une a ti! diff --git a/lang/fi.lang b/lang/fi.lang index 9848f6a..0718dda 100644 --- a/lang/fi.lang +++ b/lang/fi.lang @@ -707,6 +707,8 @@ msg.rival2_out = Lady Nightwell on voitettu - hänen sieluytimensä on murskattu msg.rival_unit_slain = Kilpailevan mestarin olento kaatuu: {0}! +{1} kultaa. msg.wave_on_rival1 = Aalto {0}: {1} sankaria marssii vihreää mestaria vastaan. msg.wave_on_rival2 = Aalto {0}: {1} sankaria marssii sinistä mestaria vastaan. +msg.wave_warn_on_master = Tiedustelijat ilmoittavat: sankariaalto {0} saapuu 30 sekunnissa ja hyökkää mestari {1} vastaan. +msg.wave_on_master = Aalto {0}: {1} sankaria marssii mestari {2} vastaan. hud.rival1 = Vihreä hud.rival2 = Sininen msg.creature_freed = Kahleet katkeavat ja uusi olento liittyy joukkoosi: {0}! diff --git a/lang/hr.lang b/lang/hr.lang index 7b8af03..16ea55b 100644 --- a/lang/hr.lang +++ b/lang/hr.lang @@ -707,6 +707,8 @@ msg.rival2_out = Lady Nightwell je poražena - njezina jezgra duše se razbila! msg.rival_unit_slain = Palo je stvorenje suparničkog gospodara: {0}! +{1} zlata. msg.wave_on_rival1 = Val {0}: junaci ({1}) marširaju na zelenog gospodara. msg.wave_on_rival2 = Val {0}: junaci ({1}) marširaju na plavu gospodaricu. +msg.wave_warn_on_master = Izvidnici javljaju: val junaka {0} stiže za 30 sekundi i kreće na gospodara {1}. +msg.wave_on_master = Val {0}: {1} junaka maršira na gospodara {2}. hud.rival1 = Zeleni hud.rival2 = Plava msg.creature_freed = Raskida okove i pridružuje ti se: {0}! diff --git a/lang/hu.lang b/lang/hu.lang index 248207b..86237e7 100644 --- a/lang/hu.lang +++ b/lang/hu.lang @@ -707,6 +707,8 @@ msg.rival2_out = Lady Nightwell legyőzve - a lélekmagja darabokra tört! msg.rival_unit_slain = Elesett egy rivális mester lénye: {0}! +{1} arany. msg.wave_on_rival1 = {0}. hullám: {1} hős vonul a zöld mester ellen. msg.wave_on_rival2 = {0}. hullám: {1} hős vonul a kék mester ellen. +msg.wave_warn_on_master = A felderítők jelentik: a(z) {0}. hősroham 30 másodperc múlva érkezik, és {1} mester ellen vonul. +msg.wave_on_master = {0}. roham: {1} hős vonul {2} mester ellen. hud.rival1 = Zöld hud.rival2 = Kék msg.creature_freed = Széttépi a láncait, és hozzád csatlakozik: {0}! diff --git a/lang/it.lang b/lang/it.lang index 9735d52..7f952b3 100644 --- a/lang/it.lang +++ b/lang/it.lang @@ -702,6 +702,8 @@ msg.rival2_out = Lady Nightwell è sconfitta - il suo nucleo dell'anima è in fr msg.rival_unit_slain = Cade una creatura di un maestro rivale: {0}! +{1} oro. msg.wave_on_rival1 = Ondata {0}: {1} eroi marciano sul maestro verde. msg.wave_on_rival2 = Ondata {0}: {1} eroi marciano sulla maestra blu. +msg.wave_warn_on_master = Gli esploratori riferiscono: l'ondata {0} arriva tra 30 secondi e punta sul maestro {1}. +msg.wave_on_master = Ondata {0}: {1} eroi marciano contro il maestro {2}. hud.rival1 = Verde hud.rival2 = Blu msg.creature_freed = Spezza le catene e si unisce a te: {0}! diff --git a/lang/nl.lang b/lang/nl.lang index 876a332..4d38831 100644 --- a/lang/nl.lang +++ b/lang/nl.lang @@ -707,6 +707,8 @@ msg.rival2_out = Lady Nightwell is verslagen - haar zielenkern is verbrijzeld! msg.rival_unit_slain = Een {0} van een rivaliserende meester valt! +{1} goud. msg.wave_on_rival1 = Golf {0}: {1} helden trekken op tegen de groene meester. msg.wave_on_rival2 = Golf {0}: {1} helden trekken op tegen de blauwe meester. +msg.wave_warn_on_master = Verkenners melden: heldengolf {0} komt over 30 seconden en trekt naar meester {1}. +msg.wave_on_master = Golf {0}: {1} helden trekken op tegen meester {2}. hud.rival1 = Groen hud.rival2 = Blauw msg.creature_freed = Een {0} breekt zijn ketens en sluit zich bij je aan! diff --git a/lang/no.lang b/lang/no.lang index de4aa65..d3a206e 100644 --- a/lang/no.lang +++ b/lang/no.lang @@ -706,6 +706,8 @@ msg.rival2_out = Lady Nightwell er beseiret - sjelekjernen hennes er knust! msg.rival_unit_slain = En {0} fra en rivaliserende mester faller! +{1} gull. msg.wave_on_rival1 = Bølge {0}: {1} helter marsjerer mot den grønne mesteren. msg.wave_on_rival2 = Bølge {0}: {1} helter marsjerer mot den blå mesteren. +msg.wave_warn_on_master = Speidere melder: heltebølge {0} kommer om 30 sekunder og går mot mester {1}. +msg.wave_on_master = Bølge {0}: {1} helter marsjerer mot mester {2}. hud.rival1 = Grønn hud.rival2 = Blå msg.creature_freed = En {0} sprenger lenkene sine og slutter seg til deg! diff --git a/lang/pt.lang b/lang/pt.lang index 5f2191f..cbf720c 100644 --- a/lang/pt.lang +++ b/lang/pt.lang @@ -707,6 +707,8 @@ msg.rival2_out = Lady Nightwell foi derrotada - o núcleo de alma dela despedaç msg.rival_unit_slain = Cai um {0} de um mestre rival! +{1} de ouro. msg.wave_on_rival1 = Vaga {0}: {1} heróis marcham contra o mestre verde. msg.wave_on_rival2 = Vaga {0}: {1} heróis marcham contra a mestra azul. +msg.wave_warn_on_master = Os batedores informam: a onda {0} chega em 30 segundos e vai contra o mestre {1}. +msg.wave_on_master = Onda {0}: {1} heróis marcham contra o mestre {2}. hud.rival1 = Verde hud.rival2 = Azul msg.creature_freed = Um {0} parte as correntes e junta-se a ti! diff --git a/lang/ro.lang b/lang/ro.lang index 4b948d7..59994f5 100644 --- a/lang/ro.lang +++ b/lang/ro.lang @@ -707,6 +707,8 @@ msg.rival2_out = Lady Nightwell e învinsă - miezul sufletului ei s-a sfărâma msg.rival_unit_slain = Un {0} al unui stăpân rival cade! +{1} aur. msg.wave_on_rival1 = Valul {0}: {1} eroi mărșăluiesc asupra stăpânului verde. msg.wave_on_rival2 = Valul {0}: {1} eroi mărșăluiesc asupra stăpânei albastre. +msg.wave_warn_on_master = Cercetașii raportează: valul de eroi {0} sosește în 30 de secunde și merge spre stăpânul {1}. +msg.wave_on_master = Valul {0}: {1} eroi mărșăluiesc spre stăpânul {2}. hud.rival1 = Verde hud.rival2 = Albastru msg.creature_freed = Un {0} își rupe lanțurile și ți se alătură! diff --git a/lang/ru.lang b/lang/ru.lang index 894bc66..722c42a 100644 --- a/lang/ru.lang +++ b/lang/ru.lang @@ -702,6 +702,8 @@ msg.rival2_out = Леди Найтвелл повержена - её ядро д msg.rival_unit_slain = Пал боец соперника: {0}! +{1} золота. msg.wave_on_rival1 = Волна {0}: {1} героев идут на зелёного хозяина. msg.wave_on_rival2 = Волна {0}: {1} героев идут на синюю хозяйку. +msg.wave_warn_on_master = Разведчики докладывают: волна {0} будет через 30 секунд и идёт на мастера {1}. +msg.wave_on_master = Волна {0}: {1} героев идут на мастера {2}. hud.rival1 = Зелёный hud.rival2 = Синяя msg.creature_freed = Существо рвёт цепи и присоединяется к тебе: {0}! diff --git a/lang/sk.lang b/lang/sk.lang index 904b247..57a56cc 100644 --- a/lang/sk.lang +++ b/lang/sk.lang @@ -707,6 +707,8 @@ msg.rival2_out = Lady Nightwell je porazená - jej jadro duše sa roztrieštilo! msg.rival_unit_slain = Padol tvor súperiaceho majstra: {0}! +{1} zlata. msg.wave_on_rival1 = Vlna {0}: hrdinovia ({1}) tiahnu na zeleného majstra. msg.wave_on_rival2 = Vlna {0}: hrdinovia ({1}) tiahnu na modrú majsterku. +msg.wave_warn_on_master = Prieskumníci hlásia: vlna hrdinov {0} príde o 30 sekúnd a mieri na pána {1}. +msg.wave_on_master = Vlna {0}: {1} hrdinov pochoduje na pána {2}. hud.rival1 = Zelený hud.rival2 = Modrá msg.creature_freed = Tvor roztrhol reťaze a pridal sa k tebe: {0}! diff --git a/lang/sr.lang b/lang/sr.lang index d1f6fcd..dc0627c 100644 --- a/lang/sr.lang +++ b/lang/sr.lang @@ -707,6 +707,8 @@ msg.rival2_out = Лејди Најтвел је поражена - њено је msg.rival_unit_slain = Пало је створење супарничког господара: {0}! +{1} злата. msg.wave_on_rival1 = Талас {0}: хероји ({1}) марширају на зеленог господара. msg.wave_on_rival2 = Талас {0}: хероји ({1}) марширају на плаву господарицу. +msg.wave_warn_on_master = Извиђачи јављају: талас јунака {0} стиже за 30 секунди и креће на господара {1}. +msg.wave_on_master = Талас {0}: {1} јунака маршира на господара {2}. hud.rival1 = Зелени hud.rival2 = Плава msg.creature_freed = Раскида окове и придружује ти се: {0}! diff --git a/lang/sv.lang b/lang/sv.lang index 3f9ad5a..c4a13b3 100644 --- a/lang/sv.lang +++ b/lang/sv.lang @@ -707,6 +707,8 @@ msg.rival2_out = Lady Nightwell är besegrad - hennes själskärna har krossats! msg.rival_unit_slain = En {0} hos en rivaliserande mästare faller! +{1} guld. msg.wave_on_rival1 = Våg {0}: {1} hjältar marscherar mot den gröna mästaren. msg.wave_on_rival2 = Våg {0}: {1} hjältar marscherar mot den blå mästaren. +msg.wave_warn_on_master = Spejare rapporterar: hjältevåg {0} kommer om 30 sekunder och går mot mästare {1}. +msg.wave_on_master = Våg {0}: {1} hjältar marscherar mot mästare {2}. hud.rival1 = Grön hud.rival2 = Blå msg.creature_freed = En {0} bryter sina kedjor och ansluter sig till dig! diff --git a/src/game.rs b/src/game.rs index 92de1cd..4994f60 100644 --- a/src/game.rs +++ b/src/game.rs @@ -1700,6 +1700,13 @@ impl Game { self.wave_timer -= dt; if !self.warned && self.wave_timer < 30.0 { self.warned = true; + let goal = self.wave_goal(); + if goal != self.me_side { + // the wave will march on another master: no entry markers in this dungeon + self.planned_entries.clear(); + self.announce_wave("msg.wave_warn", "msg.wave_warn_on_master", vec![(self.wave + 1).to_string()], goal, Sfx::WaveAlarm); + return; + } self.plan_entries(); if self.planned_entries.is_empty() { self.msg("msg.wave_warn", vec![(self.wave + 1).to_string()]); @@ -2084,12 +2091,14 @@ impl Game { } // Endless waves: strength 60% -> 100% of the army value, with some randomness, at least 2 knights. - fn spawn_wave(&mut self) { + pub(crate) fn spawn_wave(&mut self) { use EK::*; let w = self.wave + 1; let ratio = (0.6 + 0.05 * (w - 1) as f32).min(1.0) * (0.88 + self.rng.f() * 0.24); let min_value = Self::squad_value(&[(Knight, 1), (Knight, 1)]); - let target = (self.army_value() * ratio).max(min_value); + // the humans march on the strongest master; the wave is measured against him + let goal = self.wave_goal(); + let target = (self.side_army(goal) * ratio).max(min_value); let mut pool = vec![(Knight, 4), (Archer, 3)]; if w >= 2 { pool.push((Dwarf, 2)); @@ -2110,21 +2119,20 @@ impl Game { self.last_wave_value = Self::squad_value(&list); let before = self.ents.len(); self.spawn_squad(&list, false); - // the humans attack everybody: now and then a rival master instead of the player - let alive: Vec = self.rivals.iter().filter(|r| r.alive).map(|r| r.side).collect(); - if !alive.is_empty() { - let r = (self.rng.next() as usize) % (alive.len() + 2); - if r >= 2 { - let side = alive[r - 2]; - for e in self.ents[before..].iter_mut() { - e.target = Some(side); - e.waypoint = None; - } - self.planned_entries.clear(); - self.sfx_ui(Sfx::WaveStart); - self.msg(if side == 2 { "msg.wave_on_rival1" } else { "msg.wave_on_rival2" }, vec![w.to_string(), list.len().to_string()]); - return; + if goal != self.me_side { + for e in self.ents[before..].iter_mut() { + e.target = Some(goal); + e.waypoint = None; } + self.planned_entries.clear(); + let args = vec![w.to_string(), list.len().to_string()]; + if self.is_master(goal) { + self.announce_wave("msg.wave", "msg.wave_on_master", args, goal, Sfx::WaveStart); + } else { + self.sfx_ui(Sfx::WaveStart); + self.msg(if goal == 2 { "msg.wave_on_rival1" } else { "msg.wave_on_rival2" }, args); + } + return; } if self.planned_entries.is_empty() { self.plan_entries(); @@ -2145,12 +2153,60 @@ impl Game { self.entry_history.drain(0..n - 4); } } - self.sfx_ui(Sfx::WaveStart); - if boss.is_some() { - self.msg("msg.wave_boss", vec![w.to_string(), list.len().to_string()]); - } else { - self.msg("msg.wave", vec![w.to_string(), list.len().to_string()]); + let key = if boss.is_some() { "msg.wave_boss" } else { "msg.wave" }; + let goal = self.me_side; + self.announce_wave(key, "msg.wave_on_master", vec![w.to_string(), list.len().to_string()], goal, Sfx::WaveStart); + } + + // The side a hero wave marches on: the strongest master still standing (creatures, towers and + // gold), players and rival masters alike; ties go to the lower side. + pub fn wave_goal(&self) -> u8 { + let mut best = (self.me_side, f32::MIN); + let mut sides: Vec = self.master_sides().into_iter().filter(|&s| !self.is_out(s) && self.core_hp_of(s) > 0.0).collect(); + sides.extend(self.rivals.iter().filter(|r| r.alive).map(|r| r.side)); + sides.sort_unstable(); + for s in sides { + let gold = if self.is_master(s) { self.gold_of(s) } else { 0.0 }; + let score = self.side_army(s) + 0.05 * gold; + if score > best.1 { + best = (s, score); + } } + best.0 + } + + // the fighting strength of one side, like `army_value`: its creatures and (half) its towers + pub fn side_army(&self, side: u8) -> f32 { + let owner = Owner::of_side(side); + let (mut h, mut d) = (0.0, 0.0); + for e in &self.ents { + if e.alive && !e.hero && !e.chained && e.kind != EK::Worker && e.side() == side { + h += e.max_hp; + d += e.atk() / st(e.kind).interval; + } + } + for t in self.towers.iter().filter(|t| !t.hero && self.map.t[t.tile].owner == owner) { + h += 0.5 * t.max_hp(); + d += 0.5 * t.dmg() / tower_stats(t.kind).interval; + } + (h * d).sqrt() + } + + // tells about a wave: the attacked master hears `key`; every other player of a match hears + // `other` (with the attacked master's colour as last argument). A wave on a rival master is + // told to the player like a normal warning. + fn announce_wave(&mut self, key: &'static str, other: &'static str, args: Vec, goal: u8, sfx: Sfx) { + if !self.is_master(goal) || self.netplay.is_none() || self.local_side == goal { + let me = self.me_side; + self.switch_to(goal); + self.msg(key, args); + self.sfx_ui(sfx); + self.switch_to(me); + return; + } + let mut a = args; + a.push(format!("@net.side{goal}")); + self.tell_local(other, a, Some(sfx)); } // defenders at 75 / 50 / 25 % gate HP diff --git a/src/netplay.rs b/src/netplay.rs index dd01a7e..cad0aea 100644 --- a/src/netplay.rs +++ b/src/netplay.rs @@ -152,7 +152,7 @@ impl Game { } // a message and a sound for the player at this computer, whichever master is being processed - fn tell_local(&mut self, key: &'static str, args: Vec, sfx: Option) { + pub(crate) fn tell_local(&mut self, key: &'static str, args: Vec, sfx: Option) { let me = self.me_side; let local = self.local_side; self.switch_to(local); diff --git a/src/rival.rs b/src/rival.rs index 62c5c5c..2ecf08c 100644 --- a/src/rival.rs +++ b/src/rival.rs @@ -507,4 +507,35 @@ mod tests { } assert!(fought, "heroes fight the rival master"); } + + #[test] + fn hero_waves_march_on_the_strongest_master() { + let mut g = (1..200u32).map(|s| Game::new_free(s, 160, 160, 1)).find(|g| g.rivals.len() == 1 && g.gate_on).unwrap(); + let side = g.rivals[0].side; + let (rx, ry) = g.rivals[0].centre(); + let army = |g: &mut Game, side: u8, x: f32, y: f32, n: usize| { + for k in 0..n { + let id = g.spawn(EK::Ogre, false, x + k as f32 * 0.2, y, 6); + if let Some(e) = g.ents.iter_mut().find(|e| e.id == id) { + e.side = side; + } + } + }; + // the rival master is far stronger: the wave marches on him and is measured against him + g.gold = 0.0; + army(&mut g, side, rx, ry + 2.0, 8); + assert_eq!(g.wave_goal(), side); + let before = g.ents.len(); + g.spawn_wave(); + let heroes: Vec<&Ent> = g.ents[before..].iter().filter(|e| e.hero).collect(); + assert!(!heroes.is_empty()); + assert!(heroes.iter().all(|e| e.target == Some(side)), "the wave marches on the rival"); + // now the player is the strongest: the next wave comes for him + let (cx, cy) = g.map.core; + army(&mut g, 0, cx as f32 + 1.5, cy as f32 + 4.0, 20); + assert_eq!(g.wave_goal(), 0); + let before = g.ents.len(); + g.spawn_wave(); + assert!(g.ents[before..].iter().filter(|e| e.hero).all(|e| e.target.is_none()), "the wave comes for the player"); + } }