From 5405a13eb03c0878d6912660f243e717b8f9df4e Mon Sep 17 00:00:00 2001 From: Andrew Miner Date: Thu, 23 Oct 2025 10:10:41 -0600 Subject: [PATCH] Update flora/fauna spawns to use a common system --- mcpacker/format/report/biomereport.py | 18 +- mcpacker/format/report/mobspawnreport.py | 28 +- mcpacker/format/report/mobspawnreport_test.py | 50 +- mcpacker/format/report/plantspawnreport.py | 10 +- mcpacker/model/catalog.py | 15 +- mcpacker/model/ecology/biomecatalog.py | 13 +- mcpacker/model/ecology/biomefilter.py | 6 +- mcpacker/model/ecotype.py | 20 + mcpacker/model/fauna/mob.py | 22 +- mcpacker/model/fauna/mob_test.py | 7 +- mcpacker/model/fauna/mobecotype.py | 35 + mcpacker/model/fauna/mobspawn.py | 28 +- mcpacker/model/fauna/mobspawn_test.py | 61 +- mcpacker/model/fauna/mobspawnbuilder.py | 50 + mcpacker/model/flora/plant.py | 13 +- mcpacker/model/flora/plantspawn.py | 54 +- mcpacker/model/flora/plantspawnbuilder.py | 17 + mcpacker/model/flora/plantspawncatalog.py | 1 + mcpacker/model/habitat.py | 93 +- mcpacker/model/habitat_test.py | 36 +- mcpacker/model/population.py | 4 + mcpacker/model/spawn.py | 26 +- mcpacker/model/spawn_test.py | 57 +- mcpacker/model/spawnable.py | 28 + mcpacker/model/spawnbuilder.py | 107 ++ .../pack/mysteriousisland/addmobspawns.py | 1022 ++++++++--------- .../pack/mysteriousisland/addplantspawns.py | 662 +++++------ mcpacker/ui/runner_test.py | 27 +- mcpacker/write/incontrol/spawnerwriter.py | 62 +- .../write/incontrol/spawnerwriter_test.py | 63 +- requirements.txt | 11 +- 31 files changed, 1323 insertions(+), 1323 deletions(-) create mode 100644 mcpacker/model/ecotype.py create mode 100644 mcpacker/model/fauna/mobecotype.py create mode 100644 mcpacker/model/fauna/mobspawnbuilder.py create mode 100644 mcpacker/model/flora/plantspawnbuilder.py create mode 100644 mcpacker/model/population.py create mode 100644 mcpacker/model/spawnable.py create mode 100644 mcpacker/model/spawnbuilder.py diff --git a/mcpacker/format/report/biomereport.py b/mcpacker/format/report/biomereport.py index 4e6ae5a..d45d821 100644 --- a/mcpacker/format/report/biomereport.py +++ b/mcpacker/format/report/biomereport.py @@ -39,26 +39,30 @@ class BiomeReport(ReportComposer): def _composeFaunaSection(self, biome:Biome): self.line().line("Fauna:").indent() faunaLineCount = 0 - for spawn in self.pack.world.mobSpawns: + + for mob in self.pack.world.mobs: altitude = None - for habitat in spawn.acceptedBy(biome): - if habitat.location == LO.CAVE: continue - altitude = AL.span(altitude or habitat.altitude, habitat.altitude) + for spawn in self.pack.world.mobSpawns: + if not spawn.habitat.accepts(biome): continue + if spawn.ecotype.location == LO.CAVE: continue + + altitude = AL.span(altitude or spawn.habitat.altitude, spawn.habitat.altitude) if altitude != None: - self.line(f"{spawn.mob.name} @ {altitude}") + self.line(f"{mob.name} @ {altitude}") faunaLineCount += 1 if faunaLineCount == 0: self.line("") + self.outdent() # fauna def _composeFloraSection(self, biome:Biome): self.line().line("Flora:").indent() floraLineCount = 0 for spawn in self.pack.world.plantSpawns: - if not spawn.acceptsBiome(biome): continue - self.line(f"{spawn.plant.name} @ {spawn.altitude}") + if not spawn.habitat.accepts(biome): continue + self.line(f"{spawn.plant.name} @ {spawn.habitat.altitude}") floraLineCount += 1 if floraLineCount == 0: diff --git a/mcpacker/format/report/mobspawnreport.py b/mcpacker/format/report/mobspawnreport.py index 52fbfbd..2b08523 100644 --- a/mcpacker/format/report/mobspawnreport.py +++ b/mcpacker/format/report/mobspawnreport.py @@ -9,24 +9,26 @@ from pathlib import Path class MobSpawnReport(ReportComposer): def doCompose(self): - for spawn in self.pack.world.mobSpawns: - self.line(f"Mob: {spawn.name}") - self.line() - + for mob in self.pack.world.mobs: + self.line(f"Mob: {mob.name}") self.indent() - needsDelimiter = False - for index, habitat in enumerate(spawn.habitats, start=1): - if needsDelimiter: self.line() - needsDelimiter = True - self.text("Habitat ").line(index) + needsDelimiter = False + for spawn in self.pack.world.mobSpawns: + if spawn.mob != mob: continue + habitat = spawn.habitat + ecotype = spawn.ecotype + + self.line().text("Spawn: ").line(spawn.name) self.indent() self.text("altitude: ").line(habitat.altitude) - self.text("biomeFilter: ").line(habitat.biomeFilter) + self.text("biomeFilter: ") + self.line(" -OR- ".join(str(b) for b in habitat.biomeFilters)) self.text("seasons: ").line(", ".join([str(s) for s in habitat.seasons])) - self.text("group: ").line(habitat.group) - self.text("location: ").line(habitat.location) - self.text("scarcity: ").line(habitat.scarcity) + self.line() + self.text("group: ").line(ecotype.group) + self.text("location: ").line(ecotype.location) + self.text("scarcity: ").line(ecotype.scarcity) self.outdent() self.outdent() diff --git a/mcpacker/format/report/mobspawnreport_test.py b/mcpacker/format/report/mobspawnreport_test.py index ca16cc2..5b8c134 100644 --- a/mcpacker/format/report/mobspawnreport_test.py +++ b/mcpacker/format/report/mobspawnreport_test.py @@ -1,3 +1,4 @@ +from mcpacker.model.fauna.mobspawnbuilder import MobSpawnBuilder from mcpacker.format.report.mobspawnreport import MobSpawnReport from mcpacker.model.ecology.biomefilter import BiomeFilter as BF from mcpacker.model.fauna.mob import Mob @@ -34,25 +35,22 @@ def defineAddMobs(): yield addMobs @fixture(name="addMobSpawns") -def defineAddMobSpawnss(): +def defineAddMobSpawns(): def addMobSpawns(pack:ModPack): mobs = pack.world.mobs - spawns = pack.world.mobSpawns + b = MobSpawnBuilder(pack.world.mobSpawns) - pack.world.mobSpawns.add( - MobSpawn(mobs["minecraft:chicken"], - Habitat( - altitude = AL.span(AL.LOWLANDS, AL.UPLANDS), - biomeFilter = BF([HE.TROPICAL, HU.WET, FL.within(FL.CANOPY, FL.CLEARING)]), - seasons = SE.SUMMER, - group = GR.TROUP, - scarcity = SC.COMMON, - ).derive( - seasons = SE.exclude(SE.SUMMER), - scarcity = SC.UNCOMMON - ) - ) - ) + b.start(mobs["minecraft:chicken"]) + b.altitude = AL.span(AL.LOWLANDS, AL.UPLANDS) + b.biomeFilters = BF([HE.TROPICAL, HU.WET, FL.within(FL.CANOPY, FL.CLEARING)]) + b.seasons = SE.SUMMER + b.group = GR.TROUP + b.scarcity = SC.COMMON + b.save("chicken-summer") + + b.seasons = SE.exclude(SE.SUMMER) + b.scarcity = SC.UNCOMMON + b.save("chicken-normal") yield addMobSpawns @@ -71,19 +69,21 @@ def test_write(report:MobSpawnReport): assert str(report).strip() == textwrap.dedent(""" Mob: minecraft:chicken - Habitat 1 - altitude: lowlands to uplands (70 to 124) - biomeFilter: tropical and wet and (canopy or forest or clearing) - seasons: summer - group: troup (3 to 6) - location: outside - scarcity: common - - Habitat 2 + Spawn: chicken-normal altitude: lowlands to uplands (70 to 124) biomeFilter: tropical and wet and (canopy or forest or clearing) seasons: spring, autumn, winter + group: troup (3 to 6) location: outside scarcity: uncommon + + Spawn: chicken-summer + altitude: lowlands to uplands (70 to 124) + biomeFilter: tropical and wet and (canopy or forest or clearing) + seasons: summer + + group: troup (3 to 6) + location: outside + scarcity: common """).strip() diff --git a/mcpacker/format/report/plantspawnreport.py b/mcpacker/format/report/plantspawnreport.py index 692f16b..2838b66 100644 --- a/mcpacker/format/report/plantspawnreport.py +++ b/mcpacker/format/report/plantspawnreport.py @@ -44,15 +44,15 @@ class PlantSpawnReport(ReportComposer): self.line().line("Habitat:") self.indent() # habitat - self.text("altitude: ").line(spawn.altitude) - self.text("biomeFilters: ").line(" -OR- ".join(str(f) for f in spawn.biomeFilters)) - self.text("scarcity: ").line(spawn.scarcity) + self.text("altitude: ").line(spawn.habitat.altitude) + self.text("biomeFilters: ") + self.line(" -OR- ".join(str(f) for f in spawn.habitat.biomeFilters)) + self.text("scarcity: ").line(spawn.ecotype.scarcity) self.outdent() # habitat self.line().line("Biomes:") self.indent() # biomes - biomes = self.pack.world.biomes.filter(lambda b: spawn.acceptsBiome(b)) - for biome in biomes: + for biome in self.pack.world.biomes.find(spawn.habitat.biomeFilters): self.line(biome) self.outdent() # biomes diff --git a/mcpacker/model/catalog.py b/mcpacker/model/catalog.py index da35962..d3b845e 100644 --- a/mcpacker/model/catalog.py +++ b/mcpacker/model/catalog.py @@ -1,10 +1,7 @@ from collections.abc import Iterable -from typing import Any +from collections.abc import Iterator from typing import Callable -from typing import Generic -from typing import Iterator from typing import Protocol -from typing import TypeVar # Type Support ##################################################################################### @@ -14,14 +11,10 @@ class HasName(Protocol): @property def name(self) -> str: ... -CatalogItem = TypeVar("CatalogItem", bound=HasName) - -MapTarget = TypeVar("MapTarget") - # Class ############################################################################################ -class Catalog(Generic[CatalogItem]): +class Catalog[CatalogItem: HasName]: def __init__(self, items:Iterable[CatalogItem]|None=None): self._items:dict[str,CatalogItem] = {} @@ -39,7 +32,7 @@ class Catalog(Generic[CatalogItem]): return self._items[name] def __iter__(self) -> Iterator[CatalogItem]: - for name in sorted([i.name for i in self._items.values()]): + for name in sorted([item.name for item in self._items.values()]): yield self._items[name] def __len__(self) -> int: @@ -64,6 +57,6 @@ class Catalog(Generic[CatalogItem]): if isAllowed(item): yield item - def map(self, doTransform:Callable[[CatalogItem], MapTarget]) -> Iterator[MapTarget]: + def map[Target](self, doTransform:Callable[[CatalogItem], Target]) -> Iterator[Target]: for item in self: yield doTransform(item) diff --git a/mcpacker/model/ecology/biomecatalog.py b/mcpacker/model/ecology/biomecatalog.py index 479675b..f67aa2a 100644 --- a/mcpacker/model/ecology/biomecatalog.py +++ b/mcpacker/model/ecology/biomecatalog.py @@ -1,3 +1,4 @@ +from collections.abc import Iterable from mcpacker.model.catalog import Catalog from mcpacker.model.ecology.biome import Biome from mcpacker.model.ecology.biomefilter import BiomeFilter @@ -7,4 +8,14 @@ from typing import Iterator # Classes ########################################################################################## class BiomeCatalog(Catalog[Biome]): - pass + + def find(self, biomeFilters:Iterable[BiomeFilter]) -> Iterator[Biome]: + for biome in self: + accepted = True + for biomeFilter in biomeFilters: + if not biomeFilter.accepts(biome): + accepted = False + break + + if accepted: + yield biome diff --git a/mcpacker/model/ecology/biomefilter.py b/mcpacker/model/ecology/biomefilter.py index 4e45118..042ddd2 100644 --- a/mcpacker/model/ecology/biomefilter.py +++ b/mcpacker/model/ecology/biomefilter.py @@ -10,7 +10,7 @@ class BiomeFilter: """ A description of which biomes may be considered for a given purpose. - This class works by allowing *both* allowed and prohibited traits. + This class works by accepting *both* allowed and prohibited traits. The list of allowed traits may contain either individual trait objects, or a list of such objects. When an individual trait is given, only biomes with that exact trait are permitted. @@ -30,8 +30,8 @@ class BiomeFilter: required:Sequence[BiomeTrait|ResourceId|Sequence[BiomeTrait]]|None=None, prohibited:Sequence[BiomeTrait|ResourceId]|None=None, ): - self.required = required or [] - self.prohibited = prohibited or [] + self.required = list(required or []) + self.prohibited = list(prohibited or []) def __repr__(self) -> str: return ( diff --git a/mcpacker/model/ecotype.py b/mcpacker/model/ecotype.py new file mode 100644 index 0000000..6937cb9 --- /dev/null +++ b/mcpacker/model/ecotype.py @@ -0,0 +1,20 @@ +from mcpacker.model.scarcity import Scarcity +from typing import Any + +import mcpacker.model.scarcity as SC + + +# Class ############################################################################################ + +class Ecotype: + + def __init__(self, scarcity:Scarcity|None=None): + self.scarcity = scarcity or SC.SPARSE + + def __eq__(self, other:Any) -> bool: + if type(self) != type(other): return False + if self.scarcity != other.scarcity: return False + return True + + def __repr__(self) -> str: + return f"Ecotype(scarcity={self.scarcity!r})" diff --git a/mcpacker/model/fauna/mob.py b/mcpacker/model/fauna/mob.py index 4214ecb..d448e16 100644 --- a/mcpacker/model/fauna/mob.py +++ b/mcpacker/model/fauna/mob.py @@ -1,38 +1,26 @@ from collections.abc import Iterable from mcpacker.model.fauna.active import Active from mcpacker.model.resourceid import ResourceId +from mcpacker.model.spawnable import Spawnable import mcpacker.model.fauna.active as AC # Class ############################################################################################ -class Mob: +class Mob(Spawnable): """ A creature which may appear in the game world. """ def __init__(self, gameId:ResourceId|str, active:Active|Iterable[Active]=AC.DIURNAL): + super().__init__(str(gameId)) + if isinstance(active, Active): active = [active] self.gameId = ResourceId.parse(gameId) self.active = active - def __eq__(self, other) -> bool: - if type(self) != type(other): return False - if self.gameId != other.gameId: return False - return True - - def __hash__(self) -> int: - return hash(self.gameId) - - def __str__(self) -> str: - return str(self.gameId) - def __repr__(self) -> str: - return f"Mob<{self.gameId}>{{active:{self.active}}}" - - @property - def name(self) -> str: - return str(self.gameId) + return f"Mob(gameId={self.gameId!r}, active={self.active!r})" diff --git a/mcpacker/model/fauna/mob_test.py b/mcpacker/model/fauna/mob_test.py index e7d39b8..3ed0ab7 100644 --- a/mcpacker/model/fauna/mob_test.py +++ b/mcpacker/model/fauna/mob_test.py @@ -36,4 +36,9 @@ def test_str(squid): assert str(squid) == "minecraft:squid" def test_repr(cow): - assert repr(cow) == "Mob{active:[Active{start:0, end:12000}]}" + assert repr(cow) == ( + "Mob(" + "gameId=ResourceId(isTag=False, mod='minecraft', name='cow'), " + "active=[Active{start:0, end:12000}]" + ")" + ) diff --git a/mcpacker/model/fauna/mobecotype.py b/mcpacker/model/fauna/mobecotype.py new file mode 100644 index 0000000..a4968a9 --- /dev/null +++ b/mcpacker/model/fauna/mobecotype.py @@ -0,0 +1,35 @@ +from mcpacker.model.ecotype import Ecotype +from mcpacker.model.fauna.group import Group +from mcpacker.model.fauna.location import Location +from mcpacker.model.scarcity import Scarcity + +import mcpacker.model.fauna.group as GR +import mcpacker.model.fauna.location as LO +import mcpacker.model.scarcity as SC + + +# Class ############################################################################################ + +class MobEcotype(Ecotype): + + def __init__( + self, + scarcity:Scarcity=SC.SPARSE, + group:Group=GR.SOLO, + location:Location=LO.OUTSIDE + ): + super().__init__(scarcity) + self.group = group + self.location = location + + def __str__(self) -> str: + return f"{self.group!s}|{self.location!s}|{self.scarcity!s}" + + def __repr__(self) -> str: + return ( + "MobEcotype(" + f"group={self.group!r}, " + f"location={self.location!r}, " + f"scarcity={self.scarcity!r}" + ")" + ) diff --git a/mcpacker/model/fauna/mobspawn.py b/mcpacker/model/fauna/mobspawn.py index 74e82eb..265cfdc 100644 --- a/mcpacker/model/fauna/mobspawn.py +++ b/mcpacker/model/fauna/mobspawn.py @@ -1,33 +1,19 @@ -from collections.abc import Iterable -from mcpacker.model.ecology.biome import Biome from mcpacker.model.habitat import Habitat from mcpacker.model.fauna.mob import Mob +from mcpacker.model.fauna.mobecotype import MobEcotype from mcpacker.model.spawn import Spawn # Class ############################################################################################ -class MobSpawn(Spawn): +class MobSpawn(Spawn[Mob, MobEcotype]): """ - A description of where a creature should appear in the world. + Describes a certain habitat for a particular animal with its local variations. """ - def __init__(self, mob:Mob, habitats:Iterable[Habitat]|Habitat|None=None): - self.mob = mob - super().__init__(habitats) - - def __str__(self) -> str: - return self.name + def __init__(self, name:str, habitat:Habitat, mob:Mob, ecotype:MobEcotype): + super().__init__(name, habitat, mob, ecotype) @property - def name(self) -> str: - return self.mob.name - - def acceptedBy(self, biome:Biome) -> list[Habitat]: - result = [] - - for habitat in self.habitats: - if habitat.accepts(biome): - result.append(habitat) - - return result + def mob(self) -> Mob: + return self.spawnable diff --git a/mcpacker/model/fauna/mobspawn_test.py b/mcpacker/model/fauna/mobspawn_test.py index 572e655..a2f4fb6 100644 --- a/mcpacker/model/fauna/mobspawn_test.py +++ b/mcpacker/model/fauna/mobspawn_test.py @@ -1,5 +1,6 @@ from mcpacker.model.ecology.biomefilter import BiomeFilter from mcpacker.model.fauna.mob import Mob +from mcpacker.model.fauna.mobecotype import MobEcotype from mcpacker.model.fauna.mobspawn import MobSpawn from mcpacker.model.habitat import Habitat from pytest import fixture @@ -11,42 +12,44 @@ import mcpacker.model.ecology.heat as HE # Fixtures ######################################################################################### -@fixture(name="cow") -def createCow(): - yield Mob("minecraft:cow", AC.DIURNAL) - -@fixture(name="fields") -def createFieldsHabitat(): - yield Habitat(biomeFilter=BiomeFilter([FL.FIELD, HE.TEMPERATE])) - @fixture(name="spawn") -def createMobSpawn(cow, fields): - yield MobSpawn(cow, fields) +def createMobSpawn(): + yield MobSpawn( + "cow-in-field", + Habitat(biomeFilters=BiomeFilter([FL.FIELD, HE.TEMPERATE])), + Mob("minecraft:cow", AC.DIURNAL), + MobEcotype() + ) # Tests ############################################################################################ def test_cowsInFields(spawn): assert repr(spawn) == ( - "Spawn(" + - "habitats=[" + - "Habitat(" + - "altitude=Altitude(name=anywhere, bottom=-64, top=320), " + - "biomeFilter=BiomeFilter(" + - "required=[Flora(name='field'), Heat(name='temperate')], " + - "prohibited=[]" + - "), " + - "seasons=[" + - "Season(name='spring'), " + - "Season(name='summer'), " + - "Season(name='autumn'), " + - "Season(name='winter')" + - "], " + - "group=Group(name='solo', smallest=1, largest=1), " + - "location=Location(name='outside'), " + - "scarcity=Scarcity(name='sparse')" + - ")" + - "]" + + "MobSpawn(" + "ecotype=MobEcotype(" + "group=Group(name='solo', smallest=1, largest=1), " + "location=Location(name='outside'), " + "scarcity=Scarcity(name='sparse')" + "), " + "habitat=Habitat(" + "altitude=Altitude(name=anywhere, bottom=-64, top=320), " + "biomeFilters=[BiomeFilter(" + "required=[Flora(name='field'), Heat(name='temperate')], " + "prohibited=[])" + "], " + "seasons=[" + "Season(name='spring'), " + "Season(name='summer'), " + "Season(name='autumn'), " + "Season(name='winter')" + "]" + "), " + "name='cow-in-field', " + "spawnable=Mob(" + "gameId=ResourceId(isTag=False, mod='minecraft', name='cow'), " + "active=[Active{start:0, end:12000}]" + ")" ")" ) diff --git a/mcpacker/model/fauna/mobspawnbuilder.py b/mcpacker/model/fauna/mobspawnbuilder.py new file mode 100644 index 0000000..30e81e6 --- /dev/null +++ b/mcpacker/model/fauna/mobspawnbuilder.py @@ -0,0 +1,50 @@ +from mcpacker.model.fauna.group import Group +from mcpacker.model.fauna.location import Location +from mcpacker.model.fauna.mob import Mob +from mcpacker.model.fauna.mobecotype import MobEcotype +from mcpacker.model.fauna.mobspawn import MobSpawn +from mcpacker.model.fauna.mobspawncatalog import MobSpawnCatalog +from mcpacker.model.scarcity import Scarcity +from mcpacker.model.spawnbuilder import SpawnBuilder +from typing import Self + +import mcpacker.model.fauna.group as GR +import mcpacker.model.fauna.location as LO + + +# Class ############################################################################################ + +class MobSpawnBuilder(SpawnBuilder[Mob, MobEcotype, MobSpawn]): + + def __init__(self, catalog:MobSpawnCatalog): + super().__init__(catalog, MobSpawn) + + @property + def group(self) -> Group: + return self._group + + @group.setter + def group(self, value:Group|None): + if not value: + value = GR.SOLO + + self._group = value + + @property + def location(self) -> Location: + return self._location + + @location.setter + def location(self, value:Location|None): + if not value: + value = LO.OUTSIDE + + self._location = value + + def _createEcotype(self) -> MobEcotype: + return MobEcotype(self.scarcity, self.group, self.location) + + def _reset(self): + super()._reset() + self.group = None + self.location = None diff --git a/mcpacker/model/flora/plant.py b/mcpacker/model/flora/plant.py index 344dbd5..94d5b99 100644 --- a/mcpacker/model/flora/plant.py +++ b/mcpacker/model/flora/plant.py @@ -1,12 +1,9 @@ +from mcpacker.model.spawnable import Spawnable + + # Class ############################################################################################ -class Plant: - - def __init__(self, name:str): - self.name = name +class Plant(Spawnable): def __repr__(self) -> str: - return f"Plant(name={repr(self.name)})" - - def __str__(self) -> str: - return self.name + return f"Plant(name={self.name!r})" diff --git a/mcpacker/model/flora/plantspawn.py b/mcpacker/model/flora/plantspawn.py index 471c7ba..a81a612 100644 --- a/mcpacker/model/flora/plantspawn.py +++ b/mcpacker/model/flora/plantspawn.py @@ -1,51 +1,19 @@ -from collections.abc import Iterable -from mcpacker.model.altitude import Altitude -from mcpacker.model.ecology.biome import Biome -from mcpacker.model.ecology.biomefilter import BiomeFilter +from mcpacker.model.ecotype import Ecotype +from mcpacker.model.habitat import Habitat +from mcpacker.model.spawn import Spawn from mcpacker.model.flora.plant import Plant -from mcpacker.model.scarcity import Scarcity - -import mcpacker.model.altitude as AL -import mcpacker.model.scarcity as SC # Class ############################################################################################ -class PlantSpawn: +class PlantSpawn(Spawn[Plant, Ecotype]): + """ + Describes a certain habitat for a particular animal with its local variations. + """ - def __init__( - self, - plant:Plant, - altitude:Altitude=AL.ANYWHERE, - biomeFilters:Iterable[BiomeFilter]|BiomeFilter|None=None, - scarcity:Scarcity=SC.SPARSE, - ): - if not biomeFilters: - biomeFilters = BiomeFilter() - if not isinstance(biomeFilters, Iterable): - biomeFilters = [biomeFilters] - - self.altitude = altitude - self.biomeFilters = list(biomeFilters) - self.plant = plant - self.scarcity = scarcity - - def __repr__(self) -> str: - return "".join([ - "PlantSpawn(", - "altitude=", repr(self.altitude), ", ", - "biomeFilters=", repr(self.biomeFilters), ", ", - "plant=", repr(self.plant), ", ", - "scarcity=", repr(self.scarcity), - ")" - ]) + def __init__(self, name:str, habitat:Habitat, plant:Plant, ecotype:Ecotype): + super().__init__(name, habitat, plant, ecotype) @property - def name(self) -> str: - return self.plant.name - - def acceptsBiome(self, biome:Biome) -> bool: - for biomeFilter in self.biomeFilters: - if biomeFilter.accepts(biome): return True - - return False + def plant(self) -> Plant: + return self.spawnable diff --git a/mcpacker/model/flora/plantspawnbuilder.py b/mcpacker/model/flora/plantspawnbuilder.py new file mode 100644 index 0000000..1499e34 --- /dev/null +++ b/mcpacker/model/flora/plantspawnbuilder.py @@ -0,0 +1,17 @@ +from mcpacker.model.ecotype import Ecotype +from mcpacker.model.flora.plant import Plant +from mcpacker.model.flora.plantspawn import PlantSpawn +from mcpacker.model.flora.plantspawncatalog import PlantSpawnCatalog +from mcpacker.model.spawnbuilder import SpawnBuilder +from typing import Self + + +# Class ############################################################################################ + +class PlantSpawnBuilder(SpawnBuilder[Plant, Ecotype, PlantSpawn]): + + def __init__(self, catalog:PlantSpawnCatalog): + super().__init__(catalog, PlantSpawn) + + def _createEcotype(self) -> Ecotype: + return Ecotype(self.scarcity) diff --git a/mcpacker/model/flora/plantspawncatalog.py b/mcpacker/model/flora/plantspawncatalog.py index eb4486f..17bf252 100644 --- a/mcpacker/model/flora/plantspawncatalog.py +++ b/mcpacker/model/flora/plantspawncatalog.py @@ -6,3 +6,4 @@ from mcpacker.model.flora.plantspawn import PlantSpawn class PlantSpawnCatalog(Catalog[PlantSpawn]): pass + diff --git a/mcpacker/model/habitat.py b/mcpacker/model/habitat.py index 202271f..1ba4678 100644 --- a/mcpacker/model/habitat.py +++ b/mcpacker/model/habitat.py @@ -2,16 +2,10 @@ from collections.abc import Iterable from mcpacker.model.altitude import Altitude from mcpacker.model.ecology.biome import Biome from mcpacker.model.ecology.biomefilter import BiomeFilter -from mcpacker.model.fauna.group import Group -from mcpacker.model.fauna.location import Location -from mcpacker.model.scarcity import Scarcity from mcpacker.model.season import Season -import mcpacker.model.altitude as A -import mcpacker.model.fauna.group as G -import mcpacker.model.fauna.location as L -import mcpacker.model.scarcity as C -import mcpacker.model.season as E +import mcpacker.model.altitude as AL +import mcpacker.model.season as SE # Class ############################################################################################ @@ -24,77 +18,42 @@ class Habitat: def __init__( self, - altitude:Altitude=A.ANYWHERE, - biomeFilter:BiomeFilter|None=None, - seasons:Iterable[Season]|Season=E.ALL, - - group:Group=G.SOLO, - location:Location=L.OUTSIDE, - scarcity:Scarcity=C.SPARSE, + altitude:Altitude=AL.ANYWHERE, + biomeFilters:Iterable[BiomeFilter]|BiomeFilter|None=None, + seasons:Iterable[Season]|Season=SE.ALL, ): - self.altitude = altitude - self.biomeFilter = biomeFilter or BiomeFilter() - self.seasons = [seasons] if isinstance(seasons, Season) else seasons + if not biomeFilters: + biomeFilters = BiomeFilter() + if isinstance(biomeFilters, BiomeFilter): + biomeFilters = [biomeFilters] - self.group = group - self.location = location - self.scarcity = scarcity + if isinstance(seasons, Season): + seasons = [seasons] + + self.altitude = altitude + self.biomeFilters = biomeFilters + self.seasons = seasons self._source:Habitat|None = None def __str__(self) -> str: - return "".join([str(p) for p in [ - self.altitude, "|", - self.biomeFilter, "|", - ", ".join([str(s) for s in self.seasons]), "|", - self.group, "|", - self.location, "|", - self.scarcity - ]]) + return ( + f"{self.altitude!s}|" + f"{self.biomeFilters!s}|" + f"{self.seasons!s}" + ) def __repr__(self) -> str: return "".join([str(p) for p in [ "Habitat(", - "altitude=", repr(self.altitude), ", ", - "biomeFilter=", repr(self.biomeFilter), ", ", - "seasons=[", ", ".join([repr(s) for s in self.seasons]), "], ", - "group=", repr(self.group), ", ", - "location=", repr(self.location), ", ", - "scarcity=", repr(self.scarcity), + f"altitude={self.altitude!r}, " + f"biomeFilters={self.biomeFilters!r}, " + f"seasons={self.seasons!r}" ")" ]]) def accepts(self, biome:Biome) -> bool: - return self.biomeFilter.accepts(biome) + for biomeFilter in self.biomeFilters: + if not biomeFilter.accepts(biome): return False - def collect(self) -> "Iterable[Habitat]": - stack:list[Habitat] = [] - current:Habitat|None = self - while current: - stack.append(current) - current = current._source - - while stack: - yield stack.pop() - - def derive( - self, - *, - altitude:Altitude|None=None, - biomeFilter:BiomeFilter|None=None, - seasons:Iterable[Season]|Season|None=None, - group:Group|None=None, - location:Location|None=None, - scarcity:Scarcity|None=None, - ): - result = Habitat( - altitude or self.altitude, - biomeFilter or self.biomeFilter, - seasons or self.seasons, - group or self.group, - location or self.location, - scarcity or self.scarcity - ) - result._source = self - - return result + return True diff --git a/mcpacker/model/habitat_test.py b/mcpacker/model/habitat_test.py index 6acdb45..cb30145 100644 --- a/mcpacker/model/habitat_test.py +++ b/mcpacker/model/habitat_test.py @@ -2,10 +2,10 @@ from mcpacker.model.ecology.biomefilter import BiomeFilter from mcpacker.model.habitat import Habitat from pytest import fixture +import mcpacker.model.altitude as AL import mcpacker.model.ecology.flora as FL import mcpacker.model.ecology.heat as HE import mcpacker.model.season as SE -import mcpacker.model.scarcity as SC # Fixtures ######################################################################################### @@ -14,40 +14,20 @@ import mcpacker.model.scarcity as SC def createFieldsFilter(): yield BiomeFilter([FL.FIELD, HE.TEMPERATE]) -@fixture(name="derivedHabitats") -def createDerivedHabitat(fields): - yield ( - Habitat(biomeFilter=fields, seasons=SE.SUMMER, scarcity=SC.COMMON) - .derive(seasons=SE.AUTUMN, scarcity=SC.UNCOMMON) - .derive(seasons=SE.WINTER, scarcity=SC.ABSENT) - .collect() - ) - @fixture(name="fields") def createFieldsHabitat(fields): - yield Habitat(biomeFilter=fields) + yield Habitat(AL.LOWLANDS, BiomeFilter([FL.FIELD, HE.TEMPERATE]), SE.HOT) # Tests ############################################################################################ -def test_derive(derivedHabitats): - assert [h.scarcity for h in derivedHabitats] == [SC.COMMON, SC.UNCOMMON, SC.ABSENT] - def test_repr(fields): assert repr(fields) == ( - "Habitat(" + - "altitude=Altitude(name=anywhere, bottom=-64, top=320), " + - "biomeFilter=BiomeFilter(" + - "required=[Flora(name='field'), Heat(name='temperate')], " + + "Habitat(" + "altitude=Altitude(name=lowlands, bottom=70, top=92), " + "biomeFilters=[BiomeFilter(" + "required=[Flora(name='field'), Heat(name='temperate')], " "prohibited=[]" - "), " + - "seasons=[" + - "Season(name='spring'), " + - "Season(name='summer'), " + - "Season(name='autumn'), " + - "Season(name='winter')" + - "], " + - "group=Group(name='solo', smallest=1, largest=1), " + - "location=Location(name='outside'), " + - "scarcity=Scarcity(name='sparse')" + + ")], " + "seasons=[Season(name='summer')]" ")" ) diff --git a/mcpacker/model/population.py b/mcpacker/model/population.py new file mode 100644 index 0000000..bfbbdc8 --- /dev/null +++ b/mcpacker/model/population.py @@ -0,0 +1,4 @@ +class Population: + """ + Describes traits of a + """ diff --git a/mcpacker/model/spawn.py b/mcpacker/model/spawn.py index 5afee1e..f194bfd 100644 --- a/mcpacker/model/spawn.py +++ b/mcpacker/model/spawn.py @@ -1,20 +1,28 @@ from collections.abc import Iterable +from mcpacker.model.ecotype import Ecotype from mcpacker.model.habitat import Habitat +from mcpacker.model.spawnable import Spawnable # Class ############################################################################################ -class Spawn: +class Spawn[SpawnableKind: Spawnable, EcotypeKind: Ecotype]: """ - Abstract base class for placing something into its various habitats. + Abstract base class for placing something into a habitat with local variations. """ - def __init__(self, habitats:Iterable[Habitat]|Habitat|None=None): - if isinstance(habitats, Habitat): - habitats = list(habitats.collect()) - - self.habitats = list(habitats or []) + def __init__(self, name:str, habitat:Habitat, spawnable:SpawnableKind, ecotype:EcotypeKind): + self.ecotype = ecotype + self.habitat = habitat + self.name = name + self.spawnable = spawnable def __repr__(self) -> str: - return f"Spawn(habitats={self.habitats!r})" - + return ( + f"{type(self).__name__}(" + f"ecotype={self.ecotype!r}, " + f"habitat={self.habitat!r}, " + f"name={self.name!r}, " + f"spawnable={self.spawnable!r}" + ")" + ) diff --git a/mcpacker/model/spawn_test.py b/mcpacker/model/spawn_test.py index f6566e1..722c158 100644 --- a/mcpacker/model/spawn_test.py +++ b/mcpacker/model/spawn_test.py @@ -1,44 +1,53 @@ from mcpacker.model.ecology.biomefilter import BiomeFilter +from mcpacker.model.ecotype import Ecotype from mcpacker.model.habitat import Habitat from mcpacker.model.spawn import Spawn +from mcpacker.model.spawnable import Spawnable from pytest import fixture import mcpacker.model.ecology.flora as F import mcpacker.model.ecology.heat as E +# Helper Classes ################################################################################### + +class SampleSpawnable(Spawnable): + pass + + # Fixtures ######################################################################################### -@fixture(name="fields") -def createFieldsHabitat(): - yield Habitat(biomeFilter=BiomeFilter([F.FIELD, E.TEMPERATE])) - @fixture(name="spawn") -def createSpawn(fields): - yield Spawn([fields]) +def createSpawn(): + yield Spawn( + "spawn", + Habitat(biomeFilters=BiomeFilter([F.FIELD, E.TEMPERATE])), + SampleSpawnable("spawnable"), + Ecotype() + ) # Tests ############################################################################################ def test_repr(spawn): assert repr(spawn) == ( - "Spawn(habitats=[" + - "Habitat(" + - "altitude=Altitude(name=anywhere, bottom=-64, top=320), " + - "biomeFilter=BiomeFilter(" + - "required=[Flora(name='field'), Heat(name='temperate')], " + - "prohibited=[]" + - "), " - "seasons=[" + - "Season(name='spring'), " + - "Season(name='summer'), " + - "Season(name='autumn'), " + - "Season(name='winter')" + - "], " + - "group=Group(name='solo', smallest=1, largest=1), " + - "location=Location(name='outside'), " + - "scarcity=Scarcity(name='sparse')" + - ")" + - "])" + "Spawn(" + "ecotype=Ecotype(scarcity=Scarcity(name='sparse')), " + "habitat=Habitat(" + "altitude=Altitude(name=anywhere, bottom=-64, top=320), " + "biomeFilters=[BiomeFilter(" + "required=[Flora(name='field'), Heat(name='temperate')], " + "prohibited=[]" + ")], " + "seasons=[" + "Season(name='spring'), " + "Season(name='summer'), " + "Season(name='autumn'), " + "Season(name='winter')" + "]" + "), " + "name='spawn', " + "spawnable=Spawnable(name='spawnable')" + ")" ) diff --git a/mcpacker/model/spawnable.py b/mcpacker/model/spawnable.py new file mode 100644 index 0000000..dd079d8 --- /dev/null +++ b/mcpacker/model/spawnable.py @@ -0,0 +1,28 @@ +from typing import Any + + +# Class ############################################################################################ + +class Spawnable: + + def __init__(self, name:str): + self.name = name + + def __eq__(self, other:Any) -> bool: + if type(self) != type(other): return False + if self.name != other.name: return False + return True + + def __hash__(self) -> int: + return hash(self.name) + + def __lt__(self, other:Any) -> bool: + if type(self) != type(other): raise ValueError() + if self.name >= other.name: return False + return True + + def __repr__(self) -> str: + return f"Spawnable(name={self.name!r})" + + def __str__(self) -> str: + return self.name diff --git a/mcpacker/model/spawnbuilder.py b/mcpacker/model/spawnbuilder.py new file mode 100644 index 0000000..c39e28d --- /dev/null +++ b/mcpacker/model/spawnbuilder.py @@ -0,0 +1,107 @@ +from collections.abc import Iterable +from mcpacker.model.altitude import Altitude +from mcpacker.model.catalog import Catalog +from mcpacker.model.ecology.biomefilter import BiomeFilter +from mcpacker.model.ecotype import Ecotype +from mcpacker.model.habitat import Habitat +from mcpacker.model.season import Season +from mcpacker.model.scarcity import Scarcity +from mcpacker.model.spawn import Spawn +from mcpacker.model.spawnable import Spawnable +from typing import Self +from typing import Generic +from typing import TypeVar + +import mcpacker.model.altitude as AL +import mcpacker.model.scarcity as SC +import mcpacker.model.season as SE + + +# Class ############################################################################################ + +class SpawnBuilder[SpawnableKind:Spawnable, EcotypeKind:Ecotype, SpawnKind:Spawn]: + + def __init__(self, catalog:Catalog[SpawnKind], spawnClass:type[SpawnKind]): + self._catalog = catalog + self._spawnable:SpawnableKind|None = None + self._spawnClass = spawnClass + self._reset() + + def start(self, spawnable:SpawnableKind) -> Self: + self._reset() + self._spawnable = spawnable + return self + + def save(self, name:str) -> Self: + if not self._spawnable: raise RuntimeError("you must call start before save") + + self._catalog.add( + self._spawnClass(name, self._createHabitat(), self._spawnable, self._createEcotype()) + ) + return self + + # Properties ############################################################### + + @property + def altitude(self) -> Altitude: + return self._altitude + + @altitude.setter + def altitude(self, value:Altitude|None): + if value == None: + value = AL.ANYWHERE + + self._altitude = value + + @property + def biomeFilters(self) -> list[BiomeFilter]: + return self._biomeFilters + + @biomeFilters.setter + def biomeFilters(self, value:Iterable[BiomeFilter]|BiomeFilter|None): + if value == None: + value = BiomeFilter() + if isinstance(value, BiomeFilter): + value = [value] + + self._biomeFilters = list(value) + + @property + def scarcity(self) -> Scarcity: + return self._scarcity + + @scarcity.setter + def scarcity(self, value:Scarcity|None): + if value == None: + value = SC.SPARSE + + self._scarcity = value + + @property + def seasons(self) -> list[Season]: + return self._seasons + + @seasons.setter + def seasons(self, value:Iterable[Season]|Season|None): + if value == None: + value = SE.ALL + if isinstance(value, Season): + value = [value] + + self._seasons = list(value) + + # Subclass Overrides ####################################################### + + def _createEcotype(self) -> EcotypeKind: + raise NotImplementedError() + + def _reset(self): + self.altitude = None + self.biomeFilters = None + self.scarcity = None + self.seasons = None + + # Private Methods ########################################################## + + def _createHabitat(self) -> Habitat: + return Habitat(self.altitude, self.biomeFilters, self.seasons) diff --git a/mcpacker/pack/mysteriousisland/addmobspawns.py b/mcpacker/pack/mysteriousisland/addmobspawns.py index b09d927..847b8e6 100644 --- a/mcpacker/pack/mysteriousisland/addmobspawns.py +++ b/mcpacker/pack/mysteriousisland/addmobspawns.py @@ -2,6 +2,7 @@ from mcpacker.model.ecology.biomefilter import BiomeFilter as BF from mcpacker.model.fauna.mob import Mob from mcpacker.model.fauna.mobcatalog import MobCatalog from mcpacker.model.fauna.mobspawn import MobSpawn +from mcpacker.model.fauna.mobspawnbuilder import MobSpawnBuilder from mcpacker.model.habitat import Habitat from mcpacker.model.resourceid import ResourceId from mcpacker.model.modpack import ModPack @@ -24,607 +25,496 @@ import mcpacker.model.season as SE def addMobSpawns(pack:ModPack): mobs = pack.world.mobs - spawns = pack.world.mobSpawns + b = MobSpawnBuilder(pack.world.mobSpawns) - spawns.add( - MobSpawn(mobs["animalplus:raccoon"], - Habitat( - altitude = AL.span(AL.LOWLANDS, AL.HILLS), - biomeFilter = BF([(FL.CANOPY, FL.FOREST), HE.within(HE.TEMPERATE, HE.BOREAL)]), - seasons = SE.exclude(SE.WINTER), - group = GR.merge(GR.SOLO, GR.PAIR), - scarcity = SC.UNCOMMON, - ).derive( - seasons = SE.WINTER, - scarcity = SC.SPARSE, - ) - ) - ) + b.start(mobs["animalplus:raccoon"]) + b.altitude = AL.span(AL.LOWLANDS, AL.HILLS) + b.biomeFilters = BF([(FL.CANOPY, FL.FOREST), HE.within(HE.TEMPERATE, HE.BOREAL)]) + b.seasons = SE.exclude(SE.WINTER) + b.group = GR.merge(GR.SOLO, GR.PAIR) + b.scarcity = SC.UNCOMMON + b.save("raccoon-normal") - spawns.add( - MobSpawn(mobs["animalplus:red_panda"], - Habitat( - altitude = AL.span(AL.UPLANDS, AL.HILLS), - biomeFilter = BF([ResourceId.parse("bamboo_forest")]), - seasons = SE.ALL, - group = GR.merge(GR.SOLO, GR.PAIR), - scarcity = SC.SPARSE, - ) - ) - ) + b.seasons = SE.WINTER + b.scarcity = SC.SPARSE + b.save("raccoon-winter") - spawns.add( - MobSpawn(mobs["bearminimum:black_bear"], - Habitat( - altitude = AL.span(AL.LOWLANDS, AL.ALPINE), - biomeFilter = BF([HE.TEMPERATE, FL.FOREST]), - seasons = SE.AUTUMN, - group = GR.merge(GR.SOLO, GR.FAMILY), - scarcity = SC.UNCOMMON, - ).derive( - seasons = [SE.SPRING, SE.SUMMER], scarcity = SC.SPARSE - ).derive( - seasons = SE.WINTER, scarcity = SC.UNUSUAL - ).derive( - biomeFilter = BF([HE.BOREAL, (FL.CANOPY, FL.FOREST)]), - seasons = SE.AUTUMN, - scarcity = SC.SPARSE - ).derive( - seasons = [SE.SPRING, SE.SUMMER], scarcity = SC.UNUSUAL - ).derive( - seasons = SE.WINTER, scarcity = SC.RARE - ) - ) - ) + b.start(mobs["animalplus:red_panda"]) + b.altitude = AL.span(AL.UPLANDS, AL.HILLS) + b.biomeFilters = BF([ResourceId.parse("bamboo_forest")]) + b.seasons = SE.ALL + b.group = GR.merge(GR.SOLO, GR.PAIR) + b.scarcity = SC.SPARSE + b.save("redpanda-normal") - spawns.add( - MobSpawn(mobs["minecraft:armadillo"], - Habitat( - altitude = AL.LOWLANDS, - biomeFilter = BF([HE.SUBTROPICAL, HU.DRY, SO.CLAYEY]), - seasons = SE.SUMMER, - group = GR.merge(GR.SOLO, GR.PAIR), - scarcity = SC.COMMON - ).derive( - altitude = AL.UPLANDS, - scarcity = SC.SPARSE, - ).derive( - altitude = AL.LOWLANDS, - scarcity = SC.UNCOMMON, - seasons = SE.exclude(SE.SUMMER), - ).derive( - altitude = AL.UPLANDS, - scarcity = SC.UNUSUAL, - ) - ) - ) + b.start(mobs["bearminimum:black_bear"]) + b.altitude = AL.span(AL.LOWLANDS, AL.ALPINE) + b.biomeFilters = BF([HE.TEMPERATE, FL.FOREST]) + b.seasons = SE.AUTUMN + b.group = GR.merge(GR.SOLO, GR.FAMILY) + b.scarcity = SC.UNCOMMON + b.save("blackbear-temperate-autumn") - spawns.add( - MobSpawn(mobs["minecraft:blaze"], - Habitat( - altitude = AL.PLUTONIC, - location = LO.CAVE, - group = GR.SOLO, - scarcity = SC.UNCOMMON - ) - ) - ) + b.seasons = [SE.SPRING, SE.SUMMER] + b.scarcity = SC.SPARSE + b.save("blackbear-temperate-springsummer") - spawns.add( - MobSpawn(mobs["bearminimum:brown_bear"], - Habitat( - altitude = AL.span(AL.LOWLANDS, AL.ALPINE), - biomeFilter = BF([HE.BOREAL, (FL.CANOPY, FL.FOREST)]), - seasons = SE.AUTUMN, - group = GR.SOLO, - scarcity = SC.SPARSE - ).derive( - seasons = [SE.SPRING, SE.SUMMER], scarcity = SC.UNUSUAL - ).derive( - seasons = SE.WINTER, scarcity = SC.RARE - ) - ) - ) + b.seasons = SE.WINTER + b.scarcity = SC.UNUSUAL + b.save("blackbear-temperate-winter") - spawns.add( - MobSpawn(mobs["minecraft:camel"], - Habitat( - altitude = AL.span(AL.DUNES, AL.LOWLANDS), - biomeFilter = BF([HE.TROPICAL, HU.DRY, FL.BARREN, SO.SANDY]), - seasons = SE.DRY, - group = GR.FAMILY, - scarcity = SC.UNUSUAL, - ).derive( - seasons = SE.WET, scarcity = SC.RARE - ) - ) - ) + b.biomeFilters = BF([HE.BOREAL, (FL.CANOPY, FL.FOREST)]) + b.seasons = SE.AUTUMN + b.scarcity = SC.SPARSE + b.save("blackbear-boreal-autumn") - spawns.add( - MobSpawn(mobs["minecraft:cave_spider"], - Habitat( - altitude = AL.span(AL.OVERBURDEN, AL.HILLS), - location = LO.CAVE, - group = GR.SOLO, - scarcity = SC.UNCOMMON - ) - ) - ) + b.seasons = [SE.SPRING, SE.SUMMER] + b.scarcity = SC.UNUSUAL + b.save("blackbear-boreal-springsummer") - spawns.add( - MobSpawn(mobs["cold_sweat:chameleon"], - Habitat( - altitude = AL.span(AL.DUNES, AL.UPLANDS), - biomeFilter = BF([(FL.CANOPY, FL.FOREST), HE.TROPICAL, HU.WET, WA.INLAND]), - seasons = SE.WET, - group = GR.merge(GR.SOLO, GR.FAMILY), - scarcity = SC.COMMON, - ).derive( - seasons = SE.DRY, - scarcity = SC.UNCOMMON, - ) - ) - ) + b.seasons = SE.WINTER + b.scarcity = SC.RARE + b.save("blackbear-boreal-winter") - spawns.add( - MobSpawn(mobs["minecraft:chicken"], - Habitat( - altitude = AL.span(AL.LOWLANDS, AL.UPLANDS), - biomeFilter = BF([FL.CLEARING, HE.TROPICAL, HU.WET], [SO.FUNGAL]), - seasons = SE.SUMMER, - group = GR.TROUP, - scarcity = SC.COMMON, - ).derive( - seasons = SE.exclude(SE.SUMMER), - scarcity = SC.UNCOMMON - ) - ) - ) + b.start(mobs["minecraft:armadillo"]) + b.altitude = AL.LOWLANDS + b.biomeFilters = BF([HE.SUBTROPICAL, HU.DRY, SO.CLAYEY]) + b.seasons = SE.SUMMER + b.group = GR.merge(GR.SOLO, GR.PAIR) + b.scarcity = SC.COMMON + b.save("armadillo-lowlands-summer") - spawns.add( - MobSpawn(mobs["minecraft:cod"], - Habitat( - altitude = AL.span(AL.SHALLOWS, AL.DEEPS), - biomeFilter = BF([HE.BOREAL, WA.OCEAN]), - seasons = SE.ALL, - group = GR.HERD, - scarcity = SC.COMMON, - ) - ) - ) + b.altitude = AL.UPLANDS + b.scarcity = SC.SPARSE + b.save("armadillo-uplands-summer") - spawns.add( - MobSpawn(mobs["minecraft:cow"], - Habitat( - altitude = AL.span(AL.LOWLANDS, AL.UPLANDS), - biomeFilter = BF( - [ - HE.within(HE.SUBTROPICAL, HE.BOREAL), - [HU.DAMP, HU.DRY], - FL.FIELD, - ], [ - SO.FUNGAL - ] - ), - seasons = [SE.SPRING, SE.SUMMER], - group = GR.HERD, - scarcity = SC.SPARSE, - ).derive( - altitude = AL.LOWLANDS, - seasons = [SE.AUTUMN, SE.WINTER], - group = GR.TROUP, - scarcity = SC.UNUSUAL, - ) - ) - ) + b.altitude = AL.LOWLANDS + b.scarcity = SC.UNCOMMON + b.seasons = SE.exclude(SE.SUMMER) + b.save("armadillo-lowlands-normal") - spawns.add( - MobSpawn(mobs["crabbersdelight:crab"], - Habitat( - altitude = AL.DUNES, - biomeFilter = BF([WA.COAST, [HE.TROPICAL, HE.SUBTROPICAL]]), - seasons = SE.ALL, - group = GR.SOLO, - scarcity = SC.UNCOMMON, - ).derive( - biomeFilter = BF([WA.COAST, [HE.TEMPERATE, HE.BOREAL]]), - scarcity = SC.SPARSE, - ) - ) - ) + b.altitude = AL.UPLANDS + b.scarcity = SC.UNUSUAL + b.save("armadillo-uplands-normal") - spawns.add( - MobSpawn(mobs["minecraft:dolphin"], - Habitat( - altitude = AL.span(AL.SURFACE, AL.DEEPS), - biomeFilter = BF([WA.OCEAN, [HE.SUBTROPICAL, HE.TEMPERATE]]), - location = LO.WATER, - seasons = SE.SUMMER, - group = GR.FAMILY, - scarcity = SC.UNCOMMON, - ).derive( - seasons = SE.exclude(SE.SUMMER), - scarcity = SC.SPARSE, - ) - ) - ) + b.start(mobs["minecraft:blaze"]) + b.altitude = AL.PLUTONIC + b.location = LO.CAVE + b.group = GR.SOLO + b.scarcity = SC.UNCOMMON + b.save("blaze-normal") - spawns.add(MobSpawn(mobs["minecraft:donkey"], - Habitat( - altitude = AL.span(AL.UPLANDS, AL.HILLS), - biomeFilter = BF([HE.SUBTROPICAL, HU.DRY, FL.FIELD]), - seasons = [SE.SUMMER, SE.AUTUMN], - group = GR.FAMILY, - scarcity = SC.SPARSE, - ).derive( - altitude = AL.span(AL.LOWLANDS, AL.UPLANDS), - seasons = [SE.WINTER, SE.SPRING], - scarcity = SC.SPARSE, - ) - )) + b.start(mobs["bearminimum:brown_bear"]) + b.altitude = AL.span(AL.LOWLANDS, AL.ALPINE) + b.biomeFilters = BF([HE.BOREAL, (FL.CANOPY, FL.FOREST)]) + b.seasons = SE.AUTUMN + b.group = GR.SOLO + b.scarcity = SC.SPARSE + b.save("brownbear-autumn") - spawns.add(MobSpawn(mobs["minecraft:fox"], - Habitat( - altitude = AL.span(AL.LOWLANDS, AL.HILLS), - biomeFilter = BF([HE.within(HE.TEMPERATE, HE.FROZEN), FL.FOREST]), - seasons = [SE.SPRING], - group = GR.FAMILY, - scarcity = SC.UNCOMMON, - ).derive( - seasons = SE.exclude(SE.SPRING), - group = GR.SOLO, - scarcity = SC.SPARSE, - ) - )) + b.seasons = [SE.SPRING, SE.SUMMER] + b.scarcity = SC.UNUSUAL + b.save("brownbear-springsummer") - spawns.add( - MobSpawn(mobs["minecraft:frog"], - Habitat( - altitude = AL.span(AL.SURFACE, AL.DUNES), - biomeFilter = BF([(HE.TROPICAL, HE.TEMPERATE), HU.WET, WA.SWAMP]), - group = GR.HERD, - scarcity = SC.COMMON, - seasons = [SE.SUMMER], - ).derive( - seasons = [SE.SPRING], - scarcity = SC.SPARSE, - ).derive( - seasons = [SE.AUTUMN], - scarcity = SC.UNUSUAL, - ).derive( - seasons = [SE.WINTER], - scarcity = SC.ABSENT, - ) - ) - ) + b.seasons = SE.WINTER + b.scarcity = SC.RARE + b.save("brownbear-winter") - spawns.add( - MobSpawn(mobs["minecraft:glow_squid"], - Habitat( - altitude = AL.ABYSS, - biomeFilter = BF([WA.OCEAN], [HE.FROZEN]), - group = GR.SOLO, - location = LO.WATER, - scarcity = SC.RARE, - seasons = SE.ALL, - ) - ) - ) + b.start(mobs["minecraft:camel"]) + b.altitude = AL.span(AL.DUNES, AL.LOWLANDS) + b.biomeFilters = BF([HE.TROPICAL, HU.DRY, FL.BARREN, SO.SANDY]) + b.seasons = SE.DRY + b.group = GR.FAMILY + b.scarcity = SC.UNUSUAL + b.save("camel-dry") - spawns.add( - MobSpawn(mobs["minecraft:goat"], - Habitat( - altitude = AL.span(AL.HILLS, AL.CRAGS), - biomeFilter = BF([(HE.BOREAL, HE.FROZEN), SO.ROCKY]), - group = GR.FAMILY, - scarcity = SC.UNCOMMON, - seasons = SE.ALL, - ) - ) - ) + b.seasons = SE.WET + b.scarcity = SC.RARE + b.save("camel-wet") - spawns.add( - MobSpawn(mobs["minecraft:horse"], - Habitat( - altitude = AL.span(AL.DUNES, AL.LOWLANDS), - biomeFilter = BF( - [FL.FIELD, (HE.SUBTROPICAL, HE.TEMPERATE), (SO.LOAMY, SO.PEATY, SO.SANDY)] - ), - group = GR.HERD, - scarcity = SC.UNUSUAL, - seasons = SE.ALL, - ) - ) - ) + b.start(mobs["minecraft:cave_spider"]) + b.altitude = AL.span(AL.OVERBURDEN, AL.HILLS) + b.location = LO.CAVE + b.group = GR.SOLO + b.scarcity = SC.UNCOMMON + b.save("cavespider-normal") - spawns.add( - MobSpawn(mobs["minecraft:llama"], - Habitat( - altitude = AL.span(AL.ALPINE, AL.PEAKS), - biomeFilter = BF([(HE.BOREAL, HE.FROZEN), SO.ROCKY]), - group = GR.HERD, - scarcity = SC.UNCOMMON, - seasons = SE.ALL, - ) - ) - ) + b.start(mobs["cold_sweat:chameleon"]) + b.altitude = AL.span(AL.DUNES, AL.UPLANDS) + b.biomeFilters = BF([(FL.CANOPY, FL.FOREST), HE.TROPICAL, HU.WET, WA.INLAND]) + b.seasons = SE.WET + b.group = GR.merge(GR.SOLO, GR.FAMILY) + b.scarcity = SC.COMMON + b.save("chameleon-wet") - spawns.add( - MobSpawn(mobs["minecraft:magma_cube"], - Habitat( - altitude = AL.PLUTONIC, - location = LO.CAVE, - group = GR.SOLO, - scarcity = SC.UNCOMMON - ) - ) - ) + b.seasons = SE.DRY + b.scarcity = SC.UNCOMMON + b.save("chameleon-dry") - spawns.add( - MobSpawn(mobs["minecraft:mooshroom"], - Habitat( - altitude = AL.span(AL.DUNES, AL.UPLANDS), - biomeFilter = BF([SO.FUNGAL]), - group = GR.HERD, - scarcity = SC.UNCOMMON, - seasons = SE.ALL, - ) - ) - ) + b.start(mobs["minecraft:chicken"]) + b.altitude = AL.span(AL.LOWLANDS, AL.UPLANDS) + b.biomeFilters = BF([FL.CLEARING, HE.TROPICAL, HU.WET], [SO.FUNGAL]) + b.seasons = SE.SUMMER + b.group = GR.TROUP + b.scarcity = SC.COMMON + b.save("chicken-summer") - spawns.add( - MobSpawn(mobs["minecraft:ocelot"], - Habitat( - altitude = AL.span(AL.LOWLANDS, AL.HILLS), - biomeFilter = BF([FL.CANOPY, HE.TROPICAL, HU.WET]), - group = GR.SOLO, - scarcity = SC.RARE, - seasons = SE.ALL, - ) - ) - ) + b.seasons = SE.exclude(SE.SUMMER) + b.scarcity = SC.UNCOMMON + b.save("chicken-normal") - spawns.add( - MobSpawn(mobs["minecraft:panda"], - Habitat( - altitude = AL.span(AL.LOWLANDS, AL.UPLANDS), - biomeFilter = BF([ResourceId.parse("bamboo_forest")]), - group = GR.SOLO, - scarcity = SC.UNUSUAL, - seasons = SE.exclude(SE.SPRING), - ).derive( - group = GR.PAIR, - seasons = SE.SPRING, - ) - ) - ) + b.start(mobs["minecraft:cod"]) + b.altitude = AL.span(AL.SHALLOWS, AL.DEEPS) + b.biomeFilters = BF([HE.BOREAL, WA.OCEAN]) + b.seasons = SE.ALL + b.group = GR.HERD + b.scarcity = SC.COMMON + b.save("cod-normal") - spawns.add( - MobSpawn(mobs["minecraft:parrot"], - Habitat( - altitude = AL.span(AL.DUNES, AL.HILLS), - biomeFilter = BF([(FL.CANOPY, FL.FOREST), HE.TROPICAL, HU.WET]), - group = GR.PAIR, - scarcity = SC.COMMON, - seasons = SE.ALL, - ).derive( - biomeFilter = BF([FL.CLEARING, HE.TROPICAL, HU.WET], [SO.FUNGAL]), - scarcity = SC.UNCOMMON, - ) - ) - ) + b.start(mobs["minecraft:cow"]) + b.altitude = AL.span(AL.LOWLANDS, AL.UPLANDS) + b.biomeFilters = \ + BF([HE.within(HE.SUBTROPICAL, HE.BOREAL), [HU.DAMP, HU.DRY], FL.FIELD], [SO.FUNGAL]) + b.seasons = [SE.SPRING, SE.SUMMER] + b.group = GR.HERD + b.scarcity = SC.SPARSE + b.save("cow-springsummer") - spawns.add( - MobSpawn(mobs["minecraft:pig"], - Habitat( - altitude = AL.span(AL.LOWLANDS, AL.HILLS), - biomeFilter = BF([ - FL.within(FL.CANOPY, FL.CLEARING), - HE.within(HE.SUBTROPICAL, HE.TEMPERATE) - ]), - group = GR.TROUP, - scarcity = SC.COMMON, - seasons = [SE.SUMMER], - ).derive( - scarcity = SC.UNCOMMON, - seasons = SE.exclude(SE.SUMMER), - ) - ) - ) + b.altitude = AL.LOWLANDS + b.seasons = [SE.AUTUMN, SE.WINTER] + b.group = GR.TROUP + b.scarcity = SC.UNUSUAL + b.save("cow-autumnwinter") - spawns.add( - MobSpawn(mobs["minecraft:polar_bear"], - Habitat( - altitude = AL.DUNES, - biomeFilter = BF([HE.FROZEN, (WA.COAST, WA.OCEAN)]), - group = GR.PAIR, - scarcity = SC.UNUSUAL, - seasons = [SE.SPRING, SE.WINTER] - ).derive( - group = GR.SOLO, - seasons = [SE.SUMMER, SE.AUTUMN], - scarcity = SC.RARE, - ) - ) - ) + b.start(mobs["crabbersdelight:crab"]) + b.altitude = AL.DUNES + b.biomeFilters = BF([WA.COAST, [HE.TROPICAL, HE.SUBTROPICAL]]) + b.seasons = SE.ALL + b.group = GR.SOLO + b.scarcity = SC.UNCOMMON + b.save("crab-warm") - spawns.add( - MobSpawn(mobs["minecraft:pufferfish"], - Habitat( - altitude = AL.SHALLOWS, - biomeFilter = BF([(HE.TEMPERATE, HE.TROPICAL), WA.OCEAN]), - group = GR.TROUP, - location = LO.WATER, - scarcity = SC.UNCOMMON, - seasons = SE.ALL, - ) - ) - ) + b.biomeFilters = BF([WA.COAST, [HE.TEMPERATE, HE.BOREAL]]) + b.scarcity = SC.SPARSE + b.save("crab-cool") - spawns.add( - MobSpawn(mobs["minecraft:rabbit"], - Habitat( - altitude = AL.span(AL.DUNES, AL.ALPINE), - biomeFilter = BF([ - FL.within(FL.CANOPY, FL.FIELD), - HE.within(HE.SUBTROPICAL, HE.BOREAL), - WA.INLAND, - ]), - group = GR.FAMILY, - scarcity = SC.COMMON, - seasons = [SE.SPRING], - ).derive( - scarcity = SC.SPARSE, - seasons = [SE.SUMMER, SE.AUTUMN], - ).derive( - scarcity = SC.SPARSE, - seasons = [SE.WINTER], - ).derive( - altitude = AL.span(AL.DUNES, AL.LOWLANDS), - biomeFilter = BF([FL.BARREN, HE.TROPICAL, HU.DRY]), - group = GR.PAIR, - scarcity = SC.SPARSE, - seasons = SE.exclude(SE.SUMMER), - ).derive( - scarcity = SC.UNUSUAL, - seasons = [SE.SUMMER], - ).derive( - altitude = AL.span(AL.LOWLANDS, AL.UPLANDS), - biomeFilter = BF([FL.within(FL.FOREST, FL.FIELD), HE.FROZEN]), - scarcity = SC.SPARSE, - seasons = SE.exclude(SE.WINTER), - ).derive( - scarcity = SC.UNUSUAL, - seasons = [SE.WINTER], - ) - ) - ) + b.start(mobs["minecraft:dolphin"]) + b.altitude = AL.span(AL.SURFACE, AL.DEEPS) + b.biomeFilters = BF([WA.OCEAN, [HE.SUBTROPICAL, HE.TEMPERATE]]) + b.location = LO.WATER + b.seasons = SE.SUMMER + b.group = GR.FAMILY + b.scarcity = SC.UNCOMMON + b.save("dolphin-summer") - spawns.add( - MobSpawn(mobs["minecraft:salmon"], - Habitat( - altitude = AL.span(AL.SURFACE, AL.SHALLOWS), - biomeFilter = BF([WA.RIVER], [HE.FROZEN]), - group = GR.HERD, - scarcity = SC.COMMON, - seasons = [SE.AUTUMN], - ).derive( - scarcity = SC.UNCOMMON, - seasons = [SE.SPRING], - ).derive( - seasons = [SE.SUMMER, SE.WINTER], - scarcity = SC.ABSENT, - ) - ) - ) + b.seasons = SE.exclude(SE.SUMMER) + b.scarcity = SC.SPARSE + b.save("dolphin-normal") - spawns.add( - MobSpawn(mobs["minecraft:sheep"], - Habitat( - altitude = AL.span(AL.LOWLANDS, AL.UPLANDS), - biomeFilter = BF([FL.within(FL.FOREST, FL.FIELD), HE.BOREAL]), - group = GR.HERD, - scarcity = SC.UNCOMMON, - seasons = SE.ALL, - ) - ) - ) + b.start(mobs["minecraft:donkey"]) + b.altitude = AL.span(AL.UPLANDS, AL.HILLS) + b.biomeFilters = BF([HE.SUBTROPICAL, HU.DRY, FL.FIELD]) + b.seasons = [SE.SUMMER, SE.AUTUMN] + b.group = GR.FAMILY + b.scarcity = SC.SPARSE + b.save("donkey-autumnsummer") - spawns.add( - MobSpawn(mobs["minecraft:slime"], - Habitat( - altitude = AL.span(AL.SURFACE, AL.DUNES), - biomeFilter = BF([HE.TEMPERATE, WA.SWAMP]), - group = GR.SOLO, - scarcity = SC.UNCOMMON, - seasons = [SE.SPRING], - ).derive( - scarcity = SC.UNUSUAL, - seasons = SE.exclude(SE.SPRING), - ) - ) - ) + b.altitude = AL.span(AL.LOWLANDS, AL.UPLANDS) + b.seasons = [SE.WINTER, SE.SPRING] + b.scarcity = SC.SPARSE + b.save("donkey-springwinter") - spawns.add( - MobSpawn(mobs["minecraft:spider"], - Habitat( - altitude = AL.span(AL.LOWLANDS, AL.HILLS), - biomeFilter = BF([FL.CANOPY, HE.TEMPERATE]), - group = GR.SOLO, - scarcity = SC.COMMON, - seasons = [SE.SUMMER], - ).derive( - scarcity = SC.UNCOMMON, - seasons = [SE.SPRING, SE.AUTUMN], - ).derive( - scarcity = SC.SPARSE, - seasons = [SE.WINTER], - ) - ) - ) + b.start(mobs["minecraft:fox"]) + b.altitude = AL.span(AL.LOWLANDS, AL.HILLS) + b.biomeFilters = BF([HE.within(HE.TEMPERATE, HE.FROZEN), FL.FOREST]) + b.seasons = [SE.SPRING] + b.group = GR.FAMILY + b.scarcity = SC.UNCOMMON + b.save("fox-spring") - spawns.add( - MobSpawn(mobs["minecraft:squid"], - Habitat( - altitude = AL.DEEPS, - biomeFilter = BF([WA.OCEAN], [HE.FROZEN]), - group = GR.SOLO, - location = LO.WATER, - scarcity = SC.SPARSE, - seasons = SE.ALL, - ) - ) - ) + b.seasons = SE.exclude(SE.SPRING) + b.group = GR.SOLO + b.scarcity = SC.SPARSE + b.save("fox-normal") - spawns.add( - MobSpawn(mobs["minecraft:tropical_fish"], - Habitat( - altitude = AL.SHALLOWS, - biomeFilter = BF([HE.TROPICAL, WA.OCEAN]), - group = GR.HERD, - location = LO.WATER, - scarcity = SC.COMMON, - seasons = SE.ALL, - ) - ) - ) + b.start(mobs["minecraft:frog"]) + b.altitude = AL.span(AL.SURFACE, AL.DUNES) + b.biomeFilters = BF([(HE.TROPICAL, HE.TEMPERATE), HU.WET, WA.SWAMP]) + b.group = GR.HERD + b.scarcity = SC.COMMON + b.seasons = SE.SUMMER + b.save("frog-summer") - spawns.add( - MobSpawn(mobs["minecraft:turtle"], - Habitat( - altitude = AL.span(AL.SURFACE, AL.SHALLOWS), - biomeFilter = BF([HE.within(HE.TROPICAL, HE.SUBTROPICAL), WA.OCEAN]), - group = GR.SOLO, - scarcity = SC.SPARSE, - seasons = SE.ALL, - ).derive( - altitude = AL.DUNES, - biomeFilter = BF([HE.within(HE.TROPICAL, HE.SUBTROPICAL), WA.COAST]), - group = GR.SOLO, - scarcity = SC.UNUSUAL, - ) - ) - ) + b.seasons = SE.SPRING + b.scarcity = SC.SPARSE + b.save("frog-spring") - spawns.add( - MobSpawn(mobs["minecraft:wolf"], - Habitat( - altitude = AL.span(AL.LOWLANDS, AL.UPLANDS), - biomeFilter = BF([FL.CANOPY, HE.TEMPERATE, WA.INLAND]), - group = GR.TROUP, - scarcity = SC.COMMON, - seasons = SE.ALL, - ).derive( - biomeFilter = BF([ - FL.within(FL.FOREST, FL.FIELD), (HE.TEMPERATE, HE.BOREAL), WA.INLAND, - ]), - scarcity = SC.UNCOMMON, - ).derive( - biomeFilter = BF([FL.within(FL.FOREST, FL.FIELD), HE.FROZEN, WA.INLAND]), - scarcity = SC.UNUSUAL, - ).derive( - biomeFilter = BF([(FL.CLEARING, FL.FIELD), HE.SUBTROPICAL, WA.INLAND]), - scarcity = SC.SPARSE, - ) - ) - ) + b.seasons = SE.AUTUMN + b.scarcity = SC.UNUSUAL + b.save("frog-autumn") + + b.seasons = SE.WINTER + b.scarcity = SC.ABSENT + b.save("frog-winter") + + b.start(mobs["minecraft:glow_squid"]) + b.altitude = AL.ABYSS + b.biomeFilters = BF([WA.OCEAN], [HE.FROZEN]) + b.group = GR.SOLO + b.location = LO.WATER + b.scarcity = SC.RARE + b.seasons = SE.ALL + b.save("glowsquid-normal") + + b.start(mobs["minecraft:goat"]) + b.altitude = AL.span(AL.HILLS, AL.CRAGS) + b.biomeFilters = BF([(HE.BOREAL, HE.FROZEN), SO.ROCKY]) + b.group = GR.FAMILY + b.scarcity = SC.UNCOMMON + b.seasons = SE.ALL + b.save("goat-normal") + + b.start(mobs["minecraft:horse"]) + b.altitude = AL.span(AL.DUNES, AL.LOWLANDS) + b.biomeFilters = BF([FL.FIELD, (HE.SUBTROPICAL, HE.TEMPERATE), (SO.LOAMY, SO.PEATY, SO.SANDY)]) + b.group = GR.HERD + b.scarcity = SC.UNUSUAL + b.seasons = SE.ALL + b.save("horse-normal") + + b.start(mobs["minecraft:llama"]) + b.altitude = AL.span(AL.ALPINE, AL.PEAKS) + b.biomeFilters = BF([(HE.BOREAL, HE.FROZEN), SO.ROCKY]) + b.group = GR.HERD + b.scarcity = SC.UNCOMMON + b.seasons = SE.ALL + b.save("llama-normal") + + b.start(mobs["minecraft:magma_cube"]) + b.altitude = AL.PLUTONIC + b.location = LO.CAVE + b.group = GR.SOLO + b.scarcity = SC.UNCOMMON + b.save("magmacube-normal") + + b.start(mobs["minecraft:mooshroom"]) + b.altitude = AL.span(AL.DUNES, AL.UPLANDS) + b.biomeFilters = BF([SO.FUNGAL]) + b.group = GR.HERD + b.scarcity = SC.UNCOMMON + b.seasons = SE.ALL + b.save("mooshroom-normal") + + b.start(mobs["minecraft:ocelot"]) + b.altitude = AL.span(AL.LOWLANDS, AL.HILLS) + b.biomeFilters = BF([FL.CANOPY, HE.TROPICAL, HU.WET]) + b.group = GR.SOLO + b.scarcity = SC.RARE + b.seasons = SE.ALL + b.save("ocelot-normal") + + b.start(mobs["minecraft:panda"]) + b.altitude = AL.span(AL.LOWLANDS, AL.UPLANDS) + b.biomeFilters = BF([ResourceId.parse("bamboo_forest")]) + b.group = GR.SOLO + b.scarcity = SC.UNUSUAL + b.seasons = SE.exclude(SE.SPRING) + b.save("panda-normal") + + b.group = GR.PAIR + b.seasons = SE.SPRING + b.save("panda-spring") + + b.start(mobs["minecraft:parrot"]) + b.altitude = AL.span(AL.DUNES, AL.HILLS) + b.biomeFilters = BF([(FL.CANOPY, FL.FOREST), HE.TROPICAL, HU.WET]) + b.group = GR.PAIR + b.scarcity = SC.COMMON + b.seasons = SE.ALL + b.save("parrot-canopyforest") + + b.biomeFilters = BF([FL.CLEARING, HE.TROPICAL, HU.WET], [SO.FUNGAL]) + b.scarcity = SC.UNCOMMON + b.save("parrot-clearing") + + b.start(mobs["minecraft:pig"]) + b.altitude = AL.span(AL.LOWLANDS, AL.HILLS) + b.biomeFilters = BF([ + FL.within(FL.CANOPY, FL.CLEARING), + HE.within(HE.SUBTROPICAL, HE.TEMPERATE) + ]) + b.group = GR.TROUP + b.scarcity = SC.COMMON + b.seasons = SE.SUMMER + b.save("pig-summer") + + b.scarcity = SC.UNCOMMON + b.seasons = SE.exclude(SE.SUMMER) + b.save("pig-normal") + + b.start(mobs["minecraft:polar_bear"]) + b.altitude = AL.DUNES + b.biomeFilters = BF([HE.FROZEN, (WA.COAST, WA.OCEAN)]) + b.group = GR.PAIR + b.scarcity = SC.UNUSUAL + b.seasons = [SE.SPRING, SE.WINTER] + b.save("polarbear-springwinter") + + b.group = GR.SOLO + b.seasons = [SE.SUMMER, SE.AUTUMN] + b.scarcity = SC.RARE + b.save("polarbear-autumnsummer") + + b.start(mobs["minecraft:pufferfish"]) + b.altitude = AL.SHALLOWS + b.biomeFilters = BF([(HE.TEMPERATE, HE.TROPICAL), WA.OCEAN]) + b.group = GR.TROUP + b.location = LO.WATER + b.scarcity = SC.UNCOMMON + b.seasons = SE.ALL + b.save("pufferfish-normal") + + b.start(mobs["minecraft:rabbit"]) + b.altitude = AL.span(AL.DUNES, AL.ALPINE) + b.biomeFilters = BF([ + FL.within(FL.CANOPY, FL.FIELD), + HE.within(HE.SUBTROPICAL, HE.BOREAL), + WA.INLAND, + ]) + b.group = GR.FAMILY + b.scarcity = SC.COMMON + b.seasons = SE.SPRING + b.save("rabbit-temperate-spring") + + b.scarcity = SC.SPARSE + b.seasons = [SE.SUMMER, SE.AUTUMN] + b.save("rabbit-temperate-autumnsummer") + + b.scarcity = SC.SPARSE + b.seasons = SE.WINTER + b.save("rabbit-temperate-winter") + + b.altitude = AL.span(AL.DUNES, AL.LOWLANDS) + b.biomeFilters = BF([FL.BARREN, HE.TROPICAL, HU.DRY]) + b.group = GR.PAIR + b.scarcity = SC.SPARSE + b.seasons = SE.exclude(SE.SUMMER) + b.save("rabbit-desert-normal") + + b.scarcity = SC.UNUSUAL + b.seasons = SE.SUMMER + b.save("rabbit-desert-summer") + + b.altitude = AL.span(AL.LOWLANDS, AL.UPLANDS) + b.biomeFilters = BF([FL.within(FL.FOREST, FL.FIELD), HE.FROZEN]) + b.scarcity = SC.SPARSE + b.seasons = SE.exclude(SE.WINTER) + b.save("rabbit-frozen-normal") + + b.scarcity = SC.UNUSUAL + b.seasons = SE.WINTER + b.save("rabbit-frozen-winter") + + b.start(mobs["minecraft:salmon"]) + b.altitude = AL.span(AL.SURFACE, AL.SHALLOWS) + b.biomeFilters = BF([WA.RIVER], [HE.FROZEN]) + b.group = GR.HERD + b.scarcity = SC.COMMON + b.seasons = SE.AUTUMN + b.save("salmon-autumnrun") + + b.scarcity = SC.UNCOMMON + b.seasons = [SE.SPRING] + b.save("salmon-springrun") + + b.seasons = [SE.SUMMER, SE.WINTER] + b.scarcity = SC.ABSENT + b.save("salmon-notrunning") + + b.start(mobs["minecraft:sheep"]) + b.altitude = AL.span(AL.LOWLANDS, AL.UPLANDS) + b.biomeFilters = BF([FL.within(FL.FOREST, FL.FIELD), HE.BOREAL]) + b.group = GR.HERD + b.scarcity = SC.UNCOMMON + b.seasons = SE.ALL + b.save("sheep-normal") + + b.start(mobs["minecraft:slime"]) + b.altitude = AL.span(AL.SURFACE, AL.DUNES) + b.biomeFilters = BF([HE.TEMPERATE, WA.SWAMP]) + b.group = GR.SOLO + b.scarcity = SC.UNCOMMON + b.seasons = [SE.SPRING] + b.save("slime-spring") + + b.scarcity = SC.UNUSUAL + b.seasons = SE.exclude(SE.SPRING) + b.save("slime-normal") + + b.start(mobs["minecraft:spider"]) + b.altitude = AL.span(AL.LOWLANDS, AL.HILLS) + b.biomeFilters = BF([FL.CANOPY, HE.TEMPERATE]) + b.group = GR.SOLO + b.scarcity = SC.COMMON + b.seasons = SE.SUMMER + b.save("spider-summer") + + b.scarcity = SC.UNCOMMON + b.seasons = [SE.SPRING, SE.AUTUMN] + b.save("spider-springsummer") + + b.scarcity = SC.SPARSE + b.seasons = SE.WINTER + b.save("spider-winter") + + b.start(mobs["minecraft:squid"]) + b.altitude = AL.DEEPS + b.biomeFilters = BF([WA.OCEAN], [HE.FROZEN]) + b.group = GR.SOLO + b.location = LO.WATER + b.scarcity = SC.SPARSE + b.seasons = SE.ALL + b.save("squid-normal") + + b.start(mobs["minecraft:tropical_fish"]) + b.altitude = AL.SHALLOWS + b.biomeFilters = BF([HE.TROPICAL, WA.OCEAN]) + b.group = GR.HERD + b.location = LO.WATER + b.scarcity = SC.COMMON + b.seasons = SE.ALL + b.save("tropicalfish-normal") + + b.start(mobs["minecraft:turtle"]) + b.altitude = AL.span(AL.SURFACE, AL.SHALLOWS) + b.biomeFilters = BF([HE.within(HE.TROPICAL, HE.SUBTROPICAL), WA.OCEAN]) + b.group = GR.SOLO + b.scarcity = SC.SPARSE + b.seasons = SE.ALL + b.save("turtle-normal") + + b.altitude = AL.DUNES + b.biomeFilters = BF([HE.within(HE.TROPICAL, HE.SUBTROPICAL), WA.COAST]) + b.group = GR.SOLO + b.scarcity = SC.UNUSUAL + b.save("turtle-spawning") + + b.start(mobs["minecraft:wolf"]) + b.altitude = AL.span(AL.LOWLANDS, AL.UPLANDS) + b.biomeFilters = BF([FL.CANOPY, HE.TEMPERATE, WA.INLAND]) + b.group = GR.TROUP + b.scarcity = SC.COMMON + b.seasons = SE.ALL + b.save("wolf-deepforest") + + b.biomeFilters = BF([FL.within(FL.FOREST, FL.FIELD), (HE.TEMPERATE, HE.BOREAL), WA.INLAND]) + b.scarcity = SC.UNCOMMON + b.save("wolf-normal") + + b.biomeFilters = BF([FL.within(FL.FOREST, FL.FIELD), HE.FROZEN, WA.INLAND]) + b.scarcity = SC.UNUSUAL + b.save("wolf-arctic") + + b.biomeFilters = BF([(FL.CLEARING, FL.FIELD), HE.SUBTROPICAL, WA.INLAND]) + b.scarcity = SC.SPARSE + b.save("wolf-savanna") diff --git a/mcpacker/pack/mysteriousisland/addplantspawns.py b/mcpacker/pack/mysteriousisland/addplantspawns.py index 34772a3..ba622cc 100644 --- a/mcpacker/pack/mysteriousisland/addplantspawns.py +++ b/mcpacker/pack/mysteriousisland/addplantspawns.py @@ -1,8 +1,9 @@ from mcpacker.model.altitude import Altitude from mcpacker.model.ecology.biomefilter import BiomeFilter as BF from mcpacker.model.flora.plantspawn import PlantSpawn -from mcpacker.model.resourceid import ResourceId +from mcpacker.model.flora.plantspawnbuilder import PlantSpawnBuilder from mcpacker.model.modpack import ModPack +from mcpacker.model.resourceid import ResourceId import mcpacker.model.altitude as AL import mcpacker.model.ecology.flora as FL @@ -18,429 +19,370 @@ import mcpacker.model.scarcity as SC def addPlantSpawns(pack:ModPack): plants = pack.world.plants - spawns = pack.world.plantSpawns + b = PlantSpawnBuilder(pack.world.plantSpawns) # Crop Patches ############################################################# - spawns.add(PlantSpawn( - plant = plants["athelas"], - altitude = AL.span(AL.LOWLANDS, AL.HILLS), - biomeFilters = BF([FL.CANOPY, HE.TEMPERATE]), - scarcity = SC.SPARSE, - )) + b.start(plants["athelas"]) + b.altitude = AL.span(AL.LOWLANDS, AL.HILLS) + b.biomeFilters = BF([FL.CANOPY, HE.TEMPERATE]) + b.scarcity = SC.SPARSE + b.save("") - spawns.add(PlantSpawn( - plant = plants["beetroot"], - altitude = AL.LOWLANDS, - biomeFilters = BF([FL.FOREST, HE.BOREAL, SO.ACIDIC]), - scarcity = SC.COMMON, - )) + b.start(plants["beetroot"]) + b.altitude = AL.LOWLANDS + b.biomeFilters = BF([FL.FOREST, HE.BOREAL, SO.ACIDIC]) + b.scarcity = SC.COMMON + b.save("") - spawns.add(PlantSpawn( - plant = plants["bellpepper"], - altitude = AL.LOWLANDS, - biomeFilters = BF([FL.CLEARING, HE.TROPICAL, HU.WET, WA.INLAND]), - scarcity = SC.COMMON, - )) + b.start(plants["bellpepper"]) + b.altitude = AL.LOWLANDS + b.biomeFilters = BF([FL.CLEARING, HE.TROPICAL, HU.WET, WA.INLAND]) + b.scarcity = SC.COMMON + b.save("") - spawns.add(PlantSpawn( - plant = plants["blueberry"], - altitude = AL.span(AL.UPLANDS, AL.ALPINE), - biomeFilters = BF([FL.within(FL.FOREST, FL.FIELD), HE.BOREAL]), - scarcity = SC.SPARSE, - )) + b.start(plants["blueberry"]) + b.altitude = AL.span(AL.UPLANDS, AL.ALPINE) + b.biomeFilters = BF([FL.within(FL.FOREST, FL.FIELD), HE.BOREAL]) + b.scarcity = SC.SPARSE + b.save("") - spawns.add(PlantSpawn( - plant = plants["brownmushroom"], - altitude = AL.span(AL.SOIL, AL.ALPINE), - biomeFilters = [ - BF( - [(FL.CANOPY, FL.FOREST), (HE.TEMPERATE, HE.BOREAL), (SO.LOAMY, SO.CLAYEY)], - [WA.OCEAN] - ), - BF([HU.WET], [HE.FROZEN, WA.OCEAN, WA.RIVER]), - BF([SO.FUNGAL]), - ], - scarcity = SC.COMMON, - )) + b.start(plants["brownmushroom"]) + b.altitude = AL.span(AL.SOIL, AL.ALPINE) + b.biomeFilters = [ + BF([(FL.CANOPY, FL.FOREST), (HE.TEMPERATE, HE.BOREAL), (SO.LOAMY, SO.CLAYEY)], [WA.OCEAN]), + BF([HU.WET], [HE.FROZEN, WA.OCEAN, WA.RIVER]), + BF([SO.FUNGAL]), + ] + b.scarcity = SC.COMMON + b.save("") - spawns.add(PlantSpawn( - plant = plants["cabbage"], - altitude = AL.HILLS, - biomeFilters = BF([(HE.SUBTROPICAL, HE.TEMPERATE), SO.SANDY, WA.COAST]), - scarcity = SC.COMMON, - )) + b.start(plants["cabbage"]) + b.altitude = AL.HILLS + b.biomeFilters = BF([(HE.SUBTROPICAL, HE.TEMPERATE), SO.SANDY, WA.COAST]) + b.scarcity = SC.COMMON + b.save("") - spawns.add(PlantSpawn( - plant = plants["carrot"], - altitude = AL.LOWLANDS, - biomeFilters = BF( - [(FL.FOREST, FL.CLEARING), HE.TEMPERATE, SO.LOAMY], - [ResourceId.parse("flower_forest")], - ), - scarcity = SC.COMMON, - )) + b.start(plants["carrot"]) + b.altitude = AL.LOWLANDS + b.biomeFilters = BF( + [(FL.FOREST, FL.CLEARING), HE.TEMPERATE, SO.LOAMY], [ResourceId.parse("flower_forest")], + ) + b.scarcity = SC.COMMON + b.save("") - spawns.add(PlantSpawn( - plant = plants["coffee"], - altitude = AL.span(AL.HILLS, AL.ALPINE), - biomeFilters = BF([FL.CANOPY, HE.TROPICAL, HU.WET]), - scarcity = SC.SPARSE, - )) + b.start(plants["coffee"]) + b.altitude = AL.span(AL.HILLS, AL.ALPINE) + b.biomeFilters = BF([FL.CANOPY, HE.TROPICAL, HU.WET]) + b.scarcity = SC.SPARSE + b.save("") - spawns.add(PlantSpawn( - plant = plants["corn"], - altitude = AL.span(AL.LOWLANDS, AL.UPLANDS), - biomeFilters = BF([FL.FIELD, HE.SUBTROPICAL, (SO.LOAMY, SO.SANDY)]), - scarcity = SC.COMMON, - )) + b.start(plants["corn"]) + b.altitude = AL.span(AL.LOWLANDS, AL.UPLANDS) + b.biomeFilters = BF([FL.FIELD, HE.SUBTROPICAL, (SO.LOAMY, SO.SANDY)]) + b.scarcity = SC.COMMON + b.save("") - spawns.add(PlantSpawn( - plant = plants["cotton"], - altitude = AL.LOWLANDS, - biomeFilters = BF([FL.FIELD, HE.SUBTROPICAL, HU.DRY, (SO.LOAMY, SO.SANDY)]), - scarcity = SC.UNCOMMON, - )) + b.start(plants["cotton"]) + b.altitude = AL.LOWLANDS + b.biomeFilters = BF([FL.FIELD, HE.SUBTROPICAL, HU.DRY, (SO.LOAMY, SO.SANDY)]) + b.scarcity = SC.UNCOMMON + b.save("") - spawns.add(PlantSpawn( - plant = plants["currant"], - altitude = AL.span(AL.UPLANDS, AL.ALPINE), - biomeFilters = BF([FL.within(FL.FOREST, FL.FIELD), (HE.BOREAL, HE.FROZEN)]), - scarcity = SC.UNCOMMON, - )) + b.start(plants["currant"]) + b.altitude = AL.span(AL.UPLANDS, AL.ALPINE) + b.biomeFilters = BF([FL.within(FL.FOREST, FL.FIELD), (HE.BOREAL, HE.FROZEN)]) + b.scarcity = SC.UNCOMMON + b.save("") - spawns.add(PlantSpawn( - plant = plants["cranberry"], - altitude = AL.DUNES, - biomeFilters = BF([HU.WET, SO.PEATY]), - scarcity = SC.UNCOMMON, - )) + b.start(plants["cranberry"]) + b.altitude = AL.DUNES + b.biomeFilters = BF([HU.WET, SO.PEATY]) + b.scarcity = SC.UNCOMMON + b.save("") # NOTE: Due to a bug in Supplementaries, flax can *only* be planted on sand, despite the actual # plant requiring an entirely different soil. Therefore, we're imaginging *this* so-called # "flax" as a fiberous, desert-dwelling cousin. - spawns.add(PlantSpawn( - plant = plants["flax"], - altitude = AL.DUNES, - biomeFilters = BF([FL.BARREN, HU.DRY, SO.SANDY]), - scarcity = SC.COMMON, - )) + b.start(plants["flax"]) + b.altitude = AL.DUNES + b.biomeFilters = BF([FL.BARREN, HU.DRY, SO.SANDY]) + b.scarcity = SC.COMMON + b.save("") - spawns.add(PlantSpawn( - plant = plants["hamimelon"], - altitude = AL.span(AL.LOWLANDS, AL.UPLANDS), - biomeFilters = BF([FL.FIELD, HE.SUBTROPICAL, HU.DRY, (SO.LOAMY, SO.SANDY)]), - scarcity = SC.RARE, - )) + b.start(plants["hamimelon"]) + b.altitude = AL.span(AL.LOWLANDS, AL.UPLANDS) + b.biomeFilters = BF([FL.FIELD, HE.SUBTROPICAL, HU.DRY, (SO.LOAMY, SO.SANDY)]) + b.scarcity = SC.RARE + b.save("") - spawns.add(PlantSpawn( - plant = plants["hemp"], - altitude = AL.span(AL.DUNES, AL.UPLANDS), - biomeFilters = BF( - [FL.FIELD, HE.TEMPERATE, HU.DAMP, SO.LOAMY], - [ResourceId.parse("sunflower_plains")] - ), - scarcity = SC.UNCOMMON, - )) + b.start(plants["hemp"]) + b.altitude = AL.span(AL.DUNES, AL.UPLANDS) + b.biomeFilters = BF( + [FL.FIELD, HE.TEMPERATE, HU.DAMP, SO.LOAMY], [ResourceId.parse("sunflower_plains")] + ), + b.scarcity = SC.UNCOMMON + b.save("") - spawns.add(PlantSpawn( - plant = plants["lemon"], - altitude = AL.UPLANDS, - biomeFilters = BF([FL.FIELD, HE.SUBTROPICAL, HU.DRY]), - scarcity = SC.SPARSE, - )) + b.start(plants["lemon"]) + b.altitude = AL.UPLANDS + b.biomeFilters = BF([FL.FIELD, HE.SUBTROPICAL, HU.DRY]) + b.scarcity = SC.SPARSE + b.save("") - spawns.add(PlantSpawn( - plant = plants["melon"], - altitude = AL.LOWLANDS, - biomeFilters = BF([FL.CLEARING, HE.TROPICAL, HU.WET], [SO.FUNGAL]), - scarcity = SC.SPARSE, - )) + b.start(plants["melon"]) + b.altitude = AL.LOWLANDS + b.biomeFilters = BF([FL.CLEARING, HE.TROPICAL, HU.WET], [SO.FUNGAL]) + b.scarcity = SC.SPARSE + b.save("") - spawns.add(PlantSpawn( - plant = plants["onion"], - altitude = AL.span(AL.UPLANDS, AL.HILLS), - biomeFilters = BF([FL.FOREST, HE.TEMPERATE, HU.DAMP, SO.ACIDIC]), - scarcity = SC.SPARSE, - )) + b.start(plants["onion"]) + b.altitude = AL.span(AL.UPLANDS, AL.HILLS) + b.biomeFilters = BF([FL.FOREST, HE.TEMPERATE, HU.DAMP, SO.ACIDIC]) + b.scarcity = SC.SPARSE + b.save("") # NOTE: Due to a bug in Fruits Delight, pineapple can *only* be planted on sand: despite this # in now way reflecting the plant's actual ecology. As such, we have to imagine these as # a more cactus-like cousin of actual pineapples. - spawns.add(PlantSpawn( - plant = plants["pineapple"], - altitude = AL.LOWLANDS, - biomeFilters = BF([FL.BARREN, HE.TROPICAL, HU.DRY, SO.SANDY]), - scarcity = SC.SPARSE, - # TODO: add sand as substrate in addplants.py - )) + b.start(plants["pineapple"]) + b.altitude = AL.LOWLANDS + b.biomeFilters = BF([FL.BARREN, HE.TROPICAL, HU.DRY, SO.SANDY]) + b.scarcity = SC.SPARSE + # TODO: add sand as substrate in addplants.py + b.save("") - spawns.add(PlantSpawn( - plant = plants["potato"], - altitude = AL.span(AL.UPLANDS, AL.HILLS), - biomeFilters = BF([FL.FOREST, HE.BOREAL, HU.DAMP, SO.ACIDIC]), - scarcity = SC.UNCOMMON, - )) + b.start(plants["potato"]) + b.altitude = AL.span(AL.UPLANDS, AL.HILLS) + b.biomeFilters = BF([FL.FOREST, HE.BOREAL, HU.DAMP, SO.ACIDIC]) + b.scarcity = SC.UNCOMMON + b.save("") - spawns.add(PlantSpawn( - plant = plants["pumpkin"], - altitude = AL.LOWLANDS, - biomeFilters = BF([FL.FIELD, HE.TEMPERATE, SO.LOAMY]), - scarcity = SC.UNUSUAL, - )) + b.start(plants["pumpkin"]) + b.altitude = AL.LOWLANDS + b.biomeFilters = BF([FL.FIELD, HE.TEMPERATE, SO.LOAMY]) + b.scarcity = SC.UNUSUAL + b.save("") - spawns.add(PlantSpawn( - plant = plants["redmushroom"], - altitude = AL.span(AL.SOIL, AL.ALPINE), - biomeFilters = [ - BF([(FL.CANOPY, FL.FOREST), (HE.TEMPERATE, HE.BOREAL), (SO.LOAMY, SO.CLAYEY)]), - BF([HU.WET], [HE.FROZEN, WA.OCEAN, WA.RIVER]), - BF([SO.FUNGAL]), - ], - scarcity = SC.UNCOMMON, - )) + b.start(plants["redmushroom"]) + b.altitude = AL.span(AL.SOIL, AL.ALPINE) + b.biomeFilters = [ + BF([(FL.CANOPY, FL.FOREST), (HE.TEMPERATE, HE.BOREAL), (SO.LOAMY, SO.CLAYEY)]), + BF([HU.WET], [HE.FROZEN, WA.OCEAN, WA.RIVER]), + BF([SO.FUNGAL]), + ] + b.scarcity = SC.UNCOMMON + b.save("") - spawns.add(PlantSpawn( - plant = plants["rice"], - altitude = AL.SURFACE, - biomeFilters = BF([(HE.SUBTROPICAL, HE.TEMPERATE), (WA.RIVER, WA.SWAMP)]), - scarcity = SC.COMMON, - )) + b.start(plants["rice"]) + b.altitude = AL.SURFACE + b.biomeFilters = BF([(HE.SUBTROPICAL, HE.TEMPERATE), (WA.RIVER, WA.SWAMP)]) + b.scarcity = SC.COMMON + b.save("") - spawns.add(PlantSpawn( - plant = plants["sugarcane"], - altitude = AL.DUNES, - biomeFilters = BF([(HE.TROPICAL, HE.TEMPERATE), (WA.RIVER, WA.SWAMP)]), - scarcity = SC.COMMON, - )) + b.start(plants["sugarcane"]) + b.altitude = AL.DUNES + b.biomeFilters = BF([(HE.TROPICAL, HE.TEMPERATE), (WA.RIVER, WA.SWAMP)]) + b.scarcity = SC.COMMON + b.save("") - spawns.add(PlantSpawn( - plant = plants["sunflower"], - altitude = AL.span(AL.LOWLANDS, AL.UPLANDS), - biomeFilters = BF([ResourceId.parse("sunflower_plains")]), - scarcity = SC.CARPET, - )) + b.start(plants["sunflower"]) + b.altitude = AL.span(AL.LOWLANDS, AL.UPLANDS) + b.biomeFilters = BF([ResourceId.parse("sunflower_plains")]) + b.scarcity = SC.CARPET + b.save("") - spawns.add(PlantSpawn( - plant = plants["tomato"], - altitude = AL.span(AL.UPLANDS, AL.HILLS), - biomeFilters = BF([FL.CLEARING, HE.TROPICAL, HU.WET], [SO.FUNGAL]), - scarcity = SC.UNCOMMON, - )) + b.start(plants["tomato"]) + b.altitude = AL.span(AL.UPLANDS, AL.HILLS) + b.biomeFilters = BF([FL.CLEARING, HE.TROPICAL, HU.WET], [SO.FUNGAL]) + b.scarcity = SC.UNCOMMON + b.save("") - spawns.add(PlantSpawn( - plant = plants["wheat"], - altitude = AL.span(AL.LOWLANDS, AL.UPLANDS), - biomeFilters = BF([FL.FIELD, HE.TEMPERATE, HU.DAMP, SO.LOAMY]), - scarcity = SC.COMMON, - )) + b.start(plants["wheat"]) + b.altitude = AL.span(AL.LOWLANDS, AL.UPLANDS) + b.biomeFilters = BF([FL.FIELD, HE.TEMPERATE, HU.DAMP, SO.LOAMY]) + b.scarcity = SC.COMMON + b.save("") # Flower Patches ########################################################### - spawns.add(PlantSpawn( - plant = plants["allium"], - altitude = AL.span(AL.UPLANDS, AL.HILLS), - biomeFilters = BF([FL.FOREST, HE.TEMPERATE, HU.DAMP, SO.ACIDIC]), - scarcity = SC.UNCOMMON, - )) + b.start(plants["allium"]) + b.altitude = AL.span(AL.UPLANDS, AL.HILLS) + b.biomeFilters = BF([FL.FOREST, HE.TEMPERATE, HU.DAMP, SO.ACIDIC]) + b.scarcity = SC.UNCOMMON + b.save("") - spawns.add(PlantSpawn( - plant = plants["azurebluet"], - altitude = AL.span(AL.UPLANDS, AL.ALPINE), - biomeFilters = BF([FL.FIELD, (HE.TEMPERATE, HE.BOREAL), HU.DAMP, (SO.LOAMY, SO.PEATY)]), - scarcity = SC.SPARSE, - )) + b.start(plants["azurebluet"]) + b.altitude = AL.span(AL.UPLANDS, AL.ALPINE) + b.biomeFilters = BF([FL.FIELD, (HE.TEMPERATE, HE.BOREAL), HU.DAMP, (SO.LOAMY, SO.PEATY)]) + b.scarcity = SC.SPARSE + b.save("") - spawns.add(PlantSpawn( - plant = plants["blueorchid"], - altitude = AL.span(AL.LOWLANDS, AL.UPLANDS), - biomeFilters = BF([FL.CANOPY, HE.TROPICAL, HU.WET]), - scarcity = SC.RARE, - )) + b.start(plants["blueorchid"]) + b.altitude = AL.span(AL.LOWLANDS, AL.UPLANDS) + b.biomeFilters = BF([FL.CANOPY, HE.TROPICAL, HU.WET]) + b.scarcity = SC.RARE + b.save("") - spawns.add(PlantSpawn( - plant = plants["cornflower"], - altitude = AL.span(AL.UPLANDS, AL.ALPINE), - biomeFilters = BF([FL.FIELD, HE.TEMPERATE, (SO.LOAMY, SO.PEATY)]), - scarcity = SC.COMMON, - )) + b.start(plants["cornflower"]) + b.altitude = AL.span(AL.UPLANDS, AL.ALPINE) + b.biomeFilters = BF([FL.FIELD, HE.TEMPERATE, (SO.LOAMY, SO.PEATY)]) + b.scarcity = SC.COMMON + b.save("") - spawns.add(PlantSpawn( - plant = plants["daisy"], - altitude = AL.span(AL.UPLANDS, AL.ALPINE), - biomeFilters = BF([FL.FIELD, (HE.TEMPERATE, HE.BOREAL)]), - scarcity = SC.UNCOMMON, - )) + b.start(plants["daisy"]) + b.altitude = AL.span(AL.UPLANDS, AL.ALPINE) + b.biomeFilters = BF([FL.FIELD, (HE.TEMPERATE, HE.BOREAL)]) + b.scarcity = SC.UNCOMMON + b.save("") - spawns.add(PlantSpawn( - plant = plants["dandelion"], - altitude = AL.span(AL.DUNES, AL.HILLS), - biomeFilters = BF( - [(FL.CLEARING, FL.FIELD)], - [HE.FROZEN, HU.WET, SO.FUNGAL, SO.ROCKY, ResourceId.parse("flower_forest")]), - scarcity = SC.COMMON, - )) + b.start(plants["dandelion"]) + b.altitude = AL.span(AL.DUNES, AL.HILLS) + b.biomeFilters = BF( + [(FL.CLEARING, FL.FIELD)], + [HE.FROZEN, HU.WET, SO.FUNGAL, SO.ROCKY, ResourceId.parse("flower_forest")] + ) + b.scarcity = SC.COMMON + b.save("") - spawns.add(PlantSpawn( - plant = plants["lilac"], - altitude = AL.LOWLANDS, - biomeFilters = BF([FL.FOREST, HE.TEMPERATE, HU.DAMP, SO.LOAMY]), - scarcity = SC.UNUSUAL, - )) + b.start(plants["lilac"]) + b.altitude = AL.LOWLANDS + b.biomeFilters = BF([FL.FOREST, HE.TEMPERATE, HU.DAMP, SO.LOAMY]) + b.scarcity = SC.UNUSUAL + b.save("") - spawns.add(PlantSpawn( - plant = plants["lily"], - altitude = AL.span(AL.LOWLANDS, AL.UPLANDS), - biomeFilters = BF([FL.FOREST, (HE.TEMPERATE, HE.BOREAL), HU.DAMP, SO.ACIDIC]), - scarcity = SC.RARE, - )) + b.start(plants["lily"]) + b.altitude = AL.span(AL.LOWLANDS, AL.UPLANDS) + b.biomeFilters = BF([FL.FOREST, (HE.TEMPERATE, HE.BOREAL), HU.DAMP, SO.ACIDIC]) + b.scarcity = SC.RARE + b.save("") - spawns.add(PlantSpawn( - plant = plants["peony"], - altitude = AL.LOWLANDS, - biomeFilters = BF([FL.CLEARING, HE.TEMPERATE, HU.DAMP, SO.ROCKY]), - scarcity = SC.SPARSE, - )) + b.start(plants["peony"]) + b.altitude = AL.LOWLANDS + b.biomeFilters = BF([FL.CLEARING, HE.TEMPERATE, HU.DAMP, SO.ROCKY]) + b.scarcity = SC.SPARSE + b.save("") - spawns.add(PlantSpawn( - plant = plants["poppy"], - altitude = AL.span(AL.DUNES, AL.UPLANDS), - biomeFilters = BF( - [FL.FIELD, (HE.SUBTROPICAL, HE.TEMPERATE), (HU.DRY, HU.DAMP)], - [SO.FUNGAL] - ), - scarcity = SC.SPARSE, - )) + b.start(plants["poppy"]) + b.altitude = AL.span(AL.DUNES, AL.UPLANDS) + b.biomeFilters = BF( + [FL.FIELD, (HE.SUBTROPICAL, HE.TEMPERATE), (HU.DRY, HU.DAMP)], + [SO.FUNGAL] + ), + b.scarcity = SC.SPARSE + b.save("") - spawns.add(PlantSpawn( - plant = plants["rose_black"], - altitude = AL.HILLS, - biomeFilters = BF([FL.CANOPY, HE.TEMPERATE]), - scarcity = SC.RARE, - )) + b.start(plants["rose_black"]) + b.altitude = AL.HILLS + b.biomeFilters = BF([FL.CANOPY, HE.TEMPERATE]) + b.scarcity = SC.RARE + b.save("") - spawns.add(PlantSpawn( - plant = plants["rose_bush"], - altitude = AL.span(AL.UPLANDS, AL.ALPINE), - biomeFilters = BF([FL.FOREST, (HE.TEMPERATE, HE.BOREAL), SO.LOAMY]), - scarcity = SC.UNUSUAL, - )) + b.start(plants["rose_bush"]) + b.altitude = AL.span(AL.UPLANDS, AL.ALPINE) + b.biomeFilters = BF([FL.FOREST, (HE.TEMPERATE, HE.BOREAL), SO.LOAMY]) + b.scarcity = SC.UNUSUAL + b.save("") - spawns.add(PlantSpawn( - plant = plants["tulip_orange"], - altitude = AL.span(AL.DUNES, AL.LOWLANDS), - biomeFilters = BF([ResourceId.parse("flower_forest")]), - scarcity = SC.UNUSUAL, - )) + b.start(plants["tulip_orange"]) + b.altitude = AL.span(AL.DUNES, AL.LOWLANDS) + b.biomeFilters = BF([ResourceId.parse("flower_forest")]) + b.scarcity = SC.UNUSUAL + b.save("") - spawns.add(PlantSpawn( - plant = plants["tulip_pink"], - altitude = AL.span(AL.DUNES, AL.LOWLANDS), - biomeFilters = BF([ResourceId.parse("flower_forest")]), - scarcity = SC.SPARSE, - )) + b.start(plants["tulip_pink"]) + b.altitude = AL.span(AL.DUNES, AL.LOWLANDS) + b.biomeFilters = BF([ResourceId.parse("flower_forest")]) + b.scarcity = SC.SPARSE + b.save("") - spawns.add(PlantSpawn( - plant = plants["tulip_red"], - altitude = AL.span(AL.DUNES, AL.LOWLANDS), - biomeFilters = BF([ResourceId.parse("flower_forest")]), - scarcity = SC.COMMON, - )) + b.start(plants["tulip_red"]) + b.altitude = AL.span(AL.DUNES, AL.LOWLANDS) + b.biomeFilters = BF([ResourceId.parse("flower_forest")]) + b.scarcity = SC.COMMON + b.save("") - spawns.add(PlantSpawn( - plant = plants["tulip_white"], - altitude = AL.span(AL.DUNES, AL.LOWLANDS), - biomeFilters = BF([ResourceId.parse("flower_forest")]), - scarcity = SC.UNCOMMON, - )) + b.start(plants["tulip_white"]) + b.altitude = AL.span(AL.DUNES, AL.LOWLANDS) + b.biomeFilters = BF([ResourceId.parse("flower_forest")]) + b.scarcity = SC.UNCOMMON + b.save("") # Trees #################################################################### - spawns.add(PlantSpawn( - plant = plants["apple"], - altitude = AL.span(AL.LOWLANDS, AL.UPLANDS), - biomeFilters = BF( - [(FL.FOREST, FL.CLEARING), HE.TEMPERATE, SO.LOAMY], - [ResourceId.parse("flower_forest")], - ), - scarcity = SC.SPARSE, - )) + b.start(plants["apple"]) + b.altitude = AL.span(AL.LOWLANDS, AL.UPLANDS) + b.biomeFilters = BF( + [(FL.FOREST, FL.CLEARING), HE.TEMPERATE, SO.LOAMY], + [ResourceId.parse("flower_forest")], + ) + b.scarcity = SC.SPARSE + b.save("") - spawns.add(PlantSpawn( - plant = plants["bayberry"], - altitude = AL.span(AL.DUNES, AL.UPLANDS), - biomeFilters = BF([HE.TEMPERATE, (SO.ROCKY, SO.SANDY), WA.COAST]), - scarcity = SC.SPARSE, - )) + b.start(plants["bayberry"]) + b.altitude = AL.span(AL.DUNES, AL.UPLANDS) + b.biomeFilters = BF([HE.TEMPERATE, (SO.ROCKY, SO.SANDY), WA.COAST]) + b.scarcity = SC.SPARSE + b.save("") - spawns.add(PlantSpawn( - plant = plants["durian"], - altitude = AL.HILLS, - biomeFilters = BF([FL.within(FL.CANOPY, FL.CLEARING), HE.TROPICAL, HU.WET, SO.ACIDIC]), - scarcity = SC.UNUSUAL, - )) + b.start(plants["durian"]) + b.altitude = AL.HILLS + b.biomeFilters = BF([FL.within(FL.CANOPY, FL.CLEARING), HE.TROPICAL, HU.WET, SO.ACIDIC]) + b.scarcity = SC.UNUSUAL + b.save("") - spawns.add(PlantSpawn( - plant = plants["fig"], - altitude = AL.span(AL.UPLANDS, AL.HILLS), - biomeFilters = BF([HE.SUBTROPICAL, HU.DRY, SO.SANDY], [FL.BARREN]), - scarcity = SC.UNCOMMON, - )) + b.start(plants["fig"]) + b.altitude = AL.span(AL.UPLANDS, AL.HILLS) + b.biomeFilters = BF([HE.SUBTROPICAL, HU.DRY, SO.SANDY], [FL.BARREN]) + b.scarcity = SC.UNCOMMON + b.save("") - spawns.add(PlantSpawn( - plant = plants["hawberry"], - altitude = AL.span(AL.UPLANDS, AL.HILLS), - biomeFilters = BF([(FL.FOREST, FL.CLEARING), HE.TEMPERATE, (SO.ACIDIC, SO.LOAMY)]), - scarcity = SC.SPARSE, - )) + b.start(plants["hawberry"]) + b.altitude = AL.span(AL.UPLANDS, AL.HILLS) + b.biomeFilters = BF([(FL.FOREST, FL.CLEARING), HE.TEMPERATE, (SO.ACIDIC, SO.LOAMY)]) + b.scarcity = SC.SPARSE + b.save("") - spawns.add(PlantSpawn( - plant = plants["lychee"], - altitude = AL.UPLANDS, - biomeFilters = BF([FL.CLEARING, HE.TROPICAL, HU.WET], [SO.FUNGAL]), - scarcity = SC.SPARSE, - )) + b.start(plants["lychee"]) + b.altitude = AL.UPLANDS + b.biomeFilters = BF([FL.CLEARING, HE.TROPICAL, HU.WET], [SO.FUNGAL]) + b.scarcity = SC.SPARSE + b.save("") - spawns.add(PlantSpawn( - plant = plants["mango"], - altitude = AL.LOWLANDS, - biomeFilters = BF( - [FL.within(FL.CANOPY, FL.CLEARING), HE.TROPICAL, (HU.DAMP, HU.WET), SO.ACIDIC] - ), - scarcity = SC.UNCOMMON, - )) + b.start(plants["mango"]) + b.altitude = AL.LOWLANDS + b.biomeFilters = BF( + [FL.within(FL.CANOPY, FL.CLEARING), HE.TROPICAL, (HU.DAMP, HU.WET), SO.ACIDIC] + ) + b.scarcity = SC.UNCOMMON + b.save("") - spawns.add(PlantSpawn( - plant = plants["mangosteen"], - altitude = AL.UPLANDS, - biomeFilters = BF([FL.CANOPY, HE.TROPICAL, HU.WET, SO.ACIDIC]), - scarcity = SC.UNUSUAL, - )) + b.start(plants["mangosteen"]) + b.altitude = AL.UPLANDS + b.biomeFilters = BF([FL.CANOPY, HE.TROPICAL, HU.WET, SO.ACIDIC]) + b.scarcity = SC.UNUSUAL + b.save("") - spawns.add(PlantSpawn( - plant = plants["orange"], - altitude = AL.LOWLANDS, - biomeFilters = BF([FL.FIELD, HE.SUBTROPICAL, HU.DRY]), - scarcity = SC.SPARSE, - )) + b.start(plants["orange"]) + b.altitude = AL.LOWLANDS + b.biomeFilters = BF([FL.FIELD, HE.SUBTROPICAL, HU.DRY]) + b.scarcity = SC.SPARSE + b.save("") - spawns.add(PlantSpawn( - plant = plants["peach"], - altitude = AL.span(AL.LOWLANDS, AL.UPLANDS), - biomeFilters = BF([FL.CLEARING, HE.TEMPERATE, HU.DAMP, SO.ROCKY]), - scarcity = SC.UNCOMMON, - )) + b.start(plants["peach"]) + b.altitude = AL.span(AL.LOWLANDS, AL.UPLANDS) + b.biomeFilters = BF([FL.CLEARING, HE.TEMPERATE, HU.DAMP, SO.ROCKY]) + b.scarcity = SC.UNCOMMON + b.save("") - spawns.add(PlantSpawn( - plant = plants["pear"], - altitude = AL.span(AL.LOWLANDS, AL.UPLANDS), - biomeFilters = BF([FL.CLEARING, HE.TEMPERATE, HU.DAMP, (SO.ACIDIC, SO.LOAMY)]), - scarcity = SC.UNCOMMON, - )) + b.start(plants["pear"]) + b.altitude = AL.span(AL.LOWLANDS, AL.UPLANDS) + b.biomeFilters = BF([FL.CLEARING, HE.TEMPERATE, HU.DAMP, (SO.ACIDIC, SO.LOAMY)]) + b.scarcity = SC.UNCOMMON + b.save("") - spawns.add(PlantSpawn( - plant = plants["persimmon"], - altitude = AL.span(AL.LOWLANDS, AL.UPLANDS), - biomeFilters = BF([FL.FOREST, HE.TEMPERATE, SO.LOAMY]), - scarcity = SC.UNCOMMON, - )) + b.start(plants["persimmon"]) + b.altitude = AL.span(AL.LOWLANDS, AL.UPLANDS) + b.biomeFilters = BF([FL.FOREST, HE.TEMPERATE, SO.LOAMY]) + b.scarcity = SC.UNCOMMON + b.save("") diff --git a/mcpacker/ui/runner_test.py b/mcpacker/ui/runner_test.py index 2594052..6101c63 100644 --- a/mcpacker/ui/runner_test.py +++ b/mcpacker/ui/runner_test.py @@ -2,6 +2,7 @@ from mcpacker.model.ecology.biome import Biome from mcpacker.model.ecology.biomefilter import BiomeFilter as BF from mcpacker.model.fauna.mob import Mob from mcpacker.model.fauna.mobspawn import MobSpawn +from mcpacker.model.fauna.mobspawnbuilder import MobSpawnBuilder from mcpacker.model.geology.mineral import Mineral from mcpacker.model.geology.replacement import Replacement from mcpacker.model.habitat import Habitat @@ -51,21 +52,19 @@ def defineAddMobSpawnss(): def addMobSpawns(pack:ModPack): mobs = pack.world.mobs spawns = pack.world.mobSpawns + b = MobSpawnBuilder(pack.world.mobSpawns) - pack.world.mobSpawns.add( - MobSpawn(mobs["minecraft:chicken"], - Habitat( - altitude = AL.span(AL.LOWLANDS, AL.UPLANDS), - biomeFilter = BF([HE.TROPICAL, HU.WET, FL.within(FL.CANOPY, FL.CLEARING)]), - seasons = SE.SUMMER, - group = GR.TROUP, - scarcity = SC.COMMON, - ).derive( - seasons = SE.exclude(SE.SUMMER), - scarcity = SC.UNCOMMON - ) - ) - ) + b.start(mobs["minecraft:chicken"]) + b.altitude = AL.span(AL.LOWLANDS, AL.UPLANDS) + b.biomeFilters = BF([HE.TROPICAL, HU.WET, FL.within(FL.CANOPY, FL.CLEARING)]) + b.seasons = SE.SUMMER + b.group = GR.TROUP + b.scarcity = SC.COMMON + b.save("chicken-summer") + + b.seasons = SE.exclude(SE.SUMMER) + b.scarcity = SC.UNCOMMON + b.save("chicken-normal") yield addMobSpawns diff --git a/mcpacker/write/incontrol/spawnerwriter.py b/mcpacker/write/incontrol/spawnerwriter.py index c72f534..cdaf47b 100644 --- a/mcpacker/write/incontrol/spawnerwriter.py +++ b/mcpacker/write/incontrol/spawnerwriter.py @@ -1,21 +1,17 @@ +from collections.abc import Iterable from mcpacker.json import JsonBlob from mcpacker.model.ecology.biomefilter import BiomeFilter from mcpacker.model.fauna.active import Active from mcpacker.model.fauna.mobspawn import MobSpawn -from mcpacker.model.habitat import Habitat from mcpacker.model.scarcity import Scarcity from mcpacker.model.season import Season -from mcpacker.model.modpack import ModPack -from mcpacker.write.incontrol import INCONTROL_CONFIG_DIR from mcpacker.write.writer import Writer -from pathlib import Path -import mcpacker.json as json -import mcpacker.model.dimension as DI +import mcpacker.json as json +import mcpacker.model.dimension as DI import mcpacker.model.fauna.location as LO -import mcpacker.model.scarcity as SC -import mcpacker.model.time as TI -import os +import mcpacker.model.scarcity as SC +import mcpacker.model.time as TI # Class ############################################################################################ @@ -27,13 +23,12 @@ class SpawnerWriter(Writer): self.resetOutputFile(path) path.write_text(json.dumps(self._makeAllRules(), indent=json.INDENT)) + # Private Functions ######################################################## - def _computeBiomeNames(self, biomeFilter:BiomeFilter) -> JsonBlob: + def _computeBiomeNames(self, biomeFilters:Iterable[BiomeFilter]) -> JsonBlob: return list( - str(b.gameId) for b in self.pack.world.biomes.filter( - lambda b: biomeFilter.accepts(b) - ) + str(b.gameId) for b in self.pack.world.biomes.find(biomeFilters) ) def _computeSpawnRate(self, scarcity:Scarcity) -> float: @@ -60,12 +55,12 @@ class SpawnerWriter(Writer): SC.CARPET: 32, }[scarcity] - def _makeAndCondition(self, habitat:Habitat, season:Season, active:Active) -> JsonBlob: + def _makeAndCondition(self, spawn:MobSpawn, season:Season, active:Active) -> JsonBlob: return { - "biome": self._computeBiomeNames(habitat.biomeFilter), + "biome": self._computeBiomeNames(spawn.habitat.biomeFilters), season.name: True, - "seesky": True if LO.OUTSIDE == habitat.location else None, - "cave": True if LO.CAVE == habitat.location else None, + "seesky": True if LO.OUTSIDE == spawn.ecotype.location else None, + "cave": True if LO.CAVE == spawn.ecotype.location else None, "mintime": active.start, "maxtime": active.end, } @@ -74,33 +69,32 @@ class SpawnerWriter(Writer): result:list[JsonBlob] = [] for spawn in self.pack.world.mobSpawns: - for habitat in spawn.habitats: - for season in habitat.seasons: - for active in spawn.mob.active: - result.append(self._makeRule(spawn, habitat, season, active)) + for season in spawn.habitat.seasons: + for active in spawn.mob.active: + result.append(self._makeRule(spawn, season, active)) return json.removeEmptyObjects(json.removeNoneValues(result)) - def _makeCondition(self, habitat:Habitat, season:Season, active:Active) -> JsonBlob: + def _makeCondition(self, spawn:MobSpawn, season:Season, active:Active) -> JsonBlob: return { "dimension": DI.OVERWORLD.name, - "minheight": habitat.altitude.bottom, - "maxheight": habitat.altitude.top, - "maxthis": habitat.group.largest * 2, - "inwater": LO.WATER == habitat.location, - "and": self._makeAndCondition(habitat, season, active), + "minheight": spawn.habitat.altitude.bottom, + "maxheight": spawn.habitat.altitude.top, + "maxthis": spawn.ecotype.group.largest * 2, + "inwater": LO.WATER == spawn.ecotype.location, + "and": self._makeAndCondition(spawn, season, active), } - def _makeRule(self, spawn:MobSpawn, habitat:Habitat, season:Season, active:Active,) -> JsonBlob: + def _makeRule(self, spawn:MobSpawn, season:Season, active:Active) -> JsonBlob: return { "mob": str(spawn.mob.gameId), "amount": { - "minimum": habitat.group.smallest, - "maximum": habitat.group.largest, + "minimum": spawn.ecotype.group.smallest, + "maximum": spawn.ecotype.group.largest, "groupdistance": 4, }, - "persecond": self._computeSpawnRate(habitat.scarcity), - "attempts": habitat.group.largest * 4, - "weights": [ self._computeWeight(habitat.scarcity) ], - "conditions": self._makeCondition(habitat, season, active), + "persecond": self._computeSpawnRate(spawn.ecotype.scarcity), + "attempts": spawn.ecotype.group.largest * 4, + "weights": [ self._computeWeight(spawn.ecotype.scarcity) ], + "conditions": self._makeCondition(spawn, season, active), } diff --git a/mcpacker/write/incontrol/spawnerwriter_test.py b/mcpacker/write/incontrol/spawnerwriter_test.py index 1f5f777..19aa777 100644 --- a/mcpacker/write/incontrol/spawnerwriter_test.py +++ b/mcpacker/write/incontrol/spawnerwriter_test.py @@ -2,6 +2,7 @@ from mcpacker.model.ecology.biome import Biome from mcpacker.model.ecology.biomefilter import BiomeFilter as BF from mcpacker.model.fauna.mob import Mob from mcpacker.model.fauna.mobspawn import MobSpawn +from mcpacker.model.fauna.mobspawnbuilder import MobSpawnBuilder from mcpacker.model.geology.mineral import Mineral from mcpacker.model.geology.replacement import Replacement from mcpacker.model.habitat import Habitat @@ -10,19 +11,19 @@ from mcpacker.write.incontrol.spawnerwriter import SpawnerWriter from pathlib import Path from pytest import fixture -import mcpacker.json as json -import mcpacker.model.altitude as AL -import mcpacker.model.ecology.flora as FL -import mcpacker.model.ecology.geology as GE -import mcpacker.model.ecology.heat as HE +import mcpacker.json as json +import mcpacker.model.altitude as AL +import mcpacker.model.ecology.flora as FL +import mcpacker.model.ecology.geology as GE +import mcpacker.model.ecology.heat as HE import mcpacker.model.ecology.humidity as HU -import mcpacker.model.ecology.soil as SO -import mcpacker.model.ecology.water as WA -import mcpacker.model.fauna.active as AC -import mcpacker.model.fauna.group as GR -import mcpacker.model.fauna.location as LO -import mcpacker.model.scarcity as SC -import mcpacker.model.season as SE +import mcpacker.model.ecology.soil as SO +import mcpacker.model.ecology.water as WA +import mcpacker.model.fauna.active as AC +import mcpacker.model.fauna.group as GR +import mcpacker.model.fauna.location as LO +import mcpacker.model.scarcity as SC +import mcpacker.model.season as SE import textwrap @@ -49,21 +50,19 @@ def defineAddMobSpawnss(): def addMobSpawns(pack:ModPack): mobs = pack.world.mobs spawns = pack.world.mobSpawns + b = MobSpawnBuilder(pack.world.mobSpawns) - pack.world.mobSpawns.add( - MobSpawn(mobs["minecraft:chicken"], - Habitat( - altitude = AL.span(AL.LOWLANDS, AL.UPLANDS), - biomeFilter = BF([HE.TROPICAL, HU.WET, FL.within(FL.CANOPY, FL.CLEARING)]), - seasons = SE.SUMMER, - group = GR.TROUP, - scarcity = SC.COMMON, - ).derive( - seasons = SE.exclude(SE.SUMMER), - scarcity = SC.UNCOMMON - ) - ) - ) + b.start(mobs["minecraft:chicken"]) + b.altitude = AL.span(AL.LOWLANDS, AL.UPLANDS) + b.biomeFilters = BF([HE.TROPICAL, HU.WET, FL.within(FL.CANOPY, FL.CLEARING)]) + b.seasons = SE.SUMMER + b.group = GR.TROUP + b.scarcity = SC.COMMON + b.save("chicken-summer") + + b.seasons = SE.exclude(SE.SUMMER) + b.scarcity = SC.UNCOMMON + b.save("chicken-normal") yield addMobSpawns @@ -92,14 +91,12 @@ def test_write(writer:SpawnerWriter, tmp_path:Path): assert len(data) == 4 rule = data[0] + assert rule["conditions"]["and"]["spring"] + assert abs(rule["persecond"] - 0.066) < 0.001 + + rule = data[3] assert rule["mob"] == "minecraft:chicken" assert rule["amount"]["maximum"] == 6 - assert abs(rule["persecond"] - 0.2) < 0.001 assert rule["conditions"]["and"]["biome"] == ["minecraft:jungle"] assert rule["conditions"]["and"]["summer"] - - rule = data[1] - assert abs(rule["persecond"] - 0.066) < 0.001 - assert rule["conditions"]["and"]["spring"] - - + assert abs(rule["persecond"] - 0.2) < 0.001 diff --git a/requirements.txt b/requirements.txt index 45bd628..1e9e0ee 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,14 +1,17 @@ +attrs==25.4.0 +cattrs==25.3.0 +docstring-to-markdown==0.17 exceptiongroup==1.3.0 +importlib_metadata==8.7.0 iniconfig==2.1.0 -jedi==0.19.2 mypy==1.18.2 mypy_extensions==1.1.0 +nodeenv==1.9.1 packaging==25.0 -parso==0.8.4 pathspec==0.12.1 pluggy==1.6.0 Pygments==2.19.2 +pyright==1.1.406 pytest==8.4.2 -toml==0.10.2 -tomli==2.2.1 typing_extensions==4.15.0 +zipp==3.23.0