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):
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("<no fauna>")
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:
+15 -13
View File
@@ -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()
+25 -25
View File
@@ -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()
+5 -5
View File
@@ -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
+4 -11
View File
@@ -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)
+12 -1
View File
@@ -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
+3 -3
View File
@@ -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 (
+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 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})"
+6 -1
View File
@@ -36,4 +36,9 @@ def test_str(squid):
assert str(squid) == "minecraft:squid"
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.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
+32 -29
View File
@@ -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<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 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})"
+11 -43
View File
@@ -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
+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]):
pass
+26 -67
View File
@@ -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
+8 -28
View File
@@ -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')]"
")"
)
+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 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}"
")"
)
+33 -24
View File
@@ -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')"
")"
)
+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
+302 -360
View File
@@ -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("")
+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.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
+28 -34
View File
@@ -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),
}
+30 -33
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.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
+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
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