Surface world: grass in daylight, forests, cliffs with cave mouths; free-game and editor option

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-05 22:35:46 +01:00
1 parent ef10b5c432
commit 8a35a903de
32 files changed
+343 -24

No files matched your search

+6
View File
@@ -725,6 +725,9 @@ net.ai_off = Vyp.
# rival masters (free game) # rival masters (free game)
newgame.difficulty = Soupeři newgame.difficulty = Soupeři
newgame.land = Terén
newgame.land0 = Pod zemí
newgame.land1 = Napůl na povrchu
diff.0 = Lehká diff.0 = Lehká
diff.1 = Střední diff.1 = Střední
diff.2 = Těžká diff.2 = Těžká
@@ -1131,6 +1134,7 @@ ed.forts = Rytířské pevnosti
ed.rivals = Soupeřící mistři ed.rivals = Soupeřící mistři
ed.difficulty = Obtížnost ed.difficulty = Obtížnost
ed.portals = Portály ed.portals = Portály
ed.surface = Povrch (východ)
ed.edit_map = Upravit mapu ed.edit_map = Upravit mapu
ed.make_random = Udělat náhodnou mapu ed.make_random = Udělat náhodnou mapu
ed.paint_map = Vygenerovat a kreslit mapu ed.paint_map = Vygenerovat a kreslit mapu
@@ -1211,6 +1215,8 @@ ed.terrain.ruby = Rubínová žíla
ed.terrain.diamond = Diamantová žíla ed.terrain.diamond = Diamantová žíla
ed.terrain.steel = Ocelová zeď ed.terrain.steel = Ocelová zeď
ed.terrain.mushroom = Obří houba ed.terrain.mushroom = Obří houba
ed.terrain.grass = Tráva (povrch)
ed.terrain.tree = Strom
ed.place.core = Jádro duše ed.place.core = Jádro duše
ed.place.portal = Portál ed.place.portal = Portál
ed.place.gate = Brána hrdinů ed.place.gate = Brána hrdinů
+6
View File
@@ -724,6 +724,9 @@ net.ai_off = Fra
# rival masters (free game) # rival masters (free game)
newgame.difficulty = Modstandere newgame.difficulty = Modstandere
newgame.land = Terræn
newgame.land0 = Under jorden
newgame.land1 = Halvt over jorden
diff.0 = Let diff.0 = Let
diff.1 = Middel diff.1 = Middel
diff.2 = Svær diff.2 = Svær
@@ -1130,6 +1133,7 @@ ed.forts = Ridderfæstninger
ed.rivals = Rivaliserende mestre ed.rivals = Rivaliserende mestre
ed.difficulty = Sværhedsgrad ed.difficulty = Sværhedsgrad
ed.portals = Portaler ed.portals = Portaler
ed.surface = Over jorden (øst)
ed.edit_map = Rediger kortet ed.edit_map = Rediger kortet
ed.make_random = Gør det til et tilfældigt kort ed.make_random = Gør det til et tilfældigt kort
ed.paint_map = Generer og mal kortet ed.paint_map = Generer og mal kortet
@@ -1210,6 +1214,8 @@ ed.terrain.ruby = Rubinåre
ed.terrain.diamond = Diamantåre ed.terrain.diamond = Diamantåre
ed.terrain.steel = Stålmur ed.terrain.steel = Stålmur
ed.terrain.mushroom = Kæmpesvamp ed.terrain.mushroom = Kæmpesvamp
ed.terrain.grass = Græs (over jorden)
ed.terrain.tree = Træ
ed.place.core = Sjælekerne ed.place.core = Sjælekerne
ed.place.portal = Portal ed.place.portal = Portal
ed.place.gate = Helteport ed.place.gate = Helteport
+6
View File
@@ -724,6 +724,9 @@ net.ai_off = Aus
# rival masters (free game) # rival masters (free game)
newgame.difficulty = Gegner newgame.difficulty = Gegner
newgame.land = Gelände
newgame.land0 = Unterirdisch
newgame.land1 = Halb über der Erde
diff.0 = Leicht diff.0 = Leicht
diff.1 = Mittel diff.1 = Mittel
diff.2 = Schwer diff.2 = Schwer
@@ -1130,6 +1133,7 @@ ed.forts = Ritterfestungen
ed.rivals = Rivalen-Meister ed.rivals = Rivalen-Meister
ed.difficulty = Schwierigkeit ed.difficulty = Schwierigkeit
ed.portals = Portale ed.portals = Portale
ed.surface = Oberwelt (Osten)
ed.edit_map = Karte bearbeiten ed.edit_map = Karte bearbeiten
ed.make_random = Zur Zufallskarte machen ed.make_random = Zur Zufallskarte machen
ed.paint_map = Karte erzeugen und malen ed.paint_map = Karte erzeugen und malen
@@ -1210,6 +1214,8 @@ ed.terrain.ruby = Rubinader
ed.terrain.diamond = Diamantader ed.terrain.diamond = Diamantader
ed.terrain.steel = Stahlwand ed.terrain.steel = Stahlwand
ed.terrain.mushroom = Riesenpilz ed.terrain.mushroom = Riesenpilz
ed.terrain.grass = Gras (Oberwelt)
ed.terrain.tree = Baum
ed.place.core = Seelenkern ed.place.core = Seelenkern
ed.place.portal = Portal ed.place.portal = Portal
ed.place.gate = Heldentor ed.place.gate = Heldentor
+6
View File
@@ -725,6 +725,9 @@ net.ai_off = Όχι
# rival masters (free game) # rival masters (free game)
newgame.difficulty = Αντίπαλοι newgame.difficulty = Αντίπαλοι
newgame.land = Έδαφος
newgame.land0 = Υπόγεια
newgame.land1 = Μισό στην επιφάνεια
diff.0 = Εύκολο diff.0 = Εύκολο
diff.1 = Μέτριο diff.1 = Μέτριο
diff.2 = Δύσκολο diff.2 = Δύσκολο
@@ -1131,6 +1134,7 @@ ed.forts = Ιπποτικά φρούρια
ed.rivals = Αντίπαλοι άρχοντες ed.rivals = Αντίπαλοι άρχοντες
ed.difficulty = Δυσκολία ed.difficulty = Δυσκολία
ed.portals = Πύλες μετάβασης ed.portals = Πύλες μετάβασης
ed.surface = Επιφάνεια (ανατολή)
ed.edit_map = Επεξεργασία χάρτη ed.edit_map = Επεξεργασία χάρτη
ed.make_random = Κάν' τον τυχαίο χάρτη ed.make_random = Κάν' τον τυχαίο χάρτη
ed.paint_map = Δημιουργία και ζωγραφική χάρτη ed.paint_map = Δημιουργία και ζωγραφική χάρτη
@@ -1211,6 +1215,8 @@ ed.terrain.ruby = Φλέβα ρουμπινιού
ed.terrain.diamond = Φλέβα διαμαντιού ed.terrain.diamond = Φλέβα διαμαντιού
ed.terrain.steel = Ατσάλινος τοίχος ed.terrain.steel = Ατσάλινος τοίχος
ed.terrain.mushroom = Γιγάντιο μανιτάρι ed.terrain.mushroom = Γιγάντιο μανιτάρι
ed.terrain.grass = Γρασίδι (επιφάνεια)
ed.terrain.tree = Δέντρο
ed.place.core = Πυρήνας ψυχής ed.place.core = Πυρήνας ψυχής
ed.place.portal = Πύλη μετάβασης ed.place.portal = Πύλη μετάβασης
ed.place.gate = Πύλη ηρώων ed.place.gate = Πύλη ηρώων
+6
View File
@@ -725,6 +725,9 @@ net.ai_off = Off
# rival masters (free game) # rival masters (free game)
newgame.difficulty = Opponents newgame.difficulty = Opponents
newgame.land = Land
newgame.land0 = Underground
newgame.land1 = Half above ground
diff.0 = Easy diff.0 = Easy
diff.1 = Medium diff.1 = Medium
diff.2 = Hard diff.2 = Hard
@@ -1131,6 +1134,7 @@ ed.forts = Knight fortresses
ed.rivals = Rival masters ed.rivals = Rival masters
ed.difficulty = Difficulty ed.difficulty = Difficulty
ed.portals = Portals ed.portals = Portals
ed.surface = Above ground (east)
ed.edit_map = Edit the map ed.edit_map = Edit the map
ed.make_random = Make it a random map ed.make_random = Make it a random map
ed.paint_map = Generate and paint the map ed.paint_map = Generate and paint the map
@@ -1211,6 +1215,8 @@ ed.terrain.ruby = Ruby vein
ed.terrain.diamond = Diamond vein ed.terrain.diamond = Diamond vein
ed.terrain.steel = Steel wall ed.terrain.steel = Steel wall
ed.terrain.mushroom = Giant mushroom ed.terrain.mushroom = Giant mushroom
ed.terrain.grass = Grass (above ground)
ed.terrain.tree = Tree
ed.place.core = Soul core ed.place.core = Soul core
ed.place.portal = Portal ed.place.portal = Portal
ed.place.gate = Hero gate ed.place.gate = Hero gate
+6
View File
@@ -717,6 +717,9 @@ net.ai_off = No
# rival masters (free game) # rival masters (free game)
newgame.difficulty = Rivales newgame.difficulty = Rivales
newgame.land = Terreno
newgame.land0 = Subterráneo
newgame.land1 = Mitad en superficie
diff.0 = Fácil diff.0 = Fácil
diff.1 = Normal diff.1 = Normal
diff.2 = Difícil diff.2 = Difícil
@@ -1123,6 +1126,7 @@ ed.forts = Fortalezas
ed.rivals = Señores rivales ed.rivals = Señores rivales
ed.difficulty = Dificultad ed.difficulty = Dificultad
ed.portals = Portales ed.portals = Portales
ed.surface = Superficie (este)
ed.edit_map = Editar el mapa ed.edit_map = Editar el mapa
ed.make_random = Hacerlo aleatorio ed.make_random = Hacerlo aleatorio
ed.paint_map = Generar y pintar el mapa ed.paint_map = Generar y pintar el mapa
@@ -1203,6 +1207,8 @@ ed.terrain.ruby = Veta de rubíes
ed.terrain.diamond = Veta de diamantes ed.terrain.diamond = Veta de diamantes
ed.terrain.steel = Muro de acero ed.terrain.steel = Muro de acero
ed.terrain.mushroom = Seta gigante ed.terrain.mushroom = Seta gigante
ed.terrain.grass = Hierba (superficie)
ed.terrain.tree = Árbol
ed.place.core = Núcleo de alma ed.place.core = Núcleo de alma
ed.place.portal = Portal ed.place.portal = Portal
ed.place.gate = Puerta de héroes ed.place.gate = Puerta de héroes
+6
View File
@@ -725,6 +725,9 @@ net.ai_off = Pois
# rival masters (free game) # rival masters (free game)
newgame.difficulty = Vastustajat newgame.difficulty = Vastustajat
newgame.land = Maasto
newgame.land0 = Maan alla
newgame.land1 = Puoliksi maan päällä
diff.0 = Helppo diff.0 = Helppo
diff.1 = Keski diff.1 = Keski
diff.2 = Vaikea diff.2 = Vaikea
@@ -1131,6 +1134,7 @@ ed.forts = Ritarilinnat
ed.rivals = Kilpailijat ed.rivals = Kilpailijat
ed.difficulty = Vaikeus ed.difficulty = Vaikeus
ed.portals = Portaalit ed.portals = Portaalit
ed.surface = Maan päällä (itä)
ed.edit_map = Muokkaa karttaa ed.edit_map = Muokkaa karttaa
ed.make_random = Tee siitä satunnaiskartta ed.make_random = Tee siitä satunnaiskartta
ed.paint_map = Luo ja piirrä kartta ed.paint_map = Luo ja piirrä kartta
@@ -1211,6 +1215,8 @@ ed.terrain.ruby = Rubiinisuoni
ed.terrain.diamond = Timanttisuoni ed.terrain.diamond = Timanttisuoni
ed.terrain.steel = Teräsmuuri ed.terrain.steel = Teräsmuuri
ed.terrain.mushroom = Jättisieni ed.terrain.mushroom = Jättisieni
ed.terrain.grass = Ruoho (maan päällä)
ed.terrain.tree = Puu
ed.place.core = Sieluydin ed.place.core = Sieluydin
ed.place.portal = Portaali ed.place.portal = Portaali
ed.place.gate = Sankariportti ed.place.gate = Sankariportti
+6
View File
@@ -725,6 +725,9 @@ net.ai_off = Isklj.
# rival masters (free game) # rival masters (free game)
newgame.difficulty = Protivnici newgame.difficulty = Protivnici
newgame.land = Teren
newgame.land0 = Pod zemljom
newgame.land1 = Pola na površini
diff.0 = Lako diff.0 = Lako
diff.1 = Srednje diff.1 = Srednje
diff.2 = Teško diff.2 = Teško
@@ -1131,6 +1134,7 @@ ed.forts = Viteške tvrđave
ed.rivals = Suparnici ed.rivals = Suparnici
ed.difficulty = Težina ed.difficulty = Težina
ed.portals = Portali ed.portals = Portali
ed.surface = Površina (istok)
ed.edit_map = Uredi kartu ed.edit_map = Uredi kartu
ed.make_random = Pretvori u slučajnu kartu ed.make_random = Pretvori u slučajnu kartu
ed.paint_map = Generiraj i nacrtaj kartu ed.paint_map = Generiraj i nacrtaj kartu
@@ -1211,6 +1215,8 @@ ed.terrain.ruby = Žila rubina
ed.terrain.diamond = Žila dijamanata ed.terrain.diamond = Žila dijamanata
ed.terrain.steel = Čelični zid ed.terrain.steel = Čelični zid
ed.terrain.mushroom = Divovska gljiva ed.terrain.mushroom = Divovska gljiva
ed.terrain.grass = Trava (površina)
ed.terrain.tree = Stablo
ed.place.core = Jezgra duše ed.place.core = Jezgra duše
ed.place.portal = Portal ed.place.portal = Portal
ed.place.gate = Vrata junaka ed.place.gate = Vrata junaka
+6
View File
@@ -725,6 +725,9 @@ net.ai_off = Ki
# rival masters (free game) # rival masters (free game)
newgame.difficulty = Ellenfelek newgame.difficulty = Ellenfelek
newgame.land = Terep
newgame.land0 = Föld alatt
newgame.land1 = Félig a felszínen
diff.0 = Könnyű diff.0 = Könnyű
diff.1 = Közepes diff.1 = Közepes
diff.2 = Nehéz diff.2 = Nehéz
@@ -1131,6 +1134,7 @@ ed.forts = Lovagerődök
ed.rivals = Rivális mesterek ed.rivals = Rivális mesterek
ed.difficulty = Nehézség ed.difficulty = Nehézség
ed.portals = Portálok ed.portals = Portálok
ed.surface = Felszín (kelet)
ed.edit_map = Térkép szerkesztése ed.edit_map = Térkép szerkesztése
ed.make_random = Legyen véletlen térkép ed.make_random = Legyen véletlen térkép
ed.paint_map = Térkép generálása és festése ed.paint_map = Térkép generálása és festése
@@ -1211,6 +1215,8 @@ ed.terrain.ruby = Rubinér
ed.terrain.diamond = Gyémántér ed.terrain.diamond = Gyémántér
ed.terrain.steel = Acélfal ed.terrain.steel = Acélfal
ed.terrain.mushroom = Óriásgomba ed.terrain.mushroom = Óriásgomba
ed.terrain.grass = Fű (felszín)
ed.terrain.tree = Fa
ed.place.core = Lélekmag ed.place.core = Lélekmag
ed.place.portal = Portál ed.place.portal = Portál
ed.place.gate = Hőskapu ed.place.gate = Hőskapu
+6
View File
@@ -717,6 +717,9 @@ net.ai_off = No
# rival masters (free game) # rival masters (free game)
newgame.difficulty = Avversari newgame.difficulty = Avversari
newgame.land = Terreno
newgame.land0 = Sotterraneo
newgame.land1 = Metà in superficie
diff.0 = Facile diff.0 = Facile
diff.1 = Medio diff.1 = Medio
diff.2 = Difficile diff.2 = Difficile
@@ -1123,6 +1126,7 @@ ed.forts = Fortezze
ed.rivals = Maestri rivali ed.rivals = Maestri rivali
ed.difficulty = Difficoltà ed.difficulty = Difficoltà
ed.portals = Portali ed.portals = Portali
ed.surface = Superficie (est)
ed.edit_map = Modifica mappa ed.edit_map = Modifica mappa
ed.make_random = Rendila casuale ed.make_random = Rendila casuale
ed.paint_map = Genera e dipingi la mappa ed.paint_map = Genera e dipingi la mappa
@@ -1203,6 +1207,8 @@ ed.terrain.ruby = Vena di rubini
ed.terrain.diamond = Vena di diamanti ed.terrain.diamond = Vena di diamanti
ed.terrain.steel = Muro d'acciaio ed.terrain.steel = Muro d'acciaio
ed.terrain.mushroom = Fungo gigante ed.terrain.mushroom = Fungo gigante
ed.terrain.grass = Erba (superficie)
ed.terrain.tree = Albero
ed.place.core = Nucleo dell'Anima ed.place.core = Nucleo dell'Anima
ed.place.portal = Portale ed.place.portal = Portale
ed.place.gate = Porta degli Eroi ed.place.gate = Porta degli Eroi
+6
View File
@@ -725,6 +725,9 @@ net.ai_off = Uit
# rival masters (free game) # rival masters (free game)
newgame.difficulty = Tegenstanders newgame.difficulty = Tegenstanders
newgame.land = Terrein
newgame.land0 = Ondergronds
newgame.land1 = Half bovengronds
diff.0 = Makkelijk diff.0 = Makkelijk
diff.1 = Middel diff.1 = Middel
diff.2 = Moeilijk diff.2 = Moeilijk
@@ -1131,6 +1134,7 @@ ed.forts = Riddervestingen
ed.rivals = Rivalen ed.rivals = Rivalen
ed.difficulty = Moeilijkheid ed.difficulty = Moeilijkheid
ed.portals = Portalen ed.portals = Portalen
ed.surface = Bovengronds (oosten)
ed.edit_map = Kaart bewerken ed.edit_map = Kaart bewerken
ed.make_random = Maak er een willekeurige kaart van ed.make_random = Maak er een willekeurige kaart van
ed.paint_map = Kaart genereren en tekenen ed.paint_map = Kaart genereren en tekenen
@@ -1211,6 +1215,8 @@ ed.terrain.ruby = Robijnader
ed.terrain.diamond = Diamantader ed.terrain.diamond = Diamantader
ed.terrain.steel = Stalen muur ed.terrain.steel = Stalen muur
ed.terrain.mushroom = Reuzenpaddenstoel ed.terrain.mushroom = Reuzenpaddenstoel
ed.terrain.grass = Gras (bovengronds)
ed.terrain.tree = Boom
ed.place.core = Zielenkern ed.place.core = Zielenkern
ed.place.portal = Portaal ed.place.portal = Portaal
ed.place.gate = Heldenpoort ed.place.gate = Heldenpoort
+6
View File
@@ -724,6 +724,9 @@ net.ai_off = Av
# rival masters (free game) # rival masters (free game)
newgame.difficulty = Motstandere newgame.difficulty = Motstandere
newgame.land = Terreng
newgame.land0 = Under jorden
newgame.land1 = Halvt over jorden
diff.0 = Lett diff.0 = Lett
diff.1 = Middels diff.1 = Middels
diff.2 = Vanskelig diff.2 = Vanskelig
@@ -1130,6 +1133,7 @@ ed.forts = Ridderfestninger
ed.rivals = Rivaliserende mestere ed.rivals = Rivaliserende mestere
ed.difficulty = Vanskelighetsgrad ed.difficulty = Vanskelighetsgrad
ed.portals = Portaler ed.portals = Portaler
ed.surface = Over jorden (øst)
ed.edit_map = Rediger kartet ed.edit_map = Rediger kartet
ed.make_random = Gjør det til et tilfeldig kart ed.make_random = Gjør det til et tilfeldig kart
ed.paint_map = Generer og mal kartet ed.paint_map = Generer og mal kartet
@@ -1210,6 +1214,8 @@ ed.terrain.ruby = Rubinåre
ed.terrain.diamond = Diamantåre ed.terrain.diamond = Diamantåre
ed.terrain.steel = Stålvegg ed.terrain.steel = Stålvegg
ed.terrain.mushroom = Kjempesopp ed.terrain.mushroom = Kjempesopp
ed.terrain.grass = Gress (over jorden)
ed.terrain.tree = Tre
ed.place.core = Sjelekjerne ed.place.core = Sjelekjerne
ed.place.portal = Portal ed.place.portal = Portal
ed.place.gate = Helteport ed.place.gate = Helteport
+6
View File
@@ -725,6 +725,9 @@ net.ai_off = Desl.
# rival masters (free game) # rival masters (free game)
newgame.difficulty = Adversários newgame.difficulty = Adversários
newgame.land = Terreno
newgame.land0 = Subterrâneo
newgame.land1 = Metade à superfície
diff.0 = Fácil diff.0 = Fácil
diff.1 = Médio diff.1 = Médio
diff.2 = Difícil diff.2 = Difícil
@@ -1131,6 +1134,7 @@ ed.forts = Fortalezas
ed.rivals = Mestres rivais ed.rivals = Mestres rivais
ed.difficulty = Dificuldade ed.difficulty = Dificuldade
ed.portals = Portais ed.portals = Portais
ed.surface = Superfície (leste)
ed.edit_map = Editar o mapa ed.edit_map = Editar o mapa
ed.make_random = Tornar mapa aleatório ed.make_random = Tornar mapa aleatório
ed.paint_map = Gerar e pintar o mapa ed.paint_map = Gerar e pintar o mapa
@@ -1211,6 +1215,8 @@ ed.terrain.ruby = Veio de rubis
ed.terrain.diamond = Veio diamantes ed.terrain.diamond = Veio diamantes
ed.terrain.steel = Parede de aço ed.terrain.steel = Parede de aço
ed.terrain.mushroom = Cogumelo gigante ed.terrain.mushroom = Cogumelo gigante
ed.terrain.grass = Relva (superfície)
ed.terrain.tree = Árvore
ed.place.core = Núcleo de alma ed.place.core = Núcleo de alma
ed.place.portal = Portal ed.place.portal = Portal
ed.place.gate = Portão heróis ed.place.gate = Portão heróis
+6
View File
@@ -725,6 +725,9 @@ net.ai_off = Oprit
# rival masters (free game) # rival masters (free game)
newgame.difficulty = Adversari newgame.difficulty = Adversari
newgame.land = Teren
newgame.land0 = Subteran
newgame.land1 = Jumătate la suprafață
diff.0 = Ușor diff.0 = Ușor
diff.1 = Mediu diff.1 = Mediu
diff.2 = Greu diff.2 = Greu
@@ -1131,6 +1134,7 @@ ed.forts = Fortărețe cavalerești
ed.rivals = Stăpâni rivali ed.rivals = Stăpâni rivali
ed.difficulty = Dificultate ed.difficulty = Dificultate
ed.portals = Portaluri ed.portals = Portaluri
ed.surface = Suprafață (est)
ed.edit_map = Editează harta ed.edit_map = Editează harta
ed.make_random = Fă-o hartă aleatoare ed.make_random = Fă-o hartă aleatoare
ed.paint_map = Generează și pictează harta ed.paint_map = Generează și pictează harta
@@ -1211,6 +1215,8 @@ ed.terrain.ruby = Filon de rubine
ed.terrain.diamond = Filon de diamante ed.terrain.diamond = Filon de diamante
ed.terrain.steel = Zid de oțel ed.terrain.steel = Zid de oțel
ed.terrain.mushroom = Ciupercă uriașă ed.terrain.mushroom = Ciupercă uriașă
ed.terrain.grass = Iarbă (suprafață)
ed.terrain.tree = Copac
ed.place.core = Miezul sufletului ed.place.core = Miezul sufletului
ed.place.portal = Portal ed.place.portal = Portal
ed.place.gate = Poarta eroilor ed.place.gate = Poarta eroilor
+6
View File
@@ -717,6 +717,9 @@ net.ai_off = Выкл
# rival masters (free game) # rival masters (free game)
newgame.difficulty = Противники newgame.difficulty = Противники
newgame.land = Местность
newgame.land0 = Под землёй
newgame.land1 = Наполовину наверху
diff.0 = Легко diff.0 = Легко
diff.1 = Средне diff.1 = Средне
diff.2 = Сложно diff.2 = Сложно
@@ -1123,6 +1126,7 @@ ed.forts = Крепости
ed.rivals = Соперники ed.rivals = Соперники
ed.difficulty = Сложность ed.difficulty = Сложность
ed.portals = Порталы ed.portals = Порталы
ed.surface = Поверхность (восток)
ed.edit_map = Изменить карту ed.edit_map = Изменить карту
ed.make_random = Сделать случайной ed.make_random = Сделать случайной
ed.paint_map = Создать и нарисовать карту ed.paint_map = Создать и нарисовать карту
@@ -1203,6 +1207,8 @@ ed.terrain.ruby = Рубиновая жила
ed.terrain.diamond = Алмазная жила ed.terrain.diamond = Алмазная жила
ed.terrain.steel = Стальная стена ed.terrain.steel = Стальная стена
ed.terrain.mushroom = Гигантский гриб ed.terrain.mushroom = Гигантский гриб
ed.terrain.grass = Трава (поверхность)
ed.terrain.tree = Дерево
ed.place.core = Ядро души ed.place.core = Ядро души
ed.place.portal = Портал ed.place.portal = Портал
ed.place.gate = Врата героев ed.place.gate = Врата героев
+6
View File
@@ -725,6 +725,9 @@ net.ai_off = Vyp.
# rival masters (free game) # rival masters (free game)
newgame.difficulty = Súperi newgame.difficulty = Súperi
newgame.land = Terén
newgame.land0 = Pod zemou
newgame.land1 = Napoly na povrchu
diff.0 = Ľahká diff.0 = Ľahká
diff.1 = Stredná diff.1 = Stredná
diff.2 = Ťažká diff.2 = Ťažká
@@ -1131,6 +1134,7 @@ ed.forts = Rytierske pevnosti
ed.rivals = Súperiaci majstri ed.rivals = Súperiaci majstri
ed.difficulty = Obťažnosť ed.difficulty = Obťažnosť
ed.portals = Portály ed.portals = Portály
ed.surface = Povrch (východ)
ed.edit_map = Upraviť mapu ed.edit_map = Upraviť mapu
ed.make_random = Urobiť náhodnú mapu ed.make_random = Urobiť náhodnú mapu
ed.paint_map = Vygenerovať a kresliť mapu ed.paint_map = Vygenerovať a kresliť mapu
@@ -1211,6 +1215,8 @@ ed.terrain.ruby = Rubínová žila
ed.terrain.diamond = Diamantová žila ed.terrain.diamond = Diamantová žila
ed.terrain.steel = Oceľová stena ed.terrain.steel = Oceľová stena
ed.terrain.mushroom = Obria huba ed.terrain.mushroom = Obria huba
ed.terrain.grass = Tráva (povrch)
ed.terrain.tree = Strom
ed.place.core = Jadro duše ed.place.core = Jadro duše
ed.place.portal = Portál ed.place.portal = Portál
ed.place.gate = Brána hrdinov ed.place.gate = Brána hrdinov
+6
View File
@@ -725,6 +725,9 @@ net.ai_off = Иск.
# rival masters (free game) # rival masters (free game)
newgame.difficulty = Противници newgame.difficulty = Противници
newgame.land = Терен
newgame.land0 = Под земљом
newgame.land1 = Пола на површини
diff.0 = Лако diff.0 = Лако
diff.1 = Средње diff.1 = Средње
diff.2 = Тешко diff.2 = Тешко
@@ -1131,6 +1134,7 @@ ed.forts = Витешке тврђаве
ed.rivals = Супарници ed.rivals = Супарници
ed.difficulty = Тежина ed.difficulty = Тежина
ed.portals = Портали ed.portals = Портали
ed.surface = Површина (исток)
ed.edit_map = Уреди мапу ed.edit_map = Уреди мапу
ed.make_random = Претвори у случајну мапу ed.make_random = Претвори у случајну мапу
ed.paint_map = Генериши и нацртај мапу ed.paint_map = Генериши и нацртај мапу
@@ -1211,6 +1215,8 @@ ed.terrain.ruby = Жила рубина
ed.terrain.diamond = Жила дијаманата ed.terrain.diamond = Жила дијаманата
ed.terrain.steel = Челични зид ed.terrain.steel = Челични зид
ed.terrain.mushroom = Џиновска печурка ed.terrain.mushroom = Џиновска печурка
ed.terrain.grass = Трава (површина)
ed.terrain.tree = Стабло
ed.place.core = Језгро душе ed.place.core = Језгро душе
ed.place.portal = Портал ed.place.portal = Портал
ed.place.gate = Капија хероја ed.place.gate = Капија хероја
+6
View File
@@ -725,6 +725,9 @@ net.ai_off = Av
# rival masters (free game) # rival masters (free game)
newgame.difficulty = Motståndare newgame.difficulty = Motståndare
newgame.land = Terräng
newgame.land0 = Under jord
newgame.land1 = Halvt ovan jord
diff.0 = Lätt diff.0 = Lätt
diff.1 = Medel diff.1 = Medel
diff.2 = Svår diff.2 = Svår
@@ -1131,6 +1134,7 @@ ed.forts = Riddarfästningar
ed.rivals = Rivaliserande mästare ed.rivals = Rivaliserande mästare
ed.difficulty = Svårighetsgrad ed.difficulty = Svårighetsgrad
ed.portals = Portaler ed.portals = Portaler
ed.surface = Ovan jord (öster)
ed.edit_map = Redigera kartan ed.edit_map = Redigera kartan
ed.make_random = Gör den till slumpkarta ed.make_random = Gör den till slumpkarta
ed.paint_map = Generera och måla kartan ed.paint_map = Generera och måla kartan
@@ -1211,6 +1215,8 @@ ed.terrain.ruby = Rubinåder
ed.terrain.diamond = Diamantåder ed.terrain.diamond = Diamantåder
ed.terrain.steel = Stålvägg ed.terrain.steel = Stålvägg
ed.terrain.mushroom = Jättesvamp ed.terrain.mushroom = Jättesvamp
ed.terrain.grass = Gräs (ovan jord)
ed.terrain.tree = Träd
ed.place.core = Själskärna ed.place.core = Själskärna
ed.place.portal = Portal ed.place.portal = Portal
ed.place.gate = Hjälteport ed.place.gate = Hjälteport
+6
View File
@@ -48,6 +48,7 @@ pub struct MissionDef {
pub rivals: Vec<(f32, f32, u8, u8)>, // rival masters: position (fraction of the map), difficulty, side (2 Mossgrim, 3 Lady Nightwell) pub rivals: Vec<(f32, f32, u8, u8)>, // rival masters: position (fraction of the map), difficulty, side (2 Mossgrim, 3 Lady Nightwell)
pub islands: Vec<IslandDef>, pub islands: Vec<IslandDef>,
pub caves: Vec<CaveDef>, pub caves: Vec<CaveDef>,
pub surface: f32, // share of the width from where the map lies above ground (0: none)
} }
// an island in a lake of water or lava, with treasure and/or a mana spring // an island in a lake of water or lava, with treasure and/or a mana spring
@@ -166,6 +167,7 @@ fn base(key: &str, size: (usize, usize), seed: u32, goals: Vec<Goal>) -> Mission
rivals: Vec::new(), rivals: Vec::new(),
islands: Vec::new(), islands: Vec::new(),
caves: Vec::new(), caves: Vec::new(),
surface: 0.0,
} }
} }
@@ -181,6 +183,7 @@ pub fn mission_def_from_doc(d: &crate::campaign_doc::MissionDoc) -> MissionDef {
out.gate = d.waves; out.gate = d.waves;
out.gate_defenders = d.waves; out.gate_defenders = d.waves;
out.time_limit = d.time_limit; out.time_limit = d.time_limit;
out.surface = if d.gen.surface { 0.5 } else { 0.0 };
out out
} }
@@ -537,6 +540,9 @@ fn start_mission_def(d: &MissionDef, i: usize, carry: Option<&Carry>, coop: bool
} }
g.place_dwarf_caves(d.seed); g.place_dwarf_caves(d.seed);
g.add_wilds(d.seed, false); g.add_wilds(d.seed, false);
if d.surface > 0.0 {
g.add_surface(d.seed, d.surface, 2 + (h / 64) as u32);
}
g.init_research(Some(i)); g.init_research(Some(i));
g.init_fog(); g.init_fog();
let heart = d.gate || d.forts.iter().any(|f| f.heart); let heart = d.gate || d.forts.iter().any(|f| f.heart);
+3 -1
View File
@@ -145,11 +145,13 @@ pub struct GenDoc {
pub rivals: u8, // 0..2 rival masters pub rivals: u8, // 0..2 rival masters
#[serde(default = "default_difficulty")] #[serde(default = "default_difficulty")]
pub difficulty: u8, // 0 easy, 1 medium, 2 hard pub difficulty: u8, // 0 easy, 1 medium, 2 hard
#[serde(default)]
pub surface: bool, // the eastern half lies above ground (forest)
} }
impl Default for GenDoc { impl Default for GenDoc {
fn default() -> Self { fn default() -> Self {
GenDoc { rivers: 1, lava: 0, forts: 1, rivals: 0, difficulty: 1 } GenDoc { rivers: 1, lava: 0, forts: 1, rivals: 0, difficulty: 1, surface: false }
} }
} }
+1 -1
View File
@@ -91,7 +91,7 @@ pub fn coop_campaign() -> CampaignDoc {
waves: sp.waves.0, waves: sp.waves.0,
wave_limit: sp.waves.1, wave_limit: sp.waves.1,
first_wave: if sp.waves.0 { sp.waves.2 } else { 300.0 }, first_wave: if sp.waves.0 { sp.waves.2 } else { 300.0 },
gen: GenDoc { rivers: sp.gen.0, lava: sp.gen.1, forts: sp.gen.2, rivals: 0, difficulty: sp.gen.3 }, gen: GenDoc { rivers: sp.gen.0, lava: sp.gen.1, forts: sp.gen.2, rivals: 0, difficulty: sp.gen.3, surface: false },
next: (i + 1 < specs.len()).then(|| format!("c{:02}", i + 2)), next: (i + 1 < specs.len()).then(|| format!("c{:02}", i + 2)),
pos: (40.0 + (i % 5) as f32 * 220.0, 40.0 + (i / 5) as f32 * 140.0), pos: (40.0 + (i % 5) as f32 * 220.0, 40.0 + (i / 5) as f32 * 140.0),
..MissionDoc::default() ..MissionDoc::default()
+8 -3
View File
@@ -35,7 +35,7 @@ enum Cat {
const CATS: [Cat; 9] = [Cat::Terrain, Cat::Rooms, Cat::Places, Cat::Units, Cat::Towers, Cat::Traps, Cat::Forts, Cat::Zones, Cat::Erase]; const CATS: [Cat; 9] = [Cat::Terrain, Cat::Rooms, Cat::Places, Cat::Units, Cat::Towers, Cat::Traps, Cat::Forts, Cat::Zones, Cat::Erase];
const TERRAIN: [&str; 14] = ["floor", "earth", "reinforced", "rock", "water", "lava", "bridge", "lavabridge", "gold", "silver", "ruby", "diamond", "steel", "mushroom"]; const TERRAIN: [&str; 16] = ["floor", "earth", "reinforced", "rock", "water", "lava", "bridge", "lavabridge", "gold", "silver", "ruby", "diamond", "steel", "mushroom", "grass", "tree"];
const PLACES: [&str; 7] = ["core", "portal", "gate", "spring", "shard", "chest", "chained"]; const PLACES: [&str; 7] = ["core", "portal", "gate", "spring", "shard", "chest", "chained"];
const OWNERS: [Owner; 6] = [Owner::Player, Owner::Hero, Owner::Rival1, Owner::Rival2, Owner::Rival3, Owner::None]; const OWNERS: [Owner; 6] = [Owner::Player, Owner::Hero, Owner::Rival1, Owner::Rival2, Owner::Rival3, Owner::None];
const ZONE_SHAPES: [&str; 4] = ["rect", "circle", "tiles", "line"]; const ZONE_SHAPES: [&str; 4] = ["rect", "circle", "tiles", "line"];
@@ -76,6 +76,8 @@ fn kind_of_terrain(t: &str) -> (Kind, bool) {
"bridge" => (Kind::Bridge, false), "bridge" => (Kind::Bridge, false),
"lavabridge" => (Kind::LavaBridge, false), "lavabridge" => (Kind::LavaBridge, false),
"mushroom" => (Kind::Mushroom, false), "mushroom" => (Kind::Mushroom, false),
"grass" => (Kind::Floor, false),
"tree" => (Kind::Tree, false),
"gold" => (Kind::Gold, false), "gold" => (Kind::Gold, false),
"silver" => (Kind::Silver, false), "silver" => (Kind::Silver, false),
"ruby" => (Kind::Ruby, false), "ruby" => (Kind::Ruby, false),
@@ -209,7 +211,10 @@ impl MapEdit {
} }
match self.cat { match self.cat {
Cat::Terrain => { Cat::Terrain => {
let (k, reinforced) = kind_of_terrain(TERRAIN[self.item.min(TERRAIN.len() - 1)]); let name = TERRAIN[self.item.min(TERRAIN.len() - 1)];
let (k, reinforced) = kind_of_terrain(name);
// grass and trees lie above ground; anything else is painted underground
self.game.map.set_sky(i, matches!(name, "grass" | "tree"));
self.game.torches.retain(|tr| tr.wall != i); self.game.torches.retain(|tr| tr.wall != i);
let owner = if k == Kind::Floor || reinforced || matches!(k, Kind::Bridge | Kind::LavaBridge) { OWNERS[self.owner] } else { Owner::None }; let owner = if k == Kind::Floor || reinforced || matches!(k, Kind::Bridge | Kind::LavaBridge) { OWNERS[self.owner] } else { Owner::None };
let tl = &mut self.game.map.t[i]; let tl = &mut self.game.map.t[i];
@@ -218,7 +223,7 @@ impl MapEdit {
tl.owner = if reinforced && owner == Owner::None { Owner::Player } else { owner }; tl.owner = if reinforced && owner == Owner::None { Owner::Player } else { owner };
tl.marked = false; tl.marked = false;
if tl.plant() { if tl.plant() {
tl.hp = crate::wood::MUSHROOM_HP; tl.hp = if k == Kind::Tree { crate::wood::TREE_HP } else { crate::wood::MUSHROOM_HP };
} }
tl.gold = match k { tl.gold = match k {
Kind::Gold => 1200, Kind::Gold => 1200,
+2
View File
@@ -777,6 +777,7 @@ impl Editor {
(tr("ed.rivals"), m.gen.rivals.to_string(), 6), (tr("ed.rivals"), m.gen.rivals.to_string(), 6),
(tr("ed.difficulty"), tr(["diff.0", "diff.1", "diff.2"][m.gen.difficulty.min(2) as usize]), 7), (tr("ed.difficulty"), tr(["diff.0", "diff.1", "diff.2"][m.gen.difficulty.min(2) as usize]), 7),
(tr("ed.portals"), m.portals.to_string(), 8), (tr("ed.portals"), m.portals.to_string(), 8),
(tr("ed.surface"), tr(if m.gen.surface { "val.on" } else { "val.off" }), 9),
]; ];
if fixed { if fixed {
rows.truncate(0); rows.truncate(0);
@@ -794,6 +795,7 @@ impl Editor {
5 => m.gen.forts = (m.gen.forts as i32 + d.signum()).clamp(0, 3) as u8, 5 => m.gen.forts = (m.gen.forts as i32 + d.signum()).clamp(0, 3) as u8,
6 => m.gen.rivals = (m.gen.rivals as i32 + d.signum()).clamp(0, 2) as u8, 6 => m.gen.rivals = (m.gen.rivals as i32 + d.signum()).clamp(0, 2) as u8,
7 => m.gen.difficulty = (m.gen.difficulty as i32 + d.signum()).clamp(0, 2) as u8, 7 => m.gen.difficulty = (m.gen.difficulty as i32 + d.signum()).clamp(0, 2) as u8,
9 => m.gen.surface = !m.gen.surface,
_ => m.portals = (m.portals as i32 + d.signum()).clamp(0, 6) as usize, _ => m.portals = (m.portals as i32 + d.signum()).clamp(0, 6) as usize,
} }
} }
+2 -2
View File
@@ -93,7 +93,7 @@ pub fn demo_campaign() -> CampaignDoc {
seed: 4242, seed: 4242,
gold: 4000.0, gold: 4000.0,
goals: vec!["Towers".into()], goals: vec!["Towers".into()],
gen: GenDoc { rivers: 1, lava: 0, forts: 1, rivals: 0, difficulty: 0 }, gen: GenDoc { rivers: 1, lava: 0, forts: 1, rivals: 0, difficulty: 0, surface: false },
next: Some("demo2".into()), next: Some("demo2".into()),
branches: vec![BranchDoc { id: "rich".into(), goal: "Loot(2000)".into(), title: tr("demo.branch"), next: "demo2".into(), priority: Some(1) }], branches: vec![BranchDoc { id: "rich".into(), goal: "Loot(2000)".into(), title: tr("demo.branch"), next: "demo2".into(), priority: Some(1) }],
pos: (40.0, 60.0), pos: (40.0, 60.0),
@@ -226,7 +226,7 @@ pub fn demo_campaign() -> CampaignDoc {
seed: 77, seed: 77,
gold: 3000.0, gold: 3000.0,
goals: vec!["KillAll".into()], goals: vec!["KillAll".into()],
gen: GenDoc { rivers: 1, lava: 1, forts: 1, rivals: 0, difficulty: 0 }, gen: GenDoc { rivers: 1, lava: 1, forts: 1, rivals: 0, difficulty: 0, surface: false },
pos: (300.0, 60.0), pos: (300.0, 60.0),
..MissionDoc::default() ..MissionDoc::default()
}; };
+1 -1
View File
@@ -788,7 +788,7 @@ mod tests {
use crate::blocks::{Block, Val}; use crate::blocks::{Block, Val};
use crate::campaign_doc::*; use crate::campaign_doc::*;
let mut doc = CampaignDoc { name: "Coop test".into(), ..CampaignDoc::default() }; let mut doc = CampaignDoc { name: "Coop test".into(), ..CampaignDoc::default() };
let mut m = MissionDoc { key: "c1".into(), title: "Random".into(), size: (80, 80), seed: 5, goals: vec!["KillAll".into(), "Rivals".into()], waves: true, first_wave: 3.0, gen: GenDoc { rivers: 1, lava: 1, forts: 1, rivals: 1, difficulty: 1 }, ..MissionDoc::default() }; let mut m = MissionDoc { key: "c1".into(), title: "Random".into(), size: (80, 80), seed: 5, goals: vec!["KillAll".into(), "Rivals".into()], waves: true, first_wave: 3.0, gen: GenDoc { rivers: 1, lava: 1, forts: 1, rivals: 1, difficulty: 1, surface: false }, ..MissionDoc::default() };
m.scripts.start = vec![Block::Gold { amount: Val::num(250.0) }]; m.scripts.start = vec![Block::Gold { amount: Val::num(250.0) }];
m.scripts.triggers = vec![TriggerDoc { name: "wave".into(), kind: TriggerKind::Timer { secs: 2.0, every: true }, once: false, enabled: true, script: vec![Block::Wave { count: Val::num(1.0) }] }]; m.scripts.triggers = vec![TriggerDoc { name: "wave".into(), kind: TriggerKind::Timer { secs: 2.0, every: true }, once: false, enabled: true, script: vec![Block::Wave { count: Val::num(1.0) }] }];
doc.missions.push(m.clone()); doc.missions.push(m.clone());
+7 -1
View File
@@ -37,6 +37,7 @@ mod sfx;
mod tracks; mod tracks;
mod traps; mod traps;
mod sound; mod sound;
mod surface;
mod spells; mod spells;
mod touch; mod touch;
mod ui; mod ui;
@@ -173,7 +174,12 @@ fn finish_rect(app: &mut App, tool: Tool, start: (i32, i32), mx: f32, my: f32) {
} }
fn new_game(seed: u32, s: &Settings) -> (Game, Cam) { fn new_game(seed: u32, s: &Settings) -> (Game, Cam) {
let g = Game::new_free(seed, s.map_w, s.map_h, s.difficulty); let mut g = Game::new_free(seed, s.map_w, s.map_h, s.difficulty);
if s.surface {
// the eastern half above ground: forest, paths, cave mouths
g.add_surface(seed, 0.5, 2 + (s.map_h / 64) as u32);
g.init_fog();
}
let (cx, cy) = g.map.core; let (cx, cy) = g.map.core;
let cam = Cam::new(cx as f32 + 1.5, cy as f32 + 1.5); let cam = Cam::new(cx as f32 + 1.5, cy as f32 + 1.5);
(g, cam) (g, cam)
+18 -1
View File
@@ -350,6 +350,8 @@ pub struct Map {
pub explored: Vec<bool>, // fog of war: tiles the dungeon knows (empty: no fog) pub explored: Vec<bool>, // fog of war: tiles the dungeon knows (empty: no fog)
#[serde(skip)] #[serde(skip)]
pub visible: Vec<bool>, // fog of war: tiles in sight right now pub visible: Vec<bool>, // fog of war: tiles in sight right now
#[serde(default, with = "crate::fog::bits")]
pub sky: Vec<bool>, // tiles above ground (daylight, grass); empty: all underground
} }
fn default_size() -> usize { fn default_size() -> usize {
@@ -367,6 +369,21 @@ impl Map {
} }
} }
// tile i lies above ground
pub fn sky(&self, i: usize) -> bool {
self.sky.get(i).copied().unwrap_or(false)
}
pub fn set_sky(&mut self, i: usize, on: bool) {
if self.sky.len() != self.t.len() {
if !on {
return;
}
self.sky = vec![false; self.t.len()];
}
self.sky[i] = on;
}
pub fn get(&self, x: i32, y: i32) -> Option<&Tile> { pub fn get(&self, x: i32, y: i32) -> Option<&Tile> {
if inb(x, y) { if inb(x, y) {
Some(&self.t[idx(x, y)]) Some(&self.t[idx(x, y)])
@@ -587,7 +604,7 @@ impl Map {
tl.hp = 6.0; tl.hp = 6.0;
} }
} }
Map { w, h, t, core, portals, legacy_portal: None, gate, treasures, explored: Vec::new(), visible: Vec::new() } Map { w, h, t, core, portals, legacy_portal: None, gate, treasures, explored: Vec::new(), visible: Vec::new(), sky: Vec::new() }
} }
// Carves a meandering river; the player's area, the core and portals stay dry. // Carves a meandering river; the player's area, the core and portals stay dry.
+15 -7
View File
@@ -230,6 +230,7 @@ enum Btn {
Tab(usize), Tab(usize),
Change(usize, i32), Change(usize, i32),
MapW(i32), MapW(i32),
Land,
MapH(i32), MapH(i32),
Difficulty(i32), Difficulty(i32),
Start, Start,
@@ -675,6 +676,10 @@ impl Menu {
settings.difficulty = (settings.difficulty as i32 + d).rem_euclid(3) as u8; settings.difficulty = (settings.difficulty as i32 + d).rem_euclid(3) as u8;
MenuAction::SettingsChanged MenuAction::SettingsChanged
} }
Some(Btn::Land) => {
settings.surface = !settings.surface;
MenuAction::SettingsChanged
}
Some(Btn::MapH(d)) => { Some(Btn::MapH(d)) => {
settings.map_h = (settings.map_h as i32 + d * 32).clamp(MIN_SIZE as i32, MAX_SIZE as i32) as usize; settings.map_h = (settings.map_h as i32 + d * 32).clamp(MIN_SIZE as i32, MAX_SIZE as i32) as usize;
MenuAction::SettingsChanged MenuAction::SettingsChanged
@@ -1153,27 +1158,30 @@ impl Menu {
let sh = sh(); let sh = sh();
let (mx, my) = mouse(); let (mx, my) = mouse();
let cols = 48.min((sw / CW()) as i32); let cols = 48.min((sw / CW()) as i32);
let rows = 22; let rows = 25;
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.new"), FRAME_HI); gfx.window_titled(x, y, cols, rows, S(), true, FRAME_HI, Color::new(0.05, 0.03, 0.04, 0.97), &tr("menu.new"), FRAME_HI);
let diff = tr(&format!("diff.{}", s.difficulty.min(2))); let diff = tr(&format!("diff.{}", s.difficulty.min(2)));
let lines = [("newgame.width", s.map_w.to_string(), 0), ("newgame.height", s.map_h.to_string(), 1), ("newgame.difficulty", diff, 2)]; let land = tr(if s.surface { "newgame.land1" } else { "newgame.land0" });
let lines = [("newgame.width", s.map_w.to_string(), 0), ("newgame.height", s.map_h.to_string(), 1), ("newgame.difficulty", diff, 2), ("newgame.land", land, 3)];
for (k, (key, val, which)) in lines.iter().enumerate() { for (k, (key, val, which)) in lines.iter().enumerate() {
let (key, which) = (*key, *which); let (key, which) = (*key, *which);
let by = y + CH() * (2.0 + k as f32 * 2.5); let by = y + CH() * (2.0 + k as f32 * 2.5);
gfx.text(&tr(key), x + CW() * 3.0, by + CH() * 0.5, S(), TXT); gfx.text(&tr(key), x + CW() * 3.0, by + CH() * 0.5, S(), TXT);
let vw = 14.0 * CW(); let vw = 18.0 * CW();
let vx = x + cols as f32 * CW() - vw - CW() * 3.0; let vx = x + cols as f32 * CW() - vw - CW() * 3.0;
let left = Rect::new(vx, by, CW() * 2.5, CH() * 2.0); let left = Rect::new(vx, by, CW() * 2.5, CH() * 2.0);
let right = Rect::new(vx + vw - CW() * 2.5, by, CW() * 2.5, CH() * 2.0); let right = Rect::new(vx + vw - CW() * 2.5, by, CW() * 2.5, CH() * 2.0);
button(gfx, left, "<", left.contains(vec2(mx, my)), FRAME); button(gfx, left, "<", left.contains(vec2(mx, my)), FRAME);
button(gfx, right, ">", right.contains(vec2(mx, my)), FRAME); button(gfx, right, ">", right.contains(vec2(mx, my)), FRAME);
let v = val.clone(); let v = val.clone();
gfx.text(&v, vx + (vw - gfx.measure(&v, S())) * 0.5, by + CH() * 0.5, S(), GOLD); let vs = if gfx.measure(&v, S()) > vw - CW() * 5.5 { SMALL() } else { S() };
gfx.text(&v, vx + (vw - gfx.measure(&v, vs)) * 0.5, by + CH() * 0.5, vs, GOLD);
let (l, r) = match which { let (l, r) = match which {
0 => (Btn::MapW(-1), Btn::MapW(1)), 0 => (Btn::MapW(-1), Btn::MapW(1)),
1 => (Btn::MapH(-1), Btn::MapH(1)), 1 => (Btn::MapH(-1), Btn::MapH(1)),
3 => (Btn::Land, Btn::Land),
_ => (Btn::Difficulty(-1), Btn::Difficulty(1)), _ => (Btn::Difficulty(-1), Btn::Difficulty(1)),
}; };
self.buttons.push((left, l)); self.buttons.push((left, l));
@@ -1183,14 +1191,14 @@ impl Menu {
let area = (s.map_w * s.map_h) as f32 / 4096.0; let area = (s.map_w * s.map_h) as f32 / 4096.0;
let portals = (area.round() as usize).max(1); let portals = (area.round() as usize).max(1);
let info = trf("newgame.info", &[portals.to_string(), (portals - 1).to_string()]); let info = trf("newgame.info", &[portals.to_string(), (portals - 1).to_string()]);
gfx.text(&info, x + CW() * 3.0, y + CH() * 10.0, S(), DIM); gfx.text(&info, x + CW() * 3.0, y + CH() * 12.5, S(), DIM);
let opp = tr(if s.map_w * s.map_h > 128 * 128 { "newgame.opponents2" } else { "newgame.opponents1" }); let opp = tr(if s.map_w * s.map_h > 128 * 128 { "newgame.opponents2" } else { "newgame.opponents1" });
let maxc = (cols - 6) as usize; let maxc = (cols - 6) as usize;
for (k, l) in wrap(&opp, maxc).iter().take(2).enumerate() { for (k, l) in wrap(&opp, maxc).iter().take(2).enumerate() {
gfx.text(l, x + CW() * 3.0, y + CH() * (11.0 + k as f32), S(), DIM); gfx.text(l, x + CW() * 3.0, y + CH() * (13.5 + k as f32), S(), DIM);
} }
let w = (cols - 6) as f32 * CW(); let w = (cols - 6) as f32 * CW();
self.column(gfx, x + CW() * 3.0, y + CH() * 14.0, w, &[(Btn::Start, tr("newgame.start")), (Btn::Back, tr("menu.back"))]); self.column(gfx, x + CW() * 3.0, y + CH() * 16.5, w, &[(Btn::Start, tr("newgame.start")), (Btn::Back, tr("menu.back"))]);
} }
fn draw_pause(&mut self, gfx: &Gfx) { fn draw_pause(&mut self, gfx: &Gfx) {
+19 -7
View File
@@ -449,6 +449,10 @@ fn ambient_field(map: &Map) -> Vec<f32> {
(false, Owner::Hero) => 0.6, (false, Owner::Hero) => 0.6,
_ => 0.0, _ => 0.0,
}; };
// daylight above ground (cliffs and trees too)
if map.sky(i) {
f[i] = 1.6;
}
if t.river() == Some(Kind::Lava) { if t.river() == Some(Kind::Lava) {
f[i] = f[i].max(0.8); // lava lights up its surroundings f[i] = f[i].max(0.8); // lava lights up its surroundings
} }
@@ -672,7 +676,11 @@ fn build_chunk(map: &Map, amb: &[f32], glow: &[f32], cx: i32, cz: i32, atlas: &T
for z in cz * CHUNK..((cz + 1) * CHUNK).min(mh() as i32) { for z in cz * CHUNK..((cz + 1) * CHUNK).min(mh() as i32) {
for x in cx * CHUNK..((cx + 1) * CHUNK).min(mw() as i32) { for x in cx * CHUNK..((cx + 1) * CHUNK).min(mw() as i32) {
let t = map.t[idx(x, z)]; let t = map.t[idx(x, z)];
let tex = Gfx::tile_tex(t.kind, t.owner, t.var, t.reinforced); let mut tex = Gfx::tile_tex(t.kind, t.owner, t.var, t.reinforced);
// open land above ground is grass (until a master claims it)
if map.sky(idx(x, z)) && t.kind == Kind::Floor && t.owner == Owner::None {
tex = T_GRASS + (t.var as u32 & 3);
}
let (ua, ub) = cell_uv(tex); let (ua, ub) = cell_uv(tex);
let (xf, zf) = (x as f32, z as f32); let (xf, zf) = (x as f32, z as f32);
if !map.shown(idx(x, z)) { if !map.shown(idx(x, z)) {
@@ -836,11 +844,13 @@ fn build_chunk(map: &Map, amb: &[f32], glow: &[f32], cx: i32, cz: i32, atlas: &T
c, c,
[Vec3::Y; 4], [Vec3::Y; 4],
); );
// outlines: north and west edge, only between two open tiles (each edge once) // outlines: north and west edge, only between two open tiles (each edge once);
if !solid_at(map, x, z - 1) { // open land above ground has none
let outdoors = map.sky(idx(x, z)) && t.owner == Owner::None;
if !solid_at(map, x, z - 1) && !outdoors {
ln.strip(vec3(xf, 0.0, zf), vec3(xf + 1.0, 0.0, zf), Vec3::Y, OUTLINE_W, OUTLINE_C); ln.strip(vec3(xf, 0.0, zf), vec3(xf + 1.0, 0.0, zf), Vec3::Y, OUTLINE_W, OUTLINE_C);
} }
if !solid_at(map, x - 1, z) { if !solid_at(map, x - 1, z) && !outdoors {
ln.strip(vec3(xf, 0.0, zf), vec3(xf, 0.0, zf + 1.0), Vec3::Y, OUTLINE_W, OUTLINE_C); ln.strip(vec3(xf, 0.0, zf), vec3(xf, 0.0, zf + 1.0), Vec3::Y, OUTLINE_W, OUTLINE_C);
} }
// room decor // room decor
@@ -871,7 +881,7 @@ fn build_chunk(map: &Map, amb: &[f32], glow: &[f32], cx: i32, cz: i32, atlas: &T
} }
} }
// wear and tear: grit and cracks on plain floors // wear and tear: grit and cracks on plain floors
if matches!(t.kind, Kind::Floor) { if matches!(t.kind, Kind::Floor) && !outdoors {
let hsh = (x as u32).wrapping_mul(2654435761) ^ (z as u32).wrapping_mul(2246822519); let hsh = (x as u32).wrapping_mul(2654435761) ^ (z as u32).wrapping_mul(2246822519);
let hsh = (hsh ^ (hsh >> 15)).wrapping_mul(2246822519); let hsh = (hsh ^ (hsh >> 15)).wrapping_mul(2246822519);
let decal = match hsh % 11 { let decal = match hsh % 11 {
@@ -1083,7 +1093,7 @@ fn chunk_sig(map: &Map, cx: i32, cz: i32) -> u64 {
Kind::Drawbridge => t.res as u64, Kind::Drawbridge => t.res as u64,
_ => 0, _ => 0,
}; };
let shown = map.shown(idx(x, z)) as u64; let shown = map.shown(idx(x, z)) as u64 | (map.sky(idx(x, z)) as u64) << 1;
(t.kind as u64) | (t.owner as u64) << 5 | (t.marked as u64) << 8 | (t.reinforced as u64) << 9 | shown << 10 | extra << 11 (t.kind as u64) | (t.owner as u64) << 5 | (t.marked as u64) << 8 | (t.reinforced as u64) << 9 | shown << 10 | extra << 11
} }
None => 0xff, None => 0xff,
@@ -1462,7 +1472,9 @@ impl R3d {
let amb = |x: f32, z: f32| { let amb = |x: f32, z: f32| {
let (ix, iz) = (x as i32, z as i32); let (ix, iz) = (x as i32, z as i32);
let own = g.map.get(ix, iz).map_or(false, |t| t.owner == Owner::Player); let own = g.map.get(ix, iz).map_or(false, |t| t.owner == Owner::Player);
if own { if inb(ix, iz) && g.map.sky(idx(ix, iz)) {
0.95
} else if own {
0.75 0.75
} else { } else {
0.5 0.5
+9
View File
@@ -200,4 +200,13 @@ fn export_scenes() {
g.plan_walls(cx + 5, cy - 1, cx + 5, cy + 2); g.plan_walls(cx + 5, cy - 1, cx + 5, cy + 2);
run(&mut g, 2.0); run(&mut g, 2.0);
save("wood", &mut g, mx as f32 + 0.5, my as f32 + 0.5, 14.0, 0.0, false); save("wood", &mut g, mx as f32 + 0.5, my as f32 + 0.5, 14.0, 0.0, false);
// above ground: the cliff with a cave mouth, grass, a forest
let mut g = Game::new(23, 128, 96);
g.add_surface(23, 0.45, 3);
g.init_fog();
let mid = (mh() / 2) as i32;
let ex = (0..mw() as i32).find(|&x| g.map.sky(idx(x, mid))).unwrap_or(60);
run(&mut g, 1.0);
save("surface", &mut g, ex as f32 + 6.0, mid as f32, 24.0, 0.0, false);
} }
+2
View File
@@ -162,6 +162,7 @@ pub struct Settings {
pub difficulty: u8, // rival masters in free games: 0 easy, 1 medium, 2 hard pub difficulty: u8, // rival masters in free games: 0 easy, 1 medium, 2 hard
pub map_w: usize, // size of new maps (64..256) pub map_w: usize, // size of new maps (64..256)
pub map_h: usize, pub map_h: usize,
pub surface: bool, // new free games: the eastern half lies above ground (forest)
// key bindings: action id -> key name // key bindings: action id -> key name
pub keys: BTreeMap<String, String>, pub keys: BTreeMap<String, String>,
} }
@@ -196,6 +197,7 @@ impl Default for Settings {
difficulty: 1, difficulty: 1,
map_w: 64, map_w: 64,
map_h: 64, map_h: 64,
surface: false,
keys: BTreeMap::new(), keys: BTreeMap::new(),
} }
} }
+142
View File
@@ -0,0 +1,142 @@
// The surface world: part of a map lies above ground - grass in daylight, forests whose trees
// imps fell for wood, winding paths, and a cliff with a few cave mouths between the dungeon in
// the rock and the open land. (The knights' fortress up there: see fortress.rs.)
use crate::game::*;
use crate::map::*;
// value noise in 0..1, smooth over `cell` tiles
fn noise(x: i32, y: i32, cell: i32, seed: u32) -> f32 {
let h = |x: i32, y: i32| {
let mut v = (x as u32).wrapping_mul(374761393) ^ (y as u32).wrapping_mul(668265263) ^ seed.wrapping_mul(2246822519);
v = (v ^ (v >> 13)).wrapping_mul(1274126177);
((v ^ (v >> 16)) & 0xffff) as f32 / 65535.0
};
let (cx, cy) = (x.div_euclid(cell), y.div_euclid(cell));
let (fx, fy) = ((x.rem_euclid(cell)) as f32 / cell as f32, (y.rem_euclid(cell)) as f32 / cell as f32);
let s = |t: f32| t * t * (3.0 - 2.0 * t);
let (sx, sy) = (s(fx), s(fy));
let a = h(cx, cy) * (1.0 - sx) + h(cx + 1, cy) * sx;
let b = h(cx, cy + 1) * (1.0 - sx) + h(cx + 1, cy + 1) * sx;
a * (1.0 - sy) + b * sy
}
impl Game {
// Everything east of `from` (share of the width, the edge a little ragged) becomes surface:
// open grass with forests and paths. A cliff along the edge keeps the dungeon apart, broken
// by `mouths` cave entrances. Mission pieces (core, portals, gate, towers ...) stay.
pub fn add_surface(&mut self, seed: u32, from: f32, mouths: u32) {
let (w, h) = (mw() as i32, mh() as i32);
let edge = |y: i32| (from * w as f32) as i32 + ((noise(0, y, 6, seed ^ 0x51) - 0.5) * 6.0) as i32;
let mut mouth_rows: Vec<i32> = (0..mouths).map(|k| ((k as f32 + 0.5) / mouths as f32 * (h - 16) as f32) as i32 + 8 + ((noise(k as i32, 3, 2, seed) - 0.5) * 8.0) as i32).collect();
mouth_rows.sort_unstable();
// winding paths across the land (stay open, no trees)
let paths: Vec<f32> = (0..2).map(|k| 0.25 + 0.5 * k as f32 + (noise(k, 9, 2, seed) - 0.5) * 0.2).collect();
for y in 0..h {
let ex = edge(y);
for x in 0..w {
if x < ex - 1 {
continue;
}
let i = idx(x, y);
let t = self.map.t[i];
let keep = matches!(t.kind, Kind::Core | Kind::Portal | Kind::Gate | Kind::Tower | Kind::Shard | Kind::ManaSpring | Kind::RuneChest | Kind::Water | Kind::Lava | Kind::Bridge | Kind::LavaBridge | Kind::Door)
|| t.owner != Owner::None
|| x < 2
|| y < 2
|| x >= w - 2
|| y >= h - 2;
// the cliff: two tiles of rock along the edge, open where a cave mouth is
if x <= ex {
let mouth = mouth_rows.iter().any(|&m| (y - m).abs() <= 1);
if !keep {
let tl = &mut self.map.t[i];
if mouth {
tl.kind = Kind::Floor;
} else {
tl.kind = Kind::Rock;
}
tl.owner = Owner::None;
tl.gold = 0;
tl.marked = false;
tl.reinforced = false;
}
continue;
}
self.map.set_sky(i, true);
if keep {
continue;
}
let on_path = paths.iter().any(|&p| {
let py = p * h as f32 + (noise(x, 0, 9, seed ^ 0x77) - 0.5) * 14.0;
(y as f32 - py).abs() < 1.2
});
let wood = noise(x, y, 5, seed ^ 0xf0);
let rocks = noise(x, y, 4, seed ^ 0x0b);
let tl = &mut self.map.t[i];
tl.owner = Owner::None;
tl.gold = 0;
tl.marked = false;
tl.reinforced = false;
tl.hp = 0.0;
if !on_path && rocks > 0.86 {
tl.kind = Kind::Rock;
} else if !on_path && wood > 0.52 && (x * 7 + y * 13) % 3 != 0 {
tl.kind = Kind::Tree;
tl.hp = crate::wood::TREE_HP;
} else {
tl.kind = Kind::Floor;
}
}
}
// the cave mouths run through into the dungeon: dig until open ground is reached
for &m in &mouth_rows {
let ex = edge(m);
let mut x = ex - 2;
while x > 2 {
let i = idx(x, m);
if self.map.t[i].walkable() {
break;
}
if self.map.t[i].solid() && !matches!(self.map.t[i].kind, Kind::Steel) {
let tl = &mut self.map.t[i];
tl.kind = Kind::Floor;
tl.owner = Owner::None;
tl.gold = 0;
}
x -= 1;
}
}
self.recount();
}
// the share of the map above ground (0: a dungeon map)
#[allow(dead_code)] // tests and debugging
pub fn surface_share(&self) -> f32 {
if self.map.sky.is_empty() {
return 0.0;
}
self.map.sky.iter().filter(|&&s| s).count() as f32 / self.map.sky.len() as f32
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn half_the_map_becomes_a_forest_above_ground() {
let mut g = Game::new(21, 128, 96);
let core = g.map.core;
g.add_surface(21, 0.5, 3);
let share = g.surface_share();
assert!(share > 0.35 && share < 0.6, "share {}", share);
let trees = g.map.t.iter().filter(|t| t.kind == Kind::Tree).count();
assert!(trees > 200, "{} trees", trees);
assert_eq!(g.map.core, core);
assert_eq!(g.map.t[idx(core.0 + 1, core.1 + 1)].kind, Kind::Core);
// the surface can be reached from the core (cave mouths, trees can be felled)
let goal = |i: usize| g.map.sky(i) && g.map.t[i].walkable();
assert!(g.map.search(idx(core.0 + 1, core.1 + 3), g.map.hero_cost_as(false), goal, true).is_some());
}
}