Update flora/fauna spawns to use a common system
This commit is contained in:
@@ -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:
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
|||||||
@@ -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})"
|
||||||
@@ -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)
|
|
||||||
|
|||||||
@@ -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}]"
|
||||||
|
")"
|
||||||
|
)
|
||||||
|
|||||||
@@ -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}"
|
||||||
|
")"
|
||||||
|
)
|
||||||
@@ -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
|
|
||||||
|
|||||||
@@ -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}]"
|
||||||
|
")"
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
|
||||||
|
|||||||
@@ -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
|
|
||||||
|
|||||||
@@ -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
@@ -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
|
|
||||||
|
|||||||
@@ -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')" +
|
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
class Population:
|
||||||
|
"""
|
||||||
|
Describes traits of a
|
||||||
|
"""
|
||||||
+17
-9
@@ -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}"
|
||||||
|
")"
|
||||||
|
)
|
||||||
|
|||||||
@@ -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')" +
|
|
||||||
")" +
|
|
||||||
"])"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
@@ -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
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user