From ec26afda0bbfaa205ddf65d9f2e30237b7e4c00a Mon Sep 17 00:00:00 2001 From: Andrew Miner Date: Fri, 31 Oct 2025 10:53:12 -0600 Subject: [PATCH] Add tree configured feature formatter --- .../format/datapack/configuredfeature/tree.py | 87 +++++++++++++++++++ .../datapack/configuredfeature/tree_test.py | 86 ++++++++++++++++++ mcpacker/model/flora/tree.py | 2 +- 3 files changed, 174 insertions(+), 1 deletion(-) create mode 100644 mcpacker/format/datapack/configuredfeature/tree.py create mode 100644 mcpacker/format/datapack/configuredfeature/tree_test.py diff --git a/mcpacker/format/datapack/configuredfeature/tree.py b/mcpacker/format/datapack/configuredfeature/tree.py new file mode 100644 index 0000000..bb09179 --- /dev/null +++ b/mcpacker/format/datapack/configuredfeature/tree.py @@ -0,0 +1,87 @@ +from mcpacker.model.flora.tree import Tree +from mcpacker.format.json import JsonBlob + +import mcpacker.model.flora.trunkshape as TR +import math + + +# Class ############################################################################################ + +class TreeConfiguredFeature: + + def __init__(self, tree:Tree): + self.tree = tree + + def asJsonBlob(self) -> JsonBlob: + return { + "type": "minecraft:tree", + "config": { + "dirt_provider": self._buildDirtProvider(), + "foliage_placer": self._buildFoliagePlacer(), + "foliage_provider": self._buildFoliageProvider(), + "force_dirt": False, + "ignore_vines": True, + "minimum_size": self._buildMinimumSize(), + "trunk_placer": self._buildTrunkPlacer(), + "trunk_provider": self._buildTrunkProvider(), + } + } + + + # Private Methods ########################################################## + + def _buildDirtProvider(self) -> JsonBlob: + return { + "type": "minecraft:simple_state_provider", + "state": { "Name": "minecraft:dirt" }, + } + + def _buildFoliagePlacer(self) -> JsonBlob: + return { + "type": f"minecraft:{self.tree.canopyShape!s}_foliage_placer", + "height": max(1, self.tree.canopyHeight), + "offset": 0, + "radius": self.tree.canopyRadius, + } + + def _buildFoliageProvider(self) -> JsonBlob: + properties:dict[str, JsonBlob] = { + "distance": str(math.ceil(self.tree.canopyRadius * 1.5)), + "persistent": "false", + "waterlogged": "false", + } + if self.tree.foliage.mod == "fruitsdelight": + properties["type"] = "leaves" + + return { + "type": "minecraft:simple_state_provider", + "state": { + "Name": str(self.tree.foliage), + "Properties": properties, + } + } + + def _buildMinimumSize(self) -> JsonBlob: + return { + "type": "minecraft:two_layers_feature_size", + "limit": 1, + "lower_size": 0, + "upper_size": 1, + } + + def _buildTrunkPlacer(self) -> JsonBlob: + return { + "type": f"minecraft:{self.tree.trunkShape!s}_trunk_placer", + "base_height": self.tree.trunkHeightMin, + "height_rand_a": self.tree.trunkHeightMax - self.tree.trunkHeightMin, + "height_rand_b": 0, + } + + def _buildTrunkProvider(self) -> JsonBlob: + return { + "type": "minecraft:simple_state_provider", + "state": { + "Name": str(self.tree.log), + "Properties": { "axis": "y" }, + }, + } diff --git a/mcpacker/format/datapack/configuredfeature/tree_test.py b/mcpacker/format/datapack/configuredfeature/tree_test.py new file mode 100644 index 0000000..1066df5 --- /dev/null +++ b/mcpacker/format/datapack/configuredfeature/tree_test.py @@ -0,0 +1,86 @@ +from mcpacker.format.datapack.configuredfeature.tree import TreeConfiguredFeature +from mcpacker.format.json import JsonBlob +from mcpacker.model.flora.tree import Tree +from mcpacker.model.modpack import ModPack +from pytest import fixture + +import mcpacker.model.flora.canopyshape as CA +import mcpacker.model.flora.trunkshape as TR +import mcpacker.write.json as json +import textwrap + + +# Fixtures ######################################################################################### + +@fixture(name="blob") +def createBlob(): + yield TreeConfiguredFeature(Tree( + name = "apple", + foliage = "fruitsdelight:apple_leaves", + log = f"minecraft:oak_log", + trunkShape = TR.STRAIGHT, + trunkHeightMin = 3, + trunkHeightMax = 5, + canopyShape = CA.BLOB, + canopyHeight = 3, + canopyRadius = 2 + )).asJsonBlob() + + +# Tests ############################################################################################ + +def test_asJsonBlob(blob:JsonBlob): + assert json.dumps(blob) == textwrap.dedent(""" + { + "type": "minecraft:tree", + "config": { + "dirt_provider": { + "type": "minecraft:simple_state_provider", + "state": { + "Name": "minecraft:dirt" + } + }, + "foliage_placer": { + "type": "minecraft:blob_foliage_placer", + "height": 3, + "offset": 0, + "radius": 2 + }, + "foliage_provider": { + "type": "minecraft:simple_state_provider", + "state": { + "Name": "fruitsdelight:apple_leaves", + "Properties": { + "distance": "3", + "persistent": "false", + "waterlogged": "false", + "type": "leaves" + } + } + }, + "force_dirt": false, + "ignore_vines": true, + "minimum_size": { + "type": "minecraft:two_layers_feature_size", + "limit": 1, + "lower_size": 0, + "upper_size": 1 + }, + "trunk_placer": { + "type": "minecraft:straight_trunk_placer", + "base_height": 3, + "height_rand_a": 2, + "height_rand_b": 0 + }, + "trunk_provider": { + "type": "minecraft:simple_state_provider", + "state": { + "Name": "minecraft:oak_log", + "Properties": { + "axis": "y" + } + } + } + } + } + """).strip() diff --git a/mcpacker/model/flora/tree.py b/mcpacker/model/flora/tree.py index 0fd1d73..94de42f 100644 --- a/mcpacker/model/flora/tree.py +++ b/mcpacker/model/flora/tree.py @@ -25,7 +25,7 @@ class Tree(Plant): ): super().__init__(name) - self.foliage = foliage + self.foliage = ResourceId.parse(foliage) self.log = log self.trunkShape = trunkShape self.trunkHeightMin = trunkHeightMin