Hero waves march on the strongest master

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-05 20:58:46 +01:00
1 parent 988f4a3333
commit e3a9d32d61
22 files changed
+142 -17

No files matched your search

+2
View File
@@ -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. - 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. - 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. - 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 - 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 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. knocks out that player (who may keep watching); the allies win when all enemies have fallen.
+2
View File
@@ -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.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_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_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.rival1 = Zelený
hud.rival2 = Modrá hud.rival2 = Modrá
msg.creature_freed = Tvor přetrhl řetězy a přidal se k tobě: {0}! msg.creature_freed = Tvor přetrhl řetězy a přidal se k tobě: {0}!
+2
View File
@@ -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.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_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_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.rival1 = Grøn
hud.rival2 = Blå hud.rival2 = Blå
msg.creature_freed = En {0} sprænger sine lænker og slutter sig til dig! msg.creature_freed = En {0} sprænger sine lænker og slutter sig til dig!
+2
View File
@@ -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.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_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_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.rival1 = Grün
hud.rival2 = Blau hud.rival2 = Blau
msg.creature_freed = Ein {0} sprengt seine Ketten und schließt sich dir an! msg.creature_freed = Ein {0} sprengt seine Ketten und schließt sich dir an!
+2
View File
@@ -707,6 +707,8 @@ msg.rival2_out = Η Λαίδη Νάιτγουελ νικήθηκε - ο πυρή
msg.rival_unit_slain = Πέφτει πλάσμα αντίπαλου άρχοντα: {0}! +{1} χρυσός. msg.rival_unit_slain = Πέφτει πλάσμα αντίπαλου άρχοντα: {0}! +{1} χρυσός.
msg.wave_on_rival1 = Κύμα {0}: {1} ήρωες βαδίζουν κατά του πράσινου άρχοντα. msg.wave_on_rival1 = Κύμα {0}: {1} ήρωες βαδίζουν κατά του πράσινου άρχοντα.
msg.wave_on_rival2 = Κύμα {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.rival1 = Πράσινος
hud.rival2 = Γαλάζια hud.rival2 = Γαλάζια
msg.creature_freed = Σπάει τις αλυσίδες του και έρχεται μαζί σου: {0}! msg.creature_freed = Σπάει τις αλυσίδες του και έρχεται μαζί σου: {0}!
+2
View File
@@ -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.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_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_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.rival1 = Green
hud.rival2 = Blue hud.rival2 = Blue
msg.creature_freed = A {0} breaks its chains and joins you! msg.creature_freed = A {0} breaks its chains and joins you!
+2
View File
@@ -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.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_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_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.rival1 = Verde
hud.rival2 = Azul hud.rival2 = Azul
msg.creature_freed = ¡Un {0} rompe sus cadenas y se une a ti! msg.creature_freed = ¡Un {0} rompe sus cadenas y se une a ti!
+2
View File
@@ -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.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_rival1 = Aalto {0}: {1} sankaria marssii vihreää mestaria vastaan.
msg.wave_on_rival2 = Aalto {0}: {1} sankaria marssii sinistä 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.rival1 = Vihreä
hud.rival2 = Sininen hud.rival2 = Sininen
msg.creature_freed = Kahleet katkeavat ja uusi olento liittyy joukkoosi: {0}! msg.creature_freed = Kahleet katkeavat ja uusi olento liittyy joukkoosi: {0}!
+2
View File
@@ -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.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_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_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.rival1 = Zeleni
hud.rival2 = Plava hud.rival2 = Plava
msg.creature_freed = Raskida okove i pridružuje ti se: {0}! msg.creature_freed = Raskida okove i pridružuje ti se: {0}!
+2
View File
@@ -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.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_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_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.rival1 = Zöld
hud.rival2 = Kék hud.rival2 = Kék
msg.creature_freed = Széttépi a láncait, és hozzád csatlakozik: {0}! msg.creature_freed = Széttépi a láncait, és hozzád csatlakozik: {0}!
+2
View File
@@ -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.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_rival1 = Ondata {0}: {1} eroi marciano sul maestro verde.
msg.wave_on_rival2 = Ondata {0}: {1} eroi marciano sulla maestra blu. 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.rival1 = Verde
hud.rival2 = Blu hud.rival2 = Blu
msg.creature_freed = Spezza le catene e si unisce a te: {0}! msg.creature_freed = Spezza le catene e si unisce a te: {0}!
+2
View File
@@ -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.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_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_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.rival1 = Groen
hud.rival2 = Blauw hud.rival2 = Blauw
msg.creature_freed = Een {0} breekt zijn ketens en sluit zich bij je aan! msg.creature_freed = Een {0} breekt zijn ketens en sluit zich bij je aan!
+2
View File
@@ -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.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_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_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.rival1 = Grønn
hud.rival2 = Blå hud.rival2 = Blå
msg.creature_freed = En {0} sprenger lenkene sine og slutter seg til deg! msg.creature_freed = En {0} sprenger lenkene sine og slutter seg til deg!
+2
View File
@@ -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.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_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_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.rival1 = Verde
hud.rival2 = Azul hud.rival2 = Azul
msg.creature_freed = Um {0} parte as correntes e junta-se a ti! msg.creature_freed = Um {0} parte as correntes e junta-se a ti!
+2
View File
@@ -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.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_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_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.rival1 = Verde
hud.rival2 = Albastru hud.rival2 = Albastru
msg.creature_freed = Un {0} își rupe lanțurile și ți se alătură! msg.creature_freed = Un {0} își rupe lanțurile și ți se alătură!
+2
View File
@@ -702,6 +702,8 @@ msg.rival2_out = Леди Найтвелл повержена - её ядро д
msg.rival_unit_slain = Пал боец соперника: {0}! +{1} золота. msg.rival_unit_slain = Пал боец соперника: {0}! +{1} золота.
msg.wave_on_rival1 = Волна {0}: {1} героев идут на зелёного хозяина. msg.wave_on_rival1 = Волна {0}: {1} героев идут на зелёного хозяина.
msg.wave_on_rival2 = Волна {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.rival1 = Зелёный
hud.rival2 = Синяя hud.rival2 = Синяя
msg.creature_freed = Существо рвёт цепи и присоединяется к тебе: {0}! msg.creature_freed = Существо рвёт цепи и присоединяется к тебе: {0}!
+2
View File
@@ -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.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_rival1 = Vlna {0}: hrdinovia ({1}) tiahnu na zeleného majstra.
msg.wave_on_rival2 = Vlna {0}: hrdinovia ({1}) tiahnu na modrú majsterku. 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.rival1 = Zelený
hud.rival2 = Modrá hud.rival2 = Modrá
msg.creature_freed = Tvor roztrhol reťaze a pridal sa k tebe: {0}! msg.creature_freed = Tvor roztrhol reťaze a pridal sa k tebe: {0}!
+2
View File
@@ -707,6 +707,8 @@ msg.rival2_out = Лејди Најтвел је поражена - њено је
msg.rival_unit_slain = Пало је створење супарничког господара: {0}! +{1} злата. msg.rival_unit_slain = Пало је створење супарничког господара: {0}! +{1} злата.
msg.wave_on_rival1 = Талас {0}: хероји ({1}) марширају на зеленог господара. msg.wave_on_rival1 = Талас {0}: хероји ({1}) марширају на зеленог господара.
msg.wave_on_rival2 = Талас {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.rival1 = Зелени
hud.rival2 = Плава hud.rival2 = Плава
msg.creature_freed = Раскида окове и придружује ти се: {0}! msg.creature_freed = Раскида окове и придружује ти се: {0}!
+2
View File
@@ -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.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_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_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.rival1 = Grön
hud.rival2 = Blå hud.rival2 = Blå
msg.creature_freed = En {0} bryter sina kedjor och ansluter sig till dig! msg.creature_freed = En {0} bryter sina kedjor och ansluter sig till dig!
+72 -16
View File
@@ -1700,6 +1700,13 @@ impl Game {
self.wave_timer -= dt; self.wave_timer -= dt;
if !self.warned && self.wave_timer < 30.0 { if !self.warned && self.wave_timer < 30.0 {
self.warned = true; 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(); self.plan_entries();
if self.planned_entries.is_empty() { if self.planned_entries.is_empty() {
self.msg("msg.wave_warn", vec![(self.wave + 1).to_string()]); 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. // 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::*; use EK::*;
let w = self.wave + 1; 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 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 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)]; let mut pool = vec![(Knight, 4), (Archer, 3)];
if w >= 2 { if w >= 2 {
pool.push((Dwarf, 2)); pool.push((Dwarf, 2));
@@ -2110,21 +2119,20 @@ impl Game {
self.last_wave_value = Self::squad_value(&list); self.last_wave_value = Self::squad_value(&list);
let before = self.ents.len(); let before = self.ents.len();
self.spawn_squad(&list, false); self.spawn_squad(&list, false);
// the humans attack everybody: now and then a rival master instead of the player if goal != self.me_side {
let alive: Vec<u8> = 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() { for e in self.ents[before..].iter_mut() {
e.target = Some(side); e.target = Some(goal);
e.waypoint = None; e.waypoint = None;
} }
self.planned_entries.clear(); 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.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()]); self.msg(if goal == 2 { "msg.wave_on_rival1" } else { "msg.wave_on_rival2" }, args);
return;
} }
return;
} }
if self.planned_entries.is_empty() { if self.planned_entries.is_empty() {
self.plan_entries(); self.plan_entries();
@@ -2145,12 +2153,60 @@ impl Game {
self.entry_history.drain(0..n - 4); self.entry_history.drain(0..n - 4);
} }
} }
self.sfx_ui(Sfx::WaveStart); let key = if boss.is_some() { "msg.wave_boss" } else { "msg.wave" };
if boss.is_some() { let goal = self.me_side;
self.msg("msg.wave_boss", vec![w.to_string(), list.len().to_string()]); self.announce_wave(key, "msg.wave_on_master", vec![w.to_string(), list.len().to_string()], goal, Sfx::WaveStart);
} else {
self.msg("msg.wave", vec![w.to_string(), list.len().to_string()]);
} }
// 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<u8> = 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<String>, 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 // defenders at 75 / 50 / 25 % gate HP
+1 -1
View File
@@ -152,7 +152,7 @@ impl Game {
} }
// a message and a sound for the player at this computer, whichever master is being processed // 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<String>, sfx: Option<crate::game::Sfx>) { pub(crate) fn tell_local(&mut self, key: &'static str, args: Vec<String>, sfx: Option<crate::game::Sfx>) {
let me = self.me_side; let me = self.me_side;
let local = self.local_side; let local = self.local_side;
self.switch_to(local); self.switch_to(local);
+31
View File
@@ -507,4 +507,35 @@ mod tests {
} }
assert!(fought, "heroes fight the rival master"); 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");
}
} }