From f8eda88bb34000484963d9312344b20e438ad080 Mon Sep 17 00:00:00 2001 From: Richard Zink Date: Fri, 6 Nov 2026 10:25:24 +0100 Subject: [PATCH] Mushroom groves: unclaimable moss where giant mushrooms grow slowly and glow when ready to fell Co-Authored-By: Claude Opus 5.5 --- lang/cs.lang | 5 ++ lang/da.lang | 5 ++ lang/de.lang | 5 ++ lang/el.lang | 5 ++ lang/en.lang | 5 ++ lang/es.lang | 5 ++ lang/fi.lang | 5 ++ lang/hr.lang | 5 ++ lang/hu.lang | 5 ++ lang/it.lang | 5 ++ lang/nl.lang | 5 ++ lang/no.lang | 5 ++ lang/pt.lang | 5 ++ lang/ro.lang | 5 ++ lang/ru.lang | 5 ++ lang/sk.lang | 5 ++ lang/sr.lang | 5 ++ lang/sv.lang | 5 ++ src/editor/map.rs | 10 ++- src/game.rs | 7 ++- src/gfx.rs | 18 ++++++ src/map.rs | 20 +++++- src/render3d.rs | 30 ++++++++- src/scenes.rs | 19 ++++++ src/ui.rs | 16 ++++- src/wood.rs | 154 ++++++++++++++++++++++++++++++++++++---------- 26 files changed, 321 insertions(+), 43 deletions(-) diff --git a/lang/cs.lang b/lang/cs.lang index 2e761c3..963f27b 100644 --- a/lang/cs.lang +++ b/lang/cs.lang @@ -462,6 +462,10 @@ tile.lava_info = Co do ní spadne, shoří. Přecházejí ji kamenné mosty. tile.bridge = Dřevěný most tile.lavabridge = Kamenný most tile.mushroom = Obří houba +tile.mushroom_grown = Dorostlá: připravená ke kácení. +tile.mushroom_growing = Roste: {0}% - pokácet ji lze až dorostlou. +tile.grove = Houbový háj +tile.grove_info = Tento mech nikdo nedobude. Obří houby tu znovu dorůstají. tile.tree = Strom tile.plant_info = Označ krumpáčem: skřítek ho pokácí za {0} dřeva. tile.drawbridge = Padací most @@ -1275,6 +1279,7 @@ ed.terrain.ruby = Rubínová žíla ed.terrain.diamond = Diamantová žíla ed.terrain.steel = Ocelová zeď ed.terrain.mushroom = Obří houba +ed.terrain.grove = Houbový háj ed.terrain.grass = Tráva (povrch) ed.terrain.tree = Strom ed.place.core = Jádro duše diff --git a/lang/da.lang b/lang/da.lang index f558e05..3fe7169 100644 --- a/lang/da.lang +++ b/lang/da.lang @@ -461,6 +461,10 @@ tile.lava_info = Hvad der falder i, brænder. Stenbroer fører over. tile.bridge = Træbro tile.lavabridge = Stenbro tile.mushroom = Kæmpesvamp +tile.mushroom_grown = Fuldvoksen: klar til fældning. +tile.mushroom_growing = Vokser: {0}% - kan først fældes, når den er fuldvoksen. +tile.grove = Svampelund +tile.grove_info = Ingen kan erobre denne mos. Her vokser kæmpesvampe igen. tile.tree = Træ tile.plant_info = Markér med hakken: en imp fælder den for {0} træ. tile.drawbridge = Vindebro @@ -1274,6 +1278,7 @@ ed.terrain.ruby = Rubinåre ed.terrain.diamond = Diamantåre ed.terrain.steel = Stålmur ed.terrain.mushroom = Kæmpesvamp +ed.terrain.grove = Svampelund ed.terrain.grass = Græs (over jorden) ed.terrain.tree = Træ ed.place.core = Sjælekerne diff --git a/lang/de.lang b/lang/de.lang index d31d42e..4d89ae7 100644 --- a/lang/de.lang +++ b/lang/de.lang @@ -461,6 +461,10 @@ tile.lava_info = Was hineinfällt, verbrennt. Steinbrücken führen hinüber. tile.bridge = Holzbrücke tile.lavabridge = Steinbrücke tile.mushroom = Riesenpilz +tile.mushroom_grown = Ausgewachsen: bereit zum Fällen. +tile.mushroom_growing = Wächst: {0}% - erst ausgewachsen kann er gefällt werden. +tile.grove = Pilzhain +tile.grove_info = Dieses Moos kann niemand erobern. Hier wachsen Riesenpilze nach. tile.tree = Baum tile.plant_info = Mit der Spitzhacke markieren: Ein Imp fällt ihn für {0} Holz. tile.drawbridge = Zugbrücke @@ -1274,6 +1278,7 @@ ed.terrain.ruby = Rubinader ed.terrain.diamond = Diamantader ed.terrain.steel = Stahlwand ed.terrain.mushroom = Riesenpilz +ed.terrain.grove = Pilzhain ed.terrain.grass = Gras (Oberwelt) ed.terrain.tree = Baum ed.place.core = Seelenkern diff --git a/lang/el.lang b/lang/el.lang index bb38080..53b7551 100644 --- a/lang/el.lang +++ b/lang/el.lang @@ -462,6 +462,10 @@ tile.lava_info = Ό,τι πέφτει μέσα καίγεται. Οι πέτρι tile.bridge = Ξύλινη γέφυρα tile.lavabridge = Πέτρινη γέφυρα tile.mushroom = Γιγάντιο μανιτάρι +tile.mushroom_grown = Ώριμο: έτοιμο για κόψιμο. +tile.mushroom_growing = Μεγαλώνει: {0}% - κόβεται μόνο όταν ωριμάσει. +tile.grove = Άλσος μανιταριών +tile.grove_info = Κανείς δεν μπορεί να κατακτήσει αυτά τα βρύα. Εδώ ξαναφυτρώνουν γιγάντια μανιτάρια. tile.tree = Δέντρο tile.plant_info = Σημάδεψέ το με την αξίνα: ένα διαβολάκι το κόβει για {0} ξύλο. tile.drawbridge = Κινητή γέφυρα @@ -1275,6 +1279,7 @@ ed.terrain.ruby = Φλέβα ρουμπινιού ed.terrain.diamond = Φλέβα διαμαντιού ed.terrain.steel = Ατσάλινος τοίχος ed.terrain.mushroom = Γιγάντιο μανιτάρι +ed.terrain.grove = Άλσος μανιταριών ed.terrain.grass = Γρασίδι (επιφάνεια) ed.terrain.tree = Δέντρο ed.place.core = Πυρήνας ψυχής diff --git a/lang/en.lang b/lang/en.lang index d2895e0..16ad566 100644 --- a/lang/en.lang +++ b/lang/en.lang @@ -462,6 +462,10 @@ tile.lava_info = Whatever falls in burns. Stone bridges cross it. tile.bridge = Wooden bridge tile.lavabridge = Stone bridge tile.mushroom = Giant mushroom +tile.mushroom_grown = Fully grown: ready for felling. +tile.mushroom_growing = Growing: {0}% - it can only be felled when fully grown. +tile.grove = Mushroom grove +tile.grove_info = Nobody can claim this moss. Giant mushrooms grow back here. tile.tree = Tree tile.plant_info = Mark it with the pick: an imp fells it for {0} wood. tile.drawbridge = Drawbridge @@ -1275,6 +1279,7 @@ ed.terrain.ruby = Ruby vein ed.terrain.diamond = Diamond vein ed.terrain.steel = Steel wall ed.terrain.mushroom = Giant mushroom +ed.terrain.grove = Mushroom grove ed.terrain.grass = Grass (above ground) ed.terrain.tree = Tree ed.place.core = Soul core diff --git a/lang/es.lang b/lang/es.lang index 6ab1da5..bc7f968 100644 --- a/lang/es.lang +++ b/lang/es.lang @@ -462,6 +462,10 @@ tile.lava_info = Lo que cae dentro arde. Los puentes de piedra lo cruzan. tile.bridge = Puente de madera tile.lavabridge = Puente de piedra tile.mushroom = Seta gigante +tile.mushroom_grown = Adulto: listo para talar. +tile.mushroom_growing = Creciendo: {0}% - solo se puede talar cuando esté adulto. +tile.grove = Bosquecillo de setas +tile.grove_info = Nadie puede conquistar este musgo. Aquí vuelven a crecer setas gigantes. tile.tree = Árbol tile.plant_info = Márcalo con el pico: un diablillo lo tala por {0} de madera. tile.drawbridge = Puente levadizo @@ -1267,6 +1271,7 @@ ed.terrain.ruby = Veta de rubíes ed.terrain.diamond = Veta de diamantes ed.terrain.steel = Muro de acero ed.terrain.mushroom = Seta gigante +ed.terrain.grove = Bosquecillo de setas ed.terrain.grass = Hierba (superficie) ed.terrain.tree = Árbol ed.place.core = Núcleo de alma diff --git a/lang/fi.lang b/lang/fi.lang index 5dc9356..6be8229 100644 --- a/lang/fi.lang +++ b/lang/fi.lang @@ -462,6 +462,10 @@ tile.lava_info = Mikä sinne putoaa, se palaa. Kivisillat ylittävät sen. tile.bridge = Puusilta tile.lavabridge = Kivisilta tile.mushroom = Jättisieni +tile.mushroom_grown = Täysikasvuinen: valmis kaadettavaksi. +tile.mushroom_growing = Kasvaa: {0}% - sen voi kaataa vasta täysikasvuisena. +tile.grove = Sienilehto +tile.grove_info = Kukaan ei voi vallata tätä sammalta. Täällä jättisienet kasvavat uudelleen. tile.tree = Puu tile.plant_info = Merkitse hakulla: piru kaataa sen {0} puusta. tile.drawbridge = Laskusilta @@ -1275,6 +1279,7 @@ ed.terrain.ruby = Rubiinisuoni ed.terrain.diamond = Timanttisuoni ed.terrain.steel = Teräsmuuri ed.terrain.mushroom = Jättisieni +ed.terrain.grove = Sienilehto ed.terrain.grass = Ruoho (maan päällä) ed.terrain.tree = Puu ed.place.core = Sieluydin diff --git a/lang/hr.lang b/lang/hr.lang index fe4c6f6..7204422 100644 --- a/lang/hr.lang +++ b/lang/hr.lang @@ -462,6 +462,10 @@ tile.lava_info = Što god upadne, izgori. Kameni mostovi je premošćuju. tile.bridge = Drveni most tile.lavabridge = Kameni most tile.mushroom = Divovska gljiva +tile.mushroom_grown = Odrasla: spremna za sječu. +tile.mushroom_growing = Raste: {0}% - može se posjeći tek kad odraste. +tile.grove = Gljivarski gaj +tile.grove_info = Nitko ne može osvojiti ovu mahovinu. Ovdje ponovno rastu divovske gljive. tile.tree = Stablo tile.plant_info = Označi krampom: vražić je sruši za {0} drva. tile.drawbridge = Pokretni most @@ -1275,6 +1279,7 @@ ed.terrain.ruby = Žila rubina ed.terrain.diamond = Žila dijamanata ed.terrain.steel = Čelični zid ed.terrain.mushroom = Divovska gljiva +ed.terrain.grove = Gljivarski gaj ed.terrain.grass = Trava (površina) ed.terrain.tree = Stablo ed.place.core = Jezgra duše diff --git a/lang/hu.lang b/lang/hu.lang index b48d255..ebb0243 100644 --- a/lang/hu.lang +++ b/lang/hu.lang @@ -462,6 +462,10 @@ tile.lava_info = Ami beleesik, elég. Kőhidak vezetnek át rajta. tile.bridge = Fahíd tile.lavabridge = Kőhíd tile.mushroom = Óriásgomba +tile.mushroom_grown = Kifejlett: kivágható. +tile.mushroom_growing = Nő: {0}% - csak kifejletten lehet kivágni. +tile.grove = Gombaliget +tile.grove_info = Ezt a mohát senki sem foglalhatja el. Itt újra óriásgombák nőnek. tile.tree = Fa tile.plant_info = Jelöld meg a csákánnyal: egy manó kivágja {0} fáért. tile.drawbridge = Felvonóhíd @@ -1275,6 +1279,7 @@ ed.terrain.ruby = Rubinér ed.terrain.diamond = Gyémántér ed.terrain.steel = Acélfal ed.terrain.mushroom = Óriásgomba +ed.terrain.grove = Gombaliget ed.terrain.grass = Fű (felszín) ed.terrain.tree = Fa ed.place.core = Lélekmag diff --git a/lang/it.lang b/lang/it.lang index 8c8fd55..6aa140c 100644 --- a/lang/it.lang +++ b/lang/it.lang @@ -462,6 +462,10 @@ tile.lava_info = Ciò che ci cade brucia. I ponti di pietra la attraversano. tile.bridge = Ponte di legno tile.lavabridge = Ponte di pietra tile.mushroom = Fungo gigante +tile.mushroom_grown = Cresciuto: pronto per il taglio. +tile.mushroom_growing = Cresce: {0}% - si può abbattere solo quando è cresciuto. +tile.grove = Boschetto di funghi +tile.grove_info = Nessuno può conquistare questo muschio. Qui ricrescono funghi giganti. tile.tree = Albero tile.plant_info = Segnalo col piccone: un diavoletto lo abbatte per {0} legno. tile.drawbridge = Ponte levatoio @@ -1267,6 +1271,7 @@ ed.terrain.ruby = Vena di rubini ed.terrain.diamond = Vena di diamanti ed.terrain.steel = Muro d'acciaio ed.terrain.mushroom = Fungo gigante +ed.terrain.grove = Boschetto di funghi ed.terrain.grass = Erba (superficie) ed.terrain.tree = Albero ed.place.core = Nucleo dell'Anima diff --git a/lang/nl.lang b/lang/nl.lang index c9dc250..1050c6f 100644 --- a/lang/nl.lang +++ b/lang/nl.lang @@ -462,6 +462,10 @@ tile.lava_info = Wat erin valt, verbrandt. Stenen bruggen gaan eroverheen. tile.bridge = Houten brug tile.lavabridge = Stenen brug tile.mushroom = Reuzenpaddenstoel +tile.mushroom_grown = Volgroeid: klaar om te kappen. +tile.mushroom_growing = Groeit: {0}% - pas volgroeid kan hij gekapt worden. +tile.grove = Paddenstoelenbosje +tile.grove_info = Niemand kan dit mos veroveren. Hier groeien reuzenpaddenstoelen terug. tile.tree = Boom tile.plant_info = Markeer met het houweel: een duiveltje kapt hem voor {0} hout. tile.drawbridge = Ophaalbrug @@ -1275,6 +1279,7 @@ ed.terrain.ruby = Robijnader ed.terrain.diamond = Diamantader ed.terrain.steel = Stalen muur ed.terrain.mushroom = Reuzenpaddenstoel +ed.terrain.grove = Paddenstoelenbosje ed.terrain.grass = Gras (bovengronds) ed.terrain.tree = Boom ed.place.core = Zielenkern diff --git a/lang/no.lang b/lang/no.lang index 166df56..2d6e359 100644 --- a/lang/no.lang +++ b/lang/no.lang @@ -461,6 +461,10 @@ tile.lava_info = Det som faller i, brenner. Steinbroer fører over. tile.bridge = Trebro tile.lavabridge = Steinbro tile.mushroom = Kjempesopp +tile.mushroom_grown = Fullvoksen: klar til felling. +tile.mushroom_growing = Vokser: {0}% - kan først felles når den er fullvoksen. +tile.grove = Soppholt +tile.grove_info = Ingen kan erobre denne mosen. Her vokser kjempesopper igjen. tile.tree = Tre tile.plant_info = Merk med hakka: en imp feller den for {0} tre. tile.drawbridge = Vindebro @@ -1274,6 +1278,7 @@ ed.terrain.ruby = Rubinåre ed.terrain.diamond = Diamantåre ed.terrain.steel = Stålvegg ed.terrain.mushroom = Kjempesopp +ed.terrain.grove = Soppholt ed.terrain.grass = Gress (over jorden) ed.terrain.tree = Tre ed.place.core = Sjelekjerne diff --git a/lang/pt.lang b/lang/pt.lang index b0ccd75..7722e06 100644 --- a/lang/pt.lang +++ b/lang/pt.lang @@ -462,6 +462,10 @@ tile.lava_info = O que cai lá dentro arde. As pontes de pedra atravessam-na. tile.bridge = Ponte de madeira tile.lavabridge = Ponte de pedra tile.mushroom = Cogumelo gigante +tile.mushroom_grown = Crescido: pronto para cortar. +tile.mushroom_growing = A crescer: {0}% - só pode ser cortado quando estiver crescido. +tile.grove = Bosque de cogumelos +tile.grove_info = Ninguém pode conquistar este musgo. Aqui voltam a crescer cogumelos gigantes. tile.tree = Árvore tile.plant_info = Marca-o com a picareta: um diabrete abate-o por {0} de madeira. tile.drawbridge = Ponte levadiça @@ -1275,6 +1279,7 @@ ed.terrain.ruby = Veio de rubis ed.terrain.diamond = Veio diamantes ed.terrain.steel = Parede de aço ed.terrain.mushroom = Cogumelo gigante +ed.terrain.grove = Bosque de cogumelos ed.terrain.grass = Relva (superfície) ed.terrain.tree = Árvore ed.place.core = Núcleo de alma diff --git a/lang/ro.lang b/lang/ro.lang index d08ec08..868724b 100644 --- a/lang/ro.lang +++ b/lang/ro.lang @@ -462,6 +462,10 @@ tile.lava_info = Ce cade înăuntru arde. Podurile de piatră o traversează. tile.bridge = Pod de lemn tile.lavabridge = Pod de piatră tile.mushroom = Ciupercă uriașă +tile.mushroom_grown = Crescută: gata de tăiat. +tile.mushroom_growing = Crește: {0}% - poate fi tăiată doar când e crescută. +tile.grove = Crâng de ciuperci +tile.grove_info = Nimeni nu poate cuceri acest mușchi. Aici cresc din nou ciuperci uriașe. tile.tree = Copac tile.plant_info = Marchează-l cu târnăcopul: un drăcușor îl doboară pentru {0} lemn. tile.drawbridge = Pod mobil @@ -1275,6 +1279,7 @@ ed.terrain.ruby = Filon de rubine ed.terrain.diamond = Filon de diamante ed.terrain.steel = Zid de oțel ed.terrain.mushroom = Ciupercă uriașă +ed.terrain.grove = Crâng de ciuperci ed.terrain.grass = Iarbă (suprafață) ed.terrain.tree = Copac ed.place.core = Miezul sufletului diff --git a/lang/ru.lang b/lang/ru.lang index 53ff6ab..bb6b881 100644 --- a/lang/ru.lang +++ b/lang/ru.lang @@ -462,6 +462,10 @@ tile.lava_info = Всё, что туда падает, сгорает. Её пе tile.bridge = Деревянный мост tile.lavabridge = Каменный мост tile.mushroom = Гигантский гриб +tile.mushroom_grown = Вырос: можно рубить. +tile.mushroom_growing = Растёт: {0}% - срубить можно только взрослый гриб. +tile.grove = Грибная роща +tile.grove_info = Этот мох нельзя захватить. Здесь снова вырастают гигантские грибы. tile.tree = Дерево tile.plant_info = Отметьте киркой: бес срубит его за {0} дерева. tile.drawbridge = Подъёмный мост @@ -1267,6 +1271,7 @@ ed.terrain.ruby = Рубиновая жила ed.terrain.diamond = Алмазная жила ed.terrain.steel = Стальная стена ed.terrain.mushroom = Гигантский гриб +ed.terrain.grove = Грибная роща ed.terrain.grass = Трава (поверхность) ed.terrain.tree = Дерево ed.place.core = Ядро души diff --git a/lang/sk.lang b/lang/sk.lang index ab81596..6f4414d 100644 --- a/lang/sk.lang +++ b/lang/sk.lang @@ -462,6 +462,10 @@ tile.lava_info = Čo do nej spadne, zhorí. Prechádzajú ju kamenné mosty. tile.bridge = Drevený most tile.lavabridge = Kamenný most tile.mushroom = Obria huba +tile.mushroom_grown = Dorastená: pripravená na vyťaženie. +tile.mushroom_growing = Rastie: {0}% - vyťažiť sa dá až dorastená. +tile.grove = Hubový háj +tile.grove_info = Tento mach nikto nedobyje. Obrie huby tu znova dorastajú. tile.tree = Strom tile.plant_info = Označ krompáčom: škriatok ho vyrúbe za {0} dreva. tile.drawbridge = Padací most @@ -1275,6 +1279,7 @@ ed.terrain.ruby = Rubínová žila ed.terrain.diamond = Diamantová žila ed.terrain.steel = Oceľová stena ed.terrain.mushroom = Obria huba +ed.terrain.grove = Hubový háj ed.terrain.grass = Tráva (povrch) ed.terrain.tree = Strom ed.place.core = Jadro duše diff --git a/lang/sr.lang b/lang/sr.lang index 93a9eeb..cdc7c32 100644 --- a/lang/sr.lang +++ b/lang/sr.lang @@ -462,6 +462,10 @@ tile.lava_info = Шта год упадне, изгори. Камени мост tile.bridge = Дрвени мост tile.lavabridge = Камени мост tile.mushroom = Џиновска печурка +tile.mushroom_grown = Одрасла: спремна за сечу. +tile.mushroom_growing = Расте: {0}% - може се посећи тек кад одрасте. +tile.grove = Гај печурака +tile.grove_info = Нико не може да освоји ову маховину. Овде поново расту џиновске печурке. tile.tree = Стабло tile.plant_info = Означи крампом: враг је сруши за {0} дрва. tile.drawbridge = Покретни мост @@ -1275,6 +1279,7 @@ ed.terrain.ruby = Жила рубина ed.terrain.diamond = Жила дијаманата ed.terrain.steel = Челични зид ed.terrain.mushroom = Џиновска печурка +ed.terrain.grove = Гај печурака ed.terrain.grass = Трава (површина) ed.terrain.tree = Стабло ed.place.core = Језгро душе diff --git a/lang/sv.lang b/lang/sv.lang index 7901b20..51f08cf 100644 --- a/lang/sv.lang +++ b/lang/sv.lang @@ -462,6 +462,10 @@ tile.lava_info = Det som faller i brinner upp. Stenbroar korsar den. tile.bridge = Träbro tile.lavabridge = Stenbro tile.mushroom = Jättesvamp +tile.mushroom_grown = Fullvuxen: redo att fällas. +tile.mushroom_growing = Växer: {0}% - kan bara fällas när den är fullvuxen. +tile.grove = Svamplund +tile.grove_info = Ingen kan erövra den här mossan. Här växer jättesvampar upp igen. tile.tree = Träd tile.plant_info = Markera med hackan: en imp fäller den för {0} trä. tile.drawbridge = Vindbrygga @@ -1275,6 +1279,7 @@ ed.terrain.ruby = Rubinåder ed.terrain.diamond = Diamantåder ed.terrain.steel = Stålvägg ed.terrain.mushroom = Jättesvamp +ed.terrain.grove = Svamplund ed.terrain.grass = Gräs (ovan jord) ed.terrain.tree = Träd ed.place.core = Själskärna diff --git a/src/editor/map.rs b/src/editor/map.rs index 328afbf..495c4d9 100644 --- a/src/editor/map.rs +++ b/src/editor/map.rs @@ -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 TERRAIN: [&str; 16] = ["floor", "earth", "reinforced", "rock", "water", "lava", "bridge", "lavabridge", "gold", "silver", "ruby", "diamond", "steel", "mushroom", "grass", "tree"]; +const TERRAIN: [&str; 17] = ["floor", "earth", "reinforced", "rock", "water", "lava", "bridge", "lavabridge", "gold", "silver", "ruby", "diamond", "steel", "mushroom", "grove", "grass", "tree"]; 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 ZONE_SHAPES: [&str; 4] = ["rect", "circle", "tiles", "line"]; @@ -76,6 +76,7 @@ fn kind_of_terrain(t: &str) -> (Kind, bool) { "bridge" => (Kind::Bridge, false), "lavabridge" => (Kind::LavaBridge, false), "mushroom" => (Kind::Mushroom, false), + "grove" => (Kind::Grove, false), "grass" => (Kind::Floor, false), "tree" => (Kind::Tree, false), "gold" => (Kind::Gold, false), @@ -218,6 +219,13 @@ impl MapEdit { 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 tl = &mut self.game.map.t[i]; + // a mushroom painted into a grove belongs to it (it leaves moss when felled); fully grown + if k == Kind::Mushroom { + tl.res = if tl.kind == Kind::Grove || tl.res & crate::map::MUSH_GROVE != 0 { crate::map::MUSH_GROVE } else { 0 }; + tl.food = 0.0; + } else if tl.kind == Kind::Mushroom { + tl.res = 0; + } tl.kind = k; tl.reinforced = reinforced; tl.owner = if reinforced && owner == Owner::None { Owner::Player } else { owner }; diff --git a/src/game.rs b/src/game.rs index 5576d02..390376e 100644 --- a/src/game.rs +++ b/src/game.rs @@ -2609,7 +2609,12 @@ impl Game { pub fn dig_out(&mut self, i: usize) { self.torches.retain(|t| t.wall != i); let t = &mut self.map.t[i]; - t.kind = Kind::Floor; + // a grove's mushroom leaves its moss behind + let grove = t.kind == Kind::Mushroom && t.res & MUSH_GROVE != 0; + t.kind = if grove { Kind::Grove } else { Kind::Floor }; + if grove { + t.res = 0; + } t.owner = Owner::None; t.marked = false; t.reinforced = false; diff --git a/src/gfx.rs b/src/gfx.rs index 4c31118..eb53f8c 100644 --- a/src/gfx.rs +++ b/src/gfx.rs @@ -93,6 +93,7 @@ pub const I_RAM: u32 = 209; // battering ram tool pub const I_CATAPULT: u32 = 210; // catapult tool pub const T_SNOW: u32 = 211; // 4 variants: snow on the land above ground (winter) pub const T_PUMPKIN: u32 = 215; // a carved pumpkin (Halloween) +pub const T_MOSS: u32 = 216; // 4 variants: mushroom grove ground, moss with glowing spores pub struct Gfx { pub atlas: Texture2D, @@ -943,6 +944,22 @@ fn gen_atlas() -> Image { Some(if r % 11 == 0 { c(176, 200, 96) } else if r % 6 == 0 { c(112, 150, 62) } else if r % 23 == 0 { c(236, 220, 110) } else if r % 37 == 0 { c(230, 236, 240) } else { c(140, 176, 78) }) }); } + // mushroom grove: dark stones overgrown with teal moss, a few glowing spores + for v in 0..4u32 { + cv.cell(T_MOSS + v, |x, y| { + let r = hash(x, y, 520 + v); + let moss = (hash(x / 3, y / 3, 530 + v) % 5) < 3; + Some(if r % 41 == 0 { + c(150, 255, 220) // a spore glows + } else if r % 29 == 0 { + c(200, 150, 255) + } else if moss { + if r % 4 == 0 { c(46, 112, 96) } else if r % 3 == 0 { c(30, 84, 74) } else { c(38, 98, 84) } + } else { + stones(x, y, v, (52, 58, 56), (26, 30, 30), 8) + }) + }); + } // obsidian: cooled lava, dark glass with a few glowing seams for v in 0..4u32 { cv.cell(T_OBSIDIAN + v, |x, y| { @@ -2159,6 +2176,7 @@ impl Gfx { Kind::Obsidian => T_OBSIDIAN + v, Kind::Mushroom => T_FLOOR + v, Kind::Tree => T_GRASS + v, + Kind::Grove => T_MOSS + v, Kind::LavaBridge => T_STONEBRIDGE + (v & 1), Kind::Shard => T_HEROFLOOR + v, Kind::Aerie => T_AERIE + v, diff --git a/src/map.rs b/src/map.rs index 3c2ccea..a8ece9c 100644 --- a/src/map.rs +++ b/src/map.rs @@ -70,10 +70,14 @@ pub enum Kind { Altar, // room: sacrifices Drawbridge, // bridge that can be raised (res: see DB_RAISED, DB_AUTO, DB_LAVA) Obsidian, // lava cooled by frost: walkable until it melts again - Mushroom, // giant mushroom on open ground: imps fell it for wood (grows back) + Mushroom, // giant mushroom: imps fell it for wood once it is grown (food: growth left, 0 = grown; res: MUSH_GROVE) Tree, // tree above ground: imps fell it for wood + Grove, // mushroom grove: mossy ground nobody can claim, giant mushrooms grow (back) here } +// a giant mushroom of a grove: when felled, the ground turns back into grove (Tile::res) +pub const MUSH_GROVE: u8 = 1; + // drawbridge state in Tile::res pub const DB_RAISED: u8 = 1; // raised: nobody can cross pub const DB_AUTO: u8 = 2; // rises by itself when enemies come near @@ -105,7 +109,7 @@ pub struct River { } // order matters for the packed savegame codec: only append new kinds -const KINDS: [Kind; 38] = [ +const KINDS: [Kind; 39] = [ Kind::Rock, Kind::Earth, Kind::Gold, @@ -144,6 +148,7 @@ const KINDS: [Kind; 38] = [ Kind::Obsidian, Kind::Mushroom, Kind::Tree, + Kind::Grove, ]; // treasures (veins, treasury content, imp loads): 0 gold, 1 silver, 2 ruby, 3 diamond @@ -229,7 +234,15 @@ impl Tile { matches!(self.kind, Kind::Rock | Kind::Earth | Kind::Gold | Kind::Silver | Kind::Ruby | Kind::Diamond | Kind::Steel) } pub fn diggable(&self) -> bool { - matches!(self.kind, Kind::Earth | Kind::Gold | Kind::Silver | Kind::Ruby | Kind::Diamond | Kind::Steel | Kind::Mushroom | Kind::Tree) + matches!(self.kind, Kind::Earth | Kind::Gold | Kind::Silver | Kind::Ruby | Kind::Diamond | Kind::Steel | Kind::Tree) || (self.kind == Kind::Mushroom && self.grown()) + } + // a giant mushroom is felled only when it is fully grown (everything else counts as grown) + pub fn grown(&self) -> bool { + self.kind != Kind::Mushroom || self.food <= 0.0 + } + // how far a giant mushroom has grown: 0 a sprout .. 1 fully grown + pub fn growth(&self) -> f32 { + if self.kind == Kind::Mushroom { (1.0 - self.food).clamp(0.0, 1.0) } else { 1.0 } } // a giant mushroom or a tree: felled like a wall is dug, gives wood pub fn plant(&self) -> bool { @@ -272,6 +285,7 @@ impl Tile { | Kind::Arena | Kind::Altar | Kind::Obsidian + | Kind::Grove ) || (self.kind == Kind::Drawbridge && self.res & DB_RAISED == 0) } // river tiles (open, but nobody can walk there) diff --git a/src/render3d.rs b/src/render3d.rs index 19751ea..ceaddef 100644 --- a/src/render3d.rs +++ b/src/render3d.rs @@ -526,6 +526,10 @@ fn ambient_field(map: &Map) -> Vec { if t.river() == Some(Kind::Lava) { f[i] = f[i].max(0.8); // lava lights up its surroundings } + // the spores of a mushroom grove glow faintly + if t.kind == Kind::Grove || (t.kind == Kind::Mushroom && t.res & MUSH_GROVE != 0) { + f[i] = f[i].max(0.45); + } if t.kind == Kind::Core { f[i] = 1.0; } @@ -765,6 +769,10 @@ fn build_chunk(map: &Map, amb: &[f32], glow: &[f32], cx: i32, cz: i32, atlas: &T if map.sky(idx(x, z)) && t.kind == Kind::Floor && t.owner == Owner::None { tex = if season() == 2 { T_SNOW } else { T_GRASS } + (t.var as u32 & 3); } + // a grove's mushroom stands on moss + if t.kind == Kind::Mushroom && t.res & MUSH_GROVE != 0 { + tex = crate::gfx::T_MOSS + (t.var as u32 & 3); + } let (ua, ub) = cell_uv(tex); let (xf, zf) = (x as f32, z as f32); if !map.shown(idx(x, z)) { @@ -976,11 +984,16 @@ fn build_chunk(map: &Map, amb: &[f32], glow: &[f32], cx: i32, cz: i32, atlas: &T let tint = |v: Vec3| col(lava_lit(if t.marked { v * vec3(1.3, 1.15, 0.5) } else { v }, lg), 1.0); let s = 0.85 + 0.1 * (t.var & 3) as f32; if t.kind == Kind::Mushroom { + // it grows from a sprout to full size in steps; grown, its cap glows teal + let stage = mushroom_stage(&t); + let s = s * (0.3 + 0.7 * stage as f32 / 4.0); let h = 0.75 * s; let sc = tint(Vec3::splat(a.max(0.35))); - mb.boxy(vec3(cx - 0.12, 0.0, cz - 0.12), vec3(cx + 0.12, h, cz + 0.12), T_STEM, sc, sc); - // the cap glows a little in the dark - let cc = tint(Vec3::splat(a.max(0.7))); + let w = 0.12 * s.max(0.6); + mb.boxy(vec3(cx - w, 0.0, cz - w), vec3(cx + w, h, cz + w), T_STEM, sc, sc); + // the cap glows a little in the dark; a grown one in its own colour + let hue = if stage >= 4 { vec3(0.55, 1.35, 1.25) } else { vec3(0.95, 0.8, 0.62) }; + let cc = tint(Vec3::splat(a.max(0.7)) * hue); mb.boxy(vec3(cx - 0.42 * s, h, cz - 0.42 * s), vec3(cx + 0.42 * s, h + 0.26, cz + 0.42 * s), T_CAP, cc, cc); mb.boxy(vec3(cx - 0.26 * s, h + 0.26, cz - 0.26 * s), vec3(cx + 0.26 * s, h + 0.38, cz + 0.26 * s), T_CAP, cc, cc); } else { @@ -1202,6 +1215,7 @@ fn chunk_sig(map: &Map, cx: i32, cz: i32) -> u64 { Kind::Farm => t.food as u64, Kind::Treasury => (t.gold / 50) as u64 | (t.res as u64) << 5, Kind::Drawbridge => t.res as u64, + Kind::Mushroom => mushroom_stage(&t) as u64 | (t.res as u64) << 3, _ => 0, }; let shown = map.shown(idx(x, z)) as u64 | (map.sky(idx(x, z)) as u64) << 1; @@ -2449,6 +2463,15 @@ fn draw_landmarks_solid(g: &Game, t: f32) { let _ = t; } +// growth stage of a giant mushroom: 0 a sprout .. 4 fully grown (felling only then) +fn mushroom_stage(t: &Tile) -> u32 { + if t.grown() { + 4 + } else { + ((t.growth() * 4.0) as u32).min(3) + } +} + // the colour of a master side (the first master red, rivals green, blue and yellow) pub fn master_rgb(side: u8) -> Vec3 { match side { @@ -3337,6 +3360,7 @@ pub fn minimap_color(tile: &Tile) -> [u8; 4] { Kind::Drawbridge => [120, 80, 50, 255], Kind::Obsidian => [60, 40, 70, 255], Kind::Mushroom => [150, 90, 200, 255], + Kind::Grove => [50, 110, 92, 255], Kind::Tree => [40, 100, 40, 255], Kind::Shard => [255, 60, 120, 255], Kind::Aerie => [170, 140, 90, 255], diff --git a/src/scenes.rs b/src/scenes.rs index a863c3f..42115e8 100644 --- a/src/scenes.rs +++ b/src/scenes.rs @@ -224,3 +224,22 @@ fn export_scenes() { run(&mut g, 1.0); save("room-levels", &mut g, cx as f32 + 1.5, cy as f32 - 2.0, 16.0, 0.0, false); } + +// a mushroom grove: moss, grown (glowing) and young mushrooms +#[test] +#[ignore] +fn export_grove_scene() { + std::fs::create_dir_all("/tmp/scenes").unwrap(); + let mut g = Game::new(9, 96, 96); + // the grove tile with the most grove around it + let best = (0..g.map.t.len()) + .filter(|&i| g.map.t[i].kind == Kind::Grove) + .max_by_key(|&i| { + let (x, y) = xy(i); + (-3..=3).flat_map(|dy| (-3..=3).map(move |dx| (dx, dy))).filter(|&(dx, dy)| g.map.get(x + dx, y + dy).map_or(false, |t| matches!(t.kind, Kind::Grove | Kind::Mushroom))).count() + }) + .unwrap(); + let (x, y) = xy(best); + run(&mut g, 1.0); + save("mushroom-grove", &mut g, x as f32 + 0.5, y as f32 + 0.5, 12.0, 0.0, false); +} diff --git a/src/ui.rs b/src/ui.rs index e92fdd1..67c5c37 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -1804,8 +1804,20 @@ pub fn hover_info(g: &Game, ent: Option, tile: Option<(i32, i32)>) -> Vec Kind::Mushroom | Kind::Tree => { let tree = t.kind == Kind::Tree; out.push((tr(if tree { "tile.tree" } else { "tile.mushroom" }), WOOD_C)); - let wood = if tree { crate::wood::WOOD_TREE } else { crate::wood::WOOD_MUSHROOM }; - out.push((trf("tile.plant_info", &[(wood as i32).to_string()]), DIM)); + if t.grown() { + if !tree { + out.push((tr("tile.mushroom_grown"), GREEN)); + } + let wood = if tree { crate::wood::WOOD_TREE } else { crate::wood::WOOD_MUSHROOM }; + out.push((trf("tile.plant_info", &[(wood as i32).to_string()]), DIM)); + } else { + // a young mushroom: not yet to be felled + out.push((trf("tile.mushroom_growing", &[((t.growth() * 100.0) as i32).to_string()]), DIM)); + } + } + Kind::Grove => { + out.push((tr("tile.grove"), GREEN)); + out.push((tr("tile.grove_info"), DIM)); } Kind::Obsidian => { out.push((tr("tile.obsidian"), FRAME_HI)); diff --git a/src/wood.rs b/src/wood.rs index a96796e..d86762d 100644 --- a/src/wood.rs +++ b/src/wood.rs @@ -16,62 +16,106 @@ pub const WOOD_TREE: f32 = 3.0; pub const MUSHROOM_HP: f32 = 4.0; #[allow(dead_code)] // trees come with the surface world pub const TREE_HP: f32 = 6.0; -// seconds until a felled giant mushroom grows back -pub const REGROW: f32 = 240.0; +// seconds until a felled giant mushroom sprouts again, and from sprout to fully grown +pub const REGROW: f32 = 90.0; +pub const GROW_TIME: f32 = 300.0; // wood a wooden bridge tile, a wooden drawbridge tile and a wooden door cost pub const BRIDGE_WOOD: f32 = 2.0; pub const DRAWBRIDGE_WOOD: f32 = 4.0; pub const DOOR_WOOD: f32 = 3.0; -// giant mushrooms in a cavern of a random map: one per this many open tiles -const MUSHROOM_SHARE: usize = 70; +// mushroom groves of a random map: one per this many map tiles (at least 2, at most 6) +const GROVE_SHARE: usize = 2200; +const GROVE_RADIUS: i32 = 4; #[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct WoodWork { - pub regrow: Vec<(usize, f32)>, // felled mushroom tile, seconds until it grows back + pub regrow: Vec<(usize, f32)>, // felled mushroom tile, seconds until it sprouts again + #[serde(default)] + pub growing: Vec, // giant mushrooms still growing (Tile::food: growth left) } -// a tile where a plant stands alone: all eight neighbours open ground, no other plant next to -// it (so a plant never closes a corridor) +// open ground of nobody (or a grove's moss) +fn open_ground(t: &Tile) -> bool { + (t.kind == Kind::Floor && t.owner == Owner::None) || t.kind == Kind::Grove +} + +// a tile where a plant may stand: all eight neighbours open, no other plant next to it (so a +// plant never closes a corridor) fn lonely_spot(map: &Map, x: i32, y: i32) -> bool { - if !inb(x, y) || map.t[idx(x, y)].kind != Kind::Floor || map.t[idx(x, y)].owner != Owner::None { + if !inb(x, y) || !open_ground(&map.t[idx(x, y)]) { return false; } - DIR8.iter().all(|&(dx, dy)| map.get(x + dx, y + dy).map_or(false, |n| n.walkable() && !n.bridge() && n.kind != Kind::Door)) - && (-2..=2).all(|dy| (-2..=2).all(|dx| !map.get(x + dx, y + dy).map_or(false, |n| n.plant()))) + DIR8.iter().all(|&(dx, dy)| map.get(x + dx, y + dy).map_or(false, |n| n.walkable() && !n.bridge() && n.kind != Kind::Door && !n.plant())) } impl Game { - // giant mushrooms in the open caverns of a random map (deterministic, from the seed) + // mushroom groves in the open caverns of a random map (deterministic, from the seed): mossy + // ground nobody can claim, giant mushrooms in a loose grid that grow back when felled pub fn grow_mushrooms(&mut self, seed: u32) { let mut rng = Rng(seed.wrapping_mul(0x9e37_79b9) ^ 0x6d2b_79f5 | 1); let (cx, cy) = (self.map.core.0 + 1, self.map.core.1 + 1); - let open: Vec = (0..self.map.t.len()) + let open = |g: &Game, x: i32, y: i32| inb(x, y) && !g.map.sky(idx(x, y)) && open_ground(&g.map.t[idx(x, y)]) && !g.map.t[idx(x, y)].bridge(); + // centres: open ground with open ground all around, away from the heart + let r = GROVE_RADIUS; + let centres: Vec = (0..self.map.t.len()) .filter(|&i| { let (x, y) = xy(i); - (x - cx).abs().max((y - cy).abs()) > 10 && lonely_spot(&self.map, x, y) + (x - cx).abs().max((y - cy).abs()) > 12 && (-2..=2).all(|dy| (-2..=2).all(|dx| open(self, x + dx, y + dy))) }) .collect(); - let want = (open.len() / MUSHROOM_SHARE).max(6); - let mut placed = 0; + let want = (self.map.t.len() / GROVE_SHARE).clamp(2, 6); + let mut groves: Vec<(i32, i32)> = Vec::new(); let mut tries = 0; - while placed < want && tries < want * 20 && !open.is_empty() { + while groves.len() < want && tries < 200 && !centres.is_empty() { tries += 1; - let i = open[(rng.next() as usize) % open.len()]; - let (x, y) = xy(i); - if self.ents.iter().any(|e| e.alive && e.tile() == i) || !lonely_spot(&self.map, x, y) { + let (x, y) = xy(centres[(rng.next() as usize) % centres.len()]); + if groves.iter().any(|&(gx, gy)| (gx - x).abs().max((gy - y).abs()) < 3 * r) { continue; } - self.plant_mushroom(i); - placed += 1; + groves.push((x, y)); + } + for &(gx, gy) in &groves { + // the moss: the open ground of a rough disc + for y in gy - r..=gy + r { + for x in gx - r..=gx + r { + let d2 = (x - gx).pow(2) + (y - gy).pow(2); + if d2 <= r * r + (hash32(x, y) % 3) as i32 - 1 && open(self, x, y) { + let t = &mut self.map.t[idx(x, y)]; + t.kind = Kind::Grove; + t.owner = Owner::None; + } + } + } + // the mushrooms: every other tile in both directions, some still growing + for y in gy - r + 1..gy + r { + for x in gx - r + 1..gx + r { + if (x - gx) % 2 != 0 || (y - gy) % 2 != 0 { + continue; + } + let i = idx(x, y); + if self.map.t[i].kind != Kind::Grove || !lonely_spot(&self.map, x, y) || self.ents.iter().any(|e| e.alive && e.tile() == i) { + continue; + } + let left = if rng.f() < 0.6 { 0.0 } else { rng.f() }; + self.plant_mushroom(i, left); + } + } } } - pub fn plant_mushroom(&mut self, i: usize) { + // a giant mushroom on tile i; `left`: how much of its growth is still to come (0 = grown) + pub fn plant_mushroom(&mut self, i: usize, left: f32) { + let grove = self.map.t[i].kind == Kind::Grove || self.map.t[i].res & MUSH_GROVE != 0; let t = &mut self.map.t[i]; t.kind = Kind::Mushroom; t.owner = Owner::None; t.hp = MUSHROOM_HP; t.marked = false; + t.food = left.clamp(0.0, 1.0); + t.res = if grove { MUSH_GROVE } else { 0 }; + if t.food > 0.0 && !self.woodwork.growing.contains(&i) { + self.woodwork.growing.push(i); + } } // an imp felled the plant at tile i: wood for its master, a mushroom grows back later @@ -96,8 +140,23 @@ impl Game { true } - // the shared step: felled mushrooms grow back where there is room + // the shared step: young mushrooms grow, felled ones sprout again where there is room pub(crate) fn update_wood(&mut self, dt: f32) { + let mut k = 0; + while k < self.woodwork.growing.len() { + let i = self.woodwork.growing[k]; + let t = &mut self.map.t[i]; + if t.kind != Kind::Mushroom { + self.woodwork.growing.swap_remove(k); + continue; + } + t.food = (t.food - dt / GROW_TIME).max(0.0); + if t.food <= 0.0 { + self.woodwork.growing.swap_remove(k); + } else { + k += 1; + } + } let mut k = 0; while k < self.woodwork.regrow.len() { self.woodwork.regrow[k].1 -= dt; @@ -108,9 +167,10 @@ impl Game { let i = self.woodwork.regrow[k].0; let (x, y) = xy(i); if lonely_spot(&self.map, x, y) && !self.ents.iter().any(|e| e.alive && e.tile() == i) { - self.plant_mushroom(i); + // a sprout: it takes GROW_TIME until it can be felled again + self.plant_mushroom(i, 1.0); self.woodwork.regrow.swap_remove(k); - } else if self.map.t[i].kind != Kind::Floor || self.map.t[i].owner != Owner::None { + } else if !open_ground(&self.map.t[i]) { // built over or claimed: it does not come back self.woodwork.regrow.swap_remove(k); } else { @@ -121,37 +181,65 @@ impl Game { } } +fn hash32(x: i32, y: i32) -> u32 { + let h = (x as u32).wrapping_mul(2654435761) ^ (y as u32).wrapping_mul(2246822519); + (h ^ (h >> 15)).wrapping_mul(2246822519) >> 7 +} + #[cfg(test)] mod tests { use super::*; #[test] - fn random_maps_have_lonely_giant_mushrooms() { + fn random_maps_have_mushroom_groves() { let g = Game::new(9, 96, 96); + let groves = g.map.t.iter().filter(|t| t.kind == Kind::Grove).count(); let n = g.map.t.iter().filter(|t| t.kind == Kind::Mushroom).count(); + assert!(groves >= 30, "{} grove tiles", groves); assert!(n >= 6, "{} mushrooms", n); assert!((g.wood - WOOD_START).abs() < 1e-3); - // none closes a corridor: every neighbour is open ground + // none closes a corridor: every neighbour is open ground; all stand in a grove for i in 0..g.map.t.len() { if g.map.t[i].kind == Kind::Mushroom { let (x, y) = xy(i); - assert!(DIR8.iter().all(|&(dx, dy)| g.map.get(x + dx, y + dy).map_or(false, |n| n.walkable() || n.plant()))); + assert!(DIR8.iter().all(|&(dx, dy)| g.map.get(x + dx, y + dy).map_or(false, |n| n.walkable()))); + assert!(g.map.t[i].res & MUSH_GROVE != 0); } } } #[test] - fn felling_gives_wood_and_mushrooms_grow_back() { + fn grove_mushrooms_grow_slowly_are_felled_only_when_grown_and_come_back() { let mut g = Game::new(9, 96, 96); - let i = g.map.t.iter().position(|t| t.kind == Kind::Mushroom).unwrap(); + let i = g.map.t.iter().position(|t| t.kind == Kind::Mushroom && t.grown()).unwrap(); + assert!(g.map.t[i].diggable()); let w0 = g.wood; g.felled(i); g.dig_out(i); assert_eq!(g.wood, w0 + WOOD_MUSHROOM); - assert_eq!(g.map.t[i].kind, Kind::Floor); - g.woodwork.regrow[0].1 = 0.01; + assert_eq!(g.map.t[i].kind, Kind::Grove, "the moss stays"); + // the grove can't be claimed or marked + let (x, y) = xy(i); + g.mark_rect(x, y, x, y, true); + assert!(!g.map.t[i].marked); + assert!(!g.claimable(i, 1)); + // it sprouts again: small, and not to be felled yet + let idx_r = g.woodwork.regrow.iter().position(|r| r.0 == i).unwrap(); + g.woodwork.regrow[idx_r].1 = 0.01; g.update_wood(0.05); - assert_eq!(g.map.t[i].kind, Kind::Mushroom, "grown back"); + assert_eq!(g.map.t[i].kind, Kind::Mushroom, "a sprout"); + assert!(!g.map.t[i].grown() && !g.map.t[i].diggable()); + g.mark_rect(x, y, x, y, true); + assert!(!g.map.t[i].marked, "a young mushroom can't be marked"); + // halfway + for _ in 0..(GROW_TIME as usize / 2) { + g.update_wood(1.0); + } + assert!(!g.map.t[i].grown() && g.map.t[i].growth() > 0.4); + for _ in 0..(GROW_TIME as usize / 2 + 2) { + g.update_wood(1.0); + } + assert!(g.map.t[i].grown() && g.map.t[i].diggable(), "fully grown: ready to fell"); assert!(g.spend_wood(3.0) && !g.spend_wood(1000.0)); } }