Add placed feature foramtters for plants
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
from mcpacker.format.json import JsonBlob
|
||||
from mcpacker.model.flora.patch import Patch
|
||||
from mcpacker.model.flora.plantspawn import PlantSpawn
|
||||
from mcpacker.model.modpack import ModPack
|
||||
|
||||
import mcpacker.model.flora.immersion as IM
|
||||
|
||||
|
||||
# Class ############################################################################################
|
||||
|
||||
class CropPlacedFeature:
|
||||
|
||||
def __init__(self, pack:ModPack, spawn:PlantSpawn):
|
||||
self.pack = pack
|
||||
self.spawn = spawn
|
||||
|
||||
def asJsonBlob(self) -> JsonBlob:
|
||||
return {
|
||||
"feature": f"{self.pack.name}:crop/{self.spawn.plant.name}",
|
||||
"placement": self._buildPlacements(),
|
||||
}
|
||||
|
||||
@property
|
||||
def patch(self) -> Patch:
|
||||
if not isinstance(self.spawn.plant, Patch): raise ValueError()
|
||||
return self.spawn.plant
|
||||
|
||||
# Private Methods ##########################################################
|
||||
|
||||
def _buildPlacements(self) -> list[JsonBlob]:
|
||||
livesIn = "minecraft:air"
|
||||
if self.patch.immersion == IM.SUBMERGED:
|
||||
livesIn = "minecraft:water"
|
||||
|
||||
placements:list[JsonBlob] = [
|
||||
{
|
||||
"type": "minecraft:block_predicate_filter",
|
||||
"predicate": [
|
||||
{
|
||||
"type": "minecraft:matching_blocks",
|
||||
"blocks": livesIn,
|
||||
}, {
|
||||
"type": "minecraft:matching_blocks",
|
||||
"offset": [0, -1, 0],
|
||||
"blocks": list(str(s) for s in self.patch.substrates),
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
if self.patch.immersion == IM.ADJACENT:
|
||||
placements.append(self._buildWaterAdjacentPlacement())
|
||||
|
||||
return placements
|
||||
|
||||
def _buildWaterAdjacentPlacement(self) -> JsonBlob:
|
||||
return {
|
||||
"type": "minecraft:any_of",
|
||||
"predicates": [
|
||||
{
|
||||
"type": "minecraft:matching_fluids",
|
||||
"fluids": [
|
||||
"minecraft:water",
|
||||
"minecraft:flowing_water"
|
||||
],
|
||||
"offset": [ 1, -1, 0 ]
|
||||
}, {
|
||||
"type": "minecraft:matching_fluids",
|
||||
"fluids": [
|
||||
"minecraft:water",
|
||||
"minecraft:flowing_water"
|
||||
],
|
||||
"offset": [ -1, -1, 0 ]
|
||||
}, {
|
||||
"type": "minecraft:matching_fluids",
|
||||
"fluids": [
|
||||
"minecraft:water",
|
||||
"minecraft:flowing_water"
|
||||
],
|
||||
"offset": [ 0, -1, 1 ]
|
||||
}, {
|
||||
"type": "minecraft:matching_fluids",
|
||||
"fluids": [
|
||||
"minecraft:water",
|
||||
"minecraft:flowing_water"
|
||||
],
|
||||
"offset": [ 0, -1, -1 ]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
from mcpacker.format.datapack.placedfeature.crop import CropPlacedFeature
|
||||
from mcpacker.format.json import JsonBlob
|
||||
from mcpacker.model.ecology.biomefilter import BiomeFilter as BF
|
||||
from mcpacker.model.ecotype import Ecotype
|
||||
from mcpacker.model.flora.patch import Patch
|
||||
from mcpacker.model.flora.plantspawn import PlantSpawn
|
||||
from mcpacker.model.flora.tree import Tree
|
||||
from mcpacker.model.habitat import Habitat
|
||||
from mcpacker.model.modpack import ModPack
|
||||
from mcpacker.write.json import dumps
|
||||
from pytest import fixture
|
||||
|
||||
import mcpacker.model.altitude as AL
|
||||
import mcpacker.model.ecology.flora as FL
|
||||
import mcpacker.model.ecology.heat as HE
|
||||
import mcpacker.model.ecology.humidity as HU
|
||||
import mcpacker.model.ecology.soil as SO
|
||||
import mcpacker.model.ecology.water as WA
|
||||
import mcpacker.model.flora.canopyshape as CA
|
||||
import mcpacker.model.flora.density as DE
|
||||
import mcpacker.model.flora.immersion as IM
|
||||
import mcpacker.model.flora.trunkshape as TR
|
||||
import mcpacker.model.scarcity as SC
|
||||
import textwrap
|
||||
|
||||
|
||||
# Fixtures #########################################################################################
|
||||
|
||||
@fixture(name="pack")
|
||||
def createModPack():
|
||||
yield ModPack("test")
|
||||
|
||||
@fixture(name="farmCropBlob")
|
||||
def createFarmCropBlob(pack:ModPack):
|
||||
yield CropPlacedFeature(
|
||||
pack = pack,
|
||||
spawn = PlantSpawn(
|
||||
name = "potato",
|
||||
habitat = Habitat(
|
||||
altitude = AL.span(AL.UPLANDS, AL.HILLS),
|
||||
biomeFilters = BF([FL.FOREST, HE.BOREAL, HU.DAMP, SO.ACIDIC]),
|
||||
),
|
||||
ecotype = Ecotype(
|
||||
scarcity = SC.UNCOMMON,
|
||||
),
|
||||
plant = Patch(
|
||||
name = "potato",
|
||||
blocks = "farmersdelight:wild_potatoes",
|
||||
density = DE.THIN,
|
||||
radius = 4,
|
||||
),
|
||||
),
|
||||
).asJsonBlob()
|
||||
|
||||
@fixture(name="waterAdjacentCropBlob")
|
||||
def createWaterAdjacentBlob(pack:ModPack):
|
||||
yield CropPlacedFeature(
|
||||
pack = pack,
|
||||
spawn = PlantSpawn(
|
||||
name = "sugarcane",
|
||||
habitat = Habitat(
|
||||
altitude = AL.DUNES,
|
||||
biomeFilters = BF([(WA.RIVER, WA.SWAMP)]),
|
||||
),
|
||||
ecotype = Ecotype(
|
||||
scarcity = SC.COMMON,
|
||||
),
|
||||
plant = Patch(
|
||||
name = "sugarcane",
|
||||
blocks = "minecraft:sugarcane",
|
||||
density = DE.CARPET,
|
||||
immersion = IM.ADJACENT,
|
||||
radius = 3,
|
||||
),
|
||||
),
|
||||
).asJsonBlob()
|
||||
|
||||
@fixture(name="submergedCropBlob")
|
||||
def createSubmergedCropBlob(pack:ModPack):
|
||||
yield CropPlacedFeature(
|
||||
pack = pack,
|
||||
spawn = PlantSpawn(
|
||||
name = "rice",
|
||||
habitat = Habitat(
|
||||
altitude = AL.SURFACE,
|
||||
biomeFilters = BF([(WA.RIVER, WA.SWAMP)]),
|
||||
),
|
||||
ecotype = Ecotype(
|
||||
scarcity = SC.UNCOMMON,
|
||||
),
|
||||
plant = Patch(
|
||||
name = "rice",
|
||||
blocks = "farmersdelight:wild_rice",
|
||||
density = DE.PACKED,
|
||||
immersion = IM.SUBMERGED,
|
||||
radius = 6,
|
||||
substrates = [ "minecraft:sand", "minecraft:dirt" ],
|
||||
),
|
||||
),
|
||||
).asJsonBlob()
|
||||
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_farmCrop(farmCropBlob:JsonBlob):
|
||||
assert dumps(farmCropBlob) == textwrap.dedent("""
|
||||
{
|
||||
"feature": "test:crop/potato",
|
||||
"placement": [
|
||||
{
|
||||
"type": "minecraft:block_predicate_filter",
|
||||
"predicate": [
|
||||
{
|
||||
"type": "minecraft:matching_blocks",
|
||||
"blocks": "minecraft:air"
|
||||
},
|
||||
{
|
||||
"type": "minecraft:matching_blocks",
|
||||
"offset": [
|
||||
0,
|
||||
-1,
|
||||
0
|
||||
],
|
||||
"blocks": [
|
||||
"#minecraft:dirt"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
""").strip()
|
||||
|
||||
def test_submergedCrop(submergedCropBlob:JsonBlob):
|
||||
assert dumps(submergedCropBlob) == textwrap.dedent("""
|
||||
{
|
||||
"feature": "test:crop/rice",
|
||||
"placement": [
|
||||
{
|
||||
"type": "minecraft:block_predicate_filter",
|
||||
"predicate": [
|
||||
{
|
||||
"type": "minecraft:matching_blocks",
|
||||
"blocks": "minecraft:water"
|
||||
},
|
||||
{
|
||||
"type": "minecraft:matching_blocks",
|
||||
"offset": [
|
||||
0,
|
||||
-1,
|
||||
0
|
||||
],
|
||||
"blocks": [
|
||||
"minecraft:sand",
|
||||
"minecraft:dirt"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
""").strip()
|
||||
|
||||
def test_waterAdjacentCrop(waterAdjacentCropBlob:JsonBlob):
|
||||
assert dumps(waterAdjacentCropBlob) == textwrap.dedent("""
|
||||
{
|
||||
"feature": "test:crop/sugarcane",
|
||||
"placement": [
|
||||
{
|
||||
"type": "minecraft:block_predicate_filter",
|
||||
"predicate": [
|
||||
{
|
||||
"type": "minecraft:matching_blocks",
|
||||
"blocks": "minecraft:air"
|
||||
},
|
||||
{
|
||||
"type": "minecraft:matching_blocks",
|
||||
"offset": [
|
||||
0,
|
||||
-1,
|
||||
0
|
||||
],
|
||||
"blocks": [
|
||||
"#minecraft:dirt"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "minecraft:any_of",
|
||||
"predicates": [
|
||||
{
|
||||
"type": "minecraft:matching_fluids",
|
||||
"fluids": [
|
||||
"minecraft:water",
|
||||
"minecraft:flowing_water"
|
||||
],
|
||||
"offset": [
|
||||
1,
|
||||
-1,
|
||||
0
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "minecraft:matching_fluids",
|
||||
"fluids": [
|
||||
"minecraft:water",
|
||||
"minecraft:flowing_water"
|
||||
],
|
||||
"offset": [
|
||||
-1,
|
||||
-1,
|
||||
0
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "minecraft:matching_fluids",
|
||||
"fluids": [
|
||||
"minecraft:water",
|
||||
"minecraft:flowing_water"
|
||||
],
|
||||
"offset": [
|
||||
0,
|
||||
-1,
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "minecraft:matching_fluids",
|
||||
"fluids": [
|
||||
"minecraft:water",
|
||||
"minecraft:flowing_water"
|
||||
],
|
||||
"offset": [
|
||||
0,
|
||||
-1,
|
||||
-1
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
""").strip()
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
from mcpacker.model.altitude import Altitude
|
||||
from mcpacker.format.json import JsonBlob
|
||||
from mcpacker.model.flora.patch import Patch
|
||||
from mcpacker.model.flora.plantspawn import PlantSpawn
|
||||
from mcpacker.model.modpack import ModPack
|
||||
|
||||
import mcpacker.model.altitude as AL
|
||||
import mcpacker.model.scarcity as SC
|
||||
|
||||
|
||||
# Class ############################################################################################
|
||||
|
||||
class PatchPlacedFeature:
|
||||
|
||||
def __init__(self, pack:ModPack, spawn:PlantSpawn, altitude:Altitude):
|
||||
self.altitude = altitude
|
||||
self.pack = pack
|
||||
self.spawn = spawn
|
||||
|
||||
def asJsonBlob(self) -> JsonBlob|None:
|
||||
fullRange = self.spawn.habitat.altitude
|
||||
band = AL.intersect(self.altitude, fullRange)
|
||||
if not band: return None
|
||||
|
||||
kind = "tree"
|
||||
if isinstance(self.spawn.plant, Patch):
|
||||
kind = "patch"
|
||||
|
||||
return {
|
||||
"feature": f"{self.pack.name}:{kind}/{self.spawn.plant.name}",
|
||||
"placement": [
|
||||
{
|
||||
"type": "mincraft:rarity_filter",
|
||||
"chance": self._getRarityChance(),
|
||||
}, {
|
||||
"type": "minecraft:in_square",
|
||||
}, {
|
||||
"type": "minecraft:biome",
|
||||
}, {
|
||||
"type": "minecraft:height_range",
|
||||
"height": {
|
||||
"type": "minecraft:constant",
|
||||
"value": {
|
||||
"absolute": band.bottom,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
"type": "minecraft:block_predicate_filter",
|
||||
"predicate": {
|
||||
"type": "minecraft:matching_blocks",
|
||||
"blocks": "minecraft:air",
|
||||
},
|
||||
}, {
|
||||
"type": "minecraft:environment_scan",
|
||||
"direction_of_search": "up",
|
||||
"max_steps": min(32, band.top - band.bottom),
|
||||
"target_condition": {
|
||||
"type": "minecraft:matching_blocks",
|
||||
"blocks": "minecraft:air",
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
# Private Methods ##########################################################
|
||||
|
||||
def _getRarityChance(self) -> int:
|
||||
return {
|
||||
SC.ABSENT: 100000,
|
||||
SC.RARE: 64,
|
||||
SC.UNUSUAL: 32,
|
||||
SC.SPARSE: 16,
|
||||
SC.UNCOMMON: 8,
|
||||
SC.COMMON: 3,
|
||||
SC.CARPET: 2
|
||||
}[self.spawn.ecotype.scarcity]
|
||||
@@ -0,0 +1,197 @@
|
||||
from mcpacker.format.datapack.placedfeature.patch import PatchPlacedFeature
|
||||
from mcpacker.format.json import JsonBlob
|
||||
from mcpacker.model.ecology.biomefilter import BiomeFilter as BF
|
||||
from mcpacker.model.ecotype import Ecotype
|
||||
from mcpacker.model.flora.patch import Patch
|
||||
from mcpacker.model.flora.plantspawn import PlantSpawn
|
||||
from mcpacker.model.flora.tree import Tree
|
||||
from mcpacker.model.habitat import Habitat
|
||||
from mcpacker.model.modpack import ModPack
|
||||
from mcpacker.write.json import dumps
|
||||
from pytest import fixture
|
||||
|
||||
import mcpacker.model.altitude as AL
|
||||
import mcpacker.model.ecology.flora as FL
|
||||
import mcpacker.model.ecology.heat as HE
|
||||
import mcpacker.model.ecology.humidity as HU
|
||||
import mcpacker.model.ecology.soil as SO
|
||||
import mcpacker.model.ecology.water as WA
|
||||
import mcpacker.model.flora.canopyshape as CA
|
||||
import mcpacker.model.flora.density as DE
|
||||
import mcpacker.model.flora.immersion as IM
|
||||
import mcpacker.model.flora.trunkshape as TR
|
||||
import mcpacker.model.scarcity as SC
|
||||
import textwrap
|
||||
|
||||
|
||||
# Fixtures #########################################################################################
|
||||
|
||||
@fixture(name="pack")
|
||||
def createModPack():
|
||||
yield ModPack("test")
|
||||
|
||||
@fixture(name="farmPatchBlob")
|
||||
def createFarmPatchBlob(pack:ModPack):
|
||||
yield PatchPlacedFeature(
|
||||
altitude = AL.UPLANDS,
|
||||
pack = pack,
|
||||
spawn = PlantSpawn(
|
||||
name = "potato",
|
||||
habitat = Habitat(
|
||||
altitude = AL.span(AL.UPLANDS, AL.HILLS),
|
||||
biomeFilters = BF([FL.FOREST, HE.BOREAL, HU.DAMP, SO.ACIDIC]),
|
||||
),
|
||||
ecotype = Ecotype(
|
||||
scarcity = SC.UNCOMMON,
|
||||
),
|
||||
plant = Patch(
|
||||
name = "potato",
|
||||
blocks = "farmersdelight:wild_potatoes",
|
||||
density = DE.THIN,
|
||||
radius = 4,
|
||||
),
|
||||
),
|
||||
).asJsonBlob()
|
||||
|
||||
@fixture(name="waterAdjacentBlob")
|
||||
def createWaterAdjacentBlob(pack:ModPack):
|
||||
yield PatchPlacedFeature(
|
||||
altitude = AL.DUNES,
|
||||
pack = pack,
|
||||
spawn = PlantSpawn(
|
||||
name = "sugarcane",
|
||||
habitat = Habitat(
|
||||
altitude = AL.DUNES,
|
||||
biomeFilters = BF([(WA.RIVER, WA.SWAMP)]),
|
||||
),
|
||||
ecotype = Ecotype(
|
||||
scarcity = SC.COMMON,
|
||||
),
|
||||
plant = Patch(
|
||||
name = "sugarcane",
|
||||
blocks = "minecraft:sugarcane",
|
||||
density = DE.CARPET,
|
||||
immersion = IM.ADJACENT,
|
||||
radius = 3,
|
||||
),
|
||||
),
|
||||
).asJsonBlob()
|
||||
|
||||
@fixture(name="treeBlob")
|
||||
def createTreeBlob(pack:ModPack):
|
||||
yield PatchPlacedFeature(
|
||||
altitude = AL.LOWLANDS,
|
||||
pack = pack,
|
||||
spawn = PlantSpawn(
|
||||
name = "apple",
|
||||
habitat = Habitat(
|
||||
altitude = AL.LOWLANDS,
|
||||
biomeFilters = BF([(FL.FOREST, FL.CLEARING), HE.TEMPERATE, SO.LOAMY]),
|
||||
),
|
||||
ecotype = Ecotype(
|
||||
scarcity = SC.SPARSE,
|
||||
),
|
||||
plant = Tree(
|
||||
name = "apple",
|
||||
foliage = "fruitsdelight:apple_leaves",
|
||||
log = "minecraft:oak_log",
|
||||
trunkShape = TR.STRAIGHT,
|
||||
trunkHeightMin = 3,
|
||||
trunkHeightMax = 5,
|
||||
canopyShape = CA.BLOB,
|
||||
canopyHeight = 3,
|
||||
canopyRadius = 2
|
||||
)
|
||||
)
|
||||
).asJsonBlob()
|
||||
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_farmPatch(farmPatchBlob:JsonBlob):
|
||||
assert dumps(farmPatchBlob) == textwrap.dedent("""
|
||||
{
|
||||
"feature": "test:patch/potato",
|
||||
"placement": [
|
||||
{
|
||||
"type": "mincraft:rarity_filter",
|
||||
"chance": 8
|
||||
},
|
||||
{
|
||||
"type": "minecraft:in_square"
|
||||
},
|
||||
{
|
||||
"type": "minecraft:biome"
|
||||
},
|
||||
{
|
||||
"type": "minecraft:height_range",
|
||||
"height": {
|
||||
"type": "minecraft:constant",
|
||||
"value": {
|
||||
"absolute": 92
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "minecraft:block_predicate_filter",
|
||||
"predicate": {
|
||||
"type": "minecraft:matching_blocks",
|
||||
"blocks": "minecraft:air"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "minecraft:environment_scan",
|
||||
"direction_of_search": "up",
|
||||
"max_steps": 32,
|
||||
"target_condition": {
|
||||
"type": "minecraft:matching_blocks",
|
||||
"blocks": "minecraft:air"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
""").strip()
|
||||
|
||||
def test_tree(treeBlob:JsonBlob):
|
||||
assert dumps(treeBlob) == textwrap.dedent("""
|
||||
{
|
||||
"feature": "test:tree/apple",
|
||||
"placement": [
|
||||
{
|
||||
"type": "mincraft:rarity_filter",
|
||||
"chance": 16
|
||||
},
|
||||
{
|
||||
"type": "minecraft:in_square"
|
||||
},
|
||||
{
|
||||
"type": "minecraft:biome"
|
||||
},
|
||||
{
|
||||
"type": "minecraft:height_range",
|
||||
"height": {
|
||||
"type": "minecraft:constant",
|
||||
"value": {
|
||||
"absolute": 70
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "minecraft:block_predicate_filter",
|
||||
"predicate": {
|
||||
"type": "minecraft:matching_blocks",
|
||||
"blocks": "minecraft:air"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "minecraft:environment_scan",
|
||||
"direction_of_search": "up",
|
||||
"max_steps": 22,
|
||||
"target_condition": {
|
||||
"type": "minecraft:matching_blocks",
|
||||
"blocks": "minecraft:air"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
""").strip()
|
||||
@@ -70,7 +70,22 @@ ALL = OVERGROUND + UNDERGROUND + UNDERWATER
|
||||
|
||||
# Helper Functions #################################################################################
|
||||
|
||||
def span(*altitudes:Altitude):
|
||||
def intersect(*altitudes:Altitude) -> Altitude|None:
|
||||
if not altitudes: return None
|
||||
|
||||
bottom = altitudes[0].bottom
|
||||
top = altitudes[0].top
|
||||
|
||||
for index in range(1, len(altitudes)):
|
||||
bottom = max(bottom, altitudes[index].bottom)
|
||||
top = min(top, altitudes[index].top)
|
||||
|
||||
if bottom >= top:
|
||||
return None
|
||||
|
||||
return Altitude(altitudes[0].name, bottom, top)
|
||||
|
||||
def span(*altitudes:Altitude) -> Altitude:
|
||||
if not altitudes: return ANYWHERE
|
||||
result = Altitude("", ANYWHERE.top, ANYWHERE.bottom)
|
||||
|
||||
|
||||
@@ -5,9 +5,4 @@ from mcpacker.write.writer import Writer
|
||||
class BlockStateWriter(Writer):
|
||||
|
||||
def doWrite(self):
|
||||
for mod in self.pack.resourcePack.mods:
|
||||
for blockState in mod.blockStates:
|
||||
path = self.locator.blockStates() # filename?
|
||||
# TODO: not implemented
|
||||
|
||||
|
||||
pass
|
||||
|
||||
Reference in New Issue
Block a user