Compare commits

...
2 Commits
28 changed files with 723 additions and 24 deletions

No files matched your search

+5 -2
View File
@@ -129,7 +129,10 @@ grid), guard and attack markers (shields and swords), savegames with the game st
to the gate; slain, his loot is yours; from wave 3), monk (spells do not touch him, 5), ranger (shoots, lays to the gate; slain, his loot is yours; from wave 3), monk (spells do not touch him, 5), ranger (shoots, lays
snares that hurt and stun a creature, 7), inquisitor (fragile, every 20 s turns a nearby kobold or spider to snares that hurt and stun a creature, 7), inquisitor (fragile, every 20 s turns a nearby kobold or spider to
the heroes, 9). In the campaign from missions 5, 9, 12 and 16 on. the heroes, 9). In the campaign from missions 5, 9, 12 and 16 on.
4. **Master talents** - Builder, Warlord, Warlock, Merchant (5 levels each), a toolbar button; per game, 4. **Master talents** (done, v202611-00064, talents.rs) - experience from slain heroes, digging, rooms, walls,
carried over in the campaign. traps, towers, spells and gold; points get dearer (300, 450, 600 ...). Builder (imps dig +10%, rooms -6% per
level), Warlord (creatures +5% damage, -5% damage taken), Warlock (mana +15%, spells -6%), Merchant (wages
-6%, treasure +6%); 5 levels each. The "Master" button (number of free points, pulsing) opens the window.
AI masters spend their points themselves; per game, carried over in the campaign and co-op runs.
5. **Weather above ground** - rain (puts out fire), fog (less sight), storms (lightning in trees and units). 5. **Weather above ground** - rain (puts out fire), fog (less sight), storms (lightning in trees and units).
6. **Named creatures** - names per kind and language, epithets at higher levels, memorial stones. 6. **Named creatures** - names per kind and language, epithets at higher levels, memorial stones.
+19
View File
@@ -1693,3 +1693,22 @@ tut.slap = Křivoprst: Ha! Slyšel jsi to? Něco přichází od východu. Připr
tut.obj6 = Poraz dva rytíře. tut.obj6 = Poraz dva rytíře.
tut.knights = Křivoprst: Dva rytíři! Pošli na ně tvory útočnou vlajkou (Rozkazy) nebo je zasáhni bleskem. tut.knights = Křivoprst: Dva rytíři! Pošli na ně tvory útočnou vlajkou (Rozkazy) nebo je zasáhni bleskem.
tut.win = Křivoprst: Výborně, pane! To je všechno. No - skoro. Zbytek poví kampaň. Přistavím vodu na čaj. tut.win = Křivoprst: Výborně, pane! To je všechno. No - skoro. Zbytek poví kampaň. Přistavím vodu na čaj.
# master talents
cat.master = Pán
talent.title = Pánovy talenty
talent.points = Volné body talentů: {0}
talent.next = {0}/{1} zkušeností do dalšího bodu
talent.all = Všechny talenty jsou naučené.
talent.builder = Stavitel
talent.warlord = Vojevůdce
talent.warlock = Černokněžník
talent.merchant = Obchodník
talent.builder.desc = Čertíci kopou o {0}% rychleji, místnosti stojí o {1}% méně.
talent.warlord.desc = Tvorové udeří o {0}% silněji a dostávají o {1}% méně poškození.
talent.warlock.desc = Mana proudí o {0}% rychleji, kouzla stojí o {1}% méně.
talent.merchant.desc = Mzdy jsou o {0}% nižší, doručené poklady mají o {1}% vyšší hodnotu.
talent.none = Zatím nenaučeno.
talent.then = Další úroveň: {0}
msg.talent_point = Nový bod talentu! Použij ho v okně pána.
msg.talent_learned = {0}: úroveň {1} naučena.
+19
View File
@@ -1692,3 +1692,22 @@ tut.slap = Krumfinger: Ha! Hørte du det? Noget kommer fra øst. Gør dig klar:
tut.obj6 = Besejr de to riddere. tut.obj6 = Besejr de to riddere.
tut.knights = Krumfinger: To riddere! Send dine væsner af sted med angrebsflaget (Ordrer), eller ram dem med lynet. tut.knights = Krumfinger: To riddere! Send dine væsner af sted med angrebsflaget (Ordrer), eller ram dem med lynet.
tut.win = Krumfinger: Godt gået, mester! Det er alt, hvad der skal til. Altså – næsten. Kampagnen fortæller resten. Jeg sætter kedlen over. tut.win = Krumfinger: Godt gået, mester! Det er alt, hvad der skal til. Altså – næsten. Kampagnen fortæller resten. Jeg sætter kedlen over.
# master talents
cat.master = Mester
talent.title = Mestertalenter
talent.points = Frie talentpoint: {0}
talent.next = {0}/{1} erfaring til næste point
talent.all = Alle talenter er lært.
talent.builder = Bygmester
talent.warlord = Krigsherre
talent.warlock = Heksemester
talent.merchant = Købmand
talent.builder.desc = Imps graver {0}% hurtigere, rum koster {1}% mindre.
talent.warlord.desc = Væsner slår {0}% hårdere og tager {1}% mindre skade.
talent.warlock.desc = Mana flyder {0}% hurtigere, besværgelser koster {1}% mindre.
talent.merchant.desc = Lønninger er {0}% lavere, afleverede skatte er {1}% mere værd.
talent.none = Endnu ikke lært.
talent.then = Næste niveau: {0}
msg.talent_point = Et nyt talentpoint! Brug det i mestervinduet.
msg.talent_learned = {0} niveau {1} lært.
+19
View File
@@ -1692,3 +1692,22 @@ tut.slap = Crookfinger: Ha! Hast du das gehört? Aus dem Osten kommt etwas. Mach
tut.obj6 = Besiege die beiden Ritter. tut.obj6 = Besiege die beiden Ritter.
tut.knights = Crookfinger: Zwei Ritter! Schick deine Kreaturen mit der Angriffsflagge (Befehle) oder triff sie mit dem Blitz. tut.knights = Crookfinger: Zwei Ritter! Schick deine Kreaturen mit der Angriffsflagge (Befehle) oder triff sie mit dem Blitz.
tut.win = Crookfinger: Gut gemacht, Meister! Mehr ist es nicht. Na ja – fast. Den Rest erzählt die Kampagne. Ich setze Wasser auf. tut.win = Crookfinger: Gut gemacht, Meister! Mehr ist es nicht. Na ja – fast. Den Rest erzählt die Kampagne. Ich setze Wasser auf.
# master talents
cat.master = Meister
talent.title = Meister-Talente
talent.points = Freie Talentpunkte: {0}
talent.next = {0}/{1} Erfahrung bis zum nächsten Punkt
talent.all = Alle Talente sind gelernt.
talent.builder = Baumeister
talent.warlord = Kriegsherr
talent.warlock = Hexer
talent.merchant = Händler
talent.builder.desc = Imps graben {0}% schneller, Räume kosten {1}% weniger.
talent.warlord.desc = Kreaturen schlagen {0}% härter zu und nehmen {1}% weniger Schaden.
talent.warlock.desc = Mana fließt {0}% schneller, Zauber kosten {1}% weniger.
talent.merchant.desc = Löhne sind {0}% niedriger, gelieferte Schätze sind {1}% mehr wert.
talent.none = Noch nicht gelernt.
talent.then = Nächste Stufe: {0}
msg.talent_point = Ein neuer Talentpunkt! Verteile ihn im Meister-Fenster.
msg.talent_learned = {0} Stufe {1} gelernt.
+19
View File
@@ -1693,3 +1693,22 @@ tut.slap = Στραβοδάχτυλος: Χα! Το άκουσες; Κάτι έ
tut.obj6 = Νίκησε τους δύο ιππότες. tut.obj6 = Νίκησε τους δύο ιππότες.
tut.knights = Στραβοδάχτυλος: Δύο ιππότες! Στείλε τα πλάσματά σου με τη σημαία επίθεσης (Εντολές) ή χτύπησέ τους με κεραυνό. tut.knights = Στραβοδάχτυλος: Δύο ιππότες! Στείλε τα πλάσματά σου με τη σημαία επίθεσης (Εντολές) ή χτύπησέ τους με κεραυνό.
tut.win = Στραβοδάχτυλος: Μπράβο, αφέντη! Αυτό ήταν όλο. Ε - σχεδόν. Τα υπόλοιπα θα τα πει η εκστρατεία. Βάζω νερό για τσάι. tut.win = Στραβοδάχτυλος: Μπράβο, αφέντη! Αυτό ήταν όλο. Ε - σχεδόν. Τα υπόλοιπα θα τα πει η εκστρατεία. Βάζω νερό για τσάι.
# master talents
cat.master = Άρχοντας
talent.title = Ταλέντα του άρχοντα
talent.points = Ελεύθεροι πόντοι ταλέντου: {0}
talent.next = {0}/{1} εμπειρία ως τον επόμενο πόντο
talent.all = Όλα τα ταλέντα έχουν μαθευτεί.
talent.builder = Χτίστης
talent.warlord = Πολέμαρχος
talent.warlock = Μαυρομάγος
talent.merchant = Έμπορος
talent.builder.desc = Τα δαιμόνια σκάβουν {0}% πιο γρήγορα, τα δωμάτια κοστίζουν {1}% λιγότερο.
talent.warlord.desc = Τα πλάσματα χτυπούν {0}% πιο δυνατά και δέχονται {1}% λιγότερη ζημιά.
talent.warlock.desc = Η μάνα ρέει {0}% πιο γρήγορα, τα ξόρκια κοστίζουν {1}% λιγότερο.
talent.merchant.desc = Οι μισθοί είναι {0}% χαμηλότεροι, ο παραδομένος θησαυρός αξίζει {1}% περισσότερο.
talent.none = Δεν έχει μαθευτεί ακόμη.
talent.then = Επόμενο επίπεδο: {0}
msg.talent_point = Νέος πόντος ταλέντου! Ξόδεψέ τον στο παράθυρο του άρχοντα.
msg.talent_learned = {0}: μαθεύτηκε το επίπεδο {1}.
+19
View File
@@ -1693,3 +1693,22 @@ tut.slap = Crookfinger: Ha! Did you hear that? Something is coming from the east
tut.obj6 = Defeat the two knights. tut.obj6 = Defeat the two knights.
tut.knights = Crookfinger: Two knights! Send your creatures with the attack flag (Orders) or strike them with lightning. tut.knights = Crookfinger: Two knights! Send your creatures with the attack flag (Orders) or strike them with lightning.
tut.win = Crookfinger: Well done, master! That is all there is to it. Well - almost. The campaign tells the rest. I'll put the kettle on. tut.win = Crookfinger: Well done, master! That is all there is to it. Well - almost. The campaign tells the rest. I'll put the kettle on.
# master talents
cat.master = Master
talent.title = Master talents
talent.points = Free talent points: {0}
talent.next = {0}/{1} experience to the next point
talent.all = Every talent is learned.
talent.builder = Builder
talent.warlord = Warlord
talent.warlock = Warlock
talent.merchant = Merchant
talent.builder.desc = Imps dig {0}% faster, rooms cost {1}% less.
talent.warlord.desc = Creatures hit {0}% harder and take {1}% less damage.
talent.warlock.desc = Mana flows {0}% faster, spells cost {1}% less.
talent.merchant.desc = Wages are {0}% lower, delivered treasure is worth {1}% more.
talent.none = Not learned yet.
talent.then = Next level: {0}
msg.talent_point = A new talent point! Spend it in the Master window.
msg.talent_learned = {0} level {1} learned.
+19
View File
@@ -1693,3 +1693,22 @@ tut.slap = Dedotuerto: ¡Ja! ¿Lo has oído? Algo viene del este. Prepárate: el
tut.obj6 = Derrota a los dos caballeros. tut.obj6 = Derrota a los dos caballeros.
tut.knights = Dedotuerto: ¡Dos caballeros! Envía a tus criaturas con la bandera de ataque (Órdenes) o fulmínalos con el rayo. tut.knights = Dedotuerto: ¡Dos caballeros! Envía a tus criaturas con la bandera de ataque (Órdenes) o fulmínalos con el rayo.
tut.win = Dedotuerto: ¡Bien hecho, amo! Eso es todo. Bueno, casi. La campaña cuenta el resto. Voy a poner el agua a hervir. tut.win = Dedotuerto: ¡Bien hecho, amo! Eso es todo. Bueno, casi. La campaña cuenta el resto. Voy a poner el agua a hervir.
# master talents
cat.master = Maestro
talent.title = Talentos del maestro
talent.points = Puntos de talento libres: {0}
talent.next = {0}/{1} de experiencia hasta el siguiente punto
talent.all = Todos los talentos están aprendidos.
talent.builder = Constructor
talent.warlord = Caudillo
talent.warlock = Brujo
talent.merchant = Mercader
talent.builder.desc = Los diablillos cavan un {0}% más rápido y las salas cuestan un {1}% menos.
talent.warlord.desc = Las criaturas golpean un {0}% más fuerte y reciben un {1}% menos de daño.
talent.warlock.desc = El maná fluye un {0}% más rápido y los hechizos cuestan un {1}% menos.
talent.merchant.desc = Los sueldos son un {0}% más bajos y el tesoro entregado vale un {1}% más.
talent.none = Aún no aprendido.
talent.then = Siguiente nivel: {0}
msg.talent_point = ¡Un nuevo punto de talento! Gástalo en la ventana del maestro.
msg.talent_learned = {0}: nivel {1} aprendido.
+19
View File
@@ -1693,3 +1693,22 @@ tut.slap = Crookfinger: Ha! Kuulitko? Idästä tulee jotain. Valmistaudu: salama
tut.obj6 = Kukista kaksi ritaria. tut.obj6 = Kukista kaksi ritaria.
tut.knights = Crookfinger: Kaksi ritaria! Lähetä olentosi hyökkäyslipulla (Käskyt) tai iske heitä salamalla. tut.knights = Crookfinger: Kaksi ritaria! Lähetä olentosi hyökkäyslipulla (Käskyt) tai iske heitä salamalla.
tut.win = Crookfinger: Hyvin tehty, mestari! Siinä kaikki. No - melkein. Kampanja kertoo loput. Panen teeveden kiehumaan. tut.win = Crookfinger: Hyvin tehty, mestari! Siinä kaikki. No - melkein. Kampanja kertoo loput. Panen teeveden kiehumaan.
# master talents
cat.master = Mestari
talent.title = Mestarin kyvyt
talent.points = Vapaita kykypisteitä: {0}
talent.next = {0}/{1} kokemusta seuraavaan pisteeseen
talent.all = Kaikki kyvyt on opittu.
talent.builder = Rakentaja
talent.warlord = Sotaherra
talent.warlock = Noita
talent.merchant = Kauppias
talent.builder.desc = Pirut kaivavat {0}% nopeammin, huoneet maksavat {1}% vähemmän.
talent.warlord.desc = Olennot lyövät {0}% kovempaa ja ottavat {1}% vähemmän vahinkoa.
talent.warlock.desc = Mana virtaa {0}% nopeammin, loitsut maksavat {1}% vähemmän.
talent.merchant.desc = Palkat ovat {0}% pienemmät, toimitettu aarre on {1}% arvokkaampi.
talent.none = Ei vielä opittu.
talent.then = Seuraava taso: {0}
msg.talent_point = Uusi kykypiste! Käytä se mestarin ikkunassa.
msg.talent_learned = {0}: taso {1} opittu.
+19
View File
@@ -1693,3 +1693,22 @@ tut.slap = Krivoprst: Ha! Jesi li čuo? Nešto dolazi s istoka. Pripremi se: mun
tut.obj6 = Porazi dva viteza. tut.obj6 = Porazi dva viteza.
tut.knights = Krivoprst: Dva viteza! Pošalji stvorenja zastavom napada (Naredbe) ili ih udari munjom. tut.knights = Krivoprst: Dva viteza! Pošalji stvorenja zastavom napada (Naredbe) ili ih udari munjom.
tut.win = Krivoprst: Bravo, gospodaru! To je sve. Pa - skoro. Ostatak će ispričati kampanja. Idem staviti vodu za čaj. tut.win = Krivoprst: Bravo, gospodaru! To je sve. Pa - skoro. Ostatak će ispričati kampanja. Idem staviti vodu za čaj.
# master talents
cat.master = Gospodar
talent.title = Gospodarevi talenti
talent.points = Slobodni bodovi talenta: {0}
talent.next = {0}/{1} iskustva do sljedećeg boda
talent.all = Svi talenti su naučeni.
talent.builder = Graditelj
talent.warlord = Vojskovođa
talent.warlock = Vještac
talent.merchant = Trgovac
talent.builder.desc = Vražići kopaju {0}% brže, sobe koštaju {1}% manje.
talent.warlord.desc = Stvorenja udaraju {0}% jače i primaju {1}% manje štete.
talent.warlock.desc = Mana teče {0}% brže, čarolije koštaju {1}% manje.
talent.merchant.desc = Plaće su {0}% niže, dostavljeno blago vrijedi {1}% više.
talent.none = Još nije naučeno.
talent.then = Sljedeća razina: {0}
msg.talent_point = Novi bod talenta! Potroši ga u prozoru gospodara.
msg.talent_learned = {0}: naučena razina {1}.
+19
View File
@@ -1693,3 +1693,22 @@ tut.slap = Crookfinger: Ha! Hallottad? Valami jön keletről. Készülj: a vill
tut.obj6 = Győzd le a két lovagot. tut.obj6 = Győzd le a két lovagot.
tut.knights = Crookfinger: Két lovag! Küldd rájuk a lényeidet a támadózászlóval (Parancs), vagy sújtsd őket villámmal. tut.knights = Crookfinger: Két lovag! Küldd rájuk a lényeidet a támadózászlóval (Parancs), vagy sújtsd őket villámmal.
tut.win = Crookfinger: Szép munka, mester! Ennyi az egész. Nos - majdnem. A többit elmondja a hadjárat. Felteszem a teavizet. tut.win = Crookfinger: Szép munka, mester! Ennyi az egész. Nos - majdnem. A többit elmondja a hadjárat. Felteszem a teavizet.
# master talents
cat.master = Mester
talent.title = Mesteri tehetségek
talent.points = Szabad tehetségpontok: {0}
talent.next = {0}/{1} tapasztalat a következő pontig
talent.all = Minden tehetség megtanulva.
talent.builder = Építész
talent.warlord = Hadúr
talent.warlock = Boszorkánymester
talent.merchant = Kereskedő
talent.builder.desc = A manók {0}%-kal gyorsabban ásnak, a szobák {1}%-kal olcsóbbak.
talent.warlord.desc = A lények {0}%-kal erősebben ütnek, és {1}%-kal kevesebb sebzést kapnak.
talent.warlock.desc = A mana {0}%-kal gyorsabban áramlik, a varázslatok {1}%-kal olcsóbbak.
talent.merchant.desc = A bérek {0}%-kal alacsonyabbak, a leadott kincs {1}%-kal többet ér.
talent.none = Még nincs megtanulva.
talent.then = Következő szint: {0}
msg.talent_point = Új tehetségpont! Költsd el a mester ablakában.
msg.talent_learned = {0}: {1}. szint megtanulva.
+19
View File
@@ -1693,3 +1693,22 @@ tut.slap = Ditastorte: Ah! Hai sentito? Qualcosa arriva da est. Preparati: il fu
tut.obj6 = Sconfiggi i due cavalieri. tut.obj6 = Sconfiggi i due cavalieri.
tut.knights = Ditastorte: Due cavalieri! Manda le tue creature con la bandiera d'attacco (Ordini) o colpiscili con il fulmine. tut.knights = Ditastorte: Due cavalieri! Manda le tue creature con la bandiera d'attacco (Ordini) o colpiscili con il fulmine.
tut.win = Ditastorte: Ben fatto, padrone! Tutto qui. Be', quasi. Il resto lo racconta la campagna. Metto su il bollitore. tut.win = Ditastorte: Ben fatto, padrone! Tutto qui. Be', quasi. Il resto lo racconta la campagna. Metto su il bollitore.
# master talents
cat.master = Maestro
talent.title = Talenti del maestro
talent.points = Punti talento liberi: {0}
talent.next = {0}/{1} esperienza al prossimo punto
talent.all = Tutti i talenti sono appresi.
talent.builder = Costruttore
talent.warlord = Condottiero
talent.warlock = Stregone
talent.merchant = Mercante
talent.builder.desc = Gli Imp scavano il {0}% più in fretta, le stanze costano il {1}% in meno.
talent.warlord.desc = Le creature colpiscono il {0}% più forte e subiscono il {1}% di danni in meno.
talent.warlock.desc = Il mana scorre il {0}% più in fretta, le magie costano il {1}% in meno.
talent.merchant.desc = I salari sono il {0}% più bassi, il tesoro consegnato vale il {1}% in più.
talent.none = Non ancora appreso.
talent.then = Livello successivo: {0}
msg.talent_point = Un nuovo punto talento! Spendilo nella finestra del maestro.
msg.talent_learned = {0}: livello {1} appreso.
+19
View File
@@ -1693,3 +1693,22 @@ tut.slap = Krommevinger: Ha! Hoorde je dat? Er komt iets uit het oosten. Maak je
tut.obj6 = Versla de twee ridders. tut.obj6 = Versla de twee ridders.
tut.knights = Krommevinger: Twee ridders! Stuur je wezens met de aanvalsvlag (Bevelen) of tref ze met de bliksem. tut.knights = Krommevinger: Twee ridders! Stuur je wezens met de aanvalsvlag (Bevelen) of tref ze met de bliksem.
tut.win = Krommevinger: Goed gedaan, meester! Meer is het niet. Nou ja, bijna. De veldtocht vertelt de rest. Ik zet water op. tut.win = Krommevinger: Goed gedaan, meester! Meer is het niet. Nou ja, bijna. De veldtocht vertelt de rest. Ik zet water op.
# master talents
cat.master = Meester
talent.title = Meestertalenten
talent.points = Vrije talentpunten: {0}
talent.next = {0}/{1} ervaring tot het volgende punt
talent.all = Alle talenten zijn geleerd.
talent.builder = Bouwmeester
talent.warlord = Krijgsheer
talent.warlock = Heksenmeester
talent.merchant = Koopman
talent.builder.desc = Imps graven {0}% sneller, kamers kosten {1}% minder.
talent.warlord.desc = Wezens slaan {0}% harder en krijgen {1}% minder schade.
talent.warlock.desc = Mana stroomt {0}% sneller, spreuken kosten {1}% minder.
talent.merchant.desc = Lonen zijn {0}% lager, afgeleverde schatten zijn {1}% meer waard.
talent.none = Nog niet geleerd.
talent.then = Volgend niveau: {0}
msg.talent_point = Een nieuw talentpunt! Besteed het in het meestervenster.
msg.talent_learned = {0} niveau {1} geleerd.
+19
View File
@@ -1692,3 +1692,22 @@ tut.slap = Krokfinger: Ha! Hørte du det? Noe kommer fra øst. Gjør deg klar: l
tut.obj6 = Beseir de to ridderne. tut.obj6 = Beseir de to ridderne.
tut.knights = Krokfinger: To riddere! Send skapningene dine med angrepsflagget (Ordrer), eller slå dem med lynet. tut.knights = Krokfinger: To riddere! Send skapningene dine med angrepsflagget (Ordrer), eller slå dem med lynet.
tut.win = Krokfinger: Godt gjort, mester! Det var det hele. Vel – nesten. Kampanjen forteller resten. Jeg setter på kjelen. tut.win = Krokfinger: Godt gjort, mester! Det var det hele. Vel – nesten. Kampanjen forteller resten. Jeg setter på kjelen.
# master talents
cat.master = Mester
talent.title = Mestertalenter
talent.points = Ledige talentpoeng: {0}
talent.next = {0}/{1} erfaring til neste poeng
talent.all = Alle talenter er lært.
talent.builder = Byggmester
talent.warlord = Krigsherre
talent.warlock = Heksemester
talent.merchant = Kjøpmann
talent.builder.desc = Imper graver {0}% raskere, rom koster {1}% mindre.
talent.warlord.desc = Skapninger slår {0}% hardere og tar {1}% mindre skade.
talent.warlock.desc = Mana flyter {0}% raskere, formler koster {1}% mindre.
talent.merchant.desc = Lønningene er {0}% lavere, levert skatt er verdt {1}% mer.
talent.none = Ikke lært ennå.
talent.then = Neste nivå: {0}
msg.talent_point = Et nytt talentpoeng! Bruk det i mestervinduet.
msg.talent_learned = {0} nivå {1} lært.
+19
View File
@@ -1693,3 +1693,22 @@ tut.slap = Dedotorto: Ah! Ouviste aquilo? Vem aí qualquer coisa do leste. Prepa
tut.obj6 = Derrota os dois cavaleiros. tut.obj6 = Derrota os dois cavaleiros.
tut.knights = Dedotorto: Dois cavaleiros! Envia as tuas criaturas com a bandeira de ataque (Ordens) ou atinge-os com o relâmpago. tut.knights = Dedotorto: Dois cavaleiros! Envia as tuas criaturas com a bandeira de ataque (Ordens) ou atinge-os com o relâmpago.
tut.win = Dedotorto: Muito bem, mestre! É tudo. Bem, quase. A campanha conta o resto. Vou pôr a chaleira ao lume. tut.win = Dedotorto: Muito bem, mestre! É tudo. Bem, quase. A campanha conta o resto. Vou pôr a chaleira ao lume.
# master talents
cat.master = Mestre
talent.title = Talentos do mestre
talent.points = Pontos de talento livres: {0}
talent.next = {0}/{1} de experiência até ao próximo ponto
talent.all = Todos os talentos foram aprendidos.
talent.builder = Construtor
talent.warlord = Senhor da Guerra
talent.warlock = Bruxo
talent.merchant = Mercador
talent.builder.desc = Os diabretes cavam {0}% mais depressa e as salas custam {1}% menos.
talent.warlord.desc = As criaturas batem {0}% mais forte e sofrem {1}% menos dano.
talent.warlock.desc = A mana flui {0}% mais depressa e os feitiços custam {1}% menos.
talent.merchant.desc = Os salários são {0}% mais baixos e o tesouro entregue vale {1}% mais.
talent.none = Ainda não aprendido.
talent.then = Próximo nível: {0}
msg.talent_point = Um novo ponto de talento! Gasta-o na janela do mestre.
msg.talent_learned = {0}: nível {1} aprendido.
+19
View File
@@ -1693,3 +1693,22 @@ tut.slap = Crookfinger: Ha! Ai auzit? Ceva vine dinspre est. Pregătește-te: fu
tut.obj6 = Învinge cei doi cavaleri. tut.obj6 = Învinge cei doi cavaleri.
tut.knights = Crookfinger: Doi cavaleri! Trimite-ți creaturile cu steagul de atac (Ordine) sau lovește-i cu fulgerul. tut.knights = Crookfinger: Doi cavaleri! Trimite-ți creaturile cu steagul de atac (Ordine) sau lovește-i cu fulgerul.
tut.win = Crookfinger: Bravo, stăpâne! Asta e tot. Ei - aproape. Campania îți spune restul. Pun de un ceai. tut.win = Crookfinger: Bravo, stăpâne! Asta e tot. Ei - aproape. Campania îți spune restul. Pun de un ceai.
# master talents
cat.master = Stăpân
talent.title = Talentele stăpânului
talent.points = Puncte de talent libere: {0}
talent.next = {0}/{1} experiență până la următorul punct
talent.all = Toate talentele sunt învățate.
talent.builder = Constructor
talent.warlord = Căpetenie
talent.warlock = Vrăjitor
talent.merchant = Negustor
talent.builder.desc = Drăcușorii sapă cu {0}% mai repede, camerele costă cu {1}% mai puțin.
talent.warlord.desc = Creaturile lovesc cu {0}% mai tare și primesc cu {1}% mai puține daune.
talent.warlock.desc = Mana curge cu {0}% mai repede, vrăjile costă cu {1}% mai puțin.
talent.merchant.desc = Simbriile sunt cu {0}% mai mici, comorile livrate valorează cu {1}% mai mult.
talent.none = Încă neînvățat.
talent.then = Nivelul următor: {0}
msg.talent_point = Un nou punct de talent! Folosește-l în fereastra stăpânului.
msg.talent_learned = {0}: nivelul {1} învățat.
+19
View File
@@ -1693,3 +1693,22 @@ tut.slap = Кривопал: Ха! Слышал? С востока что-то
tut.obj6 = Победи двух рыцарей. tut.obj6 = Победи двух рыцарей.
tut.knights = Кривопал: Два рыцаря! Пошли своих существ флагом атаки (Приказы) или порази их молнией. tut.knights = Кривопал: Два рыцаря! Пошли своих существ флагом атаки (Приказы) или порази их молнией.
tut.win = Кривопал: Молодец, хозяин! Вот и всё. Ну — почти. Остальное расскажет кампания. Пойду поставлю чайник. tut.win = Кривопал: Молодец, хозяин! Вот и всё. Ну — почти. Остальное расскажет кампания. Пойду поставлю чайник.
# master talents
cat.master = Мастер
talent.title = Таланты мастера
talent.points = Свободные очки талантов: {0}
talent.next = {0}/{1} опыта до следующего очка
talent.all = Все таланты изучены.
talent.builder = Строитель
talent.warlord = Полководец
talent.warlock = Чернокнижник
talent.merchant = Торговец
talent.builder.desc = Бесята копают на {0}% быстрее, комнаты стоят на {1}% меньше.
talent.warlord.desc = Существа бьют на {0}% сильнее и получают на {1}% меньше урона.
talent.warlock.desc = Мана течёт на {0}% быстрее, чары стоят на {1}% меньше.
talent.merchant.desc = Жалованье на {0}% ниже, доставленные сокровища ценятся на {1}% выше.
talent.none = Ещё не изучено.
talent.then = Следующий уровень: {0}
msg.talent_point = Новое очко таланта! Потрать его в окне мастера.
msg.talent_learned = {0}: изучен уровень {1}.
+19
View File
@@ -1693,3 +1693,22 @@ tut.slap = Krivoprst: Ha! Počul si? Niečo prichádza z východu. Priprav sa: b
tut.obj6 = Poraz dvoch rytierov. tut.obj6 = Poraz dvoch rytierov.
tut.knights = Krivoprst: Dvaja rytieri! Pošli na nich tvory útočnou vlajkou (Rozkazy) alebo ich zasiahni bleskom. tut.knights = Krivoprst: Dvaja rytieri! Pošli na nich tvory útočnou vlajkou (Rozkazy) alebo ich zasiahni bleskom.
tut.win = Krivoprst: Výborne, pane! To je všetko. No - takmer. Zvyšok povie kampaň. Pristavím vodu na čaj. tut.win = Krivoprst: Výborne, pane! To je všetko. No - takmer. Zvyšok povie kampaň. Pristavím vodu na čaj.
# master talents
cat.master = Pán
talent.title = Pánove talenty
talent.points = Voľné body talentov: {0}
talent.next = {0}/{1} skúseností do ďalšieho bodu
talent.all = Všetky talenty sú naučené.
talent.builder = Staviteľ
talent.warlord = Vojvodca
talent.warlock = Černokňažník
talent.merchant = Obchodník
talent.builder.desc = Čertíci kopú o {0}% rýchlejšie, miestnosti stoja o {1}% menej.
talent.warlord.desc = Tvory udierajú o {0}% silnejšie a dostávajú o {1}% menej poškodenia.
talent.warlock.desc = Mana prúdi o {0}% rýchlejšie, kúzla stoja o {1}% menej.
talent.merchant.desc = Mzdy sú o {0}% nižšie, doručené poklady majú o {1}% vyššiu hodnotu.
talent.none = Zatiaľ nenaučené.
talent.then = Ďalšia úroveň: {0}
msg.talent_point = Nový bod talentu! Použi ho v okne pána.
msg.talent_learned = {0}: úroveň {1} naučená.
+19
View File
@@ -1693,3 +1693,22 @@ tut.slap = Кривопрст: Ха! Јеси ли чуо? Нешто долаз
tut.obj6 = Порази два витеза. tut.obj6 = Порази два витеза.
tut.knights = Кривопрст: Два витеза! Пошаљи створења заставом напада (Наредбе) или их удари муњом. tut.knights = Кривопрст: Два витеза! Пошаљи створења заставом напада (Наредбе) или их удари муњом.
tut.win = Кривопрст: Браво, господару! То је све. Па - скоро. Остатак ће испричати кампања. Идем да ставим воду за чај. tut.win = Кривопрст: Браво, господару! То је све. Па - скоро. Остатак ће испричати кампања. Идем да ставим воду за чај.
# master talents
cat.master = Господар
talent.title = Господареви таленти
talent.points = Слободни бодови талента: {0}
talent.next = {0}/{1} искуства до следећег бода
talent.all = Сви таленти су научени.
talent.builder = Градитељ
talent.warlord = Војсковођа
talent.warlock = Вештац
talent.merchant = Трговац
talent.builder.desc = Ђаволци копају {0}% брже, собе коштају {1}% мање.
talent.warlord.desc = Створења ударају {0}% јаче и примају {1}% мање штете.
talent.warlock.desc = Мана тече {0}% брже, чини коштају {1}% мање.
talent.merchant.desc = Плате су {0}% ниже, достављено благо вреди {1}% више.
talent.none = Још није научено.
talent.then = Следећи ниво: {0}
msg.talent_point = Нови бод талента! Потроши га у прозору господара.
msg.talent_learned = {0}: научен ниво {1}.
+19
View File
@@ -1693,3 +1693,22 @@ tut.slap = Crookfinger: Ha! Hörde du det? Något kommer från öster. Gör dig
tut.obj6 = Besegra de två riddarna. tut.obj6 = Besegra de två riddarna.
tut.knights = Crookfinger: Två riddare! Skicka dina varelser med anfallsflaggan (Order) eller träffa dem med blixten. tut.knights = Crookfinger: Två riddare! Skicka dina varelser med anfallsflaggan (Order) eller träffa dem med blixten.
tut.win = Crookfinger: Bra gjort, mästare! Det är allt. Nåja – nästan. Kampanjen berättar resten. Jag sätter på tevattnet. tut.win = Crookfinger: Bra gjort, mästare! Det är allt. Nåja – nästan. Kampanjen berättar resten. Jag sätter på tevattnet.
# master talents
cat.master = Mästare
talent.title = Mästartalanger
talent.points = Lediga talangpoäng: {0}
talent.next = {0}/{1} erfarenhet till nästa poäng
talent.all = Alla talanger är inlärda.
talent.builder = Byggmästare
talent.warlord = Krigsherre
talent.warlock = Häxmästare
talent.merchant = Köpman
talent.builder.desc = Hobar gräver {0}% snabbare, rum kostar {1}% mindre.
talent.warlord.desc = Varelser slår {0}% hårdare och tar {1}% mindre skada.
talent.warlock.desc = Mana flödar {0}% snabbare, besvärjelser kostar {1}% mindre.
talent.merchant.desc = Lönerna är {0}% lägre, levererade skatter är värda {1}% mer.
talent.none = Inte inlärd än.
talent.then = Nästa nivå: {0}
msg.talent_point = En ny talangpoäng! Använd den i mästarfönstret.
msg.talent_learned = {0} nivå {1} inlärd.
+1
View File
@@ -54,6 +54,7 @@ impl Game {
self.ai_rooms(cx, cy); self.ai_rooms(cx, cy);
self.ai_towers(d, cx, cy); self.ai_towers(d, cx, cy);
self.ai_fight(k, d, side, cx, cy); self.ai_fight(k, d, side, cx, cy);
self.ai_talents();
} }
// the tiles an AI master may mark: within MARK_REACH tiles of its own ground // the tiles an AI master may mark: within MARK_REACH tiles of its own ground
+6 -2
View File
@@ -84,6 +84,8 @@ pub struct Carry {
pub rune_free: u32, // unbuilt rune forge towers from chests pub rune_free: u32, // unbuilt rune forge towers from chests
#[serde(default)] #[serde(default)]
pub rune_final: u32, // unbuilt or sold rune forge towers that were already given back once pub rune_final: u32, // unbuilt or sold rune forge towers that were already given back once
#[serde(default)]
pub talents: crate::talents::Talents, // the master's talents go along from mission to mission
} }
pub const CARRY_CREATURES: usize = 5; pub const CARRY_CREATURES: usize = 5;
@@ -580,6 +582,7 @@ fn start_mission_def(d: &MissionDef, i: usize, carry: Option<&Carry>, coop: bool
g.gold += c.gold; g.gold += c.gold;
g.rune_free += c.rune_free; g.rune_free += c.rune_free;
g.rune_final += c.rune_final; g.rune_final += c.rune_final;
g.talents = c.talents.clone();
} }
for (k, &(kind, level, xp)) in joiners.iter().enumerate() { for (k, &(kind, level, xp)) in joiners.iter().enumerate() {
let a = k as f32 * 0.9; let a = k as f32 * 0.9;
@@ -796,7 +799,7 @@ pub fn carry_from(g: &Game) -> Carry {
let mut c: Vec<&Ent> = g.ents.iter().filter(|e| e.alive && e.creature()).collect(); let mut c: Vec<&Ent> = g.ents.iter().filter(|e| e.alive && e.creature()).collect();
let power = |e: &Ent| e.max_hp * e.atk() / st(e.kind).interval; let power = |e: &Ent| e.max_hp * e.atk() / st(e.kind).interval;
c.sort_by(|a, b| power(b).partial_cmp(&power(a)).unwrap_or(std::cmp::Ordering::Equal)); c.sort_by(|a, b| power(b).partial_cmp(&power(a)).unwrap_or(std::cmp::Ordering::Equal));
Carry { creatures: c.iter().take(CARRY_CREATURES).map(|e| (e.kind, e.level, e.xp)).collect(), gold: (g.gold * CARRY_GOLD).floor(), rune_free: g.rune_free, rune_final: g.rune_final + g.rune_next } Carry { creatures: c.iter().take(CARRY_CREATURES).map(|e| (e.kind, e.level, e.xp)).collect(), gold: (g.gold * CARRY_GOLD).floor(), rune_free: g.rune_free, rune_final: g.rune_final + g.rune_next, talents: g.talents.clone() }
} }
// a co-op match of an editor campaign: what every master takes into the next mission - his // a co-op match of an editor campaign: what every master takes into the next mission - his
@@ -810,7 +813,7 @@ pub fn coop_carry_from(g: &Game) -> Vec<(u8, Carry)> {
let mut c: Vec<&Ent> = g.ents.iter().filter(|e| e.alive && !e.hero && e.side == side && e.kind != EK::Worker && !e.chained && e.captive == 0).collect(); let mut c: Vec<&Ent> = g.ents.iter().filter(|e| e.alive && !e.hero && e.side == side && e.kind != EK::Worker && !e.chained && e.captive == 0).collect();
c.sort_by(|a, b| power(b).partial_cmp(&power(a)).unwrap_or(std::cmp::Ordering::Equal).then(a.id.cmp(&b.id))); c.sort_by(|a, b| power(b).partial_cmp(&power(a)).unwrap_or(std::cmp::Ordering::Equal).then(a.id.cmp(&b.id)));
let creatures = c.iter().take(CARRY_CREATURES).map(|e| (e.kind, e.level, e.xp)).collect(); let creatures = c.iter().take(CARRY_CREATURES).map(|e| (e.kind, e.level, e.xp)).collect();
(side, Carry { creatures, gold: (g.gold_of(side).max(0.0) * CARRY_GOLD).floor(), rune_free: 0, rune_final: 0 }) (side, Carry { creatures, gold: (g.gold_of(side).max(0.0) * CARRY_GOLD).floor(), rune_free: 0, rune_final: 0, talents: g.talents_of(side) })
}) })
.collect() .collect()
} }
@@ -824,6 +827,7 @@ pub fn apply_coop_carry(g: &mut Game, carry: &[(u8, Carry)]) {
} }
g.switch_to(*side); g.switch_to(*side);
g.gold += c.gold.max(0.0); g.gold += c.gold.max(0.0);
g.talents = c.talents.clone();
let (cx, cy) = g.map.core; let (cx, cy) = g.map.core;
for (k, &(kind, level, xp)) in c.creatures.iter().take(CARRY_CREATURES).enumerate() { for (k, &(kind, level, xp)) in c.creatures.iter().take(CARRY_CREATURES).enumerate() {
let a = k as f32 * 1.1; let a = k as f32 * 1.1;
+6 -1
View File
@@ -101,6 +101,10 @@ pub enum Act {
to: u8, to: u8,
amount: u32, amount: u32,
}, },
// spend a talent point on a branch (talents.rs)
Talent {
branch: u8,
},
} }
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
@@ -148,7 +152,7 @@ impl Game {
fn mark_command(&mut self, act: Act, side: u8) { fn mark_command(&mut self, act: Act, side: u8) {
let ((x, y), kind, dur) = match act { let ((x, y), kind, dur) = match act {
Act::SendGold { .. } => return, // nothing to point at on the map Act::SendGold { .. } | Act::Talent { .. } => return, // nothing to point at on the map
Act::Dig(r) => (rect_center(r), MARK_DIG, 4.0), Act::Dig(r) => (rect_center(r), MARK_DIG, 4.0),
Act::Room { r, .. } => (rect_center(r), MARK_ROOM, 5.0), Act::Room { r, .. } => (rect_center(r), MARK_ROOM, 5.0),
Act::Sell(r) => (rect_center(r), MARK_SELL, 4.0), Act::Sell(r) => (rect_center(r), MARK_SELL, 4.0),
@@ -227,6 +231,7 @@ impl Game {
Act::CancelOrders => self.cancel_orders(), Act::CancelOrders => self.cancel_orders(),
Act::Unmark { x, y } => self.unmark(x, y), Act::Unmark { x, y } => self.unmark(x, y),
Act::SendGold { to, amount } => self.send_gold(to, amount), Act::SendGold { to, amount } => self.send_gold(to, amount),
Act::Talent { branch } => self.learn_talent(branch as usize),
} }
} }
} }
+29 -14
View File
@@ -523,6 +523,8 @@ pub struct MasterState {
pub rune_next: u32, pub rune_next: u32,
pub portal_timer: f32, pub portal_timer: f32,
pub punch_ready: f32, pub punch_ready: f32,
#[serde(default)]
pub talents: crate::talents::Talents,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
@@ -640,6 +642,8 @@ pub struct Game {
#[serde(default)] #[serde(default)]
pub snares: Vec<usize>, // tiles with a ranger's snare (rogues.rs) pub snares: Vec<usize>, // tiles with a ranger's snare (rogues.rs)
#[serde(default)] #[serde(default)]
pub talents: crate::talents::Talents, // of the master being processed (talents.rs)
#[serde(default)]
pub guard_marks: Vec<usize>, // guard markers (shields) of the master being processed (orders.rs) pub guard_marks: Vec<usize>, // guard markers (shields) of the master being processed (orders.rs)
#[serde(default)] #[serde(default)]
pub attack_marks: Vec<usize>, // attack markers (swords) pub attack_marks: Vec<usize>, // attack markers (swords)
@@ -990,6 +994,7 @@ impl Game {
trading: Default::default(), trading: Default::default(),
happenings: None, happenings: None,
snares: Vec::new(), snares: Vec::new(),
talents: Default::default(),
guard_marks: Vec::new(), guard_marks: Vec::new(),
attack_marks: Vec::new(), attack_marks: Vec::new(),
marks_t: 0.0, marks_t: 0.0,
@@ -1218,7 +1223,7 @@ impl Game {
// what the next payday costs: wages of all creatures plus the training bill // what the next payday costs: wages of all creatures plus the training bill
pub fn next_payday_cost(&self) -> f32 { pub fn next_payday_cost(&self) -> f32 {
let wages: f32 = self.ents.iter().filter(|e| e.alive && e.creature()).map(|e| st(e.kind).wage as f32 * (1.0 + 0.1 * (e.level - 1) as f32)).sum(); let wages: f32 = self.ents.iter().filter(|e| e.alive && e.creature()).map(|e| st(e.kind).wage as f32 * (1.0 + 0.1 * (e.level - 1) as f32)).sum::<f32>() * self.talents.wages();
wages + self.training_bill wages + self.training_bill
} }
@@ -1514,6 +1519,7 @@ impl Game {
rune_next: self.rune_next, rune_next: self.rune_next,
portal_timer: self.portal_timer, portal_timer: self.portal_timer,
punch_ready: self.punch_ready, punch_ready: self.punch_ready,
talents: self.talents.clone(),
} }
} }
@@ -1549,6 +1555,7 @@ impl Game {
self.rune_next = m.rune_next; self.rune_next = m.rune_next;
self.portal_timer = m.portal_timer; self.portal_timer = m.portal_timer;
self.punch_ready = m.punch_ready; self.punch_ready = m.punch_ready;
self.talents = m.talents;
} }
// make `side` the master being processed (no-op for sides that are no masters) // make `side` the master being processed (no-op for sides that are no masters)
@@ -1734,7 +1741,7 @@ impl Game {
// a master's household: mana, core, portals, payday // a master's household: mana, core, portals, payday
fn economy(&mut self, dt: f32) { fn economy(&mut self, dt: f32) {
self.recount(); self.recount();
self.mana = (self.mana + (1.0 + SPRING_MANA * self.springs as f32) * dt).min(self.mana_max()); self.mana = (self.mana + (1.0 + SPRING_MANA * self.springs as f32) * self.talents.mana_rate() * dt).min(self.mana_max());
self.core_hp = (self.core_hp + 0.5 * dt).min(CORE_HP); self.core_hp = (self.core_hp + 0.5 * dt).min(CORE_HP);
let owned = self.portals_owned(); let owned = self.portals_owned();
@@ -2081,13 +2088,14 @@ impl Game {
self.training_bill = 0.0; self.training_bill = 0.0;
let mut n = 0; let mut n = 0;
let mut total = bill; let mut total = bill;
let merchant = self.talents.wages();
for i in 0..self.ents.len() { for i in 0..self.ents.len() {
if !self.ents[i].alive || !self.ents[i].creature() { if !self.ents[i].alive || !self.ents[i].creature() {
continue; continue;
} }
let r = self.rng.f(); let r = self.rng.f();
let e = &mut self.ents[i]; let e = &mut self.ents[i];
let w = st(e.kind).wage as f32 * (1.0 + 0.1 * (e.level - 1) as f32); let w = st(e.kind).wage as f32 * (1.0 + 0.1 * (e.level - 1) as f32) * merchant;
e.pay_due += w; e.pay_due += w;
e.pay_t = r * 55.0; e.pay_t = r * 55.0;
n += 1; n += 1;
@@ -2761,7 +2769,9 @@ impl Game {
} }
fn hit(&mut self, e: &mut Ent, j: usize) { fn hit(&mut self, e: &mut Ent, j: usize) {
let dmg = e.atk() * (0.85 + self.rng.f() * 0.3); // 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 dmg = e.atk() * (0.85 + self.rng.f() * 0.3) * boost;
let (tx, ty) = (self.ents[j].x, self.ents[j].y); let (tx, ty) = (self.ents[j].x, self.ents[j].y);
let s = st(e.kind); let s = st(e.kind);
// the higher the level, the bigger the attack looks (up to almost twice at Lv 10) // the higher the level, the bigger the attack looks (up to almost twice at Lv 10)
@@ -2870,8 +2880,12 @@ impl Game {
} }
let my_id = e.id; let my_id = e.id;
e.invis = 0.0; // attacking breaks invisibility e.invis = 0.0; // attacking breaks invisibility
let armour = {
let t = &self.ents[j];
if !t.hero && Owner::MASTER_SIDES.contains(&t.side) { self.talents_of(t.side).armour() } else { 1.0 }
};
let t = &mut self.ents[j]; let t = &mut self.ents[j];
t.hp -= if t.shield > 0.0 { dmg * 0.5 } else { dmg }; t.hp -= if t.shield > 0.0 { dmg * 0.5 } else { dmg } * armour;
t.flash = 0.15; t.flash = 0.15;
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);
@@ -2946,6 +2960,7 @@ impl Game {
} }
if hero { if hero {
let b = s.bounty as f32 + if kind == EK::Thief { self.thief_loot(id) } else { 0.0 }; let b = s.bounty as f32 + if kind == EK::Thief { self.thief_loot(id) } else { 0.0 };
self.add_master_xp(s.bounty as f32 * crate::talents::HERO_XP);
let got = self.add_gold(b); let got = self.add_gold(b);
self.fx(5, x, y, 0.0, 0.0, 1.0, 0xff30d0ff); self.fx(5, x, y, 0.0, 0.0, 1.0, 0xff30d0ff);
self.sfx(Sfx::HeroDeath, x, y); self.sfx(Sfx::HeroDeath, x, y);
@@ -3495,7 +3510,7 @@ impl Game {
e.face = sgn(tx as f32 + 0.5 - e.x); e.face = sgn(tx as f32 + 0.5 - e.x);
e.working = true; e.working = true;
let skilled = if crate::personality::knows(e, "dig") { 1.25 } else { 1.0 }; let skilled = if crate::personality::knows(e, "dig") { 1.25 } else { 1.0 };
e.timer += dt * self.help_speed(t) * skilled; e.timer += dt * self.help_speed(t) * skilled * self.talents.dig_speed();
if e.timer >= 0.45 { if e.timer >= 0.45 {
e.timer = 0.0; e.timer = 0.0;
let fxx = (e.x + tx as f32 + 0.5) * 0.5; let fxx = (e.x + tx as f32 + 0.5) * 0.5;
@@ -3655,7 +3670,7 @@ impl Game {
} }
Job::Deliver => { Job::Deliver => {
if self.is_deposit(cur) { if self.is_deposit(cur) {
let amount = e.carry as f32; let amount = e.carry as f32 * self.talents.treasure();
let got = self.add_res_at(amount, e.carry_res, Some(cur)); let got = self.add_res_at(amount, e.carry_res, Some(cur));
if got < amount { if got < amount {
self.hint(3, "hint.treasury_full"); self.hint(3, "hint.treasury_full");
@@ -4939,7 +4954,7 @@ impl Game {
pub fn build_rect(&mut self, room: usize, x0: i32, y0: i32, x1: i32, y1: i32) { pub fn build_rect(&mut self, room: usize, x0: i32, y0: i32, x1: i32, y1: i32) {
let kind = ROOM_KINDS[room]; let kind = ROOM_KINDS[room];
let cost = ROOM_COST[room] as f32; let cost = (ROOM_COST[room] as f32 * self.talents.room_cost()).round();
let mut built = 0; let mut built = 0;
let mut broke = false; let mut broke = false;
for y in y0.min(y1)..=y0.max(y1) { for y in y0.min(y1)..=y0.max(y1) {
@@ -5693,13 +5708,13 @@ impl Game {
} }
pub fn lightning(&mut self, wx: f32, wy: f32) { pub fn lightning(&mut self, wx: f32, wy: f32) {
const COST: f32 = 50.0; let cost = 50.0 * self.talents.spell_cost();
if self.mana < COST { if self.mana < cost {
self.msg("msg.no_mana_bolt", vec![]); self.msg("msg.no_mana_bolt", vec![]);
self.sfx_ui(Sfx::Error); self.sfx_ui(Sfx::Error);
return; return;
} }
self.mana -= COST; self.mana -= cost;
self.count(crate::stats::SPELLS, 1.0); self.count(crate::stats::SPELLS, 1.0);
self.sfx(Sfx::Lightning, wx, wy); self.sfx(Sfx::Lightning, wx, wy);
self.fx(1, wx, wy, 0.0, 0.0, 0.5, 0xffffeeaa); self.fx(1, wx, wy, 0.0, 0.0, 0.5, 0xffffeeaa);
@@ -5720,13 +5735,13 @@ impl Game {
} }
pub fn heal(&mut self, wx: f32, wy: f32) { pub fn heal(&mut self, wx: f32, wy: f32) {
const COST: f32 = 60.0; let cost = 60.0 * self.talents.spell_cost();
if self.mana < COST { if self.mana < cost {
self.msg("msg.no_mana_heal", vec![]); self.msg("msg.no_mana_heal", vec![]);
self.sfx_ui(Sfx::Error); self.sfx_ui(Sfx::Error);
return; return;
} }
self.mana -= COST; self.mana -= cost;
self.count(crate::stats::SPELLS, 1.0); self.count(crate::stats::SPELLS, 1.0);
self.sfx(Sfx::Heal, wx, wy); self.sfx(Sfx::Heal, wx, wy);
let mut fxs = Vec::new(); let mut fxs = Vec::new();
+5 -2
View File
@@ -30,6 +30,7 @@ mod props;
mod research; mod research;
mod rival; mod rival;
mod rogues; mod rogues;
mod talents;
mod render3d; mod render3d;
mod rooms; mod rooms;
mod save; mod save;
@@ -969,8 +970,9 @@ async fn main() {
// pixel look: the interface on a virtual screen whose pixels are whole parts of the world's // pixel look: the interface on a virtual screen whose pixels are whole parts of the world's
let rows = if [270, 360, 480].contains(&app.settings.pixel_rows) { app.settings.pixel_rows as u32 } else { 360 }; let rows = if [270, 360, 480].contains(&app.settings.pixel_rows) { app.settings.pixel_rows as u32 } else { 360 };
// the interface needs room: the smallest whole multiple of the world's rows with at least // the interface needs room: the smallest whole multiple of the world's rows with at least
// 900 rows on a desktop (360 -> 1080) and 540 on a touch screen (360 -> 720) // 900 rows (360 -> 1080), on a desktop and on a touch screen alike (a phone has about
let need: u32 = if touch_mode { 540 } else { 900 }; // 1080 device rows in landscape; a smaller screen draws in device pixels)
let need: u32 = 900;
ui::set_virtual_rows(rows * need.div_ceil(rows)); ui::set_virtual_rows(rows * need.div_ceil(rows));
if touch_mode { if touch_mode {
// the pointer is the finger, or the position of this frame's gesture // the pointer is the finger, or the position of this frame's gesture
@@ -1821,6 +1823,7 @@ app.pending_briefing = briefing_of(&campaign, i);
} }
UiAction::OpenMenu => menu.page = Some(Page::Pause), UiAction::OpenMenu => menu.page = Some(Page::Pause),
UiAction::SendGold(to, amount) => app.game.queue(Act::SendGold { to, amount }), UiAction::SendGold(to, amount) => app.game.queue(Act::SendGold { to, amount }),
UiAction::LearnTalent(branch) => app.game.queue(Act::Talent { branch }),
UiAction::Locked => { UiAction::Locked => {
let locked = ui.last_locked.unwrap_or(usize::MAX); let locked = ui.last_locked.unwrap_or(usize::MAX);
app.game.msg(if app.game.can_research(locked) { "msg.tool_research" } else { "msg.tool_locked" }, vec![]); app.game.msg(if app.game.can_research(locked) { "msg.tool_research" } else { "msg.tool_locked" }, vec![]);
+1 -1
View File
@@ -92,7 +92,7 @@ impl Game {
} }
pub fn cast(&mut self, spell: Spell, wx: f32, wy: f32) { pub fn cast(&mut self, spell: Spell, wx: f32, wy: f32) {
let cost = spell.cost(); let cost = spell.cost() * self.talents.spell_cost();
if self.mana < cost { if self.mana < cost {
self.msg("msg.no_mana_cost", vec![(cost as i32).to_string()]); self.msg("msg.no_mana_cost", vec![(cost as i32).to_string()]);
self.sfx_ui(Sfx::Error); self.sfx_ui(Sfx::Error);
+2
View File
@@ -48,6 +48,8 @@ impl GameStats {
impl Game { impl Game {
// counts for the master at this computer (another master's deeds count on his computer) // counts for the master at this computer (another master's deeds count on his computer)
pub(crate) fn count(&mut self, k: usize, n: f32) { pub(crate) fn count(&mut self, k: usize, n: f32) {
// every master's deeds bring him experience for his talents
self.add_master_xp(crate::talents::xp_for(k) * n);
if self.me_side != self.local_side || self.demo.is_some() { if self.me_side != self.local_side || self.demo.is_some() {
return; return;
} }
+220
View File
@@ -0,0 +1,220 @@
// Master talents: a master earns experience (slain heroes, dug tiles, rooms, walls, traps,
// towers, spells, gold) and with it talent points, spent on four branches of five levels each:
// Builder (imps dig faster, rooms cost less), Warlord (creatures hit harder and take less
// damage), Warlock (more mana, cheaper spells), Merchant (lower wages, more gold from treasure).
// Per game in free play and matches; in the campaign they go along from mission to mission.
use crate::game::*;
use serde::{Deserialize, Serialize};
pub const BRANCHES: usize = 4;
pub const BUILDER: usize = 0;
pub const WARLORD: usize = 1;
pub const WARLOCK: usize = 2;
pub const MERCHANT: usize = 3;
pub const MAX_LEVEL: u8 = 5;
pub const NAMES: [&str; BRANCHES] = ["talent.builder", "talent.warlord", "talent.warlock", "talent.merchant"];
// experience for the deeds of a master (stats counter -> experience per unit)
pub fn xp_for(counter: usize) -> f32 {
use crate::stats as k;
match counter {
k::DUG => 2.0,
k::ROOMS => 3.0,
k::WALLS => 2.0,
k::TRAPS => 8.0,
k::TOWERS => 25.0,
k::SPELLS => 5.0,
k::SIEGE => 20.0,
k::GOLD => 0.02,
_ => 0.0,
}
}
// experience for a slain hero: a share of his bounty
pub const HERO_XP: f32 = 0.5;
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
pub struct Talents {
pub xp: f32,
pub lv: [u8; BRANCHES],
}
// experience the k-th talent point needs in all (the points get dearer)
pub fn point_xp(k: u32) -> f32 {
(0..k).map(|i| 300.0 + 150.0 * i as f32).sum()
}
impl Talents {
pub fn spent(&self) -> u32 {
self.lv.iter().map(|&l| l as u32).sum()
}
// points earned so far (at most enough for every level)
pub fn earned(&self) -> u32 {
let max = BRANCHES as u32 * MAX_LEVEL as u32;
(1..=max).take_while(|&k| self.xp >= point_xp(k)).count() as u32
}
pub fn free(&self) -> u32 {
self.earned().saturating_sub(self.spent())
}
// progress towards the next point: (experience into it, experience it needs)
pub fn progress(&self) -> Option<(f32, f32)> {
let k = self.earned();
if k >= BRANCHES as u32 * MAX_LEVEL as u32 {
return None;
}
let (a, b) = (point_xp(k), point_xp(k + 1));
Some((self.xp - a, b - a))
}
pub fn can_learn(&self, branch: usize) -> bool {
branch < BRANCHES && self.lv[branch] < MAX_LEVEL && self.free() > 0
}
fn level(&self, branch: usize) -> f32 {
self.lv.get(branch).copied().unwrap_or(0) as f32
}
// Builder: imps dig and claim faster, rooms cost less
pub fn dig_speed(&self) -> f32 {
1.0 + 0.1 * self.level(BUILDER)
}
pub fn room_cost(&self) -> f32 {
1.0 - 0.06 * self.level(BUILDER)
}
// Warlord: creatures hit harder and take less damage
pub fn damage(&self) -> f32 {
1.0 + 0.05 * self.level(WARLORD)
}
pub fn armour(&self) -> f32 {
1.0 / (1.0 + 0.05 * self.level(WARLORD))
}
// Warlock: mana flows faster, spells cost less
pub fn mana_rate(&self) -> f32 {
1.0 + 0.15 * self.level(WARLOCK)
}
pub fn spell_cost(&self) -> f32 {
1.0 - 0.06 * self.level(WARLOCK)
}
// Merchant: lower wages, more gold from delivered treasure
pub fn wages(&self) -> f32 {
1.0 - 0.06 * self.level(MERCHANT)
}
pub fn treasure(&self) -> f32 {
1.0 + 0.06 * self.level(MERCHANT)
}
}
impl Game {
// the talents of the master on `side`
pub fn talents_of(&self, side: u8) -> Talents {
if side == self.me_side {
self.talents.clone()
} else {
self.masters.iter().find(|m| m.side == side).map(|m| m.talents.clone()).unwrap_or_default()
}
}
// experience for the master being processed; a new talent point is announced
pub(crate) fn add_master_xp(&mut self, xp: f32) {
if xp <= 0.0 || self.demo.is_some() {
return;
}
let before = self.talents.earned();
self.talents.xp += xp;
if self.talents.earned() > before && self.me_side == self.local_side {
self.msg("msg.talent_point", vec![]);
self.sfx_ui(Sfx::LevelUp);
}
}
// the master spends a point on a branch (an order: the same on every computer of a match)
pub fn learn_talent(&mut self, branch: usize) {
if !self.talents.can_learn(branch) {
self.sfx_ui(Sfx::Error);
return;
}
self.talents.lv[branch] += 1;
let lv = self.talents.lv[branch];
self.msg("msg.talent_learned", vec![format!("@{}", NAMES[branch]), lv.to_string()]);
self.sfx_ui(Sfx::LevelUp);
}
// an AI master spends his points by himself: the branches in turn, the lowest first
pub(crate) fn ai_talents(&mut self) {
while self.talents.free() > 0 {
let order = [WARLORD, BUILDER, MERCHANT, WARLOCK];
let Some(&b) = order.iter().filter(|&&b| self.talents.lv[b] < MAX_LEVEL).min_by_key(|&&b| self.talents.lv[b]) else { break };
self.talents.lv[b] += 1;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn points_get_dearer_and_levels_are_capped() {
let mut t = Talents::default();
assert_eq!(t.earned(), 0);
t.xp = point_xp(1);
assert_eq!((t.earned(), t.free()), (1, 1));
assert!(point_xp(3) - point_xp(2) > point_xp(2) - point_xp(1));
t.xp = 1e9;
assert_eq!(t.earned(), 20);
t.lv = [5, 5, 5, 5];
assert_eq!(t.free(), 0);
assert!(!t.can_learn(0) && t.progress().is_none());
}
#[test]
fn deeds_bring_points_and_points_bring_talents() {
let mut g = Game::new(9, 64, 64);
let (cx, cy) = g.map.core;
g.mark_rect(cx - 6, cy - 6, cx + 8, cy + 8, true);
for _ in 0..(20 * 60 * 6) {
g.update(0.05);
}
assert!(g.talents.xp > 100.0, "digging gives experience: {}", g.talents.xp);
g.talents.xp = point_xp(2);
let room0 = g.talents.room_cost();
g.learn_talent(BUILDER);
g.learn_talent(WARLORD);
g.learn_talent(WARLORD); // no third point
assert_eq!(g.talents.lv, [1, 1, 0, 0]);
assert!(g.talents.room_cost() < room0 && g.talents.damage() > 1.0);
}
#[test]
fn talents_make_rooms_cheaper_and_wages_lower() {
let mut g = Game::new(9, 64, 64);
let (cx, cy) = g.map.core;
for y in cy + 4..cy + 7 {
for x in cx..cx + 3 {
let t = &mut g.map.t[crate::map::idx(x, y)];
t.kind = crate::map::Kind::Floor;
t.owner = crate::map::Owner::Player;
}
}
g.gold = 10000.0;
let gold0 = g.gold;
g.build_rect(1, cx, cy + 4, cx + 2, cy + 6);
let plain = gold0 - g.gold;
g.sell_rect(cx, cy + 4, cx + 2, cy + 6);
g.talents.lv[BUILDER] = 5;
let gold1 = g.gold;
g.build_rect(1, cx, cy + 4, cx + 2, cy + 6);
let cheap = gold1 - g.gold;
assert!(cheap < plain * 0.8, "{} < {}", cheap, plain);
g.spawn(EK::Ogre, false, cx as f32 + 1.5, cy as f32 + 5.5, 3);
let wages = g.next_payday_cost();
g.talents.lv[MERCHANT] = 5;
assert!(g.next_payday_cost() < wages);
}
#[test]
fn ai_masters_spend_their_points() {
let mut g = Game::new(9, 64, 64);
g.talents.xp = point_xp(6);
g.ai_talents();
assert_eq!(g.talents.free(), 0);
assert!(g.talents.lv.iter().all(|&l| l >= 1), "{:?}", g.talents.lv);
}
}
+106 -2
View File
@@ -457,6 +457,8 @@ enum TBtn {
Tool(usize), Tool(usize),
Menu, Menu,
Overview, Overview,
Talents, // the master's talent window
Learn(u8), // spend a point on a branch
OvRow(u32), OvRow(u32),
OvScroll(i32), OvScroll(i32),
GoldTo, // the next ally to receive gold GoldTo, // the next ally to receive gold
@@ -478,6 +480,7 @@ pub enum UiAction {
ChatSetTeam(bool), ChatSetTeam(bool),
ChatSend, ChatSend,
SendGold(u8, u32), // gold for an ally of a match (side, amount) SendGold(u8, u32), // gold for an ally of a match (side, amount)
LearnTalent(u8), // spend a talent point on a branch
Consumed, Consumed,
} }
@@ -485,6 +488,7 @@ pub struct Ui {
pub tool: Tool, pub tool: Tool,
pub show_help: bool, pub show_help: bool,
pub show_overview: bool, // list of all creatures pub show_overview: bool, // list of all creatures
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, ov_scroll: usize,
@@ -547,6 +551,28 @@ pub fn wrap(s: &str, cols: usize) -> Vec<String> {
out out
} }
// what a talent branch gives at its level now, and at the next level
fn branch_text(t: &crate::talents::Talents, b: usize) -> String {
use crate::talents::*;
let desc = |lv: u8| {
let l = lv as i32;
let (a, c) = match b {
BUILDER => (10 * l, 6 * l),
WARLORD => (5 * l, 5 * l),
WARLOCK => (15 * l, 6 * l),
_ => (6 * l, 6 * l),
};
trf(&format!("{}.desc", NAMES[b]), &[a.to_string(), c.to_string()])
};
let lv = t.lv[b];
let now = if lv == 0 { tr("talent.none") } else { desc(lv) };
if lv < MAX_LEVEL {
format!("{} {}", now, trf("talent.then", &[desc(lv + 1)]))
} else {
now
}
}
#[cfg(test)] #[cfg(test)]
mod wrap_tests { mod wrap_tests {
#[test] #[test]
@@ -573,6 +599,7 @@ impl Ui {
tool: Tool::Dig, tool: Tool::Dig,
show_help: true, show_help: true,
show_overview: false, show_overview: false,
show_talents: false,
coop_end: None, coop_end: None,
gold_to: 0, gold_to: 0,
ov_scroll: 0, ov_scroll: 0,
@@ -664,9 +691,17 @@ impl Ui {
TBtn::Overview => { TBtn::Overview => {
self.open_cat = None; self.open_cat = None;
self.show_overview = !self.show_overview; self.show_overview = !self.show_overview;
self.show_talents = false;
self.ov_scroll = 0; self.ov_scroll = 0;
UiAction::Consumed UiAction::Consumed
} }
TBtn::Talents => {
self.open_cat = None;
self.show_talents = !self.show_talents;
self.show_overview = false;
UiAction::Consumed
}
TBtn::Learn(b) => UiAction::LearnTalent(b),
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
@@ -891,7 +926,7 @@ impl Ui {
let mut popup_top = tb_y; let mut popup_top = tb_y;
// categories + menu button; a category with several tools opens a row above // categories + menu button; a category with several tools opens a row above
// (the same layout for mouse and touch; on the desktop the keys still select tools directly) // (the same layout for mouse and touch; on the desktop the keys still select tools directly)
let n = CATS.len() + 2; let n = CATS.len() + 3;
let bcols = (((cols - 2) as usize / n) as f32).clamp(4.0, 8.0); let bcols = (((cols - 2) as usize / n) as f32).clamp(4.0, 8.0);
let bw = bcols * CW(); let bw = bcols * CW();
let x0 = grid_center(sw, bw * n as f32); let x0 = grid_center(sw, bw * n as f32);
@@ -932,10 +967,27 @@ impl Ui {
let ol: String = ol.chars().take(maxc).collect(); let ol: String = ol.chars().take(maxc).collect();
gfx.text(&ol, bx + (bw - gfx.measure(&ol, SMALL())) * 0.5, by + 2.0 * CH() + 4.0 * px, SMALL(), TXT); gfx.text(&ol, bx + (bw - gfx.measure(&ol, SMALL())) * 0.5, by + 2.0 * CH() + 4.0 * px, SMALL(), TXT);
self.touch_btns.push((r, TBtn::Overview)); self.touch_btns.push((r, TBtn::Overview));
// menu button // the master's talents: the frame pulses while points are free
let bx = x0 + (CATS.len() + 1) as f32 * bw; let bx = x0 + (CATS.len() + 1) as f32 * bw;
let r = Rect::new(bx + px, by, bw - 2.0 * px, 3.0 * CH()); let r = Rect::new(bx + px, by, bw - 2.0 * px, 3.0 * CH());
let hovered = !touch_ui() && r.contains(vec2(mx, my)); let hovered = !touch_ui() && r.contains(vec2(mx, my));
let free = g.talents.free();
let glow = free > 0 && ((now * 3.0) as i64) % 2 == 0;
draw_rectangle(r.x, r.y, r.w, r.h, if hovered || self.show_talents { Color::new(0.18, 0.13, 0.1, 1.0) } else { Color::new(0.1, 0.07, 0.07, 1.0) });
draw_rectangle_lines(r.x, r.y, r.w, r.h, 2.0 * px, if glow { GOLD } else if self.show_talents { FRAME_HI } else { Color::new(0.35, 0.28, 0.2, 1.0) });
let isz = 2.0 * CH() - 2.0 * px;
draw_texture_ex(&gfx.atlas, bx + (bw - isz) * 0.5, by + 2.0 * px, WHITE, DrawTextureParams { source: Some(Gfx::cell(I_RUNE)), dest_size: Some(vec2(isz, isz)), ..Default::default() });
if free > 0 {
gfx.text(&free.to_string(), r.x + 3.0 * px, r.y + 3.0 * px, SMALL(), if glow { WHITE } else { GOLD });
}
let tl = tr("cat.master");
let tl: String = tl.chars().take(maxc).collect();
gfx.text(&tl, bx + (bw - gfx.measure(&tl, SMALL())) * 0.5, by + 2.0 * CH() + 4.0 * px, SMALL(), TXT);
self.touch_btns.push((r, TBtn::Talents));
// menu button
let bx = x0 + (CATS.len() + 2) as f32 * bw;
let r = Rect::new(bx + px, by, bw - 2.0 * px, 3.0 * CH());
let hovered = !touch_ui() && r.contains(vec2(mx, my));
draw_rectangle(r.x, r.y, r.w, r.h, if hovered { Color::new(0.18, 0.13, 0.1, 1.0) } else { Color::new(0.1, 0.07, 0.07, 1.0) }); draw_rectangle(r.x, r.y, r.w, r.h, if hovered { Color::new(0.18, 0.13, 0.1, 1.0) } else { Color::new(0.1, 0.07, 0.07, 1.0) });
draw_rectangle_lines(r.x, r.y, r.w, r.h, 2.0 * px, Color::new(0.35, 0.28, 0.2, 1.0)); draw_rectangle_lines(r.x, r.y, r.w, r.h, 2.0 * px, Color::new(0.35, 0.28, 0.2, 1.0));
gfx.glyph('≡', bx + (bw - GW * TITLE()) * 0.5, by + 2.0 * px, TITLE(), FRAME_HI); gfx.glyph('≡', bx + (bw - GW * TITLE()) * 0.5, by + 2.0 * px, TITLE(), FRAME_HI);
@@ -1204,6 +1256,9 @@ impl Ui {
if self.show_overview { if self.show_overview {
self.draw_overview(g, gfx, tb_y); self.draw_overview(g, gfx, tb_y);
} }
if self.show_talents {
self.draw_talents(g, gfx, tb_y);
}
// ---------------- Tooltip // ---------------- Tooltip
if let Some(i) = hover_tool { if let Some(i) = hover_tool {
@@ -1232,6 +1287,55 @@ impl Ui {
} }
// all creatures, grouped by kind: level, mood, wage, job, health; a click jumps there // all creatures, grouped by kind: level, mood, wage, job, health; a click jumps there
// the master's talents: four branches of five levels, points from experience
fn draw_talents(&mut self, g: &Game, gfx: &Gfx, bottom: f32) {
use crate::talents::*;
let (mx, my) = mouse();
let t = &g.talents;
let cols = ((sw() / CW()) as i32 - 2).min(48);
let inner = (cols - 4) as usize;
// the lines of each branch: name and level, what it gives now (wrapped)
let rows_of = |b: usize| wrap(&branch_text(t, b), inner.saturating_sub(6)).len() as i32 + 2;
let rows = 5 + (0..BRANCHES).map(rows_of).sum::<i32>();
let x = grid_center(sw(), cols as f32 * CW()).max(0.0);
let y = (bottom - rows as f32 * CH() - CH()).max(CH() * 4.0);
gfx.window_titled(x, y, cols, rows, S(), false, FRAME_HI, BG, &tr("talent.title"), FRAME_HI);
self.rects.push(Rect::new(x, y, cols as f32 * CW(), rows as f32 * CH()));
let ix = x + CW() * 2.0;
let mut yy = y + CH();
// points and the way to the next one
gfx.text(&trf("talent.points", &[t.free().to_string()]), ix, yy, S(), if t.free() > 0 { GOLD } else { TXT });
yy += CH() * 1.2;
match t.progress() {
Some((have, need)) => {
ascii_bar(gfx, ix, yy, 16, (have / need).clamp(0.0, 1.0), MANA, SMALL());
gfx.text(&trf("talent.next", &[(have as i32).to_string(), (need as i32).to_string()]), ix + CW() * 17.0, yy, SMALL(), DIM);
}
None => gfx.text(&tr("talent.all"), ix, yy, SMALL(), DIM),
}
yy += CH() * 1.8;
for b in 0..BRANCHES {
let lv = t.lv[b];
let pips: String = (0..MAX_LEVEL).map(|k| if k < lv { '■' } else { '·' }).collect();
gfx.text(&format!("{} {}", tr(NAMES[b]), pips), ix, yy, S(), FRAME_HI);
// a button to learn the next level
if t.can_learn(b) {
let r = Rect::new(x + (cols - 7) as f32 * CW(), yy - CH() * 0.2, CW() * 4.0, CH() * 1.6);
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, GOLD);
gfx.text("+", r.x + (r.w - gfx.measure("+", S())) * 0.5, r.y + (r.h - GH * S()) * 0.5, S(), GOLD);
self.touch_btns.push((r, TBtn::Learn(b as u8)));
}
yy += CH() * 1.2;
for l in wrap(&branch_text(t, b), inner.saturating_sub(6)) {
gfx.text(&l, ix + CW() * 2.0, yy, SMALL(), DIM);
yy += CH();
}
yy += CH() * 0.8;
}
}
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();