Add mypy; Consolidate catalog classes

This commit is contained in:
Andrew Miner
2025-10-08 21:55:10 -06:00
parent b96a456b03
commit 52a3fdc328
74 changed files with 1169 additions and 784 deletions
+22
View File
@@ -0,0 +1,22 @@
import subprocess
####################################################################################################
def test_typeCompliance():
command = [
"mypy",
#"--allow-untyped-defs",
#"--follow-imports", "skip",
#"--ignore-missing-imports",
#"--no-check-untyped-defs",
"--python-version", "3.10",
"--show-error-codes",
"mcpacker"
]
result = subprocess.run(command, capture_output=True, text=True)
print(result.stdout)
returncode = result.returncode
assert returncode == 0
+2 -1
View File
@@ -5,8 +5,9 @@ from os.path import join
class Locator:
def __init__(self, basePath:str):
def __init__(self, basePath:str, mod:str):
self.basePath = basePath
self.mod = mod
def biomeModifier(self) -> str:
return join(self.basePath, "data", self.mod, "neoforge", "biome_modifier")
View File
@@ -0,0 +1,4 @@
class SpawnerRuleEmitter:
def __init__(self):
pass
+1 -1
View File
@@ -10,7 +10,7 @@ class BiomeReport(Report):
super().__init__("biomes.md", pack)
def build(self):
for biome in self.pack.world.biomes.all():
for biome in self.pack.world.biomes:
self.line(f"# Biome: {biome.gameId} ({biome.city})")
self.line()
+1 -1
View File
@@ -10,7 +10,7 @@ class MineralReport(Report):
super().__init__("minerals.md", pack)
def build(self):
for mineral in self.pack.world.minerals.all():
for mineral in self.pack.world.minerals:
self.line(f"# Mineral: {mineral.name}")
self.line()
+2 -2
View File
@@ -11,8 +11,8 @@ class MobReport(Report):
super().__init__("mobs.md", pack)
def build(self):
for placement in self.pack.world.mobs.all():
self.line(f"# Mob: {placement.mob.name} <{placement.mob.gameId}>")
for placement in self.pack.world.mobs:
self.line(f"# Mob: {placement.gameId}")
self.line()
self.indent()
+2 -2
View File
@@ -32,8 +32,8 @@ class Report:
raise NotImplementedError()
def reset(self) -> "Report":
self._lineBuffer = []
self._lines = []
self._lineBuffer:list[str] = []
self._lines:list[str] = []
self._indent = 0
return self
+24
View File
@@ -0,0 +1,24 @@
from json import dump
from json import dumps
from json import load
from json import loads
from typing import TypeAlias
# Type Support #####################################################################################
JsonBlob:TypeAlias = dict[str, "JsonBlob"] | list["JsonBlob"] | str | int | float | bool | None
# Functions ########################################################################################
def removeNoneValues(blob:JsonBlob) -> JsonBlob:
if not isinstance(blob, dict): return blob
for key in list(blob.keys()):
if blob[key] == None:
del blob[key]
else:
removeNoneValues(blob[key])
return blob
+74
View File
@@ -0,0 +1,74 @@
from collections.abc import Iterable
from typing import Any
from typing import Callable
from typing import Generic
from typing import Iterator
from typing import Protocol
from typing import TypeVar
# Type Support #####################################################################################
class HasGameId(Protocol):
@property
def gameId(self) -> str: ...
CatalogItem = TypeVar("CatalogItem", bound=HasGameId)
MapTarget = TypeVar("MapTarget")
# Class ############################################################################################
class Catalog(Generic[CatalogItem]):
def __init__(self, items:Iterable[CatalogItem]|None=None):
self._items:dict[str,CatalogItem] = {}
for item in (items or []):
self.add(item)
def __contains__(self, gameId:str) -> bool:
return gameId in self._items
def __delitem__(self, gameId:str):
del self._items[gameId]
def __eq__(self, other:Any) -> bool:
if type(self) != type(other): return False
if self._items != other._items: return False
return True
def __getitem__(self, gameId:str) -> CatalogItem:
return self._items[gameId]
def __iter__(self) -> Iterator[CatalogItem]:
for gameId in sorted([i.gameId for i in self._items.values()]):
yield self._items[gameId]
def __len__(self) -> int:
return len(self._items)
def __repr__(self) -> str:
return "".join(str(p) for p in [
self.__class__.__name__, "[",
", ".join(str(i) for i in self),
"]"
])
def add(self, item:CatalogItem) -> "Catalog[CatalogItem]":
if item.gameId in self._items:
raise Exception(f"Catalog already contains {item.gameId}")
self._items[item.gameId] = item
return self
def filter(self, isAllowed:Callable[[CatalogItem], bool]) -> Iterator[CatalogItem]:
for item in self:
if isAllowed(item):
yield item
def map(self, doTransform:Callable[[CatalogItem], MapTarget]) -> Iterator[MapTarget]:
for item in self:
yield doTransform(item)
+88
View File
@@ -0,0 +1,88 @@
from mcpacker.model.catalog import Catalog
from pytest import fixture
from typing import Any
# Helpers ##########################################################################################
class SampleItem:
def __init__(self, gameId:str):
self.gameId = gameId
def __eq__(self, other:Any):
return self.gameId == other.gameId
def __repr__(self) -> str:
return self.gameId
# Fixtures #########################################################################################
@fixture(name="empty")
def buildEmptyCatalog():
yield Catalog()
@fixture(name="full")
def buildFullCatalog():
yield Catalog([
SampleItem("alpha"),
SampleItem("bravo"),
SampleItem("charlie"),
SampleItem("delta"),
])
@fixture(name="almostFull")
def buildAlmostFullCatalog(full):
del full["bravo"]
yield full
@fixture(name="almostEmpty")
def buildAlmostEmptyCatalog(empty):
empty.add(SampleItem("echo"))
yield empty
@fixture(name="filtered")
def buildFilteredList(full):
yield list(full.filter(lambda x: x.gameId.endswith("a")))
@fixture(name="mapped")
def buildMappedList(full):
yield list(full.map(lambda x: x.gameId))
# Tests ############################################################################################
def test_add(almostEmpty):
assert "echo" in almostEmpty
def test_contains(empty, full):
assert "bravo" not in empty
assert "bravo" in full
def test_del(almostFull):
assert "bravo" not in almostFull
def test_eq():
assert Catalog([SampleItem("alpha")]) == Catalog([SampleItem("alpha")])
assert Catalog([SampleItem("alpha")]) != Catalog([SampleItem("bravo")])
def test_filter(filtered):
assert [a.gameId for a in filtered] == ["alpha", "delta"]
def test_getitem(full):
assert full["bravo"].gameId == "bravo"
def test_iter(full):
assert [s.gameId for s in full] == ["alpha", "bravo", "charlie", "delta"]
def test_len(empty, full):
assert len(empty) == 0
assert len(full) == 4
def test_map(mapped):
assert mapped == ["alpha", "bravo", "charlie", "delta"]
def test_repr(empty, full):
assert repr(empty) == "Catalog[]"
assert repr(full) == "Catalog[alpha, bravo, charlie, delta]"
+21
View File
@@ -0,0 +1,21 @@
# Class ############################################################################################
class Dimension:
def __init__(self, name):
self.name = name
def __str__(self) -> str:
return self.name
def __repr__(self) -> str:
return f"Dimension<{self.name}>"
# Constants ########################################################################################
OVERWORLD = Dimension("minecraft:overworld")
NETHER = Dimension("minecraft:nether")
END = Dimension("minecraft:end")
ALL = [OVERWORLD, NETHER, END]
@@ -1,4 +1,4 @@
import mcpacker.model.datapack.mod
import mcpacker.model.core.dimension
# Tests ############################################################################################
+2 -2
View File
@@ -37,12 +37,12 @@ class Biome(object):
def __eq__(self, other) -> bool:
if type(self) != type(other): return False
if self.biomeId != other.biomeId: return False
if self.gameId != other.gameId: return False
if self.city != other.city: return False
return True
def __hash__(self) -> int:
return hash((self.biomeId, self.city))
return hash((self.gameId, self.city))
def __str__(self) -> str:
return f"{self.city}<{self.gameId}>"
+5 -23
View File
@@ -1,32 +1,14 @@
from collections.abc import Iterable
from mcpacker.model.catalog import Catalog
from mcpacker.model.core.ecology.biome import Biome
from mcpacker.model.core.ecology.biomefilter import BiomeFilter
from typing import Iterator
# Classes ##########################################################################################
class BiomeCatalog:
"""
A comprehensive list of all available biomes in the modpack.
"""
class BiomeCatalog(Catalog[Biome]):
def __init__(self, biomes:Iterable[Biome]|None=None):
self._biomes = {}
for biome in (biomes or []):
self.add(biome)
def add(self, biome:Biome) -> "BiomeCatalog":
if biome.gameId in self._biomes:
raise Exception(f"Catalog already contains {biome.gameId}")
self._biomes[biome.gameId] = biome
def all(self) -> Iterable[Biome]:
for gameId in sorted([b.gameId for b in self._biomes.values()]):
yield self._biomes[gameId]
def matching(self, biomeFilter:BiomeFilter) -> Iterable[Biome]:
for biome in self.all():
def matching(self, biomeFilter:BiomeFilter) -> Iterator[Biome]:
for biome in self:
if biomeFilter.accepts(biome):
yield biome
+3 -2
View File
@@ -1,3 +1,4 @@
from collections.abc import Iterable
from mcpacker.model.core.ecology.biome import Biome
from mcpacker.model.core.ecology.biometrait import BiomeTrait
@@ -25,8 +26,8 @@ class BiomeFilter:
def __init__(
self,
required:list[BiomeTrait|list[BiomeTrait]]=None,
prohibited:list[BiomeTrait]=None,
required:Iterable[BiomeTrait|Iterable[BiomeTrait]]|None=None,
prohibited:Iterable[BiomeTrait]|None=None,
):
self.required = required or []
self.prohibited = prohibited or []
+13 -1
View File
@@ -1,3 +1,6 @@
from typing import TypeVar
# Class ############################################################################################
class BiomeTrait:
@@ -23,9 +26,18 @@ class BiomeTrait:
return f"{type(self).__name__}<{self.name}>"
# Type Support #####################################################################################
BiomeTraitSubclass = TypeVar("BiomeTraitSubclass", bound=BiomeTrait)
# Helper Functions #################################################################################
def within(allTraits:list[BiomeTrait], start:BiomeTrait, end:BiomeTrait) -> list[BiomeTrait]:
def within(
allTraits:list[BiomeTraitSubclass],
start:BiomeTraitSubclass,
end:BiomeTraitSubclass
) -> list[BiomeTraitSubclass]:
if not allTraits: return []
start = start if start else allTraits[0]
end = end if end else allTraits[-1]
+2 -1
View File
@@ -1,3 +1,4 @@
from collections.abc import Iterable
from mcpacker.model.core.ecology.biometrait import BiomeTrait
import mcpacker.model.core.ecology.biometrait as biomeTrait
@@ -23,5 +24,5 @@ ALL = [WET, DAMP, DRY]
# Helpers ##########################################################################################
def within(start:Humidity, end:Humidity) -> list[Humidity]:
def within(start:Humidity, end:Humidity) -> Iterable[Humidity]:
return biomeTrait.within(ALL, start, end)
+6 -1
View File
@@ -24,7 +24,12 @@ NIGHT = Active("night", 13000, 23000)
SUNRISE = Active("sunrise", 23000, 24000)
SUNSET = Active("sunset", 12000, 13000)
ANY = (SUNRISE, DAY, SUNSET, NIGHT)
ANY = Active("any", 0, 24000)
RAINY = Active("rainy", 0, 24000)
STORMY = Active("stormy", 0, 24000)
MIDNIGHT = Active("midnight", 18000, 19000)
ALL = (SUNRISE, DAY, SUNSET, NIGHT)
DIURNAL = (DAY)
NOCTURNAL = (NIGHT)
CREPUSCULAR = (SUNRISE, SUNSET)
+4 -4
View File
@@ -1,3 +1,4 @@
from collections.abc import Iterable
from mcpacker.model.core.fauna.active import Active
import mcpacker.model.core.fauna.active as AC
@@ -10,8 +11,7 @@ class Mob:
A creature which may appear in the game world.
"""
def __init__(self, name:str, gameId:str, active:tuple[Active]=AC.DIURNAL):
self.name = name
def __init__(self, gameId:str, active:Active|Iterable[Active]=AC.DIURNAL):
self.gameId = gameId
self.active = active
@@ -30,7 +30,7 @@ class Mob:
return hash(self.gameId)
def __str__(self) -> str:
return self.name
return self.gameId
def __repr__(self) -> str:
return f"{self.name}<{self.gameId}>{{active:{self.active}}}"
return f"Mob<{self.gameId}>{{active:{self.active}}}"
+6 -6
View File
@@ -8,11 +8,11 @@ import mcpacker.model.core.fauna.active as AC
@fixture(name="squid")
def createSquid():
yield Mob("squid", "minecraft:squid", AC.ANY)
yield Mob("minecraft:squid", AC.ANY)
@fixture(name="cow")
def createCow():
yield Mob("cow", "minecraft:cow", AC.DIURNAL)
yield Mob("minecraft:cow", AC.DIURNAL)
@fixture(name="mobMap")
def createMobMap(cow, squid):
@@ -25,15 +25,15 @@ def createMobMap(cow, squid):
# Tests ############################################################################################
def test_eq(squid):
assert squid == Mob("squid", "minecraft:squid")
assert squid != Mob("squid", "animalsplus:squid")
assert squid == Mob("minecraft:squid")
assert squid != Mob("animalsplus:squid")
def test_hash(cow, squid, mobMap):
assert mobMap[cow] == "beef"
assert mobMap[squid] == "calamari"
def test_str(squid):
assert str(squid) == "squid"
assert str(squid) == "minecraft:squid"
def test_repr(cow):
assert repr(cow) == "cow<minecraft:cow>{active:(Active<day>{start:0, end:12000},)}"
assert repr(cow) == "Mob<minecraft:cow>{active:(Active<day>{start:0, end:12000},)}"
+5 -23
View File
@@ -1,26 +1,8 @@
from collections.abc import Iterable
from mcpacker.model.core.fauna.mobplacement import MobPlacement
from mcpacker.model.catalog import Catalog
from mcpacker.model.core.fauna.mob import Mob
# Class ############################################################################################
# Classes ##########################################################################################
class MobCatalog:
def __init__(self, mobs:Iterable[MobPlacement]|None=None):
self._mobs = {}
for mob in (mobs or []):
self.add(mob)
def add(self, placement:MobPlacement) -> "MobCatalog":
if placement.mob.name in self._mobs:
raise Exception("Catalog already has an entry for {mob.name}")
self._mobs[placement.mob.name] = placement
def all(self) -> Iterable[MobPlacement]:
for placement in self._mobs.values():
yield placement
def get(self, name:str) -> MobPlacement:
return self._mobs.get(name, None)
class MobCatalog(Catalog[Mob]):
pass
-15
View File
@@ -1,15 +0,0 @@
from mcpacker.model.core.habitat import Habitat
from mcpacker.model.core.fauna.mob import Mob
from mcpacker.model.core.placement import Placement
# Class ############################################################################################
class MobPlacement(Placement):
"""
A description of where a creature should appear in the world.
"""
def __init__(self, mob:Mob, habitats:list[Habitat]=None):
super().__init__(habitats)
self.mob = mob
+20
View File
@@ -0,0 +1,20 @@
from collections.abc import Iterable
from mcpacker.model.core.habitat import Habitat
from mcpacker.model.core.fauna.mob import Mob
from mcpacker.model.core.spawn import Spawn
# Class ############################################################################################
class MobSpawn(Spawn):
"""
A description of where a creature should appear in the world.
"""
def __init__(self, mob:Mob, habitats:Iterable[Habitat]|Habitat|None=None):
super().__init__(habitats)
self.mob = mob
@property
def gameId(self) -> str:
return self.mob.gameId
@@ -1,6 +1,6 @@
from mcpacker.model.core.ecology.biomefilter import BiomeFilter
from mcpacker.model.core.fauna.mob import Mob
from mcpacker.model.core.fauna.mobplacement import MobPlacement
from mcpacker.model.core.fauna.mobspawn import MobSpawn
from mcpacker.model.core.habitat import Habitat
from pytest import fixture
@@ -18,16 +18,16 @@ def createCow():
def createFieldsHabitat():
yield Habitat(biomeFilter=BiomeFilter([F.FIELD, E.TEMPERATE]))
@fixture(name="placement")
def createMobPlacement(cow, fields):
yield MobPlacement(cow, fields)
@fixture(name="spawn")
def createMobSpawn(cow, fields):
yield MobSpawn(cow, fields)
# Tests ############################################################################################
def test_cowsInFields(placement):
assert str(placement) == (
"Placement{" +
def test_cowsInFields(spawn):
assert str(spawn) == (
"Spawn{" +
"habitats: [" +
"Habitat{" +
"altitude: anywhere <-64 to 320>, " +
@@ -0,0 +1,8 @@
from mcpacker.model.catalog import Catalog
from mcpacker.model.core.fauna.mobspawn import MobSpawn
# Classes ##########################################################################################
class MobSpawnCatalog(Catalog[MobSpawn]):
pass
@@ -0,0 +1,8 @@
import mcpacker.model.core.fauna.mobspawncatalog
# Tests ############################################################################################
def test_syntax():
pass
@@ -17,6 +17,7 @@ class Deposit:
biomeFilter:BiomeFilter|None=None,
):
self.name = name
self.gameId = name
self.biomeFilter = biomeFilter or BiomeFilter()
self.scarcity = scarcity
@@ -1,3 +1,4 @@
from collections.abc import Iterable
from mcpacker.model.core.ecology.biomefilter import BiomeFilter
from mcpacker.model.core.geology.bulk import Bulk
from mcpacker.model.core.geology.deposit import Deposit
@@ -17,10 +18,10 @@ class MetalDeposit(Deposit):
def __init__(
self,
name:str,
inclusions:tuple[Inclusion],
inclusions:Iterable[Inclusion],
bulk:Bulk=BU.MEDIUM,
proportion:Proportion=PR.BODY,
biomeFilter:BiomeFilter=None,
biomeFilter:BiomeFilter|None=None,
scarcity:Scarcity=SC.SPARSE,
):
super().__init__(name, scarcity, biomeFilter)
+4 -23
View File
@@ -1,27 +1,8 @@
from mcpacker.model.catalog import Catalog
from mcpacker.model.core.geology.deposit import Deposit
from mcpacker.model.core.geology.mineralcatalog import MineralCatalog
from typing import Iterator
# Class ############################################################################################
# Classes ##########################################################################################
class DepositCatalog:
def __init__(self, deposits:None|tuple[Deposit]=None):
self._deposits = {}
for deposit in (deposits or []):
self.add(deposit)
def add(self, deposit:Deposit) -> "DepositCatalog":
if deposit.name in self._deposits:
raise Exception("Catalog already has an entry for {deposit.name}")
self._deposits[deposit.name] = deposit
def all(self) -> Iterator[Deposit]:
for deposit in self._deposits.values():
yield deposit
def get(self, name:str) -> Deposit|None:
return self._deposits.get(name, None)
class DepositCatalog(Catalog[Deposit]):
pass
@@ -1,53 +1,7 @@
from mcpacker.model.core.ecology.biomefilter import BiomeFilter
from mcpacker.model.core.geology.deposit import Deposit
from mcpacker.model.core.geology.deposit.metaldeposit import MetalDeposit
from mcpacker.model.core.geology.depositcatalog import DepositCatalog
from mcpacker.model.core.geology.inclusion import Inclusion
from mcpacker.model.core.geology.mineral import Mineral
from mcpacker.model.core.geology.replacement import Replacement
from pytest import fixture
import mcpacker.model.core.ecology.flora as FL
import mcpacker.model.core.geology.bulk as BU
import mcpacker.model.core.scarcity as SC
# Fixtures #########################################################################################
@fixture(name="quartz")
def createQuartz():
yield Mineral("quartz", [
Replacement("#minecraft:stone_replaceables", "minecraft:quartz_ore"),
Replacement("#minecraft:deepslate_replaceables", "minecraft:deepslate_quartz_ore")
])
@fixture(name="iron")
def createIron():
yield Mineral("iron", [
Replacement("#minecraft:stone_replaceables", "minecraft:iron_ore"),
Replacement("#minecraft:deepslate_replaceables", "minecraft:deepslate_iron_ore")
])
@fixture(name="bifiron")
def createBifIron(iron, quartz):
yield MetalDeposit(
name = "bif_iron",
scarcity = SC.COMMON,
biomeFilter = BiomeFilter([FL.FOREST]),
inclusions = [Inclusion(iron, 60), Inclusion(quartz, 40)],
bulk = BU.LARGE,
proportion = 0.5,
)
@fixture(name="catalog")
def createCatalog(bifiron):
yield DepositCatalog([bifiron])
import mcpacker.model.core.geology.depositcatalog
# Tests ############################################################################################
def test_all(catalog):
assert [m.name for m in catalog.all()] == ["bif_iron"]
def test_get(catalog):
assert catalog.get("bif_iron").name == "bif_iron"
def test_syntax():
pass
+4 -11
View File
@@ -1,3 +1,4 @@
from collections.abc import Iterable
from mcpacker.model.core.geology.replacement import Replacement
from mcpacker.model.core.resourceid import ResourceId
@@ -6,9 +7,10 @@ from mcpacker.model.core.resourceid import ResourceId
class Mineral:
def __init__(self, name:str, replacements:None|tuple[Replacement]=None):
def __init__(self, name:str, replacements:Iterable[Replacement]|None=None):
self.name = name
self.replacements = replacements or []
self.gameId = name
self.replacements = list(replacements or [])
def __str__(self) -> str:
return self.name
@@ -21,12 +23,3 @@ class Mineral:
"]",
"}"
]])
def addStoneReplacement(self, blockId:str) -> "Mineral":
blockId = ResourceId.canonical(blockId)
self.replacements.append(Replacement(STONE_REPLACEABLES, blockId))
return self
def addDeepslateReplacement(self, blockId:str) -> "Mineral":
blockId = ResourceId.canonical(blockId)
self.replacements.append(Replacement(DEEPSLATE_REPLACEABLES, blockId))
+4 -22
View File
@@ -1,26 +1,8 @@
from mcpacker.model.catalog import Catalog
from mcpacker.model.core.geology.mineral import Mineral
from typing import Iterator
# Class ############################################################################################
# Classes ##########################################################################################
class MineralCatalog:
def __init__(self, minerals:tuple[Mineral]=None):
self._minerals = {}
for mineral in (minerals or []):
self.add(mineral)
def add(self, mineral:Mineral) -> "MineralCatalog":
if mineral.name in self._minerals:
raise Exception(f"Catalog already contains an entry for {mineral.name}")
self._minerals[mineral.name] = mineral
def all(self) -> Iterator[Mineral]:
for mineral in self._minerals.values():
yield mineral
def get(self, name:str) -> Mineral|None:
return self._minerals.get(name, None)
class MineralCatalog(Catalog[Mineral]):
pass
@@ -1,34 +1,7 @@
from mcpacker.model.core.geology.mineral import Mineral
from mcpacker.model.core.geology.mineralcatalog import MineralCatalog
from mcpacker.model.core.geology.replacement import Replacement
from pytest import fixture
# Fixtures #########################################################################################
@fixture(name="copper")
def createCopper():
yield Mineral("copper", [
Replacement("#minecraft:stone_replaceables", "minecraft:copper_ore"),
Replacement("#minecraft:deepslate_replaceables", "minecraft:deepslate_copper_ore")
])
@fixture(name="iron")
def createIron():
yield Mineral("iron", [
Replacement("#minecraft:stone_replaceables", "minecraft:iron_ore"),
Replacement("#minecraft:deepslate_replaceables", "minecraft:deepslate_iron_ore")
])
@fixture(name="catalog")
def createCatalog(copper, iron):
yield MineralCatalog([copper, iron])
import mcpacker.model.core.geology.mineralcatalog
# Tests ############################################################################################
def test_all(catalog):
assert [m.name for m in catalog.all()] == ["copper", "iron"]
def test_get(catalog):
assert catalog.get("iron").name == "iron"
def test_syntax():
pass
+11 -11
View File
@@ -1,3 +1,4 @@
from collections.abc import Iterable
from mcpacker.model.core.altitude import Altitude
from mcpacker.model.core.ecology.biomefilter import BiomeFilter
from mcpacker.model.core.fauna.group import Group
@@ -23,8 +24,8 @@ class Habitat:
self,
altitude:Altitude=A.ANYWHERE,
biomeFilter:BiomeFilter=None,
seasons:tuple[Season]=E.ALL,
biomeFilter:BiomeFilter|None=None,
seasons:Iterable[Season]|Season=E.ALL,
group:Group=G.SOLO,
location:Location=L.OUTSIDE,
@@ -32,7 +33,7 @@ class Habitat:
):
self.altitude = altitude
self.biomeFilter = biomeFilter or BiomeFilter()
self.seasons = (seasons,) if isinstance(seasons, Season) else seasons
self.seasons = [seasons] if isinstance(seasons, Season) else seasons
self.group = group
self.location = location
@@ -42,7 +43,7 @@ class Habitat:
def __str__(self) -> str:
return "".join([str(p) for p in [
", ".join(self.altitude), "|",
self.altitude, "|",
self.biomeFilter, "|",
", ".join([str(s) for s in self.seasons]), "|",
self.group, "|",
@@ -77,13 +78,12 @@ class Habitat:
return result
def collect(self) -> "tuple[Habitat]":
result = []
current = self
def collect(self) -> "Iterable[Habitat]":
stack:list[Habitat] = []
current:Habitat|None = self
while current:
result.append(current)
stack.append(current)
current = current._source
return tuple(reversed(result))
while stack:
yield stack.pop()
+3 -2
View File
@@ -1,3 +1,4 @@
from collections.abc import Iterable
from mcpacker.model.core.material.loottable import LootTable
from mcpacker.model.core.material.soundtype import SoundType
from mcpacker.model.core.resourceid import ResourceId
@@ -12,11 +13,11 @@ class Block:
def __init__(
self,
name:str,
gameId:ResourceId,
gameId:str,
loot:LootTable,
requiresTool:bool=False,
soundType:SoundType=ST.STONE,
tags:list[str]=None,
tags:Iterable[str]|None=None,
):
self.gameId = gameId
self.loot = loot
+4 -26
View File
@@ -1,30 +1,8 @@
from mcpacker.model.catalog import Catalog
from mcpacker.model.core.material.block import Block
from typing import Iterator
# Class ############################################################################################
class BlockCatalog:
def __init__(self, blocks:tuple[Block]=None):
self._blocks = {}
for block in (blocks or []):
self.add(block)
def __len__(self) -> int:
return len(self._blocks)
def add(self, block:Block) -> "BlockCatalog":
if block.name in self._blocks:
raise Exception(f"Catalog already contains an entry for {block.name}")
self._blocks[block.name] = block
def all(self) -> Iterator[Block]:
for block in self._blocks.values():
yield block
def get(self, name:str) -> Block|None:
return self._blocks.get(name, None)
# Classes ##########################################################################################
class BlockCatalog(Catalog[Block]):
pass
+1 -4
View File
@@ -5,8 +5,5 @@ from mcpacker.model.core.resourceid import ResourceId
class Item:
def __init__(self, gameId:ResourceId):
def __init__(self, gameId:str):
self.gameId = gameId
if not isinstance(self.gameId, ResourceId):
self.gameId = ResourceId.parse(str(self.gameId))
+5 -2
View File
@@ -1,12 +1,14 @@
from collections.abc import Iterable
from mcpacker.model.catalog import Catalog
from mcpacker.model.core.material.item import Item
from typing import Iterator
# Class ############################################################################################
class ItemCatalog:
class ItemCatalog(Catalog[Item]):
def __init__(self, items:tuple[Item]=None):
def __init__(self, items:Iterable[Item]|None=None):
self._items = {}
for item in (items or []):
@@ -17,6 +19,7 @@ class ItemCatalog:
raise Exception(f"Catalog already contains an entry for {item.gameId}")
self._items[item.gameId] = item
return self
def all(self) -> Iterator[Item]:
for item in self._items.values():
+6 -1
View File
@@ -5,6 +5,11 @@ from mcpacker.model.core.material.item import Item
class Loot:
@staticmethod
def of(item:Item|None, maxCount:int=1, minCount:int=1, weight:int=1) -> "Loot|None":
if not item: return None
return Loot(item, maxCount, minCount, weight)
def __init__(self, item:Item, maxCount:int=1, minCount:int=1, weight:int=1):
self.item = item
self.maxCount = maxCount
@@ -21,4 +26,4 @@ class Loot:
]])
def __str__(self) -> str:
return f"{str(item)} ({self.minCount}..{self.maxCount} @ {self.weight})"
return f"{str(self.item)} ({self.minCount}..{self.maxCount} @ {self.weight})"
+6 -2
View File
@@ -1,3 +1,4 @@
from collections.abc import Iterable
from mcpacker.model.core.material.loot import Loot
@@ -5,5 +6,8 @@ from mcpacker.model.core.material.loot import Loot
class LootTable:
def __init__(self, items:list[Loot]):
self.items = items
def __init__(self, items:Iterable[Loot]|Loot|None):
if isinstance(items, Loot):
items = [items]
self.items = items or []
@@ -1,26 +1,24 @@
from collections.abc import Iterable
from mcpacker.model.core.habitat import Habitat
# Class ############################################################################################
class Placement:
class Spawn:
"""
Abstract base class for placing something into its various habitats.
"""
def __init__(self, habitats:tuple[Habitat]=None):
self.habitats = habitats
def __init__(self, habitats:Iterable[Habitat]|Habitat|None=None):
if isinstance(habitats, Habitat):
habitats = habitats.collect()
if not self.habitats:
self.habitats = ()
if isinstance(self.habitats, Habitat):
self.habitats = self.habitats.collect()
self.habitats = habitats or []
def __repr__(self) -> str:
return (
"Placement{" +
"Spawn{" +
f"habitats: [{', '.join([repr(h) for h in self.habitats])}]" +
"}"
)
@@ -1,6 +1,6 @@
from mcpacker.model.core.ecology.biomefilter import BiomeFilter
from mcpacker.model.core.habitat import Habitat
from mcpacker.model.core.placement import Placement
from mcpacker.model.core.spawn import Spawn
from pytest import fixture
import mcpacker.model.core.ecology.flora as F
@@ -13,16 +13,16 @@ import mcpacker.model.core.ecology.heat as E
def createFieldsHabitat():
yield Habitat(biomeFilter=BiomeFilter([F.FIELD, E.TEMPERATE]))
@fixture(name="placement")
def createPlacement(fields):
yield Placement([fields])
@fixture(name="spawn")
def createSpawn(fields):
yield Spawn([fields])
# Tests ############################################################################################
def test_repr(placement):
assert repr(placement) == (
"Placement{" +
def test_repr(spawn):
assert repr(spawn) == (
"Spawn{" +
"habitats: [" +
"Habitat{" +
"altitude: anywhere <-64 to 320>, " +
+3
View File
@@ -1,5 +1,6 @@
from mcpacker.model.core.ecology.biomecatalog import BiomeCatalog
from mcpacker.model.core.fauna.mobcatalog import MobCatalog
from mcpacker.model.core.fauna.mobspawncatalog import MobSpawnCatalog
from mcpacker.model.core.geology.depositcatalog import DepositCatalog
from mcpacker.model.core.geology.mineralcatalog import MineralCatalog
from mcpacker.model.core.material.blockcatalog import BlockCatalog
@@ -18,6 +19,7 @@ class World:
items:ItemCatalog|None=None,
minerals:MineralCatalog|None=None,
mobs:MobCatalog|None=None,
mobSpawns:MobSpawnCatalog|None=None
):
self.biomes = biomes or BiomeCatalog()
self.blocks = blocks or BlockCatalog()
@@ -25,3 +27,4 @@ class World:
self.items = items or ItemCatalog()
self.minerals = minerals or MineralCatalog()
self.mobs = mobs or MobCatalog()
self.mobSpawns = mobSpawns or MobSpawnCatalog()
+13 -12
View File
@@ -1,39 +1,40 @@
from collections.abc import Iterable
from mcpacker.model.datapack.mod import Mod
from mcpacker.model.datapack.moddata import ModData
# Class ############################################################################################
class DataPack:
def __init__(self, name:str, mods:Iterable[Mod]|None=None):
def __init__(self, name:str, mods:Iterable[ModData]|None=None):
self.name = name
self._mods = {}
self._defaultMod = None
self._mods:dict[str,ModData] = {}
self._defaultMod:ModData|None = None
for mod in (mods or []):
self.add(mod)
def add(self, mod:Mod) -> "Datapack":
def add(self, mod:ModData) -> "DataPack":
if not self._defaultMod:
self._defaultMod = mod
self._mods[mod.name] = mod
return self
def get(self, modName:str) -> Mod:
return self._mods.get(modName, None)
def get(self, name:str) -> ModData|None:
return self._mods.get(name, None)
@property
def defaultMod(self) -> Mod:
def defaultMod(self) -> ModData|None:
return self._defaultMod
@defaultMod.setter
def setDefaultMod(self, mod:Mod) -> "Datapack":
existingMod = self.get(mod.name)
if not existingMod:
def defaultMod(self, mod:ModData):
if mod:
existingModData = self.get(mod.name)
if not existingModData:
self.add(mod)
elif existingMod != mod:
elif existingModData != mod:
raise Exception(f"Datapack already has a mod named {mod.name}")
self._defaultMod = mod
+5 -3
View File
@@ -1,16 +1,18 @@
from mcpacker.json import JsonBlob
from typing import Any
from typing import cast
# Class ############################################################################################
class BlockState:
def __init__(self, blockId:str, properties:dict[str,str]=None):
def __init__(self, blockId:str, properties:JsonBlob|None=None):
self.blockId = blockId
self.properties = properties
def asData(self) -> dict[str,Any]:
result = { "Name": self.blockId }
def asJsonBlob(self) -> JsonBlob:
result = cast(dict[str,JsonBlob], { "Name": self.blockId })
if self.properties:
result["Properties"] = self.properties
+2 -2
View File
@@ -11,8 +11,8 @@ def createBlockState():
# Tests ############################################################################################
def test_asData(state):
assert state.asData() == {
def test_asData(state:BlockState):
assert state.asJsonBlob() == {
"Name": "minecraft:button",
"Properties": {
"waterlogged": "true"
@@ -1,3 +1,6 @@
from mcpacker.json import JsonBlob
# Class ############################################################################################
class BlockStateProvider:
@@ -5,3 +8,5 @@ class BlockStateProvider:
def __init__(self, gameId:str):
self.gameId = gameId
def asJsonBlob(self) -> JsonBlob:
raise NotImplementedError()
@@ -1,3 +1,4 @@
from mcpacker.json import JsonBlob
from mcpacker.model.datapack.blockstate import BlockState
from mcpacker.model.datapack.blockstateprovider import BlockStateProvider
from typing import Any
@@ -11,8 +12,8 @@ class SimpleStateProvider(BlockStateProvider):
super().__init__("minecraft:simple_state_provider")
self.state = state
def asData(self) -> dict[str, Any]:
def asJsonBlob(self) -> JsonBlob:
return {
"type": self.gameId,
"state": self.state.asData(),
"state": self.state.asJsonBlob(),
}
@@ -10,14 +10,14 @@ def createBlockState():
yield BlockState("minecraft:button", {"waterlogged": "true"})
@fixture(name="provider")
def createProvider(state):
def createProvider(state:BlockState):
return SimpleStateProvider(state)
# Tests ############################################################################################
def test_asData(provider):
assert provider.asData() == {
def test_asData(provider:SimpleStateProvider):
assert provider.asJsonBlob() == {
"type": "minecraft:simple_state_provider",
"state": {
"Name": "minecraft:button",
@@ -1,5 +1,9 @@
from mcpacker.model.datapack.configuredfeature import ConfiguredFeature
from typing import Any
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from mcpacker.model.datapack.placedfeature import PlacedFeature
# Class ############################################################################################
@@ -9,7 +13,7 @@ class RandomPatch(ConfiguredFeature):
def __init__(
self,
gameId:str,
feature:"PlacedFeature",
feature:PlacedFeature,
tries:int=128,
xzSpread:int=7,
ySpread:int=3,
@@ -23,9 +27,9 @@ class RandomPatch(ConfiguredFeature):
def asData(self) -> dict[str,Any]:
return {
"type": self.type,
"type": self.gameId,
"feature": self.feature.gameId,
"tries": self.tries,
"xz_spread": self.xySpread,
"xz_spread": self.xzSpread,
"y_spread": self.ySpread
}
@@ -11,18 +11,18 @@ def createBlockState():
yield BlockState("minecraft:button", {"waterlogged": "true"})
@fixture(name="provider")
def createProvider(state):
def createProvider(state:BlockState):
return SimpleStateProvider(state)
@fixture(name="feature")
def createSimpleBlockFeature(provider):
def createSimpleBlockFeature(provider:SimpleStateProvider):
return SimpleBlock(provider)
# Tests ############################################################################################
def test_asData(feature):
assert feature.asData() == {
def test_asData(feature:SimpleBlock):
assert feature.asJsonBlob() == {
"type": "minecraft:simple_block",
"config": {
"to_place": {
@@ -1,3 +1,4 @@
from mcpacker.json import JsonBlob
from mcpacker.model.datapack.blockstateprovider import BlockStateProvider
from mcpacker.model.datapack.configuredfeature import ConfiguredFeature
from typing import Any
@@ -11,10 +12,10 @@ class SimpleBlock(ConfiguredFeature):
super().__init__("minecraft:simple_block")
self.toPlace = toPlace
def asData(self) -> dict[str, Any]:
def asJsonBlob(self) -> JsonBlob:
return {
"type": self.gameId,
"config": {
"to_place": self.toPlace.asData()
"to_place": self.toPlace.asJsonBlob()
}
}
@@ -11,18 +11,18 @@ def createBlockState():
yield BlockState("minecraft:button", {"waterlogged": "true"})
@fixture(name="provider")
def createProvider(state):
def createProvider(state:BlockState):
return SimpleStateProvider(state)
@fixture(name="feature")
def createSimpleBlockFeature(provider):
def createSimpleBlockFeature(provider:SimpleStateProvider):
return SimpleBlock(provider)
# Tests ############################################################################################
def test_asData(feature):
assert feature.asData() == {
def test_asData(feature:SimpleBlock):
assert feature.asJsonBlob() == {
"type": "minecraft:simple_block",
"config": {
"to_place": {
-17
View File
@@ -1,17 +0,0 @@
# Class ############################################################################################
class Mod:
def __init__(self):
self.biomeModifiers = []
self.configuredFeatures = []
self.functions = []
self.functionTags = []
self.lootModifiers = []
self.lootTables = []
self.placedFeatures = []
self.recipes = []
self.structures = []
self.structureSets = []
self.tags = []
self.templatePools = []
+23
View File
@@ -0,0 +1,23 @@
from mcpacker.model.datapack.configuredfeature import ConfiguredFeature
from mcpacker.model.datapack.placedfeature import PlacedFeature
from typing import Any
# Class ############################################################################################
class ModData:
def __init__(self, name:str):
self.name = name
self.biomeModifiers:list[Any] = []
self.configuredFeatures:list[ConfiguredFeature] = []
self.functions:list[Any] = []
self.functionTags:list[Any] = []
self.lootModifiers:list[Any] = []
self.lootTables:list[Any] = []
self.placedFeatures:list[PlacedFeature] = []
self.recipes:list[Any] = []
self.structures:list[Any] = []
self.structureSets:list[Any] = []
self.tags:list[Any] = []
self.templatePools:list[Any] = []
@@ -1,8 +1,7 @@
import mcpacker.model.resourcepack.mod
import mcpacker.model.datapack.moddata
# Tests ############################################################################################
def test_syntax():
pass
+38
View File
@@ -0,0 +1,38 @@
from mcpacker.json import JsonBlob
from mcpacker.model.incontrol.extracondition import ExtraCondition
from mcpacker.model.core.dimension import Dimension
import mcpacker.json as json
import mcpacker.model.core.altitude as A
import mcpacker.model.core.dimension as D
# Class ############################################################################################
class Condition:
def __init__(
self,
dimension:Dimension=D.OVERWORLD,
heightMin:int|None=None,
heightMax:int|None=None,
thisMax:int|None=None,
inWater:bool|None=None,
andCondition:ExtraCondition|None=None,
):
self.dimension = dimension
self.heightMin = heightMin
self.heightMax = heightMax
self.thisMax = thisMax
self.inWater = inWater
self.andCondition = andCondition
def asJsonBlob(self) -> JsonBlob:
return json.removeNoneValues({
"dimension": self.dimension.name,
"heightmin": self.heightMin,
"heightmax": self.heightMax,
"maxthis": self.thisMax,
"inwater": self.inWater,
"and": None if not self.andCondition else self.andCondition.asJsonBlob(),
})
@@ -0,0 +1,44 @@
from collections.abc import Iterable
from mcpacker.json import JsonBlob
import mcpacker.json as json
# Class ############################################################################################
class ExtraCondition:
def __init__(
self,
autumn:bool|None=None,
biome:Iterable[str]|None=None,
cave:bool|None=None,
seeSky:bool|None=None,
spring:bool|None=None,
summer:bool|None=None,
timeMax:int|None=None,
timeMin:int|None=None,
winter:bool|None=None,
):
self.autumn = autumn
self.biome = biome
self.cave = cave
self.seeSky = seeSky
self.spring = spring
self.summer = summer
self.timeMax = timeMax
self.timeMin = timeMin
self.winter = winter
def asJsonBlob(self) -> JsonBlob:
return json.removeNoneValues({
"autumn": self.autumn,
"biome": None if not self.biome else "[" + ", ".join(self.biome) + "]",
"cave": self.cave,
"seesky": self.seeSky,
"spring": self.spring,
"summer": self.summer,
"maxtime": self.timeMax,
"mintime": self.timeMin,
"winter": self.winter,
})
+41
View File
@@ -0,0 +1,41 @@
from mcpacker.json import JsonBlob
from mcpacker.model.core.fauna.mobspawn import MobSpawn
from mcpacker.model.incontrol.condition import Condition
# Class ############################################################################################
class SpawnerRule:
def __init__(
self,
mobGameId:str,
condition:Condition,
amountMin:int=1,
amountMax:int=1,
rate:float=1.0,
attempts:int=1,
weight:int=1,
):
self.mobGameId = mobGameId
self.amountMin = amountMin
self.amountMax = amountMax
self.rate = rate
self.attempts = attempts
self.weight = weight
self.condition = condition
def asJsonBlob(self) -> JsonBlob:
return {
"mob": [self.mobGameId],
"amount": {
"minimum": self.amountMin,
"maximum": self.amountMax,
"groupdistance": 4,
},
"persecond": self.rate,
"attempts": self.attempts,
"weights": [self.weight],
"conditions": self.condition.asJsonBlob(),
}
+1
View File
@@ -29,3 +29,4 @@ class ModPack:
def augment(self, doAugment:"Callable[[ModPack], None]") -> "ModPack":
doAugment(self)
return self
+10 -9
View File
@@ -1,35 +1,36 @@
from collections.abc import Iterable
from mcpacker.model.resourcepack.mod import Mod
from mcpacker.model.resourcepack.modresource import ModResource
# Class ############################################################################################
class ResourcePack:
def __init__(self, name:str, mods:Iterable[Mod]|None=None):
def __init__(self, name:str, mods:Iterable[ModResource]|None=None):
self.name = name
self._mods = {}
self._defaultMod = None
self._mods:dict[str,ModResource] = {}
self._defaultMod:ModResource|None = None
for mod in (mods or []):
self.add(mod)
def add(self, mod:Mod) -> "Resourcepack":
def add(self, mod:ModResource) -> "ResourcePack":
if not self._defaultMod:
self._defaultMod = mod
self._mods[mod.name] = mod
return self
def get(self, modName:str) -> Mod:
return self._mods.get(modName, None)
def get(self, name:str) -> ModResource|None:
return self._mods.get(name, None)
@property
def defaultMod(self) -> Mod:
def defaultMod(self) -> ModResource|None:
return self._defaultMod
@defaultMod.setter
def setDefaultMod(self, mod:Mod) -> "Resourcepack":
def defaultMod(self, mod:ModResource):
if mod:
existingMod = self.get(mod.name)
if not existingMod:
self.add(mod)
+1 -1
View File
@@ -7,7 +7,7 @@ class Kind:
def __eq__(self, other) -> bool:
if type(self) != type(other): return False
if self.name != othere.name: return False
if self.name != other.name: return False
return True
def __repr__(self) -> str:
-8
View File
@@ -1,8 +0,0 @@
# Class ############################################################################################
class Mod:
def __init__(self):
self.blockStates = []
self.models = []
self.textures = []
@@ -0,0 +1,9 @@
# Class ############################################################################################
class ModResource:
def __init__(self, name:str):
self.name = name
self.blockStates:list[str] = []
self.models:list[str] = []
self.textures:list[str] = []
@@ -0,0 +1,8 @@
import mcpacker.model.resourcepack.modresource
# Tests ############################################################################################
def test_syntax():
pass
+1 -1
View File
@@ -16,7 +16,7 @@ def addBlocks(pack:ModPack):
blocks.add(Block(
gameId = "kubejs:peat_block",
loot = LootTable(Loot(items.get("kubejs:peat_chunk"), 2, 9)),
loot = LootTable(Loot.of(items.get("kubejs:peat_chunk"), 2, 9)),
name = "Peat Block",
soundType = SO.DIRT,
tags = ["minecraft:mineable/shovel"],
@@ -11,6 +11,7 @@ import mcpacker.model.core.ecology.humidity as HU
import mcpacker.model.core.ecology.soil as SO
import mcpacker.model.core.ecology.water as WA
import mcpacker.model.core.geology.bulk as BU
import mcpacker.model.core.geology.proportion as PR
import mcpacker.model.core.scarcity as SC
# Catalog ##########################################################################################
@@ -24,10 +25,10 @@ def addDeposits(pack:ModPack):
scarcity = SC.COMMON,
biomeFilter = BF([GE.METAMORPHIC, (HE.BOREAL, HE.FROZEN), HU.DAMP]),
inclusions = [
I(minerals.get("iron"), 50),
I(minerals.get("quartz"), 45),
I(minerals.get("uranium"), 5)
I(minerals["iron"], 50),
I(minerals["quartz"], 45),
I(minerals["uranium"], 5)
],
bulk = BU.LARGE,
proportion = 0.2,
proportion = PR.BED,
))
+64 -366
View File
@@ -1,22 +1,7 @@
from mcpacker.model.core.ecology.biomefilter import BiomeFilter as BF
from mcpacker.model.core.fauna.mob import Mob
from mcpacker.model.core.fauna.mobcatalog import MobCatalog
from mcpacker.model.core.fauna.mobplacement import MobPlacement
from mcpacker.model.core.habitat import Habitat
from mcpacker.model.modpack import ModPack
import mcpacker.model.core.altitude as AL
import mcpacker.model.core.ecology.flora as FL
import mcpacker.model.core.ecology.geology as GE
import mcpacker.model.core.ecology.heat as HE
import mcpacker.model.core.ecology.humidity as HU
import mcpacker.model.core.ecology.soil as SO
import mcpacker.model.core.ecology.water as WA
import mcpacker.model.core.fauna.active as AC
import mcpacker.model.core.fauna.group as GR
import mcpacker.model.core.fauna.location as LO
import mcpacker.model.core.scarcity as SC
import mcpacker.model.core.season as SE
# Constants ########################################################################################
@@ -24,354 +9,67 @@ import mcpacker.model.core.season as SE
def addMobs(pack:ModPack):
mobs = pack.world.mobs
mobs.add(
MobPlacement(Mob("armadillo", "minecraft:armadillo", AC.NOCTURNAL),
Habitat(
altitude = AL.LOWLANDS,
biomeFilter = BF([HE.SUBTROPICAL, HU.DRY]),
seasons = SE.SUMMER,
group = GR.merge(GR.SOLO, GR.PAIR),
scarcity = SC.COMMON
).derive(
altitude = AL.UPLANDS, scarcity = SC.SPARSE,
).derive(
altitude = AL.LOWLANDS, seasons = SE.exclude(SE.SUMMER), scarcity = SC.UNCOMMON,
).derive(
altitude = AL.UPLANDS, scarcity = SC.UNUSUAL,
)
)
)
mobs.add(
MobPlacement(Mob("black_bear", "bearminimum:black_bear"),
Habitat(
altitude = AL.span(AL.LOWLANDS, AL.ALPINE),
biomeFilter = BF([HE.TEMPERATE, FL.FOREST]),
seasons = SE.AUTUMN,
group = GR.merge(GR.SOLO, GR.FAMILY),
scarcity = SC.UNCOMMON,
).derive(
seasons = [SE.SPRING, SE.SUMMER], scarcity = SC.SPARSE
).derive(
seasons = SE.WINTER, scarcity = SC.UNUSUAL
).derive(
biomeFilter = BF([HE.BOREAL, (FL.CANOPY, FL.FOREST)]),
seasons = SE.AUTUMN,
scarcity = SC.SPARSE
).derive(
seasons = [SE.SPRING, SE.SUMMER], scarcity = SC.UNUSUAL
).derive(
seasons = SE.WINTER, scarcity = SC.RARE
)
)
)
mobs.add(
MobPlacement(Mob("blaze", "minecraft:blaze"),
Habitat(
altitude = AL.PLUTONIC,
location = LO.CAVE,
group = GR.SOLO,
scarcity = SC.UNCOMMON
)
)
)
mobs.add(
MobPlacement(Mob("bogged", "minecraft:bogged"))
)
mobs.add(
MobPlacement(Mob("brown_bear", "bearminimum:brown_bear"),
Habitat(
altitude = AL.span(AL.LOWLANDS, AL.ALPINE),
biomeFilter = BF([HE.BOREAL, (FL.CANOPY, FL.FOREST)]),
seasons = SE.AUTUMN,
group = GR.SOLO,
scarcity = SC.SPARSE
).derive(
seasons = [SE.SPRING, SE.SUMMER], scarcity = SC.UNUSUAL
).derive(
seasons = SE.WINTER, scarcity = SC.RARE
)
)
)
mobs.add(
MobPlacement(Mob("bulwark", "immersiveengineering:bulwark"))
)
mobs.add(
MobPlacement(Mob("camel", "minecraft:camel", AC.CREPUSCULAR),
Habitat(
altitude = AL.span(AL.DUNES, AL.LOWLANDS),
biomeFilter = BF([HE.TROPICAL, HU.DRY, FL.BARREN, SO.SANDY]),
seasons = SE.DRY,
group = GR.FAMILY,
scarcity = SC.UNUSUAL,
).derive(
seasons = SE.WET, scarcity = SC.RARE
)
)
)
mobs.add(
MobPlacement(Mob("cat", "minecraft:cat"))
)
mobs.add(
MobPlacement(Mob("cave_spider", "minecraft:cave_spider"),
Habitat(
altitude = AL.span(AL.OVERBURDEN, AL.HILLS),
location = LO.CAVE,
group = GR.SOLO,
scarcity = SC.UNCOMMON
)
)
)
mobs.add(
MobPlacement(Mob("chameleon", "cold_sweat:chameleon"),
Habitat(
altitude = AL.span(AL.DUNES, AL.UPLANDS),
biomeFilter = BF([HE.TROPICAL, HU.WET, [FL.CANOPY, FL.FOREST]]),
seasons = SE.WET,
group = GR.merge(GR.SOLO, GR.FAMILY),
scarcity = SC.COMMON,
).derive(
seasons = SE.DRY, scarcity = SC.UNCOMMON,
).derive(
biomeFilter = BF([HE.TROPICAL, HU.WET, FL.CLEARING]),
seasons = SE.WET,
scarcity = SC.UNCOMMON
).derive(
seasons = SE.DRY, scarcity = SC.SPARSE,
)
)
)
mobs.add(
MobPlacement(Mob("chicken", "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
)
)
)
mobs.add(
MobPlacement(Mob("cod", "minecraft:cod"))
)
mobs.add(
MobPlacement(Mob("commando", "immersiveengineering:commando"))
)
mobs.add(
MobPlacement(Mob("cow", "minecraft:cow"))
)
mobs.add(
MobPlacement(Mob("crab", "crabbersdelight:crab"))
)
mobs.add(
MobPlacement(Mob("creeper", "minecraft:creeper"))
)
mobs.add(
MobPlacement(Mob("dolphin", "minecraft:dolphin"))
)
mobs.add(
MobPlacement(Mob("donkey", "minecraft:donkey"))
)
mobs.add(
MobPlacement(Mob("drowned", "minecraft:drowned"))
)
mobs.add(
MobPlacement(Mob("enderman", "minecraft:enderman"))
)
mobs.add(
MobPlacement(Mob("evoker", "minecraft:evoker"))
)
mobs.add(
MobPlacement(Mob("fox", "minecraft:fox"))
)
mobs.add(
MobPlacement(Mob("frog", "minecraft:frog"))
)
mobs.add(
MobPlacement(Mob("fusilier", "immersiveengineering:fusilier"))
)
mobs.add(
MobPlacement(Mob("glow_squid", "minecraft:glow_squid"))
)
mobs.add(
MobPlacement(Mob("goat", "minecraft:goat"))
)
mobs.add(
MobPlacement(Mob("hoglin", "minecraft:hoglin"))
)
mobs.add(
MobPlacement(Mob("horse", "minecraft:horse"))
)
mobs.add(
MobPlacement(Mob("husk", "minecraft:husk"))
)
mobs.add(
MobPlacement(Mob("llama", "minecraft:llama"))
)
mobs.add(
MobPlacement(Mob("magma_cube", "minecraft:magma_cube"))
)
mobs.add(
MobPlacement(Mob("mooshroom", "minecraft:mooshroom"))
)
mobs.add(
MobPlacement(Mob("mule", "minecraft:mule"))
)
mobs.add(
MobPlacement(Mob("ocelot", "minecraft:ocelot"))
)
mobs.add(
MobPlacement(Mob("panda", "minecraft:panda"))
)
mobs.add(
MobPlacement(Mob("parrot", "minecraft:parrot"))
)
mobs.add(
MobPlacement(Mob("pig", "minecraft:pig"))
)
mobs.add(
MobPlacement(Mob("pillager", "minecraft:pillager"))
)
mobs.add(
MobPlacement(Mob("polar_bear", "minecraft:polar_bear"))
)
mobs.add(
MobPlacement(Mob("pufferfish", "minecraft:pufferfish"))
)
mobs.add(
MobPlacement(Mob("rabbit", "minecraft:rabbit"))
)
mobs.add(
MobPlacement(Mob("raccoon", "animalplus:raccoon"))
)
mobs.add(
MobPlacement(Mob("ravager", "minecraft:ravager"))
)
mobs.add(
MobPlacement(Mob("red_panda", "animalplus:red_panda"))
)
mobs.add(
MobPlacement(Mob("salmon", "minecraft:salmon"))
)
mobs.add(
MobPlacement(Mob("sheep", "minecraft:sheep"))
)
mobs.add(
MobPlacement(Mob("skeleton", "minecraft:skeleton"))
)
mobs.add(
MobPlacement(Mob("slime", "minecraft:slime"))
)
mobs.add(
MobPlacement(Mob("spider", "minecraft:spider"))
)
mobs.add(
MobPlacement(Mob("squid", "minecraft:squid"))
)
mobs.add(
MobPlacement(Mob("tropical_fish", "minecraft:tropical_fish"))
)
mobs.add(
MobPlacement(Mob("turtle", "minecraft:turtle"))
)
mobs.add(
MobPlacement(Mob("vex", "minecraft:vex"))
)
mobs.add(
MobPlacement(Mob("villager", "minecraft:villager"))
)
mobs.add(
MobPlacement(Mob("vindicator", "minecraft:vindicator"))
)
mobs.add(
MobPlacement(Mob("wandering_trader", "minecraft:wandering_trader"))
)
mobs.add(
MobPlacement(Mob("warden", "minecraft:warden"))
)
mobs.add(
MobPlacement(Mob("witch", "minecraft:witch"))
)
mobs.add(
MobPlacement(Mob("wither_skeleton", "minecraft:wither_skeleton"))
)
mobs.add(
MobPlacement(Mob("wither", "minecraft:wither"))
)
mobs.add(
MobPlacement(Mob("wolf", "minecraft:wolf"))
)
mobs.add(
MobPlacement(Mob("zoglin", "minecraft:zoglin"))
)
mobs.add(
MobPlacement(Mob("zombie_villager", "minecraft:zombie_villager"))
)
mobs.add(
MobPlacement(Mob("zombie", "minecraft:zombie"))
)
mobs.add(Mob("animalplus:raccoon", AC.NOCTURNAL))
mobs.add(Mob("animalplus:red_panda", AC.CREPUSCULAR))
mobs.add(Mob("bearminimum:black_bear", AC.CREPUSCULAR))
mobs.add(Mob("bearminimum:brown_bear", AC.CREPUSCULAR))
mobs.add(Mob("cold_sweat:chameleon", AC.DIURNAL))
mobs.add(Mob("crabbersdelight:crab", AC.CREPUSCULAR))
mobs.add(Mob("immersiveengineering:bulwark"))
mobs.add(Mob("immersiveengineering:commando"))
mobs.add(Mob("immersiveengineering:fusilier"))
mobs.add(Mob("minecraft:armadillo", AC.NOCTURNAL))
mobs.add(Mob("minecraft:blaze", AC.ANY))
mobs.add(Mob("minecraft:bogged", AC.MIDNIGHT))
mobs.add(Mob("minecraft:camel", AC.CREPUSCULAR))
mobs.add(Mob("minecraft:cat", AC.CREPUSCULAR))
mobs.add(Mob("minecraft:cave_spider", AC.NOCTURNAL))
mobs.add(Mob("minecraft:chicken", AC.DIURNAL))
mobs.add(Mob("minecraft:cod", AC.DIURNAL))
mobs.add(Mob("minecraft:cow", AC.DIURNAL))
mobs.add(Mob("minecraft:creeper", AC.STORMY))
mobs.add(Mob("minecraft:dolphin", AC.DIURNAL))
mobs.add(Mob("minecraft:donkey", AC.DIURNAL))
mobs.add(Mob("minecraft:drowned", AC.MIDNIGHT))
mobs.add(Mob("minecraft:enderman"))
mobs.add(Mob("minecraft:evoker"))
mobs.add(Mob("minecraft:fox", AC.CREPUSCULAR))
mobs.add(Mob("minecraft:frog", AC.CREPUSCULAR))
mobs.add(Mob("minecraft:glow_squid", AC.CREPUSCULAR))
mobs.add(Mob("minecraft:goat", AC.DIURNAL))
mobs.add(Mob("minecraft:hoglin"))
mobs.add(Mob("minecraft:horse", AC.DIURNAL))
mobs.add(Mob("minecraft:husk", AC.MIDNIGHT))
mobs.add(Mob("minecraft:llama", AC.DIURNAL))
mobs.add(Mob("minecraft:magma_cube", AC.ANY))
mobs.add(Mob("minecraft:mooshroom", AC.DIURNAL))
mobs.add(Mob("minecraft:mule", AC.DIURNAL))
mobs.add(Mob("minecraft:ocelot", AC.NOCTURNAL))
mobs.add(Mob("minecraft:panda", AC.CREPUSCULAR))
mobs.add(Mob("minecraft:parrot", AC.DIURNAL))
mobs.add(Mob("minecraft:pig", AC.DIURNAL))
mobs.add(Mob("minecraft:pillager"))
mobs.add(Mob("minecraft:polar_bear", AC.CREPUSCULAR))
mobs.add(Mob("minecraft:pufferfish", AC.DIURNAL))
mobs.add(Mob("minecraft:rabbit", AC.CREPUSCULAR))
mobs.add(Mob("minecraft:ravager"))
mobs.add(Mob("minecraft:salmon", AC.DIURNAL))
mobs.add(Mob("minecraft:sheep", AC.DIURNAL))
mobs.add(Mob("minecraft:skeleton"))
mobs.add(Mob("minecraft:slime", AC.RAINY))
mobs.add(Mob("minecraft:spider", AC.NOCTURNAL))
mobs.add(Mob("minecraft:squid", AC.CREPUSCULAR))
mobs.add(Mob("minecraft:tropical_fish", AC.DIURNAL))
mobs.add(Mob("minecraft:turtle", AC.NOCTURNAL))
mobs.add(Mob("minecraft:vex"))
mobs.add(Mob("minecraft:villager"))
mobs.add(Mob("minecraft:vindicator"))
mobs.add(Mob("minecraft:wandering_trader"))
mobs.add(Mob("minecraft:warden"))
mobs.add(Mob("minecraft:witch"))
mobs.add(Mob("minecraft:wither_skeleton"))
mobs.add(Mob("minecraft:wither"))
mobs.add(Mob("minecraft:wolf", AC.NOCTURNAL))
mobs.add(Mob("minecraft:zoglin"))
mobs.add(Mob("minecraft:zombie_villager", AC.MIDNIGHT))
mobs.add(Mob("minecraft:zombie"))
@@ -0,0 +1,411 @@
from mcpacker.model.core.ecology.biomefilter import BiomeFilter as BF
from mcpacker.model.core.fauna.mob import Mob
from mcpacker.model.core.fauna.mobcatalog import MobCatalog
from mcpacker.model.core.fauna.mobspawn import MobSpawn
from mcpacker.model.core.habitat import Habitat
from mcpacker.model.modpack import ModPack
import mcpacker.model.core.altitude as AL
import mcpacker.model.core.ecology.flora as FL
import mcpacker.model.core.ecology.geology as GE
import mcpacker.model.core.ecology.heat as HE
import mcpacker.model.core.ecology.humidity as HU
import mcpacker.model.core.ecology.soil as SO
import mcpacker.model.core.ecology.water as WA
import mcpacker.model.core.fauna.active as AC
import mcpacker.model.core.fauna.group as GR
import mcpacker.model.core.fauna.location as LO
import mcpacker.model.core.scarcity as SC
import mcpacker.model.core.season as SE
# Constants ########################################################################################
def addMobs(pack:ModPack):
mobs = pack.world.mobs
spawns = pack.world.mobSpawns
spawns.add(
MobSpawn(mobs["animalplus:raccoon"])
)
spawns.add(
MobSpawn(mobs["animalplus:red_panda"])
)
spawns.add(
MobSpawn(mobs["bearminimum:black_bear"],
Habitat(
altitude = AL.span(AL.LOWLANDS, AL.ALPINE),
biomeFilter = BF([HE.TEMPERATE, FL.FOREST]),
seasons = SE.AUTUMN,
group = GR.merge(GR.SOLO, GR.FAMILY),
scarcity = SC.UNCOMMON,
).derive(
seasons = [SE.SPRING, SE.SUMMER], scarcity = SC.SPARSE
).derive(
seasons = SE.WINTER, scarcity = SC.UNUSUAL
).derive(
biomeFilter = BF([HE.BOREAL, (FL.CANOPY, FL.FOREST)]),
seasons = SE.AUTUMN,
scarcity = SC.SPARSE
).derive(
seasons = [SE.SPRING, SE.SUMMER], scarcity = SC.UNUSUAL
).derive(
seasons = SE.WINTER, scarcity = SC.RARE
)
)
)
spawns.add(
MobSpawn(mobs["minecraft:armadillo"],
Habitat(
altitude = AL.LOWLANDS,
biomeFilter = BF([HE.SUBTROPICAL, HU.DRY]),
seasons = SE.SUMMER,
group = GR.merge(GR.SOLO, GR.PAIR),
scarcity = SC.COMMON
).derive(
altitude = AL.UPLANDS, scarcity = SC.SPARSE,
).derive(
altitude = AL.LOWLANDS, seasons = SE.exclude(SE.SUMMER), scarcity = SC.UNCOMMON,
).derive(
altitude = AL.UPLANDS, scarcity = SC.UNUSUAL,
)
)
)
spawns.add(
MobSpawn(mobs["minecraft:blaze"],
Habitat(
altitude = AL.PLUTONIC,
location = LO.CAVE,
group = GR.SOLO,
scarcity = SC.UNCOMMON
)
)
)
spawns.add(
MobSpawn(mobs["minecraft:bogged"])
)
spawns.add(
MobSpawn(mobs["bearminimum:brown_bear"],
Habitat(
altitude = AL.span(AL.LOWLANDS, AL.ALPINE),
biomeFilter = BF([HE.BOREAL, (FL.CANOPY, FL.FOREST)]),
seasons = SE.AUTUMN,
group = GR.SOLO,
scarcity = SC.SPARSE
).derive(
seasons = [SE.SPRING, SE.SUMMER], scarcity = SC.UNUSUAL
).derive(
seasons = SE.WINTER, scarcity = SC.RARE
)
)
)
spawns.add(
MobSpawn(mobs["minecraft:camel"],
Habitat(
altitude = AL.span(AL.DUNES, AL.LOWLANDS),
biomeFilter = BF([HE.TROPICAL, HU.DRY, FL.BARREN, SO.SANDY]),
seasons = SE.DRY,
group = GR.FAMILY,
scarcity = SC.UNUSUAL,
).derive(
seasons = SE.WET, scarcity = SC.RARE
)
)
)
spawns.add(
MobSpawn(mobs["minecraft:cave_spider"],
Habitat(
altitude = AL.span(AL.OVERBURDEN, AL.HILLS),
location = LO.CAVE,
group = GR.SOLO,
scarcity = SC.UNCOMMON
)
)
)
spawns.add(
MobSpawn(mobs["cold_sweat:chameleon"],
Habitat(
altitude = AL.span(AL.DUNES, AL.UPLANDS),
biomeFilter = BF([HE.TROPICAL, HU.WET, [FL.CANOPY, FL.FOREST]]),
seasons = SE.WET,
group = GR.merge(GR.SOLO, GR.FAMILY),
scarcity = SC.COMMON,
).derive(
seasons = SE.DRY,
scarcity = SC.UNCOMMON,
).derive(
biomeFilter = BF([HE.TROPICAL, HU.WET, FL.CLEARING]),
seasons = SE.WET,
scarcity = SC.UNCOMMON
).derive(
seasons = SE.DRY,
scarcity = SC.SPARSE,
)
)
)
spawns.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
)
)
)
spawns.add(
MobSpawn(mobs["minecraft:cod"],
Habitat(
altitude = AL.span(AL.SHALLOWS, AL.DEEPS),
biomeFilter = BF([WA.OCEAN, [HE.TEMPERATE, HE.BOREAL]]),
seasons = SE.ALL,
group = GR.HERD,
scarcity = SC.COMMON,
).derive(
biomeFilter = BF([WA.OCEAN, HE.FROZEN]),
scarcity = SC.SPARSE,
)
)
)
spawns.add(
MobSpawn(mobs["minecraft:cow"],
Habitat(
altitude = AL.span(AL.LOWLANDS, AL.UPLANDS),
biomeFilter = BF(
[
HE.within(HE.SUBTROPICAL, HE.BOREAL),
[HU.DAMP, HU.DRY],
FL.FIELD,
], [
SO.FUNGAL
]
),
seasons = [SE.SPRING, SE.SUMMER],
group = GR.HERD,
scarcity = SC.COMMON,
).derive(
altitude = AL.LOWLANDS,
seasons = [SE.AUTUMN, SE.WINTER],
groups = GR.TROUP,
scarcity = SC.SPARSE,
)
)
)
spawns.add(
MobSpawn(mobs["crabbersdelight:crab"],
Habitat(
altitude = AL.DUNES,
biomeFilter = BF([WA.COAST, [HE.TROPICAL, HE.SUBTROPICAL]]),
seasons = SE.ALL,
group = GR.SOLO,
scarcity = SC.COMMON,
).derive(
biomeFilter = BF([WA.COAST, [HE.TEMPERATE, HE.BOREAL]]),
scarcity = SC.UNCOMMON,
)
)
)
spawns.add(
MobSpawn(mobs["minecraft:dolphin"],
Habitat(
altitude = AL.span(AL.SURFACE, AL.DEEPS),
biomeFilter = BF([WA.OCEAN, [HE.SUBTROPICAL, HE.TEMPERATE]]),
location = LO.WATER,
seasons = SE.SUMMER,
group = GR.FAMILY,
scarcity = SC.UNCOMMON,
).derive(
seasons = SE.exclude(SE.SUMMER),
scarcity = SC.SPARSE,
)
)
)
spawns.add(MobSpawn(mobs["minecraft:donkey"],
Habitat(
altitude = AL.span(AL.UPLANDS, AL.HILLS),
biomeFilter = BF([HE.SUBTROPICAL, HU.DRY, FL.FIELD]),
seasons = [SE.SUMMER, SE.AUTUMN],
group = GR.FAMILY,
scarcity = SC.UNCOMMON,
).derive(
seasons = [SE.WINTER, SE.SPRING],
scarcity = SC.SPARSE,
)
))
spawns.add(MobSpawn(mobs["minecraft:fox"],
Habitat(
altitude = AL.span(AL.LOWLANDS, AL.HILLS),
biomeFilter = BF([HE.within(HE.TEMPERATE, HE.FROZEN), FL.FOREST]),
seasons = [SE.SPRING],
group = GR.FAMILY,
scarcity = SC.UNCOMMON,
).derive(
seasons = SE.exclude(SE.SPRING),
group = GR.SOLO,
scarcity = SC.SPARSE,
)
))
spawns.add(
MobSpawn(mobs["minecraft:frog"])
)
spawns.add(
MobSpawn(mobs["minecraft:glow_squid"])
)
spawns.add(
MobSpawn(mobs["minecraft:goat"])
)
spawns.add(
MobSpawn(mobs["minecraft:horse"])
)
spawns.add(
MobSpawn(mobs["minecraft:husk"])
)
spawns.add(
MobSpawn(mobs["minecraft:llama"])
)
spawns.add(
MobSpawn(mobs["minecraft:magma_cube"])
)
spawns.add(
MobSpawn(mobs["minecraft:mooshroom"])
)
spawns.add(
MobSpawn(mobs["minecraft:mule"])
)
spawns.add(
MobSpawn(mobs["minecraft:ocelot"])
)
spawns.add(
MobSpawn(mobs["minecraft:panda"])
)
spawns.add(
MobSpawn(mobs["minecraft:parrot"])
)
spawns.add(
MobSpawn(mobs["minecraft:pig"])
)
spawns.add(
MobSpawn(mobs["minecraft:polar_bear"])
)
spawns.add(
MobSpawn(mobs["minecraft:pufferfish"])
)
spawns.add(
MobSpawn(mobs["minecraft:rabbit"])
)
spawns.add(
MobSpawn(mobs["minecraft:salmon"])
)
spawns.add(
MobSpawn(mobs["minecraft:sheep"])
)
spawns.add(
MobSpawn(mobs["minecraft:skeleton"])
)
spawns.add(
MobSpawn(mobs["minecraft:slime"])
)
spawns.add(
MobSpawn(mobs["minecraft:spider"])
)
spawns.add(
MobSpawn(mobs["minecraft:squid"])
)
spawns.add(
MobSpawn(mobs["minecraft:tropical_fish"])
)
spawns.add(
MobSpawn(mobs["minecraft:turtle"])
)
spawns.add(
MobSpawn(mobs["minecraft:vex"])
)
spawns.add(
MobSpawn(mobs["minecraft:villager"])
)
spawns.add(
MobSpawn(mobs["minecraft:vindicator"])
)
spawns.add(
MobSpawn(mobs["minecraft:wandering_trader"])
)
spawns.add(
MobSpawn(mobs["minecraft:warden"])
)
spawns.add(
MobSpawn(mobs["minecraft:witch"])
)
spawns.add(
MobSpawn(mobs["minecraft:wither_skeleton"])
)
spawns.add(
MobSpawn(mobs["minecraft:wither"])
)
spawns.add(
MobSpawn(mobs["minecraft:wolf"])
)
spawns.add(
MobSpawn(mobs["minecraft:zoglin"])
)
spawns.add(
MobSpawn(mobs["minecraft:zombie_villager"])
)
spawns.add(
MobSpawn(mobs["minecraft:zombie"])
)
@@ -0,0 +1,7 @@
import mcpacker.pack.mysteriousisland.addmobspawns
# Class ############################################################################################
def test_syntax():
pass
+2 -2
View File
@@ -27,7 +27,7 @@ class Runner:
def run(self, argv:list[str]):
if len(sys.argv) < 2:
abort("Please provide a command name")
self.abort("Please provide a command name")
commandName = "_command_" + argv[1]
for name, doCommand in inspect.getmembers(self):
@@ -35,7 +35,7 @@ class Runner:
doCommand(*argv[2:])
sys.exit(0)
abort("No command named: " + argv[1])
self.abort("No command named: " + argv[1])
# Commands #################################################################
+3
View File
@@ -1,8 +1,11 @@
exceptiongroup==1.3.0
iniconfig==2.1.0
jedi==0.19.2
mypy==1.18.2
mypy_extensions==1.1.0
packaging==25.0
parso==0.8.4
pathspec==0.12.1
pluggy==1.6.0
Pygments==2.19.2
pytest==8.4.2