Hall of the fallen: kills and causes of death, in the game and over all games in the profile
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
065fe80218
commit
c52fc4c4ae
28 files changed
+534
-5
No files matched your search
@@ -142,3 +142,7 @@ grid), guard and attack markers (shields and swords), savegames with the game st
|
|||||||
language files (20 per kind, per language), a humorous epithet from level 5 and a grand one from level 8;
|
language files (20 per kind, per language), a humorous epithet from level 5 and a grand one from level 8;
|
||||||
names in the hover info, the creature overview and the messages. A creature of level 3+ that falls leaves a
|
names in the hover info, the creature overview and the messages. A creature of level 3+ that falls leaves a
|
||||||
memorial stone on its master's ground (hover: who lies there; at most 24).
|
memorial stone on its master's ground (hover: who lies there; at most 24).
|
||||||
|
7. **Hall of the fallen** (done, v202611-00073) - every named creature of level 3+ that falls is remembered
|
||||||
|
with its level, heroes slain and how it fell (slain by a kind, lava, water, snare, lightning): in the game
|
||||||
|
(creature overview "Fallen", or a click with the hand on a memorial stone) and over all games on the
|
||||||
|
achievements page (kept in the profile, at most 200, merged with the server copy).
|
||||||
@@ -1754,3 +1754,17 @@ name.epithet = {0} {1}
|
|||||||
msg.creature_fallen = Padl tvůj tvor: {0}. Pamětní kámen uchová jméno.
|
msg.creature_fallen = Padl tvůj tvor: {0}. Pamětní kámen uchová jméno.
|
||||||
tile.memorial = Pamětní kámen
|
tile.memorial = Pamětní kámen
|
||||||
tile.memorial_info = Zde odpočívá {0} ({1}, úroveň {2}).
|
tile.memorial_info = Zde odpočívá {0} ({1}, úroveň {2}).
|
||||||
|
|
||||||
|
# hall of the fallen
|
||||||
|
hall.title = Síň padlých
|
||||||
|
hall.count = Padlí tvorové v paměti: {0}
|
||||||
|
hall.empty = Zatím nikdo nepadl. Ať to tak vydrží.
|
||||||
|
hall.kills = Zabití hrdinové: {0}
|
||||||
|
ov.fallen = Padlí ({0})
|
||||||
|
ov.living = Živí
|
||||||
|
cause.slain = vrah: {0}
|
||||||
|
cause.lava = smrt v lávě
|
||||||
|
cause.water = utonutí
|
||||||
|
cause.snare = smrt v pasti
|
||||||
|
cause.lightning = zásah bleskem
|
||||||
|
cause.fell = smrt v boji
|
||||||
@@ -1753,3 +1753,17 @@ name.epithet = {0} {1}
|
|||||||
msg.creature_fallen = {0} er faldet. En mindesten bevarer navnet.
|
msg.creature_fallen = {0} er faldet. En mindesten bevarer navnet.
|
||||||
tile.memorial = Mindesten
|
tile.memorial = Mindesten
|
||||||
tile.memorial_info = Her hviler {0} ({1}, niveau {2}).
|
tile.memorial_info = Her hviler {0} ({1}, niveau {2}).
|
||||||
|
|
||||||
|
# hall of the fallen
|
||||||
|
hall.title = De faldnes hal
|
||||||
|
hall.count = Faldne væsner i erindring: {0}
|
||||||
|
hall.empty = Endnu er ingen faldet. Længe må det vare.
|
||||||
|
hall.kills = Dræbte helte: {0}
|
||||||
|
ov.fallen = Faldne ({0})
|
||||||
|
ov.living = Levende
|
||||||
|
cause.slain = dræbt af: {0}
|
||||||
|
cause.lava = brændt i lava
|
||||||
|
cause.water = druknet
|
||||||
|
cause.snare = trådt i en saks
|
||||||
|
cause.lightning = ramt af lynet
|
||||||
|
cause.fell = faldet
|
||||||
@@ -1753,3 +1753,17 @@ name.epithet = {0} {1}
|
|||||||
msg.creature_fallen = {0} ist gefallen. Ein Gedenkstein bewahrt den Namen.
|
msg.creature_fallen = {0} ist gefallen. Ein Gedenkstein bewahrt den Namen.
|
||||||
tile.memorial = Gedenkstein
|
tile.memorial = Gedenkstein
|
||||||
tile.memorial_info = Hier ruht {0} ({1}, Stufe {2}).
|
tile.memorial_info = Hier ruht {0} ({1}, Stufe {2}).
|
||||||
|
|
||||||
|
# hall of the fallen
|
||||||
|
hall.title = Halle der Gefallenen
|
||||||
|
hall.count = {0} gefallene Kreaturen in Erinnerung
|
||||||
|
hall.empty = Noch ist niemand gefallen. Möge es so bleiben.
|
||||||
|
hall.kills = {0} Helden erschlagen
|
||||||
|
ov.fallen = Gefallene ({0})
|
||||||
|
ov.living = Lebende
|
||||||
|
cause.slain = erschlagen von: {0}
|
||||||
|
cause.lava = in Lava verbrannt
|
||||||
|
cause.water = ertrunken
|
||||||
|
cause.snare = in eine Falle getreten
|
||||||
|
cause.lightning = vom Blitz getroffen
|
||||||
|
cause.fell = gefallen
|
||||||
@@ -1754,3 +1754,17 @@ name.epithet = {0} {1}
|
|||||||
msg.creature_fallen = Έπεσε: {0}. Μια αναμνηστική πέτρα κρατά το όνομα.
|
msg.creature_fallen = Έπεσε: {0}. Μια αναμνηστική πέτρα κρατά το όνομα.
|
||||||
tile.memorial = Αναμνηστική πέτρα
|
tile.memorial = Αναμνηστική πέτρα
|
||||||
tile.memorial_info = Εδώ αναπαύεται: {0} ({1}, επίπεδο {2}).
|
tile.memorial_info = Εδώ αναπαύεται: {0} ({1}, επίπεδο {2}).
|
||||||
|
|
||||||
|
# hall of the fallen
|
||||||
|
hall.title = Αίθουσα των πεσόντων
|
||||||
|
hall.count = Πεσόντα πλάσματα στη μνήμη: {0}
|
||||||
|
hall.empty = Κανείς δεν έχει πέσει ακόμη. Είθε να κρατήσει.
|
||||||
|
hall.kills = Ήρωες που σκοτώθηκαν: {0}
|
||||||
|
ov.fallen = Πεσόντες ({0})
|
||||||
|
ov.living = Ζωντανοί
|
||||||
|
cause.slain = φονιάς: {0}
|
||||||
|
cause.lava = θάνατος στη λάβα
|
||||||
|
cause.water = πνιγμός
|
||||||
|
cause.snare = θάνατος σε παγίδα
|
||||||
|
cause.lightning = χτύπημα κεραυνού
|
||||||
|
cause.fell = θάνατος στη μάχη
|
||||||
@@ -1754,3 +1754,17 @@ name.epithet = {0} {1}
|
|||||||
msg.creature_fallen = {0} has fallen. A memorial stone keeps the name.
|
msg.creature_fallen = {0} has fallen. A memorial stone keeps the name.
|
||||||
tile.memorial = Memorial stone
|
tile.memorial = Memorial stone
|
||||||
tile.memorial_info = Here lies {0} ({1}, level {2}).
|
tile.memorial_info = Here lies {0} ({1}, level {2}).
|
||||||
|
|
||||||
|
# hall of the fallen
|
||||||
|
hall.title = Hall of the fallen
|
||||||
|
hall.count = {0} fallen creatures remembered
|
||||||
|
hall.empty = No one has fallen yet. Long may it last.
|
||||||
|
hall.kills = {0} heroes slain
|
||||||
|
ov.fallen = Fallen ({0})
|
||||||
|
ov.living = Living
|
||||||
|
cause.slain = slain by: {0}
|
||||||
|
cause.lava = burned in lava
|
||||||
|
cause.water = drowned
|
||||||
|
cause.snare = caught in a snare
|
||||||
|
cause.lightning = struck by lightning
|
||||||
|
cause.fell = fell
|
||||||
@@ -1754,3 +1754,17 @@ name.epithet = {0} {1}
|
|||||||
msg.creature_fallen = {0} ha caído. Una piedra conmemorativa guarda su nombre.
|
msg.creature_fallen = {0} ha caído. Una piedra conmemorativa guarda su nombre.
|
||||||
tile.memorial = Piedra conmemorativa
|
tile.memorial = Piedra conmemorativa
|
||||||
tile.memorial_info = Aquí yace {0} ({1}, nivel {2}).
|
tile.memorial_info = Aquí yace {0} ({1}, nivel {2}).
|
||||||
|
|
||||||
|
# hall of the fallen
|
||||||
|
hall.title = Salón de los caídos
|
||||||
|
hall.count = Criaturas caídas en el recuerdo: {0}
|
||||||
|
hall.empty = Aún no ha caído nadie. Que siga así.
|
||||||
|
hall.kills = Héroes abatidos: {0}
|
||||||
|
ov.fallen = Caídos ({0})
|
||||||
|
ov.living = Vivos
|
||||||
|
cause.slain = muerte a manos de: {0}
|
||||||
|
cause.lava = muerte en lava
|
||||||
|
cause.water = ahogamiento
|
||||||
|
cause.snare = muerte en un cepo
|
||||||
|
cause.lightning = muerte por rayo
|
||||||
|
cause.fell = caída en combate
|
||||||
@@ -1754,3 +1754,17 @@ name.epithet = {0} {1}
|
|||||||
msg.creature_fallen = {0} on kaatunut. Muistokivi säilyttää nimen.
|
msg.creature_fallen = {0} on kaatunut. Muistokivi säilyttää nimen.
|
||||||
tile.memorial = Muistokivi
|
tile.memorial = Muistokivi
|
||||||
tile.memorial_info = Tässä lepää {0} ({1}, taso {2}).
|
tile.memorial_info = Tässä lepää {0} ({1}, taso {2}).
|
||||||
|
|
||||||
|
# hall of the fallen
|
||||||
|
hall.title = Kaatuneiden sali
|
||||||
|
hall.count = Muistettuja kaatuneita olentoja: {0}
|
||||||
|
hall.empty = Kukaan ei ole vielä kaatunut. Kauan näin jatkukoon.
|
||||||
|
hall.kills = Surmattuja sankareita: {0}
|
||||||
|
ov.fallen = Kaatuneet ({0})
|
||||||
|
ov.living = Elävät
|
||||||
|
cause.slain = surmaaja: {0}
|
||||||
|
cause.lava = paloi laavassa
|
||||||
|
cause.water = hukkui
|
||||||
|
cause.snare = astui ansaan
|
||||||
|
cause.lightning = salama iski
|
||||||
|
cause.fell = kaatui
|
||||||
@@ -1754,3 +1754,17 @@ name.epithet = {0} {1}
|
|||||||
msg.creature_fallen = Palo je tvoje stvorenje: {0}. Spomen-kamen čuva ime.
|
msg.creature_fallen = Palo je tvoje stvorenje: {0}. Spomen-kamen čuva ime.
|
||||||
tile.memorial = Spomen-kamen
|
tile.memorial = Spomen-kamen
|
||||||
tile.memorial_info = Ovdje počiva {0} ({1}, razina {2}).
|
tile.memorial_info = Ovdje počiva {0} ({1}, razina {2}).
|
||||||
|
|
||||||
|
# hall of the fallen
|
||||||
|
hall.title = Dvorana palih
|
||||||
|
hall.count = Palih stvorenja u sjećanju: {0}
|
||||||
|
hall.empty = Još nitko nije pao. Neka tako i ostane.
|
||||||
|
hall.kills = Ubijenih junaka: {0}
|
||||||
|
ov.fallen = Pali ({0})
|
||||||
|
ov.living = Živi
|
||||||
|
cause.slain = ubojica: {0}
|
||||||
|
cause.lava = smrt u lavi
|
||||||
|
cause.water = utapanje
|
||||||
|
cause.snare = smrt u zamci
|
||||||
|
cause.lightning = udar munje
|
||||||
|
cause.fell = pogibija u boju
|
||||||
@@ -1754,3 +1754,17 @@ name.epithet = {1} {0}
|
|||||||
msg.creature_fallen = Elesett: {0}. Egy emlékkő őrzi a nevét.
|
msg.creature_fallen = Elesett: {0}. Egy emlékkő őrzi a nevét.
|
||||||
tile.memorial = Emlékkő
|
tile.memorial = Emlékkő
|
||||||
tile.memorial_info = Itt nyugszik {0} ({1}, szint: {2}).
|
tile.memorial_info = Itt nyugszik {0} ({1}, szint: {2}).
|
||||||
|
|
||||||
|
# hall of the fallen
|
||||||
|
hall.title = Az elesettek csarnoka
|
||||||
|
hall.count = {0} elesett lényre emlékezünk
|
||||||
|
hall.empty = Még senki sem esett el. Így maradjon sokáig.
|
||||||
|
hall.kills = Legyőzött hősök: {0}
|
||||||
|
ov.fallen = Elesettek ({0})
|
||||||
|
ov.living = Élők
|
||||||
|
cause.slain = megölte: {0}
|
||||||
|
cause.lava = lávában égett
|
||||||
|
cause.water = vízbe fulladt
|
||||||
|
cause.snare = csapdába lépett
|
||||||
|
cause.lightning = villám sújtotta
|
||||||
|
cause.fell = elesett
|
||||||
@@ -1754,3 +1754,17 @@ name.epithet = {0} {1}
|
|||||||
msg.creature_fallen = {0} ci ha lasciati. Una lapide commemorativa ne custodisce il nome.
|
msg.creature_fallen = {0} ci ha lasciati. Una lapide commemorativa ne custodisce il nome.
|
||||||
tile.memorial = Lapide commemorativa
|
tile.memorial = Lapide commemorativa
|
||||||
tile.memorial_info = Qui giace {0} ({1}, livello {2}).
|
tile.memorial_info = Qui giace {0} ({1}, livello {2}).
|
||||||
|
|
||||||
|
# hall of the fallen
|
||||||
|
hall.title = Sala dei caduti
|
||||||
|
hall.count = Creature cadute ricordate: {0}
|
||||||
|
hall.empty = Nessuno è ancora caduto. Che duri a lungo.
|
||||||
|
hall.kills = Eroi uccisi: {0}
|
||||||
|
ov.fallen = Caduti ({0})
|
||||||
|
ov.living = Vivi
|
||||||
|
cause.slain = morte per mano di: {0}
|
||||||
|
cause.lava = morte nella lava
|
||||||
|
cause.water = annegamento
|
||||||
|
cause.snare = morte in una tagliola
|
||||||
|
cause.lightning = colpo di fulmine
|
||||||
|
cause.fell = caduta in battaglia
|
||||||
@@ -1754,3 +1754,17 @@ name.epithet = {0} {1}
|
|||||||
msg.creature_fallen = {0} is gesneuveld. Een gedenksteen bewaart de naam.
|
msg.creature_fallen = {0} is gesneuveld. Een gedenksteen bewaart de naam.
|
||||||
tile.memorial = Gedenksteen
|
tile.memorial = Gedenksteen
|
||||||
tile.memorial_info = Hier rust {0} ({1}, niveau {2}).
|
tile.memorial_info = Hier rust {0} ({1}, niveau {2}).
|
||||||
|
|
||||||
|
# hall of the fallen
|
||||||
|
hall.title = Hal der gevallenen
|
||||||
|
hall.count = Gevallen wezens in herinnering: {0}
|
||||||
|
hall.empty = Nog niemand is gevallen. Moge het zo blijven.
|
||||||
|
hall.kills = Verslagen helden: {0}
|
||||||
|
ov.fallen = Gevallen ({0})
|
||||||
|
ov.living = Levend
|
||||||
|
cause.slain = verslagen door: {0}
|
||||||
|
cause.lava = verbrand in lava
|
||||||
|
cause.water = verdronken
|
||||||
|
cause.snare = in een klem getrapt
|
||||||
|
cause.lightning = door de bliksem getroffen
|
||||||
|
cause.fell = gesneuveld
|
||||||
@@ -1753,3 +1753,17 @@ name.epithet = {0} {1}
|
|||||||
msg.creature_fallen = {0} har falt. En minnestein bevarer navnet.
|
msg.creature_fallen = {0} har falt. En minnestein bevarer navnet.
|
||||||
tile.memorial = Minnestein
|
tile.memorial = Minnestein
|
||||||
tile.memorial_info = Her hviler {0} ({1}, nivå {2}).
|
tile.memorial_info = Her hviler {0} ({1}, nivå {2}).
|
||||||
|
|
||||||
|
# hall of the fallen
|
||||||
|
hall.title = De falnes hall
|
||||||
|
hall.count = Falne skapninger i minnet: {0}
|
||||||
|
hall.empty = Ingen har falt ennå. Lenge må det vare.
|
||||||
|
hall.kills = Drepte helter: {0}
|
||||||
|
ov.fallen = Falne ({0})
|
||||||
|
ov.living = Levende
|
||||||
|
cause.slain = drept av: {0}
|
||||||
|
cause.lava = brent i lava
|
||||||
|
cause.water = druknet
|
||||||
|
cause.snare = tråkket i en saks
|
||||||
|
cause.lightning = truffet av lynet
|
||||||
|
cause.fell = falt
|
||||||
@@ -1754,3 +1754,17 @@ name.epithet = {0} {1}
|
|||||||
msg.creature_fallen = {0} tombou. Uma pedra memorial guarda o seu nome.
|
msg.creature_fallen = {0} tombou. Uma pedra memorial guarda o seu nome.
|
||||||
tile.memorial = Pedra memorial
|
tile.memorial = Pedra memorial
|
||||||
tile.memorial_info = Aqui jaz {0} ({1}, nível {2}).
|
tile.memorial_info = Aqui jaz {0} ({1}, nível {2}).
|
||||||
|
|
||||||
|
# hall of the fallen
|
||||||
|
hall.title = Salão dos caídos
|
||||||
|
hall.count = Criaturas caídas recordadas: {0}
|
||||||
|
hall.empty = Ainda ninguém caiu. Que assim continue.
|
||||||
|
hall.kills = Heróis abatidos: {0}
|
||||||
|
ov.fallen = Caídos ({0})
|
||||||
|
ov.living = Vivos
|
||||||
|
cause.slain = morte às mãos de: {0}
|
||||||
|
cause.lava = morte na lava
|
||||||
|
cause.water = afogamento
|
||||||
|
cause.snare = morte numa armadilha
|
||||||
|
cause.lightning = morte por raio
|
||||||
|
cause.fell = queda em combate
|
||||||
@@ -1754,3 +1754,17 @@ name.epithet = {0} {1}
|
|||||||
msg.creature_fallen = {0} a căzut. O piatră comemorativă păstrează numele.
|
msg.creature_fallen = {0} a căzut. O piatră comemorativă păstrează numele.
|
||||||
tile.memorial = Piatră comemorativă
|
tile.memorial = Piatră comemorativă
|
||||||
tile.memorial_info = Aici se odihnește {0} ({1}, nivelul {2}).
|
tile.memorial_info = Aici se odihnește {0} ({1}, nivelul {2}).
|
||||||
|
|
||||||
|
# hall of the fallen
|
||||||
|
hall.title = Sala celor căzuți
|
||||||
|
hall.count = Creaturi căzute amintite: {0}
|
||||||
|
hall.empty = Încă n-a căzut nimeni. Să rămână așa.
|
||||||
|
hall.kills = Eroi uciși: {0}
|
||||||
|
ov.fallen = Căzuți ({0})
|
||||||
|
ov.living = Vii
|
||||||
|
cause.slain = ucigaș: {0}
|
||||||
|
cause.lava = moarte în lavă
|
||||||
|
cause.water = înec
|
||||||
|
cause.snare = moarte într-o capcană
|
||||||
|
cause.lightning = lovitură de fulger
|
||||||
|
cause.fell = moarte în luptă
|
||||||
@@ -1754,3 +1754,17 @@ name.epithet = {0} {1}
|
|||||||
msg.creature_fallen = Пал твой боец: {0}. Памятный камень хранит имя.
|
msg.creature_fallen = Пал твой боец: {0}. Памятный камень хранит имя.
|
||||||
tile.memorial = Памятный камень
|
tile.memorial = Памятный камень
|
||||||
tile.memorial_info = Здесь покоится {0} ({1}, уровень {2}).
|
tile.memorial_info = Здесь покоится {0} ({1}, уровень {2}).
|
||||||
|
|
||||||
|
# hall of the fallen
|
||||||
|
hall.title = Зал павших
|
||||||
|
hall.count = Павших существ в памяти: {0}
|
||||||
|
hall.empty = Пока никто не пал. Пусть так и будет.
|
||||||
|
hall.kills = Убито героев: {0}
|
||||||
|
ov.fallen = Павшие ({0})
|
||||||
|
ov.living = Живые
|
||||||
|
cause.slain = убийца: {0}
|
||||||
|
cause.lava = гибель в лаве
|
||||||
|
cause.water = утопление
|
||||||
|
cause.snare = гибель в капкане
|
||||||
|
cause.lightning = удар молнии
|
||||||
|
cause.fell = гибель в бою
|
||||||
@@ -1754,3 +1754,17 @@ name.epithet = {0} {1}
|
|||||||
msg.creature_fallen = Padol tvoj tvor: {0}. Pamätný kameň uchová meno.
|
msg.creature_fallen = Padol tvoj tvor: {0}. Pamätný kameň uchová meno.
|
||||||
tile.memorial = Pamätný kameň
|
tile.memorial = Pamätný kameň
|
||||||
tile.memorial_info = Tu odpočíva {0} ({1}, úroveň {2}).
|
tile.memorial_info = Tu odpočíva {0} ({1}, úroveň {2}).
|
||||||
|
|
||||||
|
# hall of the fallen
|
||||||
|
hall.title = Sieň padlých
|
||||||
|
hall.count = Padlé tvory v pamäti: {0}
|
||||||
|
hall.empty = Zatiaľ nikto nepadol. Nech to tak vydrží.
|
||||||
|
hall.kills = Zabití hrdinovia: {0}
|
||||||
|
ov.fallen = Padlí ({0})
|
||||||
|
ov.living = Živí
|
||||||
|
cause.slain = vrah: {0}
|
||||||
|
cause.lava = smrť v láve
|
||||||
|
cause.water = utopenie
|
||||||
|
cause.snare = smrť v pasci
|
||||||
|
cause.lightning = zásah bleskom
|
||||||
|
cause.fell = smrť v boji
|
||||||
@@ -1754,3 +1754,17 @@ name.epithet = {0} {1}
|
|||||||
msg.creature_fallen = Пало је твоје створење: {0}. Спомен-камен чува име.
|
msg.creature_fallen = Пало је твоје створење: {0}. Спомен-камен чува име.
|
||||||
tile.memorial = Спомен-камен
|
tile.memorial = Спомен-камен
|
||||||
tile.memorial_info = Овде почива {0} ({1}, ниво {2}).
|
tile.memorial_info = Овде почива {0} ({1}, ниво {2}).
|
||||||
|
|
||||||
|
# hall of the fallen
|
||||||
|
hall.title = Дворана палих
|
||||||
|
hall.count = Палих створења у сећању: {0}
|
||||||
|
hall.empty = Још нико није пао. Нека тако и остане.
|
||||||
|
hall.kills = Убијених хероја: {0}
|
||||||
|
ov.fallen = Пали ({0})
|
||||||
|
ov.living = Живи
|
||||||
|
cause.slain = убица: {0}
|
||||||
|
cause.lava = смрт у лави
|
||||||
|
cause.water = утапање
|
||||||
|
cause.snare = смрт у замци
|
||||||
|
cause.lightning = удар муње
|
||||||
|
cause.fell = погибија у боју
|
||||||
@@ -1754,3 +1754,17 @@ name.epithet = {0} {1}
|
|||||||
msg.creature_fallen = {0} har fallit. En minnessten bevarar namnet.
|
msg.creature_fallen = {0} har fallit. En minnessten bevarar namnet.
|
||||||
tile.memorial = Minnessten
|
tile.memorial = Minnessten
|
||||||
tile.memorial_info = Här vilar {0} ({1}, nivå {2}).
|
tile.memorial_info = Här vilar {0} ({1}, nivå {2}).
|
||||||
|
|
||||||
|
# hall of the fallen
|
||||||
|
hall.title = De stupades sal
|
||||||
|
hall.count = Stupade varelser i minnet: {0}
|
||||||
|
hall.empty = Ingen har stupat än. Må det länge förbli så.
|
||||||
|
hall.kills = Dräpta hjältar: {0}
|
||||||
|
ov.fallen = Stupade ({0})
|
||||||
|
ov.living = Levande
|
||||||
|
cause.slain = dräpt av: {0}
|
||||||
|
cause.lava = brann i lava
|
||||||
|
cause.water = drunknade
|
||||||
|
cause.snare = klev i en fälla
|
||||||
|
cause.lightning = träffad av blixten
|
||||||
|
cause.fell = stupade
|
||||||
+28
-1
@@ -63,8 +63,13 @@ pub struct Profile {
|
|||||||
pub totals: BTreeMap<String, f64>,
|
pub totals: BTreeMap<String, f64>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub unlocked: BTreeMap<String, u64>, // achievement id ("heroes.2", "flawless") -> unix time
|
pub unlocked: BTreeMap<String, u64>, // achievement id ("heroes.2", "flawless") -> unix time
|
||||||
|
#[serde(default)]
|
||||||
|
pub hall: Vec<crate::names::Fallen>, // the hall of the fallen over all games (newest last)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the hall of the fallen keeps this many
|
||||||
|
pub const HALL_MAX: usize = 200;
|
||||||
|
|
||||||
impl Profile {
|
impl Profile {
|
||||||
pub fn total(&self, id: &str) -> f64 {
|
pub fn total(&self, id: &str) -> f64 {
|
||||||
self.totals.get(id).copied().unwrap_or(0.0)
|
self.totals.get(id).copied().unwrap_or(0.0)
|
||||||
@@ -80,6 +85,15 @@ impl Profile {
|
|||||||
let e = self.unlocked.entry(k.clone()).or_insert(*t);
|
let e = self.unlocked.entry(k.clone()).or_insert(*t);
|
||||||
*e = (*e).min(*t);
|
*e = (*e).min(*t);
|
||||||
}
|
}
|
||||||
|
// the fallen of both, once each, in the order they fell
|
||||||
|
for f in &other.hall {
|
||||||
|
if !self.hall.iter().any(|h| h.name == f.name && h.kind == f.kind && h.when == f.when) {
|
||||||
|
self.hall.push(f.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.hall.sort_by_key(|h| h.when);
|
||||||
|
let extra = self.hall.len().saturating_sub(HALL_MAX);
|
||||||
|
self.hall.drain(..extra);
|
||||||
}
|
}
|
||||||
|
|
||||||
// unlocks what the totals have reached; returns the new ones
|
// unlocks what the totals have reached; returns the new ones
|
||||||
@@ -117,6 +131,7 @@ pub struct Context {
|
|||||||
pub struct Tracker {
|
pub struct Tracker {
|
||||||
pub profile: Profile,
|
pub profile: Profile,
|
||||||
flushed: Vec<f32>,
|
flushed: Vec<f32>,
|
||||||
|
hall_seen: usize, // entries of the game's hall already in the profile
|
||||||
last_time: f32,
|
last_time: f32,
|
||||||
ended: bool,
|
ended: bool,
|
||||||
t: f32,
|
t: f32,
|
||||||
@@ -126,12 +141,13 @@ pub struct Tracker {
|
|||||||
|
|
||||||
impl Tracker {
|
impl Tracker {
|
||||||
pub fn new(profile: Profile) -> Tracker {
|
pub fn new(profile: Profile) -> Tracker {
|
||||||
Tracker { profile, flushed: vec![0.0; k::COUNTERS], last_time: 0.0, ended: false, t: 0.0, dirty: false, upload: false }
|
Tracker { profile, flushed: vec![0.0; k::COUNTERS], hall_seen: 0, last_time: 0.0, ended: false, t: 0.0, dirty: false, upload: false }
|
||||||
}
|
}
|
||||||
|
|
||||||
// a different game started (new, loaded): nothing of it has been counted yet
|
// a different game started (new, loaded): nothing of it has been counted yet
|
||||||
fn restart(&mut self, g: &Game) {
|
fn restart(&mut self, g: &Game) {
|
||||||
self.flushed = (0..k::COUNTERS).map(|i| g.stats.get(i)).collect();
|
self.flushed = (0..k::COUNTERS).map(|i| g.stats.get(i)).collect();
|
||||||
|
self.hall_seen = g.hall.len();
|
||||||
self.ended = g.state != 0;
|
self.ended = g.state != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,6 +170,17 @@ impl Tracker {
|
|||||||
let played = self.t;
|
let played = self.t;
|
||||||
self.t = 0.0;
|
self.t = 0.0;
|
||||||
let mut new = Vec::new();
|
let mut new = Vec::new();
|
||||||
|
// the newly fallen go into the profile's hall
|
||||||
|
if g.hall.len() > self.hall_seen {
|
||||||
|
for f in &g.hall[self.hall_seen..] {
|
||||||
|
self.profile.hall.push(crate::names::Fallen { when: now, ..f.clone() });
|
||||||
|
}
|
||||||
|
self.hall_seen = g.hall.len();
|
||||||
|
let extra = self.profile.hall.len().saturating_sub(HALL_MAX);
|
||||||
|
self.profile.hall.drain(..extra);
|
||||||
|
self.dirty = true;
|
||||||
|
self.upload = true;
|
||||||
|
}
|
||||||
// the counters of the game
|
// the counters of the game
|
||||||
for i in 0..k::COUNTERS {
|
for i in 0..k::COUNTERS {
|
||||||
let cur = g.stats.get(i);
|
let cur = g.stats.get(i);
|
||||||
|
|||||||
+25
@@ -303,6 +303,10 @@ pub struct Ent {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub name: u32, // seed of the creature's name (names.rs), 0: none (heroes, siege engines)
|
pub name: u32, // seed of the creature's name (names.rs), 0: none (heroes, siege engines)
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
pub kills: u32, // heroes this creature has slain (names.rs: the hall of the fallen)
|
||||||
|
#[serde(default)]
|
||||||
|
pub bane: u8, // what hurt it last: sprite index + 1 of a unit kind, or names::BANE_*
|
||||||
|
#[serde(default)]
|
||||||
pub ab_cd: f32, // cooldown of the learned ability
|
pub ab_cd: f32, // cooldown of the learned ability
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub mood_t: f32, // seconds until the next mood check (likes and hatreds)
|
pub mood_t: f32, // seconds until the next mood check (likes and hatreds)
|
||||||
@@ -393,6 +397,8 @@ impl Ent {
|
|||||||
invis: 0.0,
|
invis: 0.0,
|
||||||
plague: 0.0,
|
plague: 0.0,
|
||||||
name: 0,
|
name: 0,
|
||||||
|
kills: 0,
|
||||||
|
bane: 0,
|
||||||
ab_cd: 0.0,
|
ab_cd: 0.0,
|
||||||
mood_t: 0.0,
|
mood_t: 0.0,
|
||||||
guard: false,
|
guard: false,
|
||||||
@@ -652,6 +658,8 @@ pub struct Game {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub memorials: Vec<crate::names::Memorial>, // stones for fallen creatures (names.rs)
|
pub memorials: Vec<crate::names::Memorial>, // stones for fallen creatures (names.rs)
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
pub hall: Vec<crate::names::Fallen>, // the local master's fallen of this game (names.rs)
|
||||||
|
#[serde(default)]
|
||||||
pub trading: crate::traders::Trading, // the visiting trader (traders.rs)
|
pub trading: crate::traders::Trading, // the visiting trader (traders.rs)
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub happenings: Option<crate::events::Events>, // random events (free games and versus), see events.rs
|
pub happenings: Option<crate::events::Events>, // random events (free games and versus), see events.rs
|
||||||
@@ -1009,6 +1017,7 @@ impl Game {
|
|||||||
bridgework: Default::default(),
|
bridgework: Default::default(),
|
||||||
weather: Default::default(),
|
weather: Default::default(),
|
||||||
memorials: Vec::new(),
|
memorials: Vec::new(),
|
||||||
|
hall: Vec::new(),
|
||||||
trading: Default::default(),
|
trading: Default::default(),
|
||||||
happenings: None,
|
happenings: None,
|
||||||
snares: Vec::new(),
|
snares: Vec::new(),
|
||||||
@@ -2791,6 +2800,11 @@ impl Game {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(crate) fn hit_for_test(&mut self, e: &mut Ent, j: usize) {
|
||||||
|
self.hit(e, j);
|
||||||
|
}
|
||||||
|
|
||||||
fn hit(&mut self, e: &mut Ent, j: usize) {
|
fn hit(&mut self, e: &mut Ent, j: usize) {
|
||||||
// the Warlord talent of the attacker's master
|
// the Warlord talent of the attacker's master
|
||||||
let boost = if !e.hero && Owner::MASTER_SIDES.contains(&e.side) { self.talents_of(e.side).damage() } else { 1.0 };
|
let boost = if !e.hero && Owner::MASTER_SIDES.contains(&e.side) { self.talents_of(e.side).damage() } else { 1.0 };
|
||||||
@@ -2910,6 +2924,10 @@ impl Game {
|
|||||||
let t = &mut self.ents[j];
|
let t = &mut self.ents[j];
|
||||||
t.hp -= if t.shield > 0.0 { dmg * 0.5 } else { dmg } * armour;
|
t.hp -= if t.shield > 0.0 { dmg * 0.5 } else { dmg } * armour;
|
||||||
t.flash = 0.15;
|
t.flash = 0.15;
|
||||||
|
t.bane = crate::gfx::sprite_index(e.kind) as u8 + 1;
|
||||||
|
if t.hp <= 0.0 && t.hero && t.captive == 0 {
|
||||||
|
e.kills += 1;
|
||||||
|
}
|
||||||
if t.creature() && !matches!(t.job, Job::Attack(_)) {
|
if t.creature() && !matches!(t.job, Job::Attack(_)) {
|
||||||
t.job = Job::Attack(my_id);
|
t.job = Job::Attack(my_id);
|
||||||
t.path.clear();
|
t.path.clear();
|
||||||
@@ -3025,6 +3043,13 @@ impl Game {
|
|||||||
}
|
}
|
||||||
self.release_all(id);
|
self.release_all(id);
|
||||||
self.hand.retain(|&h| h != id);
|
self.hand.retain(|&h| h != id);
|
||||||
|
// a named creature lost in lava or water is remembered too
|
||||||
|
if !hero {
|
||||||
|
let mut dead = self.ents[j].clone();
|
||||||
|
let lava = self.map.get(x as i32, y as i32).map_or(false, |t| t.river() == Some(Kind::Lava));
|
||||||
|
dead.bane = if lava { crate::names::BANE_LAVA } else { crate::names::BANE_WATER };
|
||||||
|
self.raise_memorial(&dead);
|
||||||
|
}
|
||||||
self.fx(3, x, y, 0.0, 0.0, 1.0, 0xff2060ff);
|
self.fx(3, x, y, 0.0, 0.0, 1.0, 0xff2060ff);
|
||||||
self.fx(2, x, y, 0.0, 0.0, 0.5, 0xff30a0ff);
|
self.fx(2, x, y, 0.0, 0.0, 0.5, 0xff30a0ff);
|
||||||
self.sfx(Sfx::Death, x, y);
|
self.sfx(Sfx::Death, x, y);
|
||||||
|
|||||||
+10
-1
@@ -141,7 +141,16 @@ fn world_action(app: &mut App, ui: &mut Ui, r3d: &R3d, inp: &mut Input, mx: f32,
|
|||||||
(Tool::Dig | Tool::Room(_) | Tool::Sell | Tool::Bridge | Tool::Steel | Tool::Drawbridge | Tool::Wall, Some(t)) => inp.ldown = Some(t),
|
(Tool::Dig | Tool::Room(_) | Tool::Sell | Tool::Bridge | Tool::Steel | Tool::Drawbridge | Tool::Wall, Some(t)) => inp.ldown = Some(t),
|
||||||
(Tool::Hand, _) => {
|
(Tool::Hand, _) => {
|
||||||
let j = r3d.pick_ent(&app.game, &app.cam, mx, my, false);
|
let j = r3d.pick_ent(&app.game, &app.cam, mx, my, false);
|
||||||
let Some(j) = j else { return };
|
let Some(j) = j else {
|
||||||
|
// a memorial stone: the hall of the fallen opens
|
||||||
|
let (gx, gy) = (wx.floor() as i32, wy.floor() as i32);
|
||||||
|
if map::inb(gx, gy) && app.game.memorial_at(map::idx(gx, gy)).is_some() {
|
||||||
|
ui.show_overview = true;
|
||||||
|
ui.ov_fallen = true;
|
||||||
|
ui.ov_scroll = 0;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
};
|
||||||
let (id, hero, dwarf) = (app.game.ents[j].id, app.game.ents[j].hero, app.game.ents[j].dwarf());
|
let (id, hero, dwarf) = (app.game.ents[j].id, app.game.ents[j].hero, app.game.ents[j].dwarf());
|
||||||
if dwarf {
|
if dwarf {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -303,6 +303,7 @@ enum Btn {
|
|||||||
MatchChat,
|
MatchChat,
|
||||||
Bestiary,
|
Bestiary,
|
||||||
Achievements,
|
Achievements,
|
||||||
|
AchHall, // achievements page: switch to the hall of the fallen and back
|
||||||
Tutorial,
|
Tutorial,
|
||||||
BestTab(usize),
|
BestTab(usize),
|
||||||
BestSel(i32),
|
BestSel(i32),
|
||||||
@@ -454,6 +455,7 @@ pub struct Menu {
|
|||||||
scroll: usize,
|
scroll: usize,
|
||||||
rows_visible: usize,
|
rows_visible: usize,
|
||||||
pub profile: crate::achievements::Profile, // shown on the achievements page
|
pub profile: crate::achievements::Profile, // shown on the achievements page
|
||||||
|
ach_hall: bool, // the achievements page shows the hall of the fallen
|
||||||
buttons: Vec<(Rect, Btn)>,
|
buttons: Vec<(Rect, Btn)>,
|
||||||
lang_open: Option<Rect>, // the language dropdown is open below/above this field
|
lang_open: Option<Rect>, // the language dropdown is open below/above this field
|
||||||
lang_scroll: usize, // first language shown in the open dropdown
|
lang_scroll: usize, // first language shown in the open dropdown
|
||||||
@@ -497,6 +499,7 @@ impl Menu {
|
|||||||
focus: 0,
|
focus: 0,
|
||||||
error: None,
|
error: None,
|
||||||
match_ui: MatchUi::default(),
|
match_ui: MatchUi::default(),
|
||||||
|
ach_hall: false,
|
||||||
coop_mission: crate::campaign::coop_mission_indexes().first().copied().unwrap_or(0),
|
coop_mission: crate::campaign::coop_mission_indexes().first().copied().unwrap_or(0),
|
||||||
match_size: 128,
|
match_size: 128,
|
||||||
coop_editor: false,
|
coop_editor: false,
|
||||||
@@ -939,6 +942,11 @@ impl Menu {
|
|||||||
}
|
}
|
||||||
MenuAction::Consumed
|
MenuAction::Consumed
|
||||||
}
|
}
|
||||||
|
Some(Btn::AchHall) => {
|
||||||
|
self.ach_hall = !self.ach_hall;
|
||||||
|
self.scroll = 0;
|
||||||
|
MenuAction::Consumed
|
||||||
|
}
|
||||||
Some(Btn::Achievements) => {
|
Some(Btn::Achievements) => {
|
||||||
self.page = Some(Page::Achievements { from_title });
|
self.page = Some(Page::Achievements { from_title });
|
||||||
self.scroll = 0;
|
self.scroll = 0;
|
||||||
|
|||||||
@@ -15,7 +15,13 @@ impl Menu {
|
|||||||
let rows = 40.min((sh / CH()) as i32 - 2);
|
let rows = 40.min((sh / CH()) as i32 - 2);
|
||||||
let x = grid_center(sw, cols as f32 * CW()).max(0.0);
|
let x = grid_center(sw, cols as f32 * CW()).max(0.0);
|
||||||
let y = grid_center_y(sh, rows as f32 * CH()).max(0.0);
|
let y = grid_center_y(sh, rows as f32 * CH()).max(0.0);
|
||||||
gfx.window_titled(x, y, cols, rows, S(), true, FRAME_HI, Color::new(0.05, 0.03, 0.04, 0.97), &tr("menu.achievements"), FRAME_HI);
|
let title = if self.ach_hall { tr("hall.title") } else { tr("menu.achievements") };
|
||||||
|
gfx.window_titled(x, y, cols, rows, S(), true, FRAME_HI, Color::new(0.05, 0.03, 0.04, 0.97), &title, FRAME_HI);
|
||||||
|
if self.ach_hall {
|
||||||
|
self.draw_hall_page(gfx, x, y, cols, rows);
|
||||||
|
self.ach_buttons(gfx, x, y, rows);
|
||||||
|
return;
|
||||||
|
}
|
||||||
let list = all();
|
let list = all();
|
||||||
let done = list.iter().filter(|a| self.profile.unlocked.contains_key(&a.0)).count();
|
let done = list.iter().filter(|a| self.profile.unlocked.contains_key(&a.0)).count();
|
||||||
let head = trf("ach.count", &[done.to_string(), list.len().to_string(), crate::achievements::fmt_count(self.profile.total("minutes"))]);
|
let head = trf("ach.count", &[done.to_string(), list.len().to_string(), crate::achievements::fmt_count(self.profile.total("minutes"))]);
|
||||||
@@ -57,14 +63,75 @@ impl Menu {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// scrolling and back
|
// scrolling and back
|
||||||
|
let _ = ix;
|
||||||
|
self.ach_buttons(gfx, x, y, rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
// scrolling, the switch between achievements and the hall, back
|
||||||
|
fn ach_buttons(&mut self, gfx: &Gfx, x: f32, y: f32, rows: i32) {
|
||||||
|
let ix = x + CW() * 3.0;
|
||||||
let by = y + (rows - 3) as f32 * CH();
|
let by = y + (rows - 3) as f32 * CH();
|
||||||
let (mx, my) = mouse();
|
let (mx, my) = mouse();
|
||||||
let bw = 14.0 * CW();
|
let bw = 14.0 * CW();
|
||||||
let items = [(Btn::Scroll(-1), "▲".to_string()), (Btn::Scroll(1), "▼".to_string()), (Btn::Back, tr("menu.back"))];
|
let switch = if self.ach_hall { tr("menu.achievements") } else { tr("hall.title") };
|
||||||
|
let items = [(Btn::Scroll(-1), "▲".to_string()), (Btn::Scroll(1), "▼".to_string()), (Btn::AchHall, switch), (Btn::Back, tr("menu.back"))];
|
||||||
|
let bw = bw.min((sw() - ix - CW() * 4.0) / 4.0 - CW());
|
||||||
for (k, (b, label)) in items.iter().enumerate() {
|
for (k, (b, label)) in items.iter().enumerate() {
|
||||||
let r = Rect::new(ix + k as f32 * (bw + CW()), by, bw, CH() * 2.0);
|
let r = Rect::new(ix + k as f32 * (bw + CW()), by, bw, CH() * 2.0);
|
||||||
button(gfx, r, label, r.contains(vec2(mx, my)), FRAME);
|
button(gfx, r, label, r.contains(vec2(mx, my)), FRAME);
|
||||||
self.buttons.push((r, *b));
|
self.buttons.push((r, *b));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the hall of the fallen over all games: newest first, two lines each
|
||||||
|
fn draw_hall_page(&mut self, gfx: &Gfx, x: f32, y: f32, cols: i32, rows: i32) {
|
||||||
|
let hall = &self.profile.hall;
|
||||||
|
gfx.text(&trf("hall.count", &[hall.len().to_string()]), x + CW() * 3.0, y + CH() * 1.6, S(), GOLD);
|
||||||
|
let top = y + CH() * 3.5;
|
||||||
|
let bottom = y + (rows - 4) as f32 * CH();
|
||||||
|
let row_h = CH() * 2.4;
|
||||||
|
self.rows_visible = (((bottom - top) / row_h).floor() as usize).max(1);
|
||||||
|
self.scroll = self.scroll.min(hall.len().saturating_sub(self.rows_visible));
|
||||||
|
let ix = x + CW() * 3.0;
|
||||||
|
let maxc = (cols - 6) as usize;
|
||||||
|
if hall.is_empty() {
|
||||||
|
gfx.text(&tr("hall.empty"), ix, top, S(), DIM);
|
||||||
|
}
|
||||||
|
for (k, f) in hall.iter().rev().enumerate().skip(self.scroll).take(self.rows_visible) {
|
||||||
|
let ry = top + (k - self.scroll) as f32 * row_h;
|
||||||
|
let name = format!("{} ({})", crate::names::full_name(f.kind, f.name, f.level), trf("info.level", &[tr(crate::game::st(f.kind).name), f.level.to_string()]));
|
||||||
|
gfx.text(&name.chars().take(maxc).collect::<String>(), ix, ry, S(), FRAME_HI);
|
||||||
|
let kills = if f.kills > 0 { format!(" · {}", trf("hall.kills", &[f.kills.to_string()])) } else { String::new() };
|
||||||
|
let line = format!("{}{} · {}", crate::names::cause(f.bane), kills, date(f.when));
|
||||||
|
gfx.text(&line.chars().take(maxc * 2).collect::<String>(), ix + CW() * 2.0, ry + CH(), SMALL(), DIM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// a unix time as a date (year-month-day)
|
||||||
|
fn date(unix: u64) -> String {
|
||||||
|
if unix == 0 {
|
||||||
|
return String::new();
|
||||||
|
}
|
||||||
|
// days since 1970-01-01 to a civil date (Howard Hinnant's algorithm)
|
||||||
|
let z = (unix / 86400) as i64 + 719468;
|
||||||
|
let era = z.div_euclid(146097);
|
||||||
|
let doe = z - era * 146097;
|
||||||
|
let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
|
||||||
|
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
|
||||||
|
let mp = (5 * doy + 2) / 153;
|
||||||
|
let d = doy - (153 * mp + 2) / 5 + 1;
|
||||||
|
let m = if mp < 10 { mp + 3 } else { mp - 9 };
|
||||||
|
let y = yoe + era * 400 + if m <= 2 { 1 } else { 0 };
|
||||||
|
format!("{:04}-{:02}-{:02}", y, m, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#[test]
|
||||||
|
fn dates_are_civil() {
|
||||||
|
assert_eq!(super::date(0), "");
|
||||||
|
assert_eq!(super::date(1_798_675_200), "2026-12-31");
|
||||||
|
assert_eq!(super::date(1_709_164_800), "2024-02-29");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -12,6 +12,40 @@ use serde::{Deserialize, Serialize};
|
|||||||
pub const MEMORIAL_LEVEL: u32 = 3;
|
pub const MEMORIAL_LEVEL: u32 = 3;
|
||||||
pub const MAX_MEMORIALS: usize = 24;
|
pub const MAX_MEMORIALS: usize = 24;
|
||||||
|
|
||||||
|
// what killed a creature besides another unit (Ent::bane)
|
||||||
|
pub const BANE_SNARE: u8 = 250;
|
||||||
|
pub const BANE_LIGHTNING: u8 = 251;
|
||||||
|
pub const BANE_LAVA: u8 = 252;
|
||||||
|
pub const BANE_WATER: u8 = 253;
|
||||||
|
|
||||||
|
// an entry of the hall of the fallen (in the game and in the player's profile)
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub struct Fallen {
|
||||||
|
pub kind: EK,
|
||||||
|
pub name: u32,
|
||||||
|
pub level: u32,
|
||||||
|
pub kills: u32,
|
||||||
|
pub bane: u8,
|
||||||
|
pub time: f32, // game seconds when it fell
|
||||||
|
#[serde(default)]
|
||||||
|
pub when: u64, // unix time (set when it goes into the profile)
|
||||||
|
}
|
||||||
|
|
||||||
|
// how it fell, in words
|
||||||
|
pub fn cause(bane: u8) -> String {
|
||||||
|
match bane {
|
||||||
|
BANE_SNARE => tr("cause.snare"),
|
||||||
|
BANE_LIGHTNING => tr("cause.lightning"),
|
||||||
|
BANE_LAVA => tr("cause.lava"),
|
||||||
|
BANE_WATER => tr("cause.water"),
|
||||||
|
0 => tr("cause.fell"),
|
||||||
|
k => match crate::gfx::SPRITE_KINDS.get(k as usize - 1) {
|
||||||
|
Some(&kind) => trf("cause.slain", &[tr(st(kind).name)]),
|
||||||
|
None => tr("cause.fell"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
pub struct Memorial {
|
pub struct Memorial {
|
||||||
pub tile: usize,
|
pub tile: usize,
|
||||||
@@ -103,6 +137,10 @@ impl Game {
|
|||||||
if e.hero || e.name == 0 || e.level < MEMORIAL_LEVEL || !Owner::MASTER_SIDES.contains(&e.side) {
|
if e.hero || e.name == 0 || e.level < MEMORIAL_LEVEL || !Owner::MASTER_SIDES.contains(&e.side) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// the hall of the fallen keeps the local master's (also without room for a stone)
|
||||||
|
if e.side == self.local_side {
|
||||||
|
self.hall.push(Fallen { kind: e.kind, name: e.name, level: e.level, kills: e.kills, bane: e.bane, time: self.time, when: 0 });
|
||||||
|
}
|
||||||
let owner = Owner::of_side(e.side);
|
let owner = Owner::of_side(e.side);
|
||||||
let (x, y) = (e.x as i32, e.y as i32);
|
let (x, y) = (e.x as i32, e.y as i32);
|
||||||
let taken: Vec<usize> = self.memorials.iter().map(|m| m.tile).collect();
|
let taken: Vec<usize> = self.memorials.iter().map(|m| m.tile).collect();
|
||||||
@@ -168,6 +206,50 @@ mod tests {
|
|||||||
assert_eq!(g.ents.iter().find(|e| e.id == h).unwrap().name, 0);
|
assert_eq!(g.ents.iter().find(|e| e.id == h).unwrap().name, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn the_hall_remembers_kills_and_how_they_fell() {
|
||||||
|
let mut g = Game::new(9, 64, 64);
|
||||||
|
let (cx, cy) = g.map.core;
|
||||||
|
for y in cy + 4..cy + 8 {
|
||||||
|
for x in cx..cx + 6 {
|
||||||
|
let t = &mut g.map.t[idx(x, y)];
|
||||||
|
t.kind = Kind::Floor;
|
||||||
|
t.owner = Owner::Player;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut t = crate::achievements::Tracker::new(Default::default());
|
||||||
|
let ctx = crate::achievements::Context { coop: false, editor_test: false };
|
||||||
|
g.time = 1.0;
|
||||||
|
t.update(&g, &ctx, 6.0, 1_800_000_000);
|
||||||
|
let ogre = g.spawn(EK::Ogre, false, cx as f32 + 1.5, cy as f32 + 5.5, 5);
|
||||||
|
let knight = g.spawn(EK::Knight, true, cx as f32 + 2.5, cy as f32 + 5.5, 1);
|
||||||
|
// the ogre slays the knight
|
||||||
|
let (oj, kj) = (g.ents.iter().position(|e| e.id == ogre).unwrap(), g.ents.iter().position(|e| e.id == knight).unwrap());
|
||||||
|
g.ents[kj].hp = 1.0;
|
||||||
|
let mut o = g.ents[oj].clone();
|
||||||
|
g.hit_for_test(&mut o, kj);
|
||||||
|
assert_eq!(o.kills, 1, "a hero slain");
|
||||||
|
g.ents[oj] = o;
|
||||||
|
// a second knight brings the ogre down
|
||||||
|
let k2 = g.spawn(EK::Knight, true, cx as f32 + 2.5, cy as f32 + 6.5, 1);
|
||||||
|
let k2j = g.ents.iter().position(|e| e.id == k2).unwrap();
|
||||||
|
let oj = g.ents.iter().position(|e| e.id == ogre).unwrap();
|
||||||
|
g.ents[oj].hp = 1.0;
|
||||||
|
let mut k = g.ents[k2j].clone();
|
||||||
|
g.hit_for_test(&mut k, oj);
|
||||||
|
assert_eq!(g.hall.len(), 1);
|
||||||
|
let f = &g.hall[0];
|
||||||
|
assert_eq!((f.kind, f.kills), (EK::Ogre, 1));
|
||||||
|
assert_eq!(f.bane as u32, crate::gfx::sprite_index(EK::Knight) + 1);
|
||||||
|
assert!(cause(f.bane).contains(&crate::i18n::tr("unit.knight")));
|
||||||
|
// into the profile, once
|
||||||
|
g.time = 100.0;
|
||||||
|
t.update(&g, &ctx, 6.0, 1_800_000_100);
|
||||||
|
t.update(&g, &ctx, 6.0, 1_800_000_200);
|
||||||
|
assert_eq!(t.profile.hall.len(), 1);
|
||||||
|
assert_eq!(t.profile.hall[0].when, 1_800_000_100);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn the_fallen_get_a_memorial_stone() {
|
fn the_fallen_get_a_memorial_stone() {
|
||||||
let mut g = Game::new(9, 64, 64);
|
let mut g = Game::new(9, 64, 64);
|
||||||
|
|||||||
@@ -193,6 +193,7 @@ impl Game {
|
|||||||
let e = &mut self.ents[j];
|
let e = &mut self.ents[j];
|
||||||
e.stun = e.stun.max(SNARE_STUN);
|
e.stun = e.stun.max(SNARE_STUN);
|
||||||
e.hp -= SNARE_DAMAGE;
|
e.hp -= SNARE_DAMAGE;
|
||||||
|
e.bane = crate::names::BANE_SNARE;
|
||||||
e.flash = 0.2;
|
e.flash = 0.2;
|
||||||
let (x, y, side, kind, dead) = (e.x, e.y, e.side, e.kind, e.hp <= 0.0);
|
let (x, y, side, kind, dead) = (e.x, e.y, e.side, e.kind, e.hp <= 0.0);
|
||||||
self.fx(2, x, y, 0.0, 0.0, 0.4, 0xff4040ff);
|
self.fx(2, x, y, 0.0, 0.0, 0.4, 0xff4040ff);
|
||||||
|
|||||||
@@ -461,6 +461,7 @@ enum TBtn {
|
|||||||
Learn(u8), // spend a point on a branch
|
Learn(u8), // spend a point on a branch
|
||||||
OvRow(u32),
|
OvRow(u32),
|
||||||
OvScroll(i32),
|
OvScroll(i32),
|
||||||
|
OvFallen, // switch the overview between the living and the fallen
|
||||||
GoldTo, // the next ally to receive gold
|
GoldTo, // the next ally to receive gold
|
||||||
Gold(u32), // send that much gold to him
|
Gold(u32), // send that much gold to him
|
||||||
}
|
}
|
||||||
@@ -491,7 +492,8 @@ pub struct Ui {
|
|||||||
pub show_talents: bool, // the master's talents
|
pub show_talents: bool, // the master's talents
|
||||||
pub coop_end: Option<(bool, bool, bool)>, // end of an editor co-op mission: (host, a next mission, asked for it)
|
pub coop_end: Option<(bool, bool, bool)>, // end of an editor co-op mission: (host, a next mission, asked for it)
|
||||||
gold_to: usize, // the ally chosen in the creature overview
|
gold_to: usize, // the ally chosen in the creature overview
|
||||||
ov_scroll: usize,
|
pub ov_scroll: usize,
|
||||||
|
pub ov_fallen: bool, // the overview shows the hall of the fallen
|
||||||
ov_rect: Option<Rect>, // where the creature overview is (mouse wheel, swipe)
|
ov_rect: Option<Rect>, // where the creature overview is (mouse wheel, swipe)
|
||||||
pub show_langsel: bool,
|
pub show_langsel: bool,
|
||||||
pub log: Vec<(Msg, f64)>,
|
pub log: Vec<(Msg, f64)>,
|
||||||
@@ -603,6 +605,7 @@ impl Ui {
|
|||||||
coop_end: None,
|
coop_end: None,
|
||||||
gold_to: 0,
|
gold_to: 0,
|
||||||
ov_scroll: 0,
|
ov_scroll: 0,
|
||||||
|
ov_fallen: false,
|
||||||
ov_rect: None,
|
ov_rect: None,
|
||||||
show_langsel: false,
|
show_langsel: false,
|
||||||
log: Vec::new(),
|
log: Vec::new(),
|
||||||
@@ -702,6 +705,11 @@ impl Ui {
|
|||||||
UiAction::Consumed
|
UiAction::Consumed
|
||||||
}
|
}
|
||||||
TBtn::Learn(b) => UiAction::LearnTalent(b),
|
TBtn::Learn(b) => UiAction::LearnTalent(b),
|
||||||
|
TBtn::OvFallen => {
|
||||||
|
self.ov_fallen = !self.ov_fallen;
|
||||||
|
self.ov_scroll = 0;
|
||||||
|
UiAction::Consumed
|
||||||
|
}
|
||||||
TBtn::OvScroll(d) => {
|
TBtn::OvScroll(d) => {
|
||||||
self.ov_scroll = (self.ov_scroll as i32 + d).max(0) as usize;
|
self.ov_scroll = (self.ov_scroll as i32 + d).max(0) as usize;
|
||||||
UiAction::Consumed
|
UiAction::Consumed
|
||||||
@@ -1343,6 +1351,36 @@ impl Ui {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the hall of the fallen of this game: name, kind, level, heroes slain, how it fell
|
||||||
|
fn draw_hall(&mut self, g: &Game, gfx: &Gfx, x: f32, y: f32, cols: i32, rows: i32) {
|
||||||
|
gfx.text(&tr("hall.title"), x + CW() * 2.0, y + CH(), S(), GOLD);
|
||||||
|
let mut lines: Vec<(String, Color)> = Vec::new();
|
||||||
|
for f in g.hall.iter().rev() {
|
||||||
|
lines.push((format!("{:>2} {}", f.level, crate::names::full_name(f.kind, f.name, f.level)), FRAME_HI));
|
||||||
|
let kills = if f.kills > 0 { format!(" · {}", trf("hall.kills", &[f.kills.to_string()])) } else { String::new() };
|
||||||
|
lines.push((format!(" {} · {}{} · {}", tr(st(f.kind).name), crate::names::cause(f.bane), kills, mmss(f.time)), DIM));
|
||||||
|
}
|
||||||
|
if lines.is_empty() {
|
||||||
|
lines.push((tr("hall.empty"), DIM));
|
||||||
|
}
|
||||||
|
let first = y + CH() * 2.6;
|
||||||
|
let visible = (rows - 4).max(1) as usize;
|
||||||
|
self.ov_scroll = self.ov_scroll.min(lines.len().saturating_sub(visible));
|
||||||
|
let maxc = (cols - 4) as usize;
|
||||||
|
for (n, (text, col)) in lines.iter().enumerate().skip(self.ov_scroll).take(visible) {
|
||||||
|
let t: String = text.chars().take(maxc).collect();
|
||||||
|
gfx.text(&t, x + CW() * 2.0, first + (n - self.ov_scroll) as f32 * CH(), S(), *col);
|
||||||
|
}
|
||||||
|
if lines.len() > visible {
|
||||||
|
let up = Rect::new(x + (cols - 4) as f32 * CW(), y + CH() * 2.4, CW() * 2.0, CH() * 1.5);
|
||||||
|
let down = Rect::new(x + (cols - 4) as f32 * CW(), y + (rows - 2) as f32 * CH(), CW() * 2.0, CH() * 1.5);
|
||||||
|
gfx.text("\u{25b2}", up.x + CW() * 0.5, up.y, S(), FRAME_HI);
|
||||||
|
gfx.text("\u{25bc}", down.x + CW() * 0.5, down.y, S(), FRAME_HI);
|
||||||
|
self.touch_btns.push((up, TBtn::OvScroll(-2)));
|
||||||
|
self.touch_btns.push((down, TBtn::OvScroll(2)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn draw_overview(&mut self, g: &Game, gfx: &Gfx, bottom: f32) {
|
fn draw_overview(&mut self, g: &Game, gfx: &Gfx, bottom: f32) {
|
||||||
let (sw, _) = (sw(), sh());
|
let (sw, _) = (sw(), sh());
|
||||||
let (mx, my) = mouse();
|
let (mx, my) = mouse();
|
||||||
@@ -1357,6 +1395,21 @@ impl Ui {
|
|||||||
let mine: Vec<&Ent> = g.ents.iter().filter(|e| e.alive && e.creature()).collect();
|
let mine: Vec<&Ent> = g.ents.iter().filter(|e| e.alive && e.creature()).collect();
|
||||||
let wage: f32 = mine.iter().map(|e| st(e.kind).wage as f32 * (1.0 + 0.1 * (e.level - 1) as f32)).sum();
|
let wage: f32 = mine.iter().map(|e| st(e.kind).wage as f32 * (1.0 + 0.1 * (e.level - 1) as f32)).sum();
|
||||||
let mood = if mine.is_empty() { 0.0 } else { mine.iter().map(|e| e.happy.clamp(0.0, 1.0)).sum::<f32>() / mine.len() as f32 };
|
let mood = if mine.is_empty() { 0.0 } else { mine.iter().map(|e| e.happy.clamp(0.0, 1.0)).sum::<f32>() / mine.len() as f32 };
|
||||||
|
// the switch to the hall of the fallen (top right)
|
||||||
|
{
|
||||||
|
let label = if self.ov_fallen { tr("ov.living") } else { trf("ov.fallen", &[g.hall.len().to_string()]) };
|
||||||
|
let bw = gfx.measure(&label, SMALL()) + CW() * 2.0;
|
||||||
|
let r = Rect::new(x + (cols - 3) as f32 * CW() - bw, y + CH() * 0.8, bw, CH() * 1.4);
|
||||||
|
let hov = r.contains(vec2(mx, my));
|
||||||
|
draw_rectangle(r.x, r.y, r.w, r.h, if hov { Color::new(0.35, 0.22, 0.1, 1.0) } else { Color::new(0.15, 0.1, 0.08, 1.0) });
|
||||||
|
draw_rectangle_lines(r.x, r.y, r.w, r.h, 2.0, FRAME_HI);
|
||||||
|
gfx.text(&label, r.x + CW(), r.y + (r.h - GH * SMALL()) * 0.5, SMALL(), FRAME_HI);
|
||||||
|
self.touch_btns.push((r, TBtn::OvFallen));
|
||||||
|
}
|
||||||
|
if self.ov_fallen {
|
||||||
|
self.draw_hall(g, gfx, x, y, cols, rows);
|
||||||
|
return;
|
||||||
|
}
|
||||||
let head = trf("ov.summary", &[mine.len().to_string(), (wage as i32).to_string(), ((mood * 100.0) as i32).to_string()]);
|
let head = trf("ov.summary", &[mine.len().to_string(), (wage as i32).to_string(), ((mood * 100.0) as i32).to_string()]);
|
||||||
gfx.text(&head, x + CW() * 2.0, y + CH(), S(), GOLD);
|
gfx.text(&head, x + CW() * 2.0, y + CH(), S(), GOLD);
|
||||||
// a match with allies: a row to send them gold (the name switches between the allies)
|
// a match with allies: a row to send them gold (the name switches between the allies)
|
||||||
|
|||||||
@@ -145,6 +145,7 @@ impl Game {
|
|||||||
let e = &mut self.ents[j];
|
let e = &mut self.ents[j];
|
||||||
e.hp -= BOLT_DAMAGE;
|
e.hp -= BOLT_DAMAGE;
|
||||||
e.flash = 0.3;
|
e.flash = 0.3;
|
||||||
|
e.bane = crate::names::BANE_LIGHTNING;
|
||||||
let (x, y, side, kind, hero, dead) = (e.x, e.y, e.side, e.kind, e.hero, e.hp <= 0.0);
|
let (x, y, side, kind, hero, dead) = (e.x, e.y, e.side, e.kind, e.hero, e.hp <= 0.0);
|
||||||
if !hero && side == self.local_side {
|
if !hero && side == self.local_side {
|
||||||
self.tell_local("msg.lightning_unit", vec![format!("@{}", st(kind).name)], None);
|
self.tell_local("msg.lightning_unit", vec![format!("@{}", st(kind).name)], None);
|
||||||
|
|||||||
Reference in new issue
Block a user