Update flora/fauna spawns to use a common system

This commit is contained in:
Andrew Miner
2025-10-23 10:10:41 -06:00
parent d0b5760067
commit 5405a13eb0
31 changed files with 1323 additions and 1323 deletions
+11 -7
View File
@@ -39,26 +39,30 @@ class BiomeReport(ReportComposer):
def _composeFaunaSection(self, biome:Biome): def _composeFaunaSection(self, biome:Biome):
self.line().line("Fauna:").indent() self.line().line("Fauna:").indent()
faunaLineCount = 0 faunaLineCount = 0
for spawn in self.pack.world.mobSpawns:
for mob in self.pack.world.mobs:
altitude = None altitude = None
for habitat in spawn.acceptedBy(biome): for spawn in self.pack.world.mobSpawns:
if habitat.location == LO.CAVE: continue if not spawn.habitat.accepts(biome): continue
altitude = AL.span(altitude or habitat.altitude, habitat.altitude) if spawn.ecotype.location == LO.CAVE: continue
altitude = AL.span(altitude or spawn.habitat.altitude, spawn.habitat.altitude)
if altitude != None: if altitude != None:
self.line(f"{spawn.mob.name} @ {altitude}") self.line(f"{mob.name} @ {altitude}")
faunaLineCount += 1 faunaLineCount += 1
if faunaLineCount == 0: if faunaLineCount == 0:
self.line("<no fauna>") self.line("<no fauna>")
self.outdent() # fauna self.outdent() # fauna
def _composeFloraSection(self, biome:Biome): def _composeFloraSection(self, biome:Biome):
self.line().line("Flora:").indent() self.line().line("Flora:").indent()
floraLineCount = 0 floraLineCount = 0
for spawn in self.pack.world.plantSpawns: for spawn in self.pack.world.plantSpawns:
if not spawn.acceptsBiome(biome): continue if not spawn.habitat.accepts(biome): continue
self.line(f"{spawn.plant.name} @ {spawn.altitude}") self.line(f"{spawn.plant.name} @ {spawn.habitat.altitude}")
floraLineCount += 1 floraLineCount += 1
if floraLineCount == 0: if floraLineCount == 0:
+15 -13
View File
@@ -9,24 +9,26 @@ from pathlib import Path
class MobSpawnReport(ReportComposer): class MobSpawnReport(ReportComposer):
def doCompose(self): def doCompose(self):
for spawn in self.pack.world.mobSpawns: for mob in self.pack.world.mobs:
self.line(f"Mob: {spawn.name}") self.line(f"Mob: {mob.name}")
self.line()
self.indent() 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.indent()
self.text("altitude: ").line(habitat.altitude) 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("seasons: ").line(", ".join([str(s) for s in habitat.seasons]))
self.text("group: ").line(habitat.group) self.line()
self.text("location: ").line(habitat.location) self.text("group: ").line(ecotype.group)
self.text("scarcity: ").line(habitat.scarcity) self.text("location: ").line(ecotype.location)
self.text("scarcity: ").line(ecotype.scarcity)
self.outdent() self.outdent()
self.outdent() self.outdent()
+25 -25
View File
@@ -1,3 +1,4 @@
from mcpacker.model.fauna.mobspawnbuilder import MobSpawnBuilder
from mcpacker.format.report.mobspawnreport import MobSpawnReport from mcpacker.format.report.mobspawnreport import MobSpawnReport
from mcpacker.model.ecology.biomefilter import BiomeFilter as BF from mcpacker.model.ecology.biomefilter import BiomeFilter as BF
from mcpacker.model.fauna.mob import Mob from mcpacker.model.fauna.mob import Mob
@@ -34,25 +35,22 @@ def defineAddMobs():
yield addMobs yield addMobs
@fixture(name="addMobSpawns") @fixture(name="addMobSpawns")
def defineAddMobSpawnss(): def defineAddMobSpawns():
def addMobSpawns(pack:ModPack): def addMobSpawns(pack:ModPack):
mobs = pack.world.mobs mobs = pack.world.mobs
spawns = pack.world.mobSpawns b = MobSpawnBuilder(pack.world.mobSpawns)
pack.world.mobSpawns.add( b.start(mobs["minecraft:chicken"])
MobSpawn(mobs["minecraft:chicken"], b.altitude = AL.span(AL.LOWLANDS, AL.UPLANDS)
Habitat( b.biomeFilters = BF([HE.TROPICAL, HU.WET, FL.within(FL.CANOPY, FL.CLEARING)])
altitude = AL.span(AL.LOWLANDS, AL.UPLANDS), b.seasons = SE.SUMMER
biomeFilter = BF([HE.TROPICAL, HU.WET, FL.within(FL.CANOPY, FL.CLEARING)]), b.group = GR.TROUP
seasons = SE.SUMMER, b.scarcity = SC.COMMON
group = GR.TROUP, b.save("chicken-summer")
scarcity = SC.COMMON,
).derive( b.seasons = SE.exclude(SE.SUMMER)
seasons = SE.exclude(SE.SUMMER), b.scarcity = SC.UNCOMMON
scarcity = SC.UNCOMMON b.save("chicken-normal")
)
)
)
yield addMobSpawns yield addMobSpawns
@@ -71,19 +69,21 @@ def test_write(report:MobSpawnReport):
assert str(report).strip() == textwrap.dedent(""" assert str(report).strip() == textwrap.dedent("""
Mob: minecraft:chicken Mob: minecraft:chicken
Habitat 1 Spawn: chicken-normal
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
altitude: lowlands to uplands (70 to 124) altitude: lowlands to uplands (70 to 124)
biomeFilter: tropical and wet and (canopy or forest or clearing) biomeFilter: tropical and wet and (canopy or forest or clearing)
seasons: spring, autumn, winter seasons: spring, autumn, winter
group: troup (3 to 6) group: troup (3 to 6)
location: outside location: outside
scarcity: uncommon 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() """).strip()
+5 -5
View File
@@ -44,15 +44,15 @@ class PlantSpawnReport(ReportComposer):
self.line().line("Habitat:") self.line().line("Habitat:")
self.indent() # habitat self.indent() # habitat
self.text("altitude: ").line(spawn.altitude) self.text("altitude: ").line(spawn.habitat.altitude)
self.text("biomeFilters: ").line(" -OR- ".join(str(f) for f in spawn.biomeFilters)) self.text("biomeFilters: ")
self.text("scarcity: ").line(spawn.scarcity) self.line(" -OR- ".join(str(f) for f in spawn.habitat.biomeFilters))
self.text("scarcity: ").line(spawn.ecotype.scarcity)
self.outdent() # habitat self.outdent() # habitat
self.line().line("Biomes:") self.line().line("Biomes:")
self.indent() # biomes self.indent() # biomes
biomes = self.pack.world.biomes.filter(lambda b: spawn.acceptsBiome(b)) for biome in self.pack.world.biomes.find(spawn.habitat.biomeFilters):
for biome in biomes:
self.line(biome) self.line(biome)
self.outdent() # biomes self.outdent() # biomes
+4 -11
View File
@@ -1,10 +1,7 @@
from collections.abc import Iterable from collections.abc import Iterable
from typing import Any from collections.abc import Iterator
from typing import Callable from typing import Callable
from typing import Generic
from typing import Iterator
from typing import Protocol from typing import Protocol
from typing import TypeVar
# Type Support ##################################################################################### # Type Support #####################################################################################
@@ -14,14 +11,10 @@ class HasName(Protocol):
@property @property
def name(self) -> str: ... def name(self) -> str: ...
CatalogItem = TypeVar("CatalogItem", bound=HasName)
MapTarget = TypeVar("MapTarget")
# Class ############################################################################################ # Class ############################################################################################
class Catalog(Generic[CatalogItem]): class Catalog[CatalogItem: HasName]:
def __init__(self, items:Iterable[CatalogItem]|None=None): def __init__(self, items:Iterable[CatalogItem]|None=None):
self._items:dict[str,CatalogItem] = {} self._items:dict[str,CatalogItem] = {}
@@ -39,7 +32,7 @@ class Catalog(Generic[CatalogItem]):
return self._items[name] return self._items[name]
def __iter__(self) -> Iterator[CatalogItem]: 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] yield self._items[name]
def __len__(self) -> int: def __len__(self) -> int:
@@ -64,6 +57,6 @@ class Catalog(Generic[CatalogItem]):
if isAllowed(item): if isAllowed(item):
yield 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: for item in self:
yield doTransform(item) yield doTransform(item)
+12 -1
View File
@@ -1,3 +1,4 @@
from collections.abc import Iterable
from mcpacker.model.catalog import Catalog from mcpacker.model.catalog import Catalog
from mcpacker.model.ecology.biome import Biome from mcpacker.model.ecology.biome import Biome
from mcpacker.model.ecology.biomefilter import BiomeFilter from mcpacker.model.ecology.biomefilter import BiomeFilter
@@ -7,4 +8,14 @@ from typing import Iterator
# Classes ########################################################################################## # Classes ##########################################################################################
class BiomeCatalog(Catalog[Biome]): 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
+3 -3
View File
@@ -10,7 +10,7 @@ class BiomeFilter:
""" """
A description of which biomes may be considered for a given purpose. 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 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. 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, required:Sequence[BiomeTrait|ResourceId|Sequence[BiomeTrait]]|None=None,
prohibited:Sequence[BiomeTrait|ResourceId]|None=None, prohibited:Sequence[BiomeTrait|ResourceId]|None=None,
): ):
self.required = required or [] self.required = list(required or [])
self.prohibited = prohibited or [] self.prohibited = list(prohibited or [])
def __repr__(self) -> str: def __repr__(self) -> str:
return ( return (
+20
View File
@@ -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})"
+5 -17
View File
@@ -1,38 +1,26 @@
from collections.abc import Iterable from collections.abc import Iterable
from mcpacker.model.fauna.active import Active from mcpacker.model.fauna.active import Active
from mcpacker.model.resourceid import ResourceId from mcpacker.model.resourceid import ResourceId
from mcpacker.model.spawnable import Spawnable
import mcpacker.model.fauna.active as AC import mcpacker.model.fauna.active as AC
# Class ############################################################################################ # Class ############################################################################################
class Mob: class Mob(Spawnable):
""" """
A creature which may appear in the game world. A creature which may appear in the game world.
""" """
def __init__(self, gameId:ResourceId|str, active:Active|Iterable[Active]=AC.DIURNAL): def __init__(self, gameId:ResourceId|str, active:Active|Iterable[Active]=AC.DIURNAL):
super().__init__(str(gameId))
if isinstance(active, Active): if isinstance(active, Active):
active = [active] active = [active]
self.gameId = ResourceId.parse(gameId) self.gameId = ResourceId.parse(gameId)
self.active = active 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: def __repr__(self) -> str:
return f"Mob<{self.gameId}>{{active:{self.active}}}" return f"Mob(gameId={self.gameId!r}, active={self.active!r})"
@property
def name(self) -> str:
return str(self.gameId)
+6 -1
View File
@@ -36,4 +36,9 @@ def test_str(squid):
assert str(squid) == "minecraft:squid" assert str(squid) == "minecraft:squid"
def test_repr(cow): def test_repr(cow):
assert repr(cow) == "Mob<minecraft:cow>{active:[Active<day>{start:0, end:12000}]}" assert repr(cow) == (
"Mob("
"gameId=ResourceId(isTag=False, mod='minecraft', name='cow'), "
"active=[Active<day>{start:0, end:12000}]"
")"
)
+35
View File
@@ -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}"
")"
)
+7 -21
View File
@@ -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.habitat import Habitat
from mcpacker.model.fauna.mob import Mob from mcpacker.model.fauna.mob import Mob
from mcpacker.model.fauna.mobecotype import MobEcotype
from mcpacker.model.spawn import Spawn from mcpacker.model.spawn import Spawn
# Class ############################################################################################ # 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): def __init__(self, name:str, habitat:Habitat, mob:Mob, ecotype:MobEcotype):
self.mob = mob super().__init__(name, habitat, mob, ecotype)
super().__init__(habitats)
def __str__(self) -> str:
return self.name
@property @property
def name(self) -> str: def mob(self) -> Mob:
return self.mob.name return self.spawnable
def acceptedBy(self, biome:Biome) -> list[Habitat]:
result = []
for habitat in self.habitats:
if habitat.accepts(biome):
result.append(habitat)
return result
+32 -29
View File
@@ -1,5 +1,6 @@
from mcpacker.model.ecology.biomefilter import BiomeFilter from mcpacker.model.ecology.biomefilter import BiomeFilter
from mcpacker.model.fauna.mob import Mob 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.mobspawn import MobSpawn
from mcpacker.model.habitat import Habitat from mcpacker.model.habitat import Habitat
from pytest import fixture from pytest import fixture
@@ -11,42 +12,44 @@ import mcpacker.model.ecology.heat as HE
# Fixtures ######################################################################################### # 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") @fixture(name="spawn")
def createMobSpawn(cow, fields): def createMobSpawn():
yield MobSpawn(cow, fields) yield MobSpawn(
"cow-in-field",
Habitat(biomeFilters=BiomeFilter([FL.FIELD, HE.TEMPERATE])),
Mob("minecraft:cow", AC.DIURNAL),
MobEcotype()
)
# Tests ############################################################################################ # Tests ############################################################################################
def test_cowsInFields(spawn): def test_cowsInFields(spawn):
assert repr(spawn) == ( assert repr(spawn) == (
"Spawn(" + "MobSpawn("
"habitats=[" + "ecotype=MobEcotype("
"Habitat(" + "group=Group(name='solo', smallest=1, largest=1), "
"altitude=Altitude(name=anywhere, bottom=-64, top=320), " + "location=Location(name='outside'), "
"biomeFilter=BiomeFilter(" + "scarcity=Scarcity(name='sparse')"
"required=[Flora(name='field'), Heat(name='temperate')], " + "), "
"prohibited=[]" + "habitat=Habitat("
"), " + "altitude=Altitude(name=anywhere, bottom=-64, top=320), "
"seasons=[" + "biomeFilters=[BiomeFilter("
"Season(name='spring'), " + "required=[Flora(name='field'), Heat(name='temperate')], "
"Season(name='summer'), " + "prohibited=[])"
"Season(name='autumn'), " + "], "
"Season(name='winter')" + "seasons=["
"], " + "Season(name='spring'), "
"group=Group(name='solo', smallest=1, largest=1), " + "Season(name='summer'), "
"location=Location(name='outside'), " + "Season(name='autumn'), "
"scarcity=Scarcity(name='sparse')" + "Season(name='winter')"
")" + "]"
"]" + "), "
"name='cow-in-field', "
"spawnable=Mob("
"gameId=ResourceId(isTag=False, mod='minecraft', name='cow'), "
"active=[Active<day>{start:0, end:12000}]"
")"
")" ")"
) )
+50
View File
@@ -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
+5 -8
View File
@@ -1,12 +1,9 @@
from mcpacker.model.spawnable import Spawnable
# Class ############################################################################################ # Class ############################################################################################
class Plant: class Plant(Spawnable):
def __init__(self, name:str):
self.name = name
def __repr__(self) -> str: def __repr__(self) -> str:
return f"Plant(name={repr(self.name)})" return f"Plant(name={self.name!r})"
def __str__(self) -> str:
return self.name
+11 -43
View File
@@ -1,51 +1,19 @@
from collections.abc import Iterable from mcpacker.model.ecotype import Ecotype
from mcpacker.model.altitude import Altitude from mcpacker.model.habitat import Habitat
from mcpacker.model.ecology.biome import Biome from mcpacker.model.spawn import Spawn
from mcpacker.model.ecology.biomefilter import BiomeFilter
from mcpacker.model.flora.plant import Plant 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 ############################################################################################
class PlantSpawn: class PlantSpawn(Spawn[Plant, Ecotype]):
"""
Describes a certain habitat for a particular animal with its local variations.
"""
def __init__( def __init__(self, name:str, habitat:Habitat, plant:Plant, ecotype:Ecotype):
self, super().__init__(name, habitat, plant, ecotype)
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),
")"
])
@property @property
def name(self) -> str: def plant(self) -> Plant:
return self.plant.name return self.spawnable
def acceptsBiome(self, biome:Biome) -> bool:
for biomeFilter in self.biomeFilters:
if biomeFilter.accepts(biome): return True
return False
+17
View File
@@ -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)
@@ -6,3 +6,4 @@ from mcpacker.model.flora.plantspawn import PlantSpawn
class PlantSpawnCatalog(Catalog[PlantSpawn]): class PlantSpawnCatalog(Catalog[PlantSpawn]):
pass pass
+26 -67
View File
@@ -2,16 +2,10 @@ from collections.abc import Iterable
from mcpacker.model.altitude import Altitude from mcpacker.model.altitude import Altitude
from mcpacker.model.ecology.biome import Biome from mcpacker.model.ecology.biome import Biome
from mcpacker.model.ecology.biomefilter import BiomeFilter 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 from mcpacker.model.season import Season
import mcpacker.model.altitude as A import mcpacker.model.altitude as AL
import mcpacker.model.fauna.group as G import mcpacker.model.season as SE
import mcpacker.model.fauna.location as L
import mcpacker.model.scarcity as C
import mcpacker.model.season as E
# Class ############################################################################################ # Class ############################################################################################
@@ -24,77 +18,42 @@ class Habitat:
def __init__( def __init__(
self, self,
altitude:Altitude=A.ANYWHERE, altitude:Altitude=AL.ANYWHERE,
biomeFilter:BiomeFilter|None=None, biomeFilters:Iterable[BiomeFilter]|BiomeFilter|None=None,
seasons:Iterable[Season]|Season=E.ALL, seasons:Iterable[Season]|Season=SE.ALL,
group:Group=G.SOLO,
location:Location=L.OUTSIDE,
scarcity:Scarcity=C.SPARSE,
): ):
self.altitude = altitude if not biomeFilters:
self.biomeFilter = biomeFilter or BiomeFilter() biomeFilters = BiomeFilter()
self.seasons = [seasons] if isinstance(seasons, Season) else seasons if isinstance(biomeFilters, BiomeFilter):
biomeFilters = [biomeFilters]
self.group = group if isinstance(seasons, Season):
self.location = location seasons = [seasons]
self.scarcity = scarcity
self.altitude = altitude
self.biomeFilters = biomeFilters
self.seasons = seasons
self._source:Habitat|None = None self._source:Habitat|None = None
def __str__(self) -> str: def __str__(self) -> str:
return "".join([str(p) for p in [ return (
self.altitude, "|", f"{self.altitude!s}|"
self.biomeFilter, "|", f"{self.biomeFilters!s}|"
", ".join([str(s) for s in self.seasons]), "|", f"{self.seasons!s}"
self.group, "|", )
self.location, "|",
self.scarcity
]])
def __repr__(self) -> str: def __repr__(self) -> str:
return "".join([str(p) for p in [ return "".join([str(p) for p in [
"Habitat(", "Habitat(",
"altitude=", repr(self.altitude), ", ", f"altitude={self.altitude!r}, "
"biomeFilter=", repr(self.biomeFilter), ", ", f"biomeFilters={self.biomeFilters!r}, "
"seasons=[", ", ".join([repr(s) for s in self.seasons]), "], ", f"seasons={self.seasons!r}"
"group=", repr(self.group), ", ",
"location=", repr(self.location), ", ",
"scarcity=", repr(self.scarcity),
")" ")"
]]) ]])
def accepts(self, biome:Biome) -> bool: 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]": return True
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
+8 -28
View File
@@ -2,10 +2,10 @@ from mcpacker.model.ecology.biomefilter import BiomeFilter
from mcpacker.model.habitat import Habitat from mcpacker.model.habitat import Habitat
from pytest import fixture from pytest import fixture
import mcpacker.model.altitude as AL
import mcpacker.model.ecology.flora as FL import mcpacker.model.ecology.flora as FL
import mcpacker.model.ecology.heat as HE import mcpacker.model.ecology.heat as HE
import mcpacker.model.season as SE import mcpacker.model.season as SE
import mcpacker.model.scarcity as SC
# Fixtures ######################################################################################### # Fixtures #########################################################################################
@@ -14,40 +14,20 @@ import mcpacker.model.scarcity as SC
def createFieldsFilter(): def createFieldsFilter():
yield BiomeFilter([FL.FIELD, HE.TEMPERATE]) 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") @fixture(name="fields")
def createFieldsHabitat(fields): def createFieldsHabitat(fields):
yield Habitat(biomeFilter=fields) yield Habitat(AL.LOWLANDS, BiomeFilter([FL.FIELD, HE.TEMPERATE]), SE.HOT)
# Tests ############################################################################################ # Tests ############################################################################################
def test_derive(derivedHabitats):
assert [h.scarcity for h in derivedHabitats] == [SC.COMMON, SC.UNCOMMON, SC.ABSENT]
def test_repr(fields): def test_repr(fields):
assert repr(fields) == ( assert repr(fields) == (
"Habitat(" + "Habitat("
"altitude=Altitude(name=anywhere, bottom=-64, top=320), " + "altitude=Altitude(name=lowlands, bottom=70, top=92), "
"biomeFilter=BiomeFilter(" + "biomeFilters=[BiomeFilter("
"required=[Flora(name='field'), Heat(name='temperate')], " + "required=[Flora(name='field'), Heat(name='temperate')], "
"prohibited=[]" "prohibited=[]"
"), " + ")], "
"seasons=[" + "seasons=[Season(name='summer')]"
"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')" +
")" ")"
) )
+4
View File
@@ -0,0 +1,4 @@
class Population:
"""
Describes traits of a
"""
+17 -9
View File
@@ -1,20 +1,28 @@
from collections.abc import Iterable from collections.abc import Iterable
from mcpacker.model.ecotype import Ecotype
from mcpacker.model.habitat import Habitat from mcpacker.model.habitat import Habitat
from mcpacker.model.spawnable import Spawnable
# Class ############################################################################################ # 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): def __init__(self, name:str, habitat:Habitat, spawnable:SpawnableKind, ecotype:EcotypeKind):
if isinstance(habitats, Habitat): self.ecotype = ecotype
habitats = list(habitats.collect()) self.habitat = habitat
self.name = name
self.habitats = list(habitats or []) self.spawnable = spawnable
def __repr__(self) -> str: 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}"
")"
)
+32 -23
View File
@@ -1,44 +1,53 @@
from mcpacker.model.ecology.biomefilter import BiomeFilter from mcpacker.model.ecology.biomefilter import BiomeFilter
from mcpacker.model.ecotype import Ecotype
from mcpacker.model.habitat import Habitat from mcpacker.model.habitat import Habitat
from mcpacker.model.spawn import Spawn from mcpacker.model.spawn import Spawn
from mcpacker.model.spawnable import Spawnable
from pytest import fixture from pytest import fixture
import mcpacker.model.ecology.flora as F import mcpacker.model.ecology.flora as F
import mcpacker.model.ecology.heat as E import mcpacker.model.ecology.heat as E
# Helper Classes ###################################################################################
class SampleSpawnable(Spawnable):
pass
# Fixtures ######################################################################################### # Fixtures #########################################################################################
@fixture(name="fields")
def createFieldsHabitat():
yield Habitat(biomeFilter=BiomeFilter([F.FIELD, E.TEMPERATE]))
@fixture(name="spawn") @fixture(name="spawn")
def createSpawn(fields): def createSpawn():
yield Spawn([fields]) yield Spawn(
"spawn",
Habitat(biomeFilters=BiomeFilter([F.FIELD, E.TEMPERATE])),
SampleSpawnable("spawnable"),
Ecotype()
)
# Tests ############################################################################################ # Tests ############################################################################################
def test_repr(spawn): def test_repr(spawn):
assert repr(spawn) == ( assert repr(spawn) == (
"Spawn(habitats=[" + "Spawn("
"Habitat(" + "ecotype=Ecotype(scarcity=Scarcity(name='sparse')), "
"altitude=Altitude(name=anywhere, bottom=-64, top=320), " + "habitat=Habitat("
"biomeFilter=BiomeFilter(" + "altitude=Altitude(name=anywhere, bottom=-64, top=320), "
"required=[Flora(name='field'), Heat(name='temperate')], " + "biomeFilters=[BiomeFilter("
"prohibited=[]" + "required=[Flora(name='field'), Heat(name='temperate')], "
"prohibited=[]"
")], "
"seasons=["
"Season(name='spring'), "
"Season(name='summer'), "
"Season(name='autumn'), "
"Season(name='winter')"
"]"
"), " "), "
"seasons=[" + "name='spawn', "
"Season(name='spring'), " + "spawnable=Spawnable(name='spawnable')"
"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')" +
")" +
"])"
) )
+28
View File
@@ -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
+107
View File
@@ -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)
File diff suppressed because it is too large Load Diff
+288 -346
View File
@@ -1,8 +1,9 @@
from mcpacker.model.altitude import Altitude from mcpacker.model.altitude import Altitude
from mcpacker.model.ecology.biomefilter import BiomeFilter as BF from mcpacker.model.ecology.biomefilter import BiomeFilter as BF
from mcpacker.model.flora.plantspawn import PlantSpawn 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.modpack import ModPack
from mcpacker.model.resourceid import ResourceId
import mcpacker.model.altitude as AL import mcpacker.model.altitude as AL
import mcpacker.model.ecology.flora as FL import mcpacker.model.ecology.flora as FL
@@ -18,429 +19,370 @@ import mcpacker.model.scarcity as SC
def addPlantSpawns(pack:ModPack): def addPlantSpawns(pack:ModPack):
plants = pack.world.plants plants = pack.world.plants
spawns = pack.world.plantSpawns b = PlantSpawnBuilder(pack.world.plantSpawns)
# Crop Patches ############################################################# # Crop Patches #############################################################
spawns.add(PlantSpawn( b.start(plants["athelas"])
plant = plants["athelas"], b.altitude = AL.span(AL.LOWLANDS, AL.HILLS)
altitude = AL.span(AL.LOWLANDS, AL.HILLS), b.biomeFilters = BF([FL.CANOPY, HE.TEMPERATE])
biomeFilters = BF([FL.CANOPY, HE.TEMPERATE]), b.scarcity = SC.SPARSE
scarcity = SC.SPARSE, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["beetroot"])
plant = plants["beetroot"], b.altitude = AL.LOWLANDS
altitude = AL.LOWLANDS, b.biomeFilters = BF([FL.FOREST, HE.BOREAL, SO.ACIDIC])
biomeFilters = BF([FL.FOREST, HE.BOREAL, SO.ACIDIC]), b.scarcity = SC.COMMON
scarcity = SC.COMMON, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["bellpepper"])
plant = plants["bellpepper"], b.altitude = AL.LOWLANDS
altitude = AL.LOWLANDS, b.biomeFilters = BF([FL.CLEARING, HE.TROPICAL, HU.WET, WA.INLAND])
biomeFilters = BF([FL.CLEARING, HE.TROPICAL, HU.WET, WA.INLAND]), b.scarcity = SC.COMMON
scarcity = SC.COMMON, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["blueberry"])
plant = plants["blueberry"], b.altitude = AL.span(AL.UPLANDS, AL.ALPINE)
altitude = AL.span(AL.UPLANDS, AL.ALPINE), b.biomeFilters = BF([FL.within(FL.FOREST, FL.FIELD), HE.BOREAL])
biomeFilters = BF([FL.within(FL.FOREST, FL.FIELD), HE.BOREAL]), b.scarcity = SC.SPARSE
scarcity = SC.SPARSE, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["brownmushroom"])
plant = plants["brownmushroom"], b.altitude = AL.span(AL.SOIL, AL.ALPINE)
altitude = AL.span(AL.SOIL, AL.ALPINE), b.biomeFilters = [
biomeFilters = [ BF([(FL.CANOPY, FL.FOREST), (HE.TEMPERATE, HE.BOREAL), (SO.LOAMY, SO.CLAYEY)], [WA.OCEAN]),
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([HU.WET], [HE.FROZEN, WA.OCEAN, WA.RIVER]),
BF([SO.FUNGAL]), BF([SO.FUNGAL]),
], ]
scarcity = SC.COMMON, b.scarcity = SC.COMMON
)) b.save("")
spawns.add(PlantSpawn( b.start(plants["cabbage"])
plant = plants["cabbage"], b.altitude = AL.HILLS
altitude = AL.HILLS, b.biomeFilters = BF([(HE.SUBTROPICAL, HE.TEMPERATE), SO.SANDY, WA.COAST])
biomeFilters = BF([(HE.SUBTROPICAL, HE.TEMPERATE), SO.SANDY, WA.COAST]), b.scarcity = SC.COMMON
scarcity = SC.COMMON, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["carrot"])
plant = plants["carrot"], b.altitude = AL.LOWLANDS
altitude = AL.LOWLANDS, b.biomeFilters = BF(
biomeFilters = BF( [(FL.FOREST, FL.CLEARING), HE.TEMPERATE, SO.LOAMY], [ResourceId.parse("flower_forest")],
[(FL.FOREST, FL.CLEARING), HE.TEMPERATE, SO.LOAMY], )
[ResourceId.parse("flower_forest")], b.scarcity = SC.COMMON
), b.save("")
scarcity = SC.COMMON,
))
spawns.add(PlantSpawn( b.start(plants["coffee"])
plant = plants["coffee"], b.altitude = AL.span(AL.HILLS, AL.ALPINE)
altitude = AL.span(AL.HILLS, AL.ALPINE), b.biomeFilters = BF([FL.CANOPY, HE.TROPICAL, HU.WET])
biomeFilters = BF([FL.CANOPY, HE.TROPICAL, HU.WET]), b.scarcity = SC.SPARSE
scarcity = SC.SPARSE, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["corn"])
plant = plants["corn"], b.altitude = AL.span(AL.LOWLANDS, AL.UPLANDS)
altitude = AL.span(AL.LOWLANDS, AL.UPLANDS), b.biomeFilters = BF([FL.FIELD, HE.SUBTROPICAL, (SO.LOAMY, SO.SANDY)])
biomeFilters = BF([FL.FIELD, HE.SUBTROPICAL, (SO.LOAMY, SO.SANDY)]), b.scarcity = SC.COMMON
scarcity = SC.COMMON, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["cotton"])
plant = plants["cotton"], b.altitude = AL.LOWLANDS
altitude = AL.LOWLANDS, b.biomeFilters = BF([FL.FIELD, HE.SUBTROPICAL, HU.DRY, (SO.LOAMY, SO.SANDY)])
biomeFilters = BF([FL.FIELD, HE.SUBTROPICAL, HU.DRY, (SO.LOAMY, SO.SANDY)]), b.scarcity = SC.UNCOMMON
scarcity = SC.UNCOMMON, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["currant"])
plant = plants["currant"], b.altitude = AL.span(AL.UPLANDS, AL.ALPINE)
altitude = AL.span(AL.UPLANDS, AL.ALPINE), b.biomeFilters = BF([FL.within(FL.FOREST, FL.FIELD), (HE.BOREAL, HE.FROZEN)])
biomeFilters = BF([FL.within(FL.FOREST, FL.FIELD), (HE.BOREAL, HE.FROZEN)]), b.scarcity = SC.UNCOMMON
scarcity = SC.UNCOMMON, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["cranberry"])
plant = plants["cranberry"], b.altitude = AL.DUNES
altitude = AL.DUNES, b.biomeFilters = BF([HU.WET, SO.PEATY])
biomeFilters = BF([HU.WET, SO.PEATY]), b.scarcity = SC.UNCOMMON
scarcity = SC.UNCOMMON, b.save("")
))
# NOTE: Due to a bug in Supplementaries, flax can *only* be planted on sand, despite the actual # 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 # plant requiring an entirely different soil. Therefore, we're imaginging *this* so-called
# "flax" as a fiberous, desert-dwelling cousin. # "flax" as a fiberous, desert-dwelling cousin.
spawns.add(PlantSpawn( b.start(plants["flax"])
plant = plants["flax"], b.altitude = AL.DUNES
altitude = AL.DUNES, b.biomeFilters = BF([FL.BARREN, HU.DRY, SO.SANDY])
biomeFilters = BF([FL.BARREN, HU.DRY, SO.SANDY]), b.scarcity = SC.COMMON
scarcity = SC.COMMON, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["hamimelon"])
plant = plants["hamimelon"], b.altitude = AL.span(AL.LOWLANDS, AL.UPLANDS)
altitude = AL.span(AL.LOWLANDS, AL.UPLANDS), b.biomeFilters = BF([FL.FIELD, HE.SUBTROPICAL, HU.DRY, (SO.LOAMY, SO.SANDY)])
biomeFilters = BF([FL.FIELD, HE.SUBTROPICAL, HU.DRY, (SO.LOAMY, SO.SANDY)]), b.scarcity = SC.RARE
scarcity = SC.RARE, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["hemp"])
plant = plants["hemp"], b.altitude = AL.span(AL.DUNES, AL.UPLANDS)
altitude = AL.span(AL.DUNES, AL.UPLANDS), b.biomeFilters = BF(
biomeFilters = BF( [FL.FIELD, HE.TEMPERATE, HU.DAMP, SO.LOAMY], [ResourceId.parse("sunflower_plains")]
[FL.FIELD, HE.TEMPERATE, HU.DAMP, SO.LOAMY],
[ResourceId.parse("sunflower_plains")]
), ),
scarcity = SC.UNCOMMON, b.scarcity = SC.UNCOMMON
)) b.save("")
spawns.add(PlantSpawn( b.start(plants["lemon"])
plant = plants["lemon"], b.altitude = AL.UPLANDS
altitude = AL.UPLANDS, b.biomeFilters = BF([FL.FIELD, HE.SUBTROPICAL, HU.DRY])
biomeFilters = BF([FL.FIELD, HE.SUBTROPICAL, HU.DRY]), b.scarcity = SC.SPARSE
scarcity = SC.SPARSE, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["melon"])
plant = plants["melon"], b.altitude = AL.LOWLANDS
altitude = AL.LOWLANDS, b.biomeFilters = BF([FL.CLEARING, HE.TROPICAL, HU.WET], [SO.FUNGAL])
biomeFilters = BF([FL.CLEARING, HE.TROPICAL, HU.WET], [SO.FUNGAL]), b.scarcity = SC.SPARSE
scarcity = SC.SPARSE, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["onion"])
plant = plants["onion"], b.altitude = AL.span(AL.UPLANDS, AL.HILLS)
altitude = AL.span(AL.UPLANDS, AL.HILLS), b.biomeFilters = BF([FL.FOREST, HE.TEMPERATE, HU.DAMP, SO.ACIDIC])
biomeFilters = BF([FL.FOREST, HE.TEMPERATE, HU.DAMP, SO.ACIDIC]), b.scarcity = SC.SPARSE
scarcity = SC.SPARSE, b.save("")
))
# NOTE: Due to a bug in Fruits Delight, pineapple can *only* be planted on sand: despite this # 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 # 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. # a more cactus-like cousin of actual pineapples.
spawns.add(PlantSpawn( b.start(plants["pineapple"])
plant = plants["pineapple"], b.altitude = AL.LOWLANDS
altitude = AL.LOWLANDS, b.biomeFilters = BF([FL.BARREN, HE.TROPICAL, HU.DRY, SO.SANDY])
biomeFilters = BF([FL.BARREN, HE.TROPICAL, HU.DRY, SO.SANDY]), b.scarcity = SC.SPARSE
scarcity = SC.SPARSE,
# TODO: add sand as substrate in addplants.py # TODO: add sand as substrate in addplants.py
)) b.save("")
spawns.add(PlantSpawn( b.start(plants["potato"])
plant = plants["potato"], b.altitude = AL.span(AL.UPLANDS, AL.HILLS)
altitude = AL.span(AL.UPLANDS, AL.HILLS), b.biomeFilters = BF([FL.FOREST, HE.BOREAL, HU.DAMP, SO.ACIDIC])
biomeFilters = BF([FL.FOREST, HE.BOREAL, HU.DAMP, SO.ACIDIC]), b.scarcity = SC.UNCOMMON
scarcity = SC.UNCOMMON, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["pumpkin"])
plant = plants["pumpkin"], b.altitude = AL.LOWLANDS
altitude = AL.LOWLANDS, b.biomeFilters = BF([FL.FIELD, HE.TEMPERATE, SO.LOAMY])
biomeFilters = BF([FL.FIELD, HE.TEMPERATE, SO.LOAMY]), b.scarcity = SC.UNUSUAL
scarcity = SC.UNUSUAL, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["redmushroom"])
plant = plants["redmushroom"], b.altitude = AL.span(AL.SOIL, AL.ALPINE)
altitude = AL.span(AL.SOIL, AL.ALPINE), b.biomeFilters = [
biomeFilters = [
BF([(FL.CANOPY, FL.FOREST), (HE.TEMPERATE, HE.BOREAL), (SO.LOAMY, SO.CLAYEY)]), BF([(FL.CANOPY, FL.FOREST), (HE.TEMPERATE, HE.BOREAL), (SO.LOAMY, SO.CLAYEY)]),
BF([HU.WET], [HE.FROZEN, WA.OCEAN, WA.RIVER]), BF([HU.WET], [HE.FROZEN, WA.OCEAN, WA.RIVER]),
BF([SO.FUNGAL]), BF([SO.FUNGAL]),
], ]
scarcity = SC.UNCOMMON, b.scarcity = SC.UNCOMMON
)) b.save("")
spawns.add(PlantSpawn( b.start(plants["rice"])
plant = plants["rice"], b.altitude = AL.SURFACE
altitude = AL.SURFACE, b.biomeFilters = BF([(HE.SUBTROPICAL, HE.TEMPERATE), (WA.RIVER, WA.SWAMP)])
biomeFilters = BF([(HE.SUBTROPICAL, HE.TEMPERATE), (WA.RIVER, WA.SWAMP)]), b.scarcity = SC.COMMON
scarcity = SC.COMMON, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["sugarcane"])
plant = plants["sugarcane"], b.altitude = AL.DUNES
altitude = AL.DUNES, b.biomeFilters = BF([(HE.TROPICAL, HE.TEMPERATE), (WA.RIVER, WA.SWAMP)])
biomeFilters = BF([(HE.TROPICAL, HE.TEMPERATE), (WA.RIVER, WA.SWAMP)]), b.scarcity = SC.COMMON
scarcity = SC.COMMON, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["sunflower"])
plant = plants["sunflower"], b.altitude = AL.span(AL.LOWLANDS, AL.UPLANDS)
altitude = AL.span(AL.LOWLANDS, AL.UPLANDS), b.biomeFilters = BF([ResourceId.parse("sunflower_plains")])
biomeFilters = BF([ResourceId.parse("sunflower_plains")]), b.scarcity = SC.CARPET
scarcity = SC.CARPET, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["tomato"])
plant = plants["tomato"], b.altitude = AL.span(AL.UPLANDS, AL.HILLS)
altitude = AL.span(AL.UPLANDS, AL.HILLS), b.biomeFilters = BF([FL.CLEARING, HE.TROPICAL, HU.WET], [SO.FUNGAL])
biomeFilters = BF([FL.CLEARING, HE.TROPICAL, HU.WET], [SO.FUNGAL]), b.scarcity = SC.UNCOMMON
scarcity = SC.UNCOMMON, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["wheat"])
plant = plants["wheat"], b.altitude = AL.span(AL.LOWLANDS, AL.UPLANDS)
altitude = AL.span(AL.LOWLANDS, AL.UPLANDS), b.biomeFilters = BF([FL.FIELD, HE.TEMPERATE, HU.DAMP, SO.LOAMY])
biomeFilters = BF([FL.FIELD, HE.TEMPERATE, HU.DAMP, SO.LOAMY]), b.scarcity = SC.COMMON
scarcity = SC.COMMON, b.save("")
))
# Flower Patches ########################################################### # Flower Patches ###########################################################
spawns.add(PlantSpawn( b.start(plants["allium"])
plant = plants["allium"], b.altitude = AL.span(AL.UPLANDS, AL.HILLS)
altitude = AL.span(AL.UPLANDS, AL.HILLS), b.biomeFilters = BF([FL.FOREST, HE.TEMPERATE, HU.DAMP, SO.ACIDIC])
biomeFilters = BF([FL.FOREST, HE.TEMPERATE, HU.DAMP, SO.ACIDIC]), b.scarcity = SC.UNCOMMON
scarcity = SC.UNCOMMON, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["azurebluet"])
plant = plants["azurebluet"], b.altitude = AL.span(AL.UPLANDS, AL.ALPINE)
altitude = AL.span(AL.UPLANDS, AL.ALPINE), b.biomeFilters = BF([FL.FIELD, (HE.TEMPERATE, HE.BOREAL), HU.DAMP, (SO.LOAMY, SO.PEATY)])
biomeFilters = BF([FL.FIELD, (HE.TEMPERATE, HE.BOREAL), HU.DAMP, (SO.LOAMY, SO.PEATY)]), b.scarcity = SC.SPARSE
scarcity = SC.SPARSE, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["blueorchid"])
plant = plants["blueorchid"], b.altitude = AL.span(AL.LOWLANDS, AL.UPLANDS)
altitude = AL.span(AL.LOWLANDS, AL.UPLANDS), b.biomeFilters = BF([FL.CANOPY, HE.TROPICAL, HU.WET])
biomeFilters = BF([FL.CANOPY, HE.TROPICAL, HU.WET]), b.scarcity = SC.RARE
scarcity = SC.RARE, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["cornflower"])
plant = plants["cornflower"], b.altitude = AL.span(AL.UPLANDS, AL.ALPINE)
altitude = AL.span(AL.UPLANDS, AL.ALPINE), b.biomeFilters = BF([FL.FIELD, HE.TEMPERATE, (SO.LOAMY, SO.PEATY)])
biomeFilters = BF([FL.FIELD, HE.TEMPERATE, (SO.LOAMY, SO.PEATY)]), b.scarcity = SC.COMMON
scarcity = SC.COMMON, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["daisy"])
plant = plants["daisy"], b.altitude = AL.span(AL.UPLANDS, AL.ALPINE)
altitude = AL.span(AL.UPLANDS, AL.ALPINE), b.biomeFilters = BF([FL.FIELD, (HE.TEMPERATE, HE.BOREAL)])
biomeFilters = BF([FL.FIELD, (HE.TEMPERATE, HE.BOREAL)]), b.scarcity = SC.UNCOMMON
scarcity = SC.UNCOMMON, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["dandelion"])
plant = plants["dandelion"], b.altitude = AL.span(AL.DUNES, AL.HILLS)
altitude = AL.span(AL.DUNES, AL.HILLS), b.biomeFilters = BF(
biomeFilters = BF(
[(FL.CLEARING, FL.FIELD)], [(FL.CLEARING, FL.FIELD)],
[HE.FROZEN, HU.WET, SO.FUNGAL, SO.ROCKY, ResourceId.parse("flower_forest")]), [HE.FROZEN, HU.WET, SO.FUNGAL, SO.ROCKY, ResourceId.parse("flower_forest")]
scarcity = SC.COMMON, )
)) b.scarcity = SC.COMMON
b.save("")
spawns.add(PlantSpawn( b.start(plants["lilac"])
plant = plants["lilac"], b.altitude = AL.LOWLANDS
altitude = AL.LOWLANDS, b.biomeFilters = BF([FL.FOREST, HE.TEMPERATE, HU.DAMP, SO.LOAMY])
biomeFilters = BF([FL.FOREST, HE.TEMPERATE, HU.DAMP, SO.LOAMY]), b.scarcity = SC.UNUSUAL
scarcity = SC.UNUSUAL, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["lily"])
plant = plants["lily"], b.altitude = AL.span(AL.LOWLANDS, AL.UPLANDS)
altitude = AL.span(AL.LOWLANDS, AL.UPLANDS), b.biomeFilters = BF([FL.FOREST, (HE.TEMPERATE, HE.BOREAL), HU.DAMP, SO.ACIDIC])
biomeFilters = BF([FL.FOREST, (HE.TEMPERATE, HE.BOREAL), HU.DAMP, SO.ACIDIC]), b.scarcity = SC.RARE
scarcity = SC.RARE, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["peony"])
plant = plants["peony"], b.altitude = AL.LOWLANDS
altitude = AL.LOWLANDS, b.biomeFilters = BF([FL.CLEARING, HE.TEMPERATE, HU.DAMP, SO.ROCKY])
biomeFilters = BF([FL.CLEARING, HE.TEMPERATE, HU.DAMP, SO.ROCKY]), b.scarcity = SC.SPARSE
scarcity = SC.SPARSE, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["poppy"])
plant = plants["poppy"], b.altitude = AL.span(AL.DUNES, AL.UPLANDS)
altitude = AL.span(AL.DUNES, AL.UPLANDS), b.biomeFilters = BF(
biomeFilters = BF(
[FL.FIELD, (HE.SUBTROPICAL, HE.TEMPERATE), (HU.DRY, HU.DAMP)], [FL.FIELD, (HE.SUBTROPICAL, HE.TEMPERATE), (HU.DRY, HU.DAMP)],
[SO.FUNGAL] [SO.FUNGAL]
), ),
scarcity = SC.SPARSE, b.scarcity = SC.SPARSE
)) b.save("")
spawns.add(PlantSpawn( b.start(plants["rose_black"])
plant = plants["rose_black"], b.altitude = AL.HILLS
altitude = AL.HILLS, b.biomeFilters = BF([FL.CANOPY, HE.TEMPERATE])
biomeFilters = BF([FL.CANOPY, HE.TEMPERATE]), b.scarcity = SC.RARE
scarcity = SC.RARE, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["rose_bush"])
plant = plants["rose_bush"], b.altitude = AL.span(AL.UPLANDS, AL.ALPINE)
altitude = AL.span(AL.UPLANDS, AL.ALPINE), b.biomeFilters = BF([FL.FOREST, (HE.TEMPERATE, HE.BOREAL), SO.LOAMY])
biomeFilters = BF([FL.FOREST, (HE.TEMPERATE, HE.BOREAL), SO.LOAMY]), b.scarcity = SC.UNUSUAL
scarcity = SC.UNUSUAL, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["tulip_orange"])
plant = plants["tulip_orange"], b.altitude = AL.span(AL.DUNES, AL.LOWLANDS)
altitude = AL.span(AL.DUNES, AL.LOWLANDS), b.biomeFilters = BF([ResourceId.parse("flower_forest")])
biomeFilters = BF([ResourceId.parse("flower_forest")]), b.scarcity = SC.UNUSUAL
scarcity = SC.UNUSUAL, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["tulip_pink"])
plant = plants["tulip_pink"], b.altitude = AL.span(AL.DUNES, AL.LOWLANDS)
altitude = AL.span(AL.DUNES, AL.LOWLANDS), b.biomeFilters = BF([ResourceId.parse("flower_forest")])
biomeFilters = BF([ResourceId.parse("flower_forest")]), b.scarcity = SC.SPARSE
scarcity = SC.SPARSE, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["tulip_red"])
plant = plants["tulip_red"], b.altitude = AL.span(AL.DUNES, AL.LOWLANDS)
altitude = AL.span(AL.DUNES, AL.LOWLANDS), b.biomeFilters = BF([ResourceId.parse("flower_forest")])
biomeFilters = BF([ResourceId.parse("flower_forest")]), b.scarcity = SC.COMMON
scarcity = SC.COMMON, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["tulip_white"])
plant = plants["tulip_white"], b.altitude = AL.span(AL.DUNES, AL.LOWLANDS)
altitude = AL.span(AL.DUNES, AL.LOWLANDS), b.biomeFilters = BF([ResourceId.parse("flower_forest")])
biomeFilters = BF([ResourceId.parse("flower_forest")]), b.scarcity = SC.UNCOMMON
scarcity = SC.UNCOMMON, b.save("")
))
# Trees #################################################################### # Trees ####################################################################
spawns.add(PlantSpawn( b.start(plants["apple"])
plant = plants["apple"], b.altitude = AL.span(AL.LOWLANDS, AL.UPLANDS)
altitude = AL.span(AL.LOWLANDS, AL.UPLANDS), b.biomeFilters = BF(
biomeFilters = BF(
[(FL.FOREST, FL.CLEARING), HE.TEMPERATE, SO.LOAMY], [(FL.FOREST, FL.CLEARING), HE.TEMPERATE, SO.LOAMY],
[ResourceId.parse("flower_forest")], [ResourceId.parse("flower_forest")],
), )
scarcity = SC.SPARSE, b.scarcity = SC.SPARSE
)) b.save("")
spawns.add(PlantSpawn( b.start(plants["bayberry"])
plant = plants["bayberry"], b.altitude = AL.span(AL.DUNES, AL.UPLANDS)
altitude = AL.span(AL.DUNES, AL.UPLANDS), b.biomeFilters = BF([HE.TEMPERATE, (SO.ROCKY, SO.SANDY), WA.COAST])
biomeFilters = BF([HE.TEMPERATE, (SO.ROCKY, SO.SANDY), WA.COAST]), b.scarcity = SC.SPARSE
scarcity = SC.SPARSE, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["durian"])
plant = plants["durian"], b.altitude = AL.HILLS
altitude = AL.HILLS, b.biomeFilters = BF([FL.within(FL.CANOPY, FL.CLEARING), HE.TROPICAL, HU.WET, SO.ACIDIC])
biomeFilters = BF([FL.within(FL.CANOPY, FL.CLEARING), HE.TROPICAL, HU.WET, SO.ACIDIC]), b.scarcity = SC.UNUSUAL
scarcity = SC.UNUSUAL, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["fig"])
plant = plants["fig"], b.altitude = AL.span(AL.UPLANDS, AL.HILLS)
altitude = AL.span(AL.UPLANDS, AL.HILLS), b.biomeFilters = BF([HE.SUBTROPICAL, HU.DRY, SO.SANDY], [FL.BARREN])
biomeFilters = BF([HE.SUBTROPICAL, HU.DRY, SO.SANDY], [FL.BARREN]), b.scarcity = SC.UNCOMMON
scarcity = SC.UNCOMMON, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["hawberry"])
plant = plants["hawberry"], b.altitude = AL.span(AL.UPLANDS, AL.HILLS)
altitude = AL.span(AL.UPLANDS, AL.HILLS), b.biomeFilters = BF([(FL.FOREST, FL.CLEARING), HE.TEMPERATE, (SO.ACIDIC, SO.LOAMY)])
biomeFilters = BF([(FL.FOREST, FL.CLEARING), HE.TEMPERATE, (SO.ACIDIC, SO.LOAMY)]), b.scarcity = SC.SPARSE
scarcity = SC.SPARSE, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["lychee"])
plant = plants["lychee"], b.altitude = AL.UPLANDS
altitude = AL.UPLANDS, b.biomeFilters = BF([FL.CLEARING, HE.TROPICAL, HU.WET], [SO.FUNGAL])
biomeFilters = BF([FL.CLEARING, HE.TROPICAL, HU.WET], [SO.FUNGAL]), b.scarcity = SC.SPARSE
scarcity = SC.SPARSE, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["mango"])
plant = plants["mango"], b.altitude = AL.LOWLANDS
altitude = AL.LOWLANDS, b.biomeFilters = BF(
biomeFilters = BF(
[FL.within(FL.CANOPY, FL.CLEARING), HE.TROPICAL, (HU.DAMP, HU.WET), SO.ACIDIC] [FL.within(FL.CANOPY, FL.CLEARING), HE.TROPICAL, (HU.DAMP, HU.WET), SO.ACIDIC]
), )
scarcity = SC.UNCOMMON, b.scarcity = SC.UNCOMMON
)) b.save("")
spawns.add(PlantSpawn( b.start(plants["mangosteen"])
plant = plants["mangosteen"], b.altitude = AL.UPLANDS
altitude = AL.UPLANDS, b.biomeFilters = BF([FL.CANOPY, HE.TROPICAL, HU.WET, SO.ACIDIC])
biomeFilters = BF([FL.CANOPY, HE.TROPICAL, HU.WET, SO.ACIDIC]), b.scarcity = SC.UNUSUAL
scarcity = SC.UNUSUAL, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["orange"])
plant = plants["orange"], b.altitude = AL.LOWLANDS
altitude = AL.LOWLANDS, b.biomeFilters = BF([FL.FIELD, HE.SUBTROPICAL, HU.DRY])
biomeFilters = BF([FL.FIELD, HE.SUBTROPICAL, HU.DRY]), b.scarcity = SC.SPARSE
scarcity = SC.SPARSE, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["peach"])
plant = plants["peach"], b.altitude = AL.span(AL.LOWLANDS, AL.UPLANDS)
altitude = AL.span(AL.LOWLANDS, AL.UPLANDS), b.biomeFilters = BF([FL.CLEARING, HE.TEMPERATE, HU.DAMP, SO.ROCKY])
biomeFilters = BF([FL.CLEARING, HE.TEMPERATE, HU.DAMP, SO.ROCKY]), b.scarcity = SC.UNCOMMON
scarcity = SC.UNCOMMON, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["pear"])
plant = plants["pear"], b.altitude = AL.span(AL.LOWLANDS, AL.UPLANDS)
altitude = AL.span(AL.LOWLANDS, AL.UPLANDS), b.biomeFilters = BF([FL.CLEARING, HE.TEMPERATE, HU.DAMP, (SO.ACIDIC, SO.LOAMY)])
biomeFilters = BF([FL.CLEARING, HE.TEMPERATE, HU.DAMP, (SO.ACIDIC, SO.LOAMY)]), b.scarcity = SC.UNCOMMON
scarcity = SC.UNCOMMON, b.save("")
))
spawns.add(PlantSpawn( b.start(plants["persimmon"])
plant = plants["persimmon"], b.altitude = AL.span(AL.LOWLANDS, AL.UPLANDS)
altitude = AL.span(AL.LOWLANDS, AL.UPLANDS), b.biomeFilters = BF([FL.FOREST, HE.TEMPERATE, SO.LOAMY])
biomeFilters = BF([FL.FOREST, HE.TEMPERATE, SO.LOAMY]), b.scarcity = SC.UNCOMMON
scarcity = SC.UNCOMMON, b.save("")
))
+13 -14
View File
@@ -2,6 +2,7 @@ from mcpacker.model.ecology.biome import Biome
from mcpacker.model.ecology.biomefilter import BiomeFilter as BF from mcpacker.model.ecology.biomefilter import BiomeFilter as BF
from mcpacker.model.fauna.mob import Mob from mcpacker.model.fauna.mob import Mob
from mcpacker.model.fauna.mobspawn import MobSpawn 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.mineral import Mineral
from mcpacker.model.geology.replacement import Replacement from mcpacker.model.geology.replacement import Replacement
from mcpacker.model.habitat import Habitat from mcpacker.model.habitat import Habitat
@@ -51,21 +52,19 @@ def defineAddMobSpawnss():
def addMobSpawns(pack:ModPack): def addMobSpawns(pack:ModPack):
mobs = pack.world.mobs mobs = pack.world.mobs
spawns = pack.world.mobSpawns spawns = pack.world.mobSpawns
b = MobSpawnBuilder(pack.world.mobSpawns)
pack.world.mobSpawns.add( b.start(mobs["minecraft:chicken"])
MobSpawn(mobs["minecraft:chicken"], b.altitude = AL.span(AL.LOWLANDS, AL.UPLANDS)
Habitat( b.biomeFilters = BF([HE.TROPICAL, HU.WET, FL.within(FL.CANOPY, FL.CLEARING)])
altitude = AL.span(AL.LOWLANDS, AL.UPLANDS), b.seasons = SE.SUMMER
biomeFilter = BF([HE.TROPICAL, HU.WET, FL.within(FL.CANOPY, FL.CLEARING)]), b.group = GR.TROUP
seasons = SE.SUMMER, b.scarcity = SC.COMMON
group = GR.TROUP, b.save("chicken-summer")
scarcity = SC.COMMON,
).derive( b.seasons = SE.exclude(SE.SUMMER)
seasons = SE.exclude(SE.SUMMER), b.scarcity = SC.UNCOMMON
scarcity = SC.UNCOMMON b.save("chicken-normal")
)
)
)
yield addMobSpawns yield addMobSpawns
+23 -29
View File
@@ -1,21 +1,17 @@
from collections.abc import Iterable
from mcpacker.json import JsonBlob from mcpacker.json import JsonBlob
from mcpacker.model.ecology.biomefilter import BiomeFilter from mcpacker.model.ecology.biomefilter import BiomeFilter
from mcpacker.model.fauna.active import Active from mcpacker.model.fauna.active import Active
from mcpacker.model.fauna.mobspawn import MobSpawn from mcpacker.model.fauna.mobspawn import MobSpawn
from mcpacker.model.habitat import Habitat
from mcpacker.model.scarcity import Scarcity from mcpacker.model.scarcity import Scarcity
from mcpacker.model.season import Season 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 mcpacker.write.writer import Writer
from pathlib import Path
import mcpacker.json as json import mcpacker.json as json
import mcpacker.model.dimension as DI import mcpacker.model.dimension as DI
import mcpacker.model.fauna.location as LO import mcpacker.model.fauna.location as LO
import mcpacker.model.scarcity as SC import mcpacker.model.scarcity as SC
import mcpacker.model.time as TI import mcpacker.model.time as TI
import os
# Class ############################################################################################ # Class ############################################################################################
@@ -27,13 +23,12 @@ class SpawnerWriter(Writer):
self.resetOutputFile(path) self.resetOutputFile(path)
path.write_text(json.dumps(self._makeAllRules(), indent=json.INDENT)) path.write_text(json.dumps(self._makeAllRules(), indent=json.INDENT))
# Private Functions ######################################################## # Private Functions ########################################################
def _computeBiomeNames(self, biomeFilter:BiomeFilter) -> JsonBlob: def _computeBiomeNames(self, biomeFilters:Iterable[BiomeFilter]) -> JsonBlob:
return list( return list(
str(b.gameId) for b in self.pack.world.biomes.filter( str(b.gameId) for b in self.pack.world.biomes.find(biomeFilters)
lambda b: biomeFilter.accepts(b)
)
) )
def _computeSpawnRate(self, scarcity:Scarcity) -> float: def _computeSpawnRate(self, scarcity:Scarcity) -> float:
@@ -60,12 +55,12 @@ class SpawnerWriter(Writer):
SC.CARPET: 32, SC.CARPET: 32,
}[scarcity] }[scarcity]
def _makeAndCondition(self, habitat:Habitat, season:Season, active:Active) -> JsonBlob: def _makeAndCondition(self, spawn:MobSpawn, season:Season, active:Active) -> JsonBlob:
return { return {
"biome": self._computeBiomeNames(habitat.biomeFilter), "biome": self._computeBiomeNames(spawn.habitat.biomeFilters),
season.name: True, season.name: True,
"seesky": True if LO.OUTSIDE == habitat.location else None, "seesky": True if LO.OUTSIDE == spawn.ecotype.location else None,
"cave": True if LO.CAVE == habitat.location else None, "cave": True if LO.CAVE == spawn.ecotype.location else None,
"mintime": active.start, "mintime": active.start,
"maxtime": active.end, "maxtime": active.end,
} }
@@ -74,33 +69,32 @@ class SpawnerWriter(Writer):
result:list[JsonBlob] = [] result:list[JsonBlob] = []
for spawn in self.pack.world.mobSpawns: for spawn in self.pack.world.mobSpawns:
for habitat in spawn.habitats: for season in spawn.habitat.seasons:
for season in habitat.seasons:
for active in spawn.mob.active: for active in spawn.mob.active:
result.append(self._makeRule(spawn, habitat, season, active)) result.append(self._makeRule(spawn, season, active))
return json.removeEmptyObjects(json.removeNoneValues(result)) 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 { return {
"dimension": DI.OVERWORLD.name, "dimension": DI.OVERWORLD.name,
"minheight": habitat.altitude.bottom, "minheight": spawn.habitat.altitude.bottom,
"maxheight": habitat.altitude.top, "maxheight": spawn.habitat.altitude.top,
"maxthis": habitat.group.largest * 2, "maxthis": spawn.ecotype.group.largest * 2,
"inwater": LO.WATER == habitat.location, "inwater": LO.WATER == spawn.ecotype.location,
"and": self._makeAndCondition(habitat, season, active), "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 { return {
"mob": str(spawn.mob.gameId), "mob": str(spawn.mob.gameId),
"amount": { "amount": {
"minimum": habitat.group.smallest, "minimum": spawn.ecotype.group.smallest,
"maximum": habitat.group.largest, "maximum": spawn.ecotype.group.largest,
"groupdistance": 4, "groupdistance": 4,
}, },
"persecond": self._computeSpawnRate(habitat.scarcity), "persecond": self._computeSpawnRate(spawn.ecotype.scarcity),
"attempts": habitat.group.largest * 4, "attempts": spawn.ecotype.group.largest * 4,
"weights": [ self._computeWeight(habitat.scarcity) ], "weights": [ self._computeWeight(spawn.ecotype.scarcity) ],
"conditions": self._makeCondition(habitat, season, active), "conditions": self._makeCondition(spawn, season, active),
} }
+18 -21
View File
@@ -2,6 +2,7 @@ from mcpacker.model.ecology.biome import Biome
from mcpacker.model.ecology.biomefilter import BiomeFilter as BF from mcpacker.model.ecology.biomefilter import BiomeFilter as BF
from mcpacker.model.fauna.mob import Mob from mcpacker.model.fauna.mob import Mob
from mcpacker.model.fauna.mobspawn import MobSpawn 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.mineral import Mineral
from mcpacker.model.geology.replacement import Replacement from mcpacker.model.geology.replacement import Replacement
from mcpacker.model.habitat import Habitat from mcpacker.model.habitat import Habitat
@@ -49,21 +50,19 @@ def defineAddMobSpawnss():
def addMobSpawns(pack:ModPack): def addMobSpawns(pack:ModPack):
mobs = pack.world.mobs mobs = pack.world.mobs
spawns = pack.world.mobSpawns spawns = pack.world.mobSpawns
b = MobSpawnBuilder(pack.world.mobSpawns)
pack.world.mobSpawns.add( b.start(mobs["minecraft:chicken"])
MobSpawn(mobs["minecraft:chicken"], b.altitude = AL.span(AL.LOWLANDS, AL.UPLANDS)
Habitat( b.biomeFilters = BF([HE.TROPICAL, HU.WET, FL.within(FL.CANOPY, FL.CLEARING)])
altitude = AL.span(AL.LOWLANDS, AL.UPLANDS), b.seasons = SE.SUMMER
biomeFilter = BF([HE.TROPICAL, HU.WET, FL.within(FL.CANOPY, FL.CLEARING)]), b.group = GR.TROUP
seasons = SE.SUMMER, b.scarcity = SC.COMMON
group = GR.TROUP, b.save("chicken-summer")
scarcity = SC.COMMON,
).derive( b.seasons = SE.exclude(SE.SUMMER)
seasons = SE.exclude(SE.SUMMER), b.scarcity = SC.UNCOMMON
scarcity = SC.UNCOMMON b.save("chicken-normal")
)
)
)
yield addMobSpawns yield addMobSpawns
@@ -92,14 +91,12 @@ def test_write(writer:SpawnerWriter, tmp_path:Path):
assert len(data) == 4 assert len(data) == 4
rule = data[0] 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["mob"] == "minecraft:chicken"
assert rule["amount"]["maximum"] == 6 assert rule["amount"]["maximum"] == 6
assert abs(rule["persecond"] - 0.2) < 0.001
assert rule["conditions"]["and"]["biome"] == ["minecraft:jungle"] assert rule["conditions"]["and"]["biome"] == ["minecraft:jungle"]
assert rule["conditions"]["and"]["summer"] assert rule["conditions"]["and"]["summer"]
assert abs(rule["persecond"] - 0.2) < 0.001
rule = data[1]
assert abs(rule["persecond"] - 0.066) < 0.001
assert rule["conditions"]["and"]["spring"]
+7 -4
View File
@@ -1,14 +1,17 @@
attrs==25.4.0
cattrs==25.3.0
docstring-to-markdown==0.17
exceptiongroup==1.3.0 exceptiongroup==1.3.0
importlib_metadata==8.7.0
iniconfig==2.1.0 iniconfig==2.1.0
jedi==0.19.2
mypy==1.18.2 mypy==1.18.2
mypy_extensions==1.1.0 mypy_extensions==1.1.0
nodeenv==1.9.1
packaging==25.0 packaging==25.0
parso==0.8.4
pathspec==0.12.1 pathspec==0.12.1
pluggy==1.6.0 pluggy==1.6.0
Pygments==2.19.2 Pygments==2.19.2
pyright==1.1.406
pytest==8.4.2 pytest==8.4.2
toml==0.10.2
tomli==2.2.1
typing_extensions==4.15.0 typing_extensions==4.15.0
zipp==3.23.0