From bd5e13d838ed3e7956b49580502665967c19007f Mon Sep 17 00:00:00 2001 From: Andrew Miner Date: Thu, 30 Oct 2025 14:47:01 -0600 Subject: [PATCH] Add configured features for patch and crop --- .../datapack/configuredfeature/__init__.py | 0 .../format/datapack/configuredfeature/crop.py | 64 +++++++++++++++++++ .../datapack/configuredfeature/crop_test.py | 56 ++++++++++++++++ .../datapack/configuredfeature/patch.py | 23 +++++++ .../datapack/configuredfeature/patch_test.py | 41 ++++++++++++ mcpacker/model/flora/companion.py | 6 +- 6 files changed, 187 insertions(+), 3 deletions(-) create mode 100644 mcpacker/format/datapack/configuredfeature/__init__.py create mode 100644 mcpacker/format/datapack/configuredfeature/crop.py create mode 100644 mcpacker/format/datapack/configuredfeature/crop_test.py create mode 100644 mcpacker/format/datapack/configuredfeature/patch.py create mode 100644 mcpacker/format/datapack/configuredfeature/patch_test.py diff --git a/mcpacker/format/datapack/configuredfeature/__init__.py b/mcpacker/format/datapack/configuredfeature/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/mcpacker/format/datapack/configuredfeature/crop.py b/mcpacker/format/datapack/configuredfeature/crop.py new file mode 100644 index 0000000..a687bc5 --- /dev/null +++ b/mcpacker/format/datapack/configuredfeature/crop.py @@ -0,0 +1,64 @@ +from mcpacker.format.json import JsonBlob +from mcpacker.model.flora.companion import Companion +from mcpacker.model.flora.patch import Patch +from typing import cast + +import mcpacker.format.json as json +import mcpacker.model.flora.immersion as IM + + +# Class ############################################################################################ + +class CropConfiguredFeature: + + def __init__(self, patch:Patch): + self.patch = patch + + def asJsonBlob(self) -> JsonBlob: + return { + "type": "minecraft:simple_block", + "config": { + "to_place": { + "type": "minecraft:weighted_state_provider", + "entries": self._buildCompanionEntries(), + } + } + } + + # Private Methods ########################################################## + + def _buildBaseProperties(self) -> JsonBlob: + result = { k: str(v) for k, v in (self.patch.blocks[0].properties or {}).items() } + if self.patch.immersion == IM.SUBMERGED: + result["waterlogged"] = "true" + + return cast(JsonBlob, result) + + def _buildCompanionEntries(self) -> JsonBlob: + usedWeight = sum(c.weight for c in self.patch.companions) + weight = 100 - usedWeight + + results:list[JsonBlob] = [] + results.append(self._buildPlantState(weight)) + + for companion in self.patch.companions: + results.append({ + "data": { "Name": str(companion.gameId) }, + "weight": companion.weight, + }) + + return results + + def _buildPlantState(self, weight) -> JsonBlob: + result = { + "data": { + "Name": str(self.patch.blocks[0].gameId), + }, + "weight": weight, + } + + properties = self._buildBaseProperties() + if properties: + result["data"]["Properties"] = properties + + return result diff --git a/mcpacker/format/datapack/configuredfeature/crop_test.py b/mcpacker/format/datapack/configuredfeature/crop_test.py new file mode 100644 index 0000000..062327f --- /dev/null +++ b/mcpacker/format/datapack/configuredfeature/crop_test.py @@ -0,0 +1,56 @@ +from mcpacker.format.datapack.configuredfeature.crop import CropConfiguredFeature +from mcpacker.format.json import JsonBlob +from mcpacker.model.flora.companion import Companion +from mcpacker.model.flora.patch import Patch +from pytest import fixture + +import mcpacker.model.flora.density as DE +import mcpacker.model.flora.immersion as IM +import mcpacker.write.json as json +import textwrap + + +# Fixtures ######################################################################################### + +@fixture(name="blob") +def createBlob(): + yield CropConfiguredFeature(Patch( + blocks = "minecraft:poppy", + companions = [Companion("dandelion", 25)], + density = DE.THIN, + immersion = IM.SUBMERGED, + name = "poppy", + radius = 3, + )).asJsonBlob() + + +# Tests ############################################################################################ + +def test_asJsonBlob(blob:JsonBlob): + assert json.dumps(blob) == textwrap.dedent(""" + { + "type": "minecraft:simple_block", + "config": { + "to_place": { + "type": "minecraft:weighted_state_provider", + "entries": [ + { + "data": { + "Name": "minecraft:poppy", + "Properties": { + "waterlogged": "true" + } + }, + "weight": 75 + }, + { + "data": { + "Name": "minecraft:dandelion" + }, + "weight": 25 + } + ] + } + } + } + """).strip() diff --git a/mcpacker/format/datapack/configuredfeature/patch.py b/mcpacker/format/datapack/configuredfeature/patch.py new file mode 100644 index 0000000..9873e52 --- /dev/null +++ b/mcpacker/format/datapack/configuredfeature/patch.py @@ -0,0 +1,23 @@ +from mcpacker.format.json import JsonBlob +from mcpacker.model.flora.patch import Patch +from mcpacker.model.modpack import ModPack + + +# Class ############################################################################################ + +class PatchConfiguredFeature: + + def __init__(self, pack:ModPack, patch:Patch): + self.pack = pack + self.patch = patch + + def asJsonBlob(self) -> JsonBlob: + return { + "type": "minecraft:random_patch", + "config": { + "tries": self.patch.attempts(), + "xz_spread": self.patch.radius, + "y_spread": self.patch.radius / 2, + "feature": f"{self.pack.name}:plant/{self.patch.name}", + } + } diff --git a/mcpacker/format/datapack/configuredfeature/patch_test.py b/mcpacker/format/datapack/configuredfeature/patch_test.py new file mode 100644 index 0000000..e8052ab --- /dev/null +++ b/mcpacker/format/datapack/configuredfeature/patch_test.py @@ -0,0 +1,41 @@ +from mcpacker.format.datapack.configuredfeature.patch import PatchConfiguredFeature +from mcpacker.format.json import JsonBlob +from mcpacker.model.flora.companion import Companion +from mcpacker.model.flora.patch import Patch +from mcpacker.model.modpack import ModPack +from pytest import fixture + +import mcpacker.model.flora.density as DE +import mcpacker.model.flora.immersion as IM +import mcpacker.write.json as json +import textwrap + + +# Fixtures ######################################################################################### + +@fixture(name="blob") +def createBlob(): + yield PatchConfiguredFeature(ModPack("test"), Patch( + blocks = "minecraft:poppy", + companions = [Companion("dandelion", 25)], + density = DE.THIN, + immersion = IM.SUBMERGED, + name = "poppy", + radius = 3, + )).asJsonBlob() + + +# Tests ############################################################################################ + +def test_asJsonBlob(blob:JsonBlob): + assert json.dumps(blob) == textwrap.dedent(""" + { + "type": "minecraft:random_patch", + "config": { + "tries": 58, + "xz_spread": 3, + "y_spread": 1.5, + "feature": "test:plant/poppy" + } + } + """).strip() diff --git a/mcpacker/model/flora/companion.py b/mcpacker/model/flora/companion.py index 2986c51..27c7abd 100644 --- a/mcpacker/model/flora/companion.py +++ b/mcpacker/model/flora/companion.py @@ -1,10 +1,10 @@ -from mcpacker.model.flora.plant import Plant +from mcpacker.model.resourceid import ResourceId # Class ############################################################################################ class Companion: - def __init__(self, plant:Plant, weight:int): - self.plant = plant + def __init__(self, gameId:ResourceId|str, weight:int): + self.gameId = ResourceId.parse(gameId) self.weight = weight