From 2585e10f042c928333afc00fd5b29f902f8f16b5 Mon Sep 17 00:00:00 2001 From: Flavio Felder Date: Sun, 20 Sep 2026 18:48:44 +0200 Subject: [PATCH 01/27] Drop support for versions before 26.1 Remove the legacy overlays, raise min_format to 101 and update the incompatibility warning. Co-Authored-By: Claude Sonnet 5 --- .../loot_tables/blocks/mushroom_stem.json | 52 ---- .../functions/autoplant/plant.mcfunction | 17 -- 18-26/data/timber/functions/init.mcfunction | 38 --- 18-26/data/timber/functions/run.mcfunction | 46 ---- .../slow_chop/destroy_loop.mcfunction | 25 -- .../timber/functions/tool/wear_out.mcfunction | 3 - .../data/timber/functions/used_axe.mcfunction | 18 -- .../data/timber/item_modifiers/wear_out.json | 16 -- 18-26/data/timber/predicates/block/cap.json | 37 --- 18-26/data/timber/predicates/block/leaf.json | 26 -- 18-26/data/timber/predicates/block/log.json | 8 - 18-26/data/timber/predicates/block/stem.json | 21 -- 18-26/data/timber/predicates/item/log.json | 11 - 18-26/data/timber/predicates/item/root.json | 14 - .../data/timber/predicates/item/sapling.json | 11 - 18-26/data/timber/predicates/item/stem.json | 39 --- 18-26/data/timber/predicates/used_axe.json | 65 ----- .../timber/tags/functions/api/break_cap.json | 4 - .../timber/tags/functions/api/break_leaf.json | 4 - .../timber/tags/functions/api/break_log.json | 4 - .../timber/tags/functions/api/break_root.json | 4 - .../timber/tags/functions/api/break_stem.json | 4 - 18-26/data/timber/tags/functions/help.json | 5 - 18-26/data/timber/tags/items/axes.json | 10 - .../loot_tables/blocks/mushroom_stem.json | 51 ---- 18-41/data/minecraft/tags/functions/load.json | 6 - .../algorithm/fungus/chop.mcfunction | 31 --- .../fungus/leaves/destroy.mcfunction | 3 - .../fungus/leaves/find_other_stems.mcfunction | 7 - .../algorithm/fungus/leaves/search.mcfunction | 18 -- .../functions/algorithm/fungus/run.mcfunction | 5 - .../algorithm/fungus/search.mcfunction | 24 -- .../algorithm/fungus/search_down.mcfunction | 10 - .../algorithm/fungus/validate_cap.mcfunction | 6 - .../functions/algorithm/tree/chop.mcfunction | 29 -- .../algorithm/tree/chop_roots.mcfunction | 17 -- .../algorithm/tree/leaves/destroy.mcfunction | 8 - .../tree/leaves/get_distance.mcfunction | 17 -- .../algorithm/tree/leaves/search.mcfunction | 17 -- .../functions/algorithm/tree/run.mcfunction | 6 - .../algorithm/tree/search_1.mcfunction | 24 -- .../algorithm/tree/search_2.mcfunction | 4 - .../algorithm/tree/search_down.mcfunction | 10 - .../functions/autoplant/loop.mcfunction | 5 - .../functions/autoplant/mud_fix.mcfunction | 2 - .../functions/autoplant/plant.mcfunction | 18 -- .../functions/autoplant/validate.mcfunction | 8 - 18-41/data/timber/functions/help.mcfunction | 25 -- 18-41/data/timber/functions/init.mcfunction | 39 --- 18-41/data/timber/functions/main.mcfunction | 11 - 18-41/data/timber/functions/run.mcfunction | 46 ---- .../timber/functions/run_fungi.mcfunction | 18 -- .../settings/default_values.mcfunction | 28 -- .../settings/end_of_message.mcfunction | 3 - .../settings/reset_gamerule.mcfunction | 1 - .../functions/settings/settings.mcfunction | 1 - .../functions/settings/settings1.mcfunction | 57 ---- .../settings/settings1_click.mcfunction | 2 - .../functions/settings/settings2.mcfunction | 55 ---- .../settings/settings2_click.mcfunction | 2 - .../settings/toggle/chop_down.mcfunction | 7 - .../settings/toggle/chop_fungi.mcfunction | 7 - .../settings/toggle/chop_trees.mcfunction | 7 - .../settings/toggle/destroy_leaves.mcfunction | 7 - .../settings/toggle/destroy_roots.mcfunction | 7 - .../settings/toggle/diamond_axe.mcfunction | 7 - .../settings/toggle/drop_loot.mcfunction | 7 - .../settings/toggle/golden_axe.mcfunction | 7 - .../settings/toggle/hunger.mcfunction | 7 - .../settings/toggle/interfere.mcfunction | 7 - .../settings/toggle/iron_axe.mcfunction | 7 - .../settings/toggle/netherite_axe.mcfunction | 7 - .../settings/toggle/persistent.mcfunction | 7 - .../settings/toggle/plant_sapling.mcfunction | 8 - .../settings/toggle/slow_chop.mcfunction | 8 - .../settings/toggle/sneaking.mcfunction | 7 - .../settings/toggle/standing.mcfunction | 7 - .../toggle/start_chop_on_root.mcfunction | 7 - .../settings/toggle/stone_axe.mcfunction | 7 - .../settings/toggle/stop_chopping.mcfunction | 7 - .../settings/toggle/stopsound.mcfunction | 7 - .../settings/toggle/timber.mcfunction | 9 - .../settings/toggle/wear_out.mcfunction | 7 - .../settings/toggle/wooden_axe.mcfunction | 7 - .../timber/functions/slow_chop/api.mcfunction | 6 - .../slow_chop/delete_markers.mcfunction | 3 - .../slow_chop/destroy_loop.mcfunction | 25 -- .../functions/slow_chop/loop.mcfunction | 4 - .../tool/durability_check.mcfunction | 7 - .../tool/durability_count.mcfunction | 5 - .../functions/tool/durability_get.mcfunction | 5 - .../functions/tool/durability_max.mcfunction | 7 - .../timber/functions/tool/wear_out.mcfunction | 9 - .../timber/functions/tool/worn_out.mcfunction | 13 - .../timber/functions/uninstall.mcfunction | 19 -- .../check_max_command_chain_length.mcfunction | 13 - .../functions/utils/check_server.mcfunction | 8 - .../timber/functions/utils/hunger.mcfunction | 255 ------------------ .../timber/functions/utils/reset.mcfunction | 24 -- .../functions/utils/sneaking.mcfunction | 3 - .../functions/utils/stopsound.mcfunction | 6 - .../timber/functions/utils/tp_item.mcfunction | 9 - .../functions/utils/used_axe.mcfunction | 18 -- .../timber/functions/validated.mcfunction | 51 ---- .../data/timber/item_modifiers/wear_out.json | 152 ----------- 18-41/data/timber/predicates/block/cap.json | 37 --- 18-41/data/timber/predicates/block/leaf.json | 24 -- 18-41/data/timber/predicates/block/log.json | 8 - 18-41/data/timber/predicates/block/stem.json | 21 -- 18-41/data/timber/predicates/item/log.json | 11 - 18-41/data/timber/predicates/item/root.json | 11 - .../data/timber/predicates/item/sapling.json | 11 - 18-41/data/timber/predicates/item/stem.json | 45 ---- .../timber/predicates/random_chance/0.25.json | 4 - .../predicates/random_chance/0.333.json | 4 - .../timber/predicates/random_chance/0.5.json | 4 - 18-41/data/timber/predicates/sneaking.json | 9 - 18-41/data/timber/predicates/used_axe.json | 59 ---- .../timber/tags/functions/api/break_cap.json | 4 - .../timber/tags/functions/api/break_leaf.json | 4 - .../timber/tags/functions/api/break_log.json | 4 - .../timber/tags/functions/api/break_root.json | 4 - .../timber/tags/functions/api/break_stem.json | 4 - 18-41/data/timber/tags/functions/help.json | 5 - 18-41/data/timber/tags/items/axes.json | 10 - .../function/algorithm/fungus/run.mcfunction | 5 - .../function/algorithm/tree/run.mcfunction | 6 - .../function/autoplant/validate.mcfunction | 8 - 18-61/data/timber/function/help.mcfunction | 25 -- 18-61/data/timber/function/init.mcfunction | 39 --- .../function/settings/settings1.mcfunction | 57 ---- .../function/settings/settings2.mcfunction | 55 ---- .../settings/toggle/timber.mcfunction | 9 - .../check_max_command_chain_length.mcfunction | 13 - .../function/utils/check_server.mcfunction | 8 - .../data/timber/function/validated.mcfunction | 51 ---- 18-81/data/timber/function/init.mcfunction | 39 --- 18-81/data/timber/function/main.mcfunction | 11 - .../settings/end_of_message.mcfunction | 3 - .../settings/reset_gamerule.mcfunction | 1 - .../function/settings/settings2.mcfunction | 55 ---- .../slow_chop/destroy_loop.mcfunction | 25 -- .../check_max_command_chain_length.mcfunction | 13 - ...eck_max_command_sequence_length.mcfunction | 0 .../data/timber/function/validated.mcfunction | 51 ---- 18-81/data/timber/item_modifier/wear_out.json | 152 ----------- 18-81/data/timber/predicate/used_axe.json | 59 ---- 18-81/data/timber/tags/item/axes.json | 10 - 82-88/data/timber/function/init.mcfunction | 40 --- 82-88/data/timber/function/main.mcfunction | 11 - .../settings/end_of_message.mcfunction | 3 - .../settings/reset_gamerule.mcfunction | 1 - .../slow_chop/destroy_loop.mcfunction | 25 -- .../check_max_command_chain_length.mcfunction | 13 - ...eck_max_command_sequence_length.mcfunction | 0 .../data/timber/function/validated.mcfunction | 51 ---- data/timber/function/init.mcfunction | 2 +- pack.mcmeta | 12 +- 158 files changed, 4 insertions(+), 3042 deletions(-) delete mode 100644 18-26/data/minecraft/loot_tables/blocks/mushroom_stem.json delete mode 100644 18-26/data/timber/functions/autoplant/plant.mcfunction delete mode 100644 18-26/data/timber/functions/init.mcfunction delete mode 100644 18-26/data/timber/functions/run.mcfunction delete mode 100644 18-26/data/timber/functions/slow_chop/destroy_loop.mcfunction delete mode 100644 18-26/data/timber/functions/tool/wear_out.mcfunction delete mode 100644 18-26/data/timber/functions/used_axe.mcfunction delete mode 100644 18-26/data/timber/item_modifiers/wear_out.json delete mode 100644 18-26/data/timber/predicates/block/cap.json delete mode 100644 18-26/data/timber/predicates/block/leaf.json delete mode 100644 18-26/data/timber/predicates/block/log.json delete mode 100644 18-26/data/timber/predicates/block/stem.json delete mode 100644 18-26/data/timber/predicates/item/log.json delete mode 100644 18-26/data/timber/predicates/item/root.json delete mode 100644 18-26/data/timber/predicates/item/sapling.json delete mode 100644 18-26/data/timber/predicates/item/stem.json delete mode 100644 18-26/data/timber/predicates/used_axe.json delete mode 100644 18-26/data/timber/tags/functions/api/break_cap.json delete mode 100644 18-26/data/timber/tags/functions/api/break_leaf.json delete mode 100644 18-26/data/timber/tags/functions/api/break_log.json delete mode 100644 18-26/data/timber/tags/functions/api/break_root.json delete mode 100644 18-26/data/timber/tags/functions/api/break_stem.json delete mode 100644 18-26/data/timber/tags/functions/help.json delete mode 100644 18-26/data/timber/tags/items/axes.json delete mode 100644 18-41/data/minecraft/loot_tables/blocks/mushroom_stem.json delete mode 100644 18-41/data/minecraft/tags/functions/load.json delete mode 100644 18-41/data/timber/functions/algorithm/fungus/chop.mcfunction delete mode 100644 18-41/data/timber/functions/algorithm/fungus/leaves/destroy.mcfunction delete mode 100644 18-41/data/timber/functions/algorithm/fungus/leaves/find_other_stems.mcfunction delete mode 100644 18-41/data/timber/functions/algorithm/fungus/leaves/search.mcfunction delete mode 100644 18-41/data/timber/functions/algorithm/fungus/run.mcfunction delete mode 100644 18-41/data/timber/functions/algorithm/fungus/search.mcfunction delete mode 100644 18-41/data/timber/functions/algorithm/fungus/search_down.mcfunction delete mode 100644 18-41/data/timber/functions/algorithm/fungus/validate_cap.mcfunction delete mode 100644 18-41/data/timber/functions/algorithm/tree/chop.mcfunction delete mode 100644 18-41/data/timber/functions/algorithm/tree/chop_roots.mcfunction delete mode 100644 18-41/data/timber/functions/algorithm/tree/leaves/destroy.mcfunction delete mode 100644 18-41/data/timber/functions/algorithm/tree/leaves/get_distance.mcfunction delete mode 100644 18-41/data/timber/functions/algorithm/tree/leaves/search.mcfunction delete mode 100644 18-41/data/timber/functions/algorithm/tree/run.mcfunction delete mode 100644 18-41/data/timber/functions/algorithm/tree/search_1.mcfunction delete mode 100644 18-41/data/timber/functions/algorithm/tree/search_2.mcfunction delete mode 100644 18-41/data/timber/functions/algorithm/tree/search_down.mcfunction delete mode 100644 18-41/data/timber/functions/autoplant/loop.mcfunction delete mode 100644 18-41/data/timber/functions/autoplant/mud_fix.mcfunction delete mode 100644 18-41/data/timber/functions/autoplant/plant.mcfunction delete mode 100644 18-41/data/timber/functions/autoplant/validate.mcfunction delete mode 100644 18-41/data/timber/functions/help.mcfunction delete mode 100644 18-41/data/timber/functions/init.mcfunction delete mode 100644 18-41/data/timber/functions/main.mcfunction delete mode 100644 18-41/data/timber/functions/run.mcfunction delete mode 100644 18-41/data/timber/functions/run_fungi.mcfunction delete mode 100644 18-41/data/timber/functions/settings/default_values.mcfunction delete mode 100644 18-41/data/timber/functions/settings/end_of_message.mcfunction delete mode 100644 18-41/data/timber/functions/settings/reset_gamerule.mcfunction delete mode 100644 18-41/data/timber/functions/settings/settings.mcfunction delete mode 100644 18-41/data/timber/functions/settings/settings1.mcfunction delete mode 100644 18-41/data/timber/functions/settings/settings1_click.mcfunction delete mode 100644 18-41/data/timber/functions/settings/settings2.mcfunction delete mode 100644 18-41/data/timber/functions/settings/settings2_click.mcfunction delete mode 100644 18-41/data/timber/functions/settings/toggle/chop_down.mcfunction delete mode 100644 18-41/data/timber/functions/settings/toggle/chop_fungi.mcfunction delete mode 100644 18-41/data/timber/functions/settings/toggle/chop_trees.mcfunction delete mode 100644 18-41/data/timber/functions/settings/toggle/destroy_leaves.mcfunction delete mode 100644 18-41/data/timber/functions/settings/toggle/destroy_roots.mcfunction delete mode 100644 18-41/data/timber/functions/settings/toggle/diamond_axe.mcfunction delete mode 100644 18-41/data/timber/functions/settings/toggle/drop_loot.mcfunction delete mode 100644 18-41/data/timber/functions/settings/toggle/golden_axe.mcfunction delete mode 100644 18-41/data/timber/functions/settings/toggle/hunger.mcfunction delete mode 100644 18-41/data/timber/functions/settings/toggle/interfere.mcfunction delete mode 100644 18-41/data/timber/functions/settings/toggle/iron_axe.mcfunction delete mode 100644 18-41/data/timber/functions/settings/toggle/netherite_axe.mcfunction delete mode 100644 18-41/data/timber/functions/settings/toggle/persistent.mcfunction delete mode 100644 18-41/data/timber/functions/settings/toggle/plant_sapling.mcfunction delete mode 100644 18-41/data/timber/functions/settings/toggle/slow_chop.mcfunction delete mode 100644 18-41/data/timber/functions/settings/toggle/sneaking.mcfunction delete mode 100644 18-41/data/timber/functions/settings/toggle/standing.mcfunction delete mode 100644 18-41/data/timber/functions/settings/toggle/start_chop_on_root.mcfunction delete mode 100644 18-41/data/timber/functions/settings/toggle/stone_axe.mcfunction delete mode 100644 18-41/data/timber/functions/settings/toggle/stop_chopping.mcfunction delete mode 100644 18-41/data/timber/functions/settings/toggle/stopsound.mcfunction delete mode 100644 18-41/data/timber/functions/settings/toggle/timber.mcfunction delete mode 100644 18-41/data/timber/functions/settings/toggle/wear_out.mcfunction delete mode 100644 18-41/data/timber/functions/settings/toggle/wooden_axe.mcfunction delete mode 100644 18-41/data/timber/functions/slow_chop/api.mcfunction delete mode 100644 18-41/data/timber/functions/slow_chop/delete_markers.mcfunction delete mode 100644 18-41/data/timber/functions/slow_chop/destroy_loop.mcfunction delete mode 100644 18-41/data/timber/functions/slow_chop/loop.mcfunction delete mode 100644 18-41/data/timber/functions/tool/durability_check.mcfunction delete mode 100644 18-41/data/timber/functions/tool/durability_count.mcfunction delete mode 100644 18-41/data/timber/functions/tool/durability_get.mcfunction delete mode 100644 18-41/data/timber/functions/tool/durability_max.mcfunction delete mode 100644 18-41/data/timber/functions/tool/wear_out.mcfunction delete mode 100644 18-41/data/timber/functions/tool/worn_out.mcfunction delete mode 100644 18-41/data/timber/functions/uninstall.mcfunction delete mode 100644 18-41/data/timber/functions/utils/check_max_command_chain_length.mcfunction delete mode 100644 18-41/data/timber/functions/utils/check_server.mcfunction delete mode 100644 18-41/data/timber/functions/utils/hunger.mcfunction delete mode 100644 18-41/data/timber/functions/utils/reset.mcfunction delete mode 100644 18-41/data/timber/functions/utils/sneaking.mcfunction delete mode 100644 18-41/data/timber/functions/utils/stopsound.mcfunction delete mode 100644 18-41/data/timber/functions/utils/tp_item.mcfunction delete mode 100644 18-41/data/timber/functions/utils/used_axe.mcfunction delete mode 100644 18-41/data/timber/functions/validated.mcfunction delete mode 100644 18-41/data/timber/item_modifiers/wear_out.json delete mode 100644 18-41/data/timber/predicates/block/cap.json delete mode 100644 18-41/data/timber/predicates/block/leaf.json delete mode 100644 18-41/data/timber/predicates/block/log.json delete mode 100644 18-41/data/timber/predicates/block/stem.json delete mode 100644 18-41/data/timber/predicates/item/log.json delete mode 100644 18-41/data/timber/predicates/item/root.json delete mode 100644 18-41/data/timber/predicates/item/sapling.json delete mode 100644 18-41/data/timber/predicates/item/stem.json delete mode 100644 18-41/data/timber/predicates/random_chance/0.25.json delete mode 100644 18-41/data/timber/predicates/random_chance/0.333.json delete mode 100644 18-41/data/timber/predicates/random_chance/0.5.json delete mode 100644 18-41/data/timber/predicates/sneaking.json delete mode 100644 18-41/data/timber/predicates/used_axe.json delete mode 100644 18-41/data/timber/tags/functions/api/break_cap.json delete mode 100644 18-41/data/timber/tags/functions/api/break_leaf.json delete mode 100644 18-41/data/timber/tags/functions/api/break_log.json delete mode 100644 18-41/data/timber/tags/functions/api/break_root.json delete mode 100644 18-41/data/timber/tags/functions/api/break_stem.json delete mode 100644 18-41/data/timber/tags/functions/help.json delete mode 100644 18-41/data/timber/tags/items/axes.json delete mode 100644 18-61/data/timber/function/algorithm/fungus/run.mcfunction delete mode 100644 18-61/data/timber/function/algorithm/tree/run.mcfunction delete mode 100644 18-61/data/timber/function/autoplant/validate.mcfunction delete mode 100644 18-61/data/timber/function/help.mcfunction delete mode 100644 18-61/data/timber/function/init.mcfunction delete mode 100644 18-61/data/timber/function/settings/settings1.mcfunction delete mode 100644 18-61/data/timber/function/settings/settings2.mcfunction delete mode 100644 18-61/data/timber/function/settings/toggle/timber.mcfunction delete mode 100644 18-61/data/timber/function/utils/check_max_command_chain_length.mcfunction delete mode 100644 18-61/data/timber/function/utils/check_server.mcfunction delete mode 100644 18-61/data/timber/function/validated.mcfunction delete mode 100644 18-81/data/timber/function/init.mcfunction delete mode 100644 18-81/data/timber/function/main.mcfunction delete mode 100644 18-81/data/timber/function/settings/end_of_message.mcfunction delete mode 100644 18-81/data/timber/function/settings/reset_gamerule.mcfunction delete mode 100644 18-81/data/timber/function/settings/settings2.mcfunction delete mode 100644 18-81/data/timber/function/slow_chop/destroy_loop.mcfunction delete mode 100644 18-81/data/timber/function/utils/check_max_command_chain_length.mcfunction delete mode 100644 18-81/data/timber/function/utils/check_max_command_sequence_length.mcfunction delete mode 100644 18-81/data/timber/function/validated.mcfunction delete mode 100644 18-81/data/timber/item_modifier/wear_out.json delete mode 100644 18-81/data/timber/predicate/used_axe.json delete mode 100644 18-81/data/timber/tags/item/axes.json delete mode 100644 82-88/data/timber/function/init.mcfunction delete mode 100644 82-88/data/timber/function/main.mcfunction delete mode 100644 82-88/data/timber/function/settings/end_of_message.mcfunction delete mode 100644 82-88/data/timber/function/settings/reset_gamerule.mcfunction delete mode 100644 82-88/data/timber/function/slow_chop/destroy_loop.mcfunction delete mode 100644 82-88/data/timber/function/utils/check_max_command_chain_length.mcfunction delete mode 100644 82-88/data/timber/function/utils/check_max_command_sequence_length.mcfunction delete mode 100644 82-88/data/timber/function/validated.mcfunction diff --git a/18-26/data/minecraft/loot_tables/blocks/mushroom_stem.json b/18-26/data/minecraft/loot_tables/blocks/mushroom_stem.json deleted file mode 100644 index 4de3d97..0000000 --- a/18-26/data/minecraft/loot_tables/blocks/mushroom_stem.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "type": "minecraft:block", - "pools": [ - { - "rolls": 1, - "entries": [ - { - "type": "minecraft:item", - "name": "minecraft:mushroom_stem" - } - ], - "conditions": [ - { - "condition": "minecraft:match_tool", - "predicate": { - "enchantments": [ - { - "enchantment": "minecraft:silk_touch", - "levels": { - "min": 1 - } - } - ] - } - } - ] - }, - { - "rolls": 1, - "entries": [ - { - "type": "minecraft:item", - "name": "minecraft:knowledge_book", - "functions": [ - { - "function": "minecraft:set_nbt", - "tag": "{Tags:[timber_location]}" - } - ], - "conditions": [ - { - "condition": "minecraft:match_tool", - "predicate": { - "tag": "timber:axes" - } - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/18-26/data/timber/functions/autoplant/plant.mcfunction b/18-26/data/timber/functions/autoplant/plant.mcfunction deleted file mode 100644 index a9bcf97..0000000 --- a/18-26/data/timber/functions/autoplant/plant.mcfunction +++ /dev/null @@ -1,17 +0,0 @@ -# replace item-sapling with block-sapling (special case for mud, because fallingblock get destroyed. 0.125 is the height when you sink into the mud) -execute unless block ~ ~ ~ minecraft:air run summon marker ~ ~ ~ {Tags:["timber_mud","global.ignore"]} -execute unless block ~ ~ ~ minecraft:air run schedule function timber:autoplant/mud_fix 1t -execute unless block ~ ~ ~ minecraft:air run setblock ~ ~ ~ minecraft:muddy_mangrove_roots -summon minecraft:falling_block ~ ~0.125 ~ {BlockState:{Name:"minecraft:barrier"},Time:1,DropItem:0b,Tags:["timber_item_to_block"]} -execute positioned ~ ~0.125 ~ run data modify entity @e[type=minecraft:falling_block,tag=timber_item_to_block,distance=...1,sort=arbitrary,limit=1] BlockState.Name set from entity @s Item.id - -# on stacks -1 -execute store result score @s timber run data get entity @s Item.Count -scoreboard players remove @s timber 1 -execute store result entity @s Item.Count byte 1 run scoreboard players get @s timber -scoreboard players reset @s timber -# cheap custom server fix -execute if score not_vanilla timber matches 1 run kill @s - -# sapling will be checked every time -tag @s remove timber_checked \ No newline at end of file diff --git a/18-26/data/timber/functions/init.mcfunction b/18-26/data/timber/functions/init.mcfunction deleted file mode 100644 index dd644cf..0000000 --- a/18-26/data/timber/functions/init.mcfunction +++ /dev/null @@ -1,38 +0,0 @@ -# longer maxCommandChainLength -gamerule maxCommandChainLength 1000000000 - -# create scoreboards -scoreboard objectives add timber_w_axe minecraft.used:minecraft.wooden_axe -scoreboard objectives add timber_s_axe minecraft.used:minecraft.stone_axe -scoreboard objectives add timber_i_axe minecraft.used:minecraft.iron_axe -scoreboard objectives add timber_g_axe minecraft.used:minecraft.golden_axe -scoreboard objectives add timber_d_axe minecraft.used:minecraft.diamond_axe -scoreboard objectives add timber_n_axe minecraft.used:minecraft.netherite_axe -scoreboard objectives add timber dummy -scoreboard objectives add TimberToggle trigger {"text":"Timber Toggle","color":"#724E21"} -scoreboard objectives add timber_disabled dummy -scoreboard objectives add timber_prsistent dummy - -# non-overriding default values -function timber:settings/default_values -scoreboard players set running timber 0 - -# start optional loops -execute if score plant_sapling timber matches 1.. run function timber:autoplant/loop -execute if score slow_chop timber matches 1.. run function timber:slow_chop/loop - -# check loottable compatibility of mushroom stem -setblock ~ 255 ~ minecraft:chest -setblock ~ 255 ~1 minecraft:mushroom_stem -loot insert ~ 255 ~ mine ~ 255 ~1 minecraft:diamond_axe -execute unless data block ~ 255 ~ Items[{id:"minecraft:knowledge_book"}] run tellraw @a[tag=!noTimberWarning] ["",{"text":"Timber Datapack Warning: ","color":"red"},{"text":"Incompatibility with the mushroom_stem loottable of another datapack!"}] -fill ~-1 255 ~-1 ~1 255 ~1 minecraft:air -execute store result score doTileDrops timber run gamerule doTileDrops -execute if score doTileDrops timber matches 0 run tellraw @a[tag=!noTimberWarning] ["",{"text":"Timber Datapack Warning: ","color":"red"},{"text":"This datapack only works when gamerule doTileDrops is set to true!"}] - -# Getting the game version and checking if it is compatible with the datapack https://minecraft.fandom.com/wiki/Data_version -execute store result score game_version timber run data get entity @r DataVersion -execute if score game_version timber matches ..3442 run tellraw @a[tag=!noTimberWarning] ["",{"text":"Timber Datapack Warning: ","color":"red"},{"text":"This datapack is not compatible with all the versions before 1.20!"}] - -# Checking if the server uses a different jar from the vanilla one -function timber:utils/check_server \ No newline at end of file diff --git a/18-26/data/timber/functions/run.mcfunction b/18-26/data/timber/functions/run.mcfunction deleted file mode 100644 index 3466cdd..0000000 --- a/18-26/data/timber/functions/run.mcfunction +++ /dev/null @@ -1,46 +0,0 @@ -# reset -scoreboard players set leaves_found timber 0 -scoreboard players set tree_size timber 0 -scoreboard players set tree_type timber 0 - -# load settings to other scoreboard (leaves persistent) -execute unless score persistent timber matches 1.. run scoreboard players set @s timber_prsistent 0 -execute if score persistent timber matches 1.. run scoreboard players set @s timber_prsistent 1 - -# search last broken item (place of tree) and set a marker (limit=1 removed, because carpet on log breaks this) -execute at @e[type=minecraft:item,distance=..7,nbt={Age:0s}] run summon marker ~ ~ ~ {Tags:["timber_tree","global.ignore"]} - -# Enchantments randomizer -scoreboard players set unbreaking timber 0 -execute if entity @s[nbt={SelectedItem:{tag:{Enchantments:[{id:"minecraft:unbreaking",lvl:1s}]}}}] run scoreboard players set unbreaking timber 1 -execute if entity @s[nbt={SelectedItem:{tag:{Enchantments:[{id:"minecraft:unbreaking",lvl:2s}]}}}] run scoreboard players set unbreaking timber 2 -execute if entity @s[nbt={SelectedItem:{tag:{Enchantments:[{id:"minecraft:unbreaking",lvl:3s}]}}}] run scoreboard players set unbreaking timber 3 - -# get durability -function timber:tool/durability_get - -# scan through tree - # tree (limit=1,sort=arbitrary removed, because carpet on log breaks this) - execute if score chop_trees timber matches 1.. at @e[type=minecraft:marker,tag=timber_tree,tag=!timber_slow_chop,distance=..7] run function timber:algorithm/tree/run - execute store result score leaves_found timber if entity @e[type=minecraft:marker,tag=timber_leaves_found] - # if tree is valid - execute if score leaves_found timber >= min_leaves_found timber unless score tree_size timber matches 0 run scoreboard players set tree_type timber 1 - - # fungi (only if not tree already) - execute if score tree_type timber matches 0 if score chop_fungi timber matches 1.. run function timber:run_fungi - -# if tree or fungi is valid (tree = 1 | fungus = 2) -execute unless score tree_type timber matches 0 if score tree_size timber <= max_tree_size timber run function timber:validated - -# remove markers -execute if score chop_fungi timber matches 1.. run kill @e[type=minecraft:item,nbt={Item:{tag:{Tags:[timber_location]}}}] -execute if score chop_fungi timber matches 1.. run clear @s knowledge_book{Tags:[timber_location]} -kill @e[type=minecraft:marker,tag=timber_tree,tag=!timber_slow_chop] -kill @e[type=minecraft:marker,tag=timber_log,tag=!timber_destroy] -kill @e[type=minecraft:marker,tag=timber_leaf,tag=!timber_destroy] -kill @e[type=minecraft:marker,tag=timber_root,tag=!timber_destroy] -kill @e[type=minecraft:marker,tag=timber_stem,tag=!timber_destroy] -kill @e[type=minecraft:marker,tag=timber_cap,tag=!timber_destroy] -kill @e[type=minecraft:marker,tag=timber_leaves_found] -kill @e[type=minecraft:marker,tag=timber_leaf_distance] -kill @e[type=minecraft:marker,tag=timber_other_stem] \ No newline at end of file diff --git a/18-26/data/timber/functions/slow_chop/destroy_loop.mcfunction b/18-26/data/timber/functions/slow_chop/destroy_loop.mcfunction deleted file mode 100644 index 97282b3..0000000 --- a/18-26/data/timber/functions/slow_chop/destroy_loop.mcfunction +++ /dev/null @@ -1,25 +0,0 @@ -# loop -$schedule function timber:slow_chop/loop $(time_between_chops)t replace - -# api -$execute at @e[type=minecraft:marker,tag=timber_slow_chop] as @e[type=minecraft:marker,tag=timber_destroy,sort=nearest,limit=$(blocks_per_chop)] at @s run function timber:slow_chop/api - -# destroy n block -$execute at @e[type=minecraft:marker,tag=timber_slow_chop] as @e[type=minecraft:armor_stand,tag=timber_tool,y=-64,distance=...1,sort=arbitrary,limit=1] at @e[type=minecraft:marker,tag=timber_destroy,sort=nearest,limit=$(blocks_per_chop)] run loot spawn ~ ~ ~ mine ~ ~ ~ mainhand - -# destroy animation (doTileDrops can be changed, because datapack wouldn't get to this point otherwise) -gamerule doTileDrops false -$execute at @e[type=minecraft:marker,tag=timber_slow_chop] at @e[type=minecraft:marker,tag=timber_destroy,sort=nearest,limit=$(blocks_per_chop)] run setblock ~ ~ ~ minecraft:air destroy -gamerule doTileDrops true - -# stop sound -$execute at @e[type=minecraft:marker,tag=timber_slow_chop] if score stopsound timber matches 1.. at @e[type=minecraft:marker,tag=timber_destroy,sort=nearest,limit=$(blocks_per_chop)] as @a[distance=..20,tag=!global.ignore.gui] run function timber:utils/stopsound - -# delete location item -$execute at @e[type=minecraft:marker,tag=timber_slow_chop] at @e[type=minecraft:marker,tag=timber_destroy,sort=nearest,limit=$(blocks_per_chop)] run kill @e[type=minecraft:item,distance=..1,nbt={Item:{tag:{Tags:["timber_location"]}}}] - -# remove marker of processed blocks -$execute at @e[type=minecraft:marker,tag=timber_slow_chop] run kill @e[type=minecraft:marker,tag=timber_destroy,sort=nearest,limit=$(blocks_per_chop)] - -# delete tree markers after chopping -execute unless entity @e[type=minecraft:marker,tag=timber_destroy] run function timber:slow_chop/delete_markers \ No newline at end of file diff --git a/18-26/data/timber/functions/tool/wear_out.mcfunction b/18-26/data/timber/functions/tool/wear_out.mcfunction deleted file mode 100644 index d6b6e58..0000000 --- a/18-26/data/timber/functions/tool/wear_out.mcfunction +++ /dev/null @@ -1,3 +0,0 @@ -# wear out tool -execute store result storage timber:temp durability int 1 run scoreboard players get durability timber -item modify entity @s weapon.mainhand timber:wear_out \ No newline at end of file diff --git a/18-26/data/timber/functions/used_axe.mcfunction b/18-26/data/timber/functions/used_axe.mcfunction deleted file mode 100644 index 744ef4b..0000000 --- a/18-26/data/timber/functions/used_axe.mcfunction +++ /dev/null @@ -1,18 +0,0 @@ -# is this axe enabled? -execute unless score @s timber_disabled matches 1.. if score @s timber_w_axe matches 1.. if score wooden_axe timber matches 1.. run function timber:sneaking -execute unless score @s timber_disabled matches 1.. if score @s timber_s_axe matches 1.. if score stone_axe timber matches 1.. run function timber:sneaking -execute unless score @s timber_disabled matches 1.. if score @s timber_i_axe matches 1.. if score iron_axe timber matches 1.. run function timber:sneaking -execute unless score @s timber_disabled matches 1.. if score @s timber_g_axe matches 1.. if score golden_axe timber matches 1.. run function timber:sneaking -execute unless score @s timber_disabled matches 1.. if score @s timber_d_axe matches 1.. if score diamond_axe timber matches 1.. run function timber:sneaking -execute unless score @s timber_disabled matches 1.. if score @s timber_n_axe matches 1.. if score netherite_axe timber matches 1.. run function timber:sneaking - -# remove mushroom_stem marker -kill @e[type=minecraft:item,distance=..7,nbt={Item:{tag:{Tags:[timber_location]}}}] - -# reset scores -scoreboard players set @s timber_w_axe 0 -scoreboard players set @s timber_s_axe 0 -scoreboard players set @s timber_i_axe 0 -scoreboard players set @s timber_g_axe 0 -scoreboard players set @s timber_d_axe 0 -scoreboard players set @s timber_n_axe 0 \ No newline at end of file diff --git a/18-26/data/timber/item_modifiers/wear_out.json b/18-26/data/timber/item_modifiers/wear_out.json deleted file mode 100644 index 605acf8..0000000 --- a/18-26/data/timber/item_modifiers/wear_out.json +++ /dev/null @@ -1,16 +0,0 @@ -[ - { - "function": "minecraft:copy_nbt", - "source": { - "type": "minecraft:storage", - "source": "timber:temp" - }, - "ops": [ - { - "source": "durability", - "target": "Damage", - "op": "replace" - } - ] - } -] \ No newline at end of file diff --git a/18-26/data/timber/predicates/block/cap.json b/18-26/data/timber/predicates/block/cap.json deleted file mode 100644 index 2e456e1..0000000 --- a/18-26/data/timber/predicates/block/cap.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "condition": "minecraft:any_of", - "terms": [ - { - "condition": "minecraft:location_check", - "predicate": { - "block": { - "tag": "minecraft:wart_blocks" - } - } - }, - { - "condition": "minecraft:location_check", - "predicate": { - "block": { - "blocks": ["minecraft:shroomlight"] - } - } - }, - { - "condition": "minecraft:location_check", - "predicate": { - "block": { - "blocks": ["minecraft:brown_mushroom_block"] - } - } - }, - { - "condition": "minecraft:location_check", - "predicate": { - "block": { - "blocks": ["minecraft:red_mushroom_block"] - } - } - } - ] -} \ No newline at end of file diff --git a/18-26/data/timber/predicates/block/leaf.json b/18-26/data/timber/predicates/block/leaf.json deleted file mode 100644 index 86425b5..0000000 --- a/18-26/data/timber/predicates/block/leaf.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "condition": "minecraft:any_of", - "terms": [ - { - "condition": "minecraft:location_check", - "predicate": { - "block": { - "tag": "minecraft:leaves" - } - } - }, - { - "condition": "minecraft:location_check", - "predicate": { - "block": { - "blocks": [ - "minecraft:mangrove_propagule" - ], - "state": { - "hanging": "true" - } - } - } - } - ] -} diff --git a/18-26/data/timber/predicates/block/log.json b/18-26/data/timber/predicates/block/log.json deleted file mode 100644 index 759b71c..0000000 --- a/18-26/data/timber/predicates/block/log.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "condition": "minecraft:location_check", - "predicate": { - "block": { - "tag": "minecraft:logs" - } - } -} diff --git a/18-26/data/timber/predicates/block/stem.json b/18-26/data/timber/predicates/block/stem.json deleted file mode 100644 index a6b9e8e..0000000 --- a/18-26/data/timber/predicates/block/stem.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "condition": "minecraft:any_of", - "terms": [ - { - "condition": "minecraft:location_check", - "predicate": { - "block": { - "tag": "minecraft:logs" - } - } - }, - { - "condition": "minecraft:location_check", - "predicate": { - "block": { - "blocks": ["minecraft:mushroom_stem"] - } - } - } - ] -} \ No newline at end of file diff --git a/18-26/data/timber/predicates/item/log.json b/18-26/data/timber/predicates/item/log.json deleted file mode 100644 index 941fb0b..0000000 --- a/18-26/data/timber/predicates/item/log.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "condition": "minecraft:entity_properties", - "entity": "this", - "predicate": { - "equipment": { - "mainhand": { - "tag": "minecraft:logs" - } - } - } -} \ No newline at end of file diff --git a/18-26/data/timber/predicates/item/root.json b/18-26/data/timber/predicates/item/root.json deleted file mode 100644 index 3fd61cd..0000000 --- a/18-26/data/timber/predicates/item/root.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "condition": "minecraft:entity_properties", - "entity": "this", - "predicate": { - "equipment": { - "mainhand": { - "items": [ - "minecraft:mangrove_roots" - ] - } - } - } -} - \ No newline at end of file diff --git a/18-26/data/timber/predicates/item/sapling.json b/18-26/data/timber/predicates/item/sapling.json deleted file mode 100644 index ab208e0..0000000 --- a/18-26/data/timber/predicates/item/sapling.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "condition": "minecraft:entity_properties", - "entity": "this", - "predicate": { - "equipment": { - "mainhand": { - "tag": "minecraft:saplings" - } - } - } -} \ No newline at end of file diff --git a/18-26/data/timber/predicates/item/stem.json b/18-26/data/timber/predicates/item/stem.json deleted file mode 100644 index 5d8c572..0000000 --- a/18-26/data/timber/predicates/item/stem.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "condition": "minecraft:any_of", - "terms": [ - { - "condition": "minecraft:entity_properties", - "entity": "this", - "predicate": { - "equipment": { - "mainhand": { - "tag": "minecraft:logs" - } - } - } - }, - { - "condition": "minecraft:entity_properties", - "entity": "this", - "predicate": { - "equipment": { - "mainhand": { - "items": ["minecraft:mushroom_stem"] - } - } - } - }, - { - "condition": "minecraft:entity_properties", - "entity": "this", - "predicate": { - "equipment": { - "mainhand": { - "items": ["minecraft:knowledge_book"], - "nbt": "{Tags:[timber_location]}" - } - } - } - } - ] -} \ No newline at end of file diff --git a/18-26/data/timber/predicates/used_axe.json b/18-26/data/timber/predicates/used_axe.json deleted file mode 100644 index 1639a11..0000000 --- a/18-26/data/timber/predicates/used_axe.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "condition": "minecraft:any_of", - "terms": [ - { - "condition": "minecraft:entity_scores", - "entity": "this", - "scores": { - "timber_w_axe": { - "min": 1, - "max": 2147483647 - } - } - }, - { - "condition": "minecraft:entity_scores", - "entity": "this", - "scores": { - "timber_s_axe": { - "min": 1, - "max": 2147483647 - } - } - }, - { - "condition": "minecraft:entity_scores", - "entity": "this", - "scores": { - "timber_i_axe": { - "min": 1, - "max": 2147483647 - } - } - }, - { - "condition": "minecraft:entity_scores", - "entity": "this", - "scores": { - "timber_g_axe": { - "min": 1, - "max": 2147483647 - } - } - }, - { - "condition": "minecraft:entity_scores", - "entity": "this", - "scores": { - "timber_d_axe": { - "min": 1, - "max": 2147483647 - } - } - }, - { - "condition": "minecraft:entity_scores", - "entity": "this", - "scores": { - "timber_n_axe": { - "min": 1, - "max": 2147483647 - } - } - } - ] -} \ No newline at end of file diff --git a/18-26/data/timber/tags/functions/api/break_cap.json b/18-26/data/timber/tags/functions/api/break_cap.json deleted file mode 100644 index ab3fce9..0000000 --- a/18-26/data/timber/tags/functions/api/break_cap.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "values":[ - ] -} \ No newline at end of file diff --git a/18-26/data/timber/tags/functions/api/break_leaf.json b/18-26/data/timber/tags/functions/api/break_leaf.json deleted file mode 100644 index ab3fce9..0000000 --- a/18-26/data/timber/tags/functions/api/break_leaf.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "values":[ - ] -} \ No newline at end of file diff --git a/18-26/data/timber/tags/functions/api/break_log.json b/18-26/data/timber/tags/functions/api/break_log.json deleted file mode 100644 index ab3fce9..0000000 --- a/18-26/data/timber/tags/functions/api/break_log.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "values":[ - ] -} \ No newline at end of file diff --git a/18-26/data/timber/tags/functions/api/break_root.json b/18-26/data/timber/tags/functions/api/break_root.json deleted file mode 100644 index ab3fce9..0000000 --- a/18-26/data/timber/tags/functions/api/break_root.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "values":[ - ] -} \ No newline at end of file diff --git a/18-26/data/timber/tags/functions/api/break_stem.json b/18-26/data/timber/tags/functions/api/break_stem.json deleted file mode 100644 index ab3fce9..0000000 --- a/18-26/data/timber/tags/functions/api/break_stem.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "values":[ - ] -} \ No newline at end of file diff --git a/18-26/data/timber/tags/functions/help.json b/18-26/data/timber/tags/functions/help.json deleted file mode 100644 index 082ff6b..0000000 --- a/18-26/data/timber/tags/functions/help.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "values":[ - "timber:help" - ] -} \ No newline at end of file diff --git a/18-26/data/timber/tags/items/axes.json b/18-26/data/timber/tags/items/axes.json deleted file mode 100644 index c496215..0000000 --- a/18-26/data/timber/tags/items/axes.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "values":[ - "minecraft:wooden_axe", - "minecraft:stone_axe", - "minecraft:iron_axe", - "minecraft:golden_axe", - "minecraft:diamond_axe", - "minecraft:netherite_axe" - ] -} \ No newline at end of file diff --git a/18-41/data/minecraft/loot_tables/blocks/mushroom_stem.json b/18-41/data/minecraft/loot_tables/blocks/mushroom_stem.json deleted file mode 100644 index efe0d2b..0000000 --- a/18-41/data/minecraft/loot_tables/blocks/mushroom_stem.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "type": "minecraft:block", - "pools": [ - { - "rolls": 1, - "entries": [ - { - "type": "minecraft:item", - "name": "minecraft:mushroom_stem" - } - ], - "conditions": [ - { - "condition": "minecraft:match_tool", - "predicate": { - "components": { - "minecraft:enchantments": { - "levels": { - "minecraft:silk_touch": 1 - } - } - } - } - } - ] - }, - { - "rolls": 1, - "entries": [ - { - "type": "minecraft:item", - "name": "minecraft:knowledge_book", - "functions": [ - { - "function": "minecraft:set_custom_data", - "tag": "{Tags:[\"timber_location\"]}" - } - ], - "conditions": [ - { - "condition": "minecraft:match_tool", - "predicate": { - "items": "#timber:axes" - } - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/18-41/data/minecraft/tags/functions/load.json b/18-41/data/minecraft/tags/functions/load.json deleted file mode 100644 index 420a05c..0000000 --- a/18-41/data/minecraft/tags/functions/load.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "values": [ - "timber:init", - "timber:main" - ] -} diff --git a/18-41/data/timber/functions/algorithm/fungus/chop.mcfunction b/18-41/data/timber/functions/algorithm/fungus/chop.mcfunction deleted file mode 100644 index 1308490..0000000 --- a/18-41/data/timber/functions/algorithm/fungus/chop.mcfunction +++ /dev/null @@ -1,31 +0,0 @@ -# resets -scoreboard players set durability_okay timber 1 - -# keep track of tree size -scoreboard players add tree_size timber 1 - -# wear out tool -function timber:tool/durability_count - -# tool durability check -execute if score stop_chopping timber matches 1.. run function timber:tool/durability_check - -##### if on top -execute positioned ~ ~1 ~ unless predicate timber:block/stem run function timber:algorithm/fungus/validate_cap -# mark cap that have been checked -execute positioned ~ ~ ~1 unless entity @e[type=minecraft:marker,tag=timber_leaves_found,distance=...1,sort=nearest,limit=1] if predicate timber:block/cap run summon marker ~ ~ ~ {Tags:["timber_leaves_found"]} -execute positioned ~ ~ ~-1 unless entity @e[type=minecraft:marker,tag=timber_leaves_found,distance=...1,sort=nearest,limit=1] if predicate timber:block/cap run summon marker ~ ~ ~ {Tags:["timber_leaves_found"]} -execute positioned ~1 ~ ~ unless entity @e[type=minecraft:marker,tag=timber_leaves_found,distance=...1,sort=nearest,limit=1] if predicate timber:block/cap run summon marker ~ ~ ~ {Tags:["timber_leaves_found"]} -execute positioned ~-1 ~ ~ unless entity @e[type=minecraft:marker,tag=timber_leaves_found,distance=...1,sort=nearest,limit=1] if predicate timber:block/cap run summon marker ~ ~ ~ {Tags:["timber_leaves_found"]} -execute positioned ~ ~1 ~ unless entity @e[type=minecraft:marker,tag=timber_leaves_found,distance=...1,sort=nearest,limit=1] if predicate timber:block/cap run summon marker ~ ~ ~ {Tags:["timber_leaves_found"]} -execute positioned ~ ~-1 ~ unless entity @e[type=minecraft:marker,tag=timber_leaves_found,distance=...1,sort=nearest,limit=1] if predicate timber:block/cap run summon marker ~ ~ ~ {Tags:["timber_leaves_found"]} - -# mark block that will get destroyed -execute if score durability_okay timber matches 1.. if score tree_size timber < max_tree_size timber run summon marker ~ ~ ~ {Tags:["timber_stem"]} - -# search for next stem -execute if score durability_okay timber matches 1.. if score tree_size timber < max_tree_size timber run function timber:algorithm/fungus/search - -# destroy cap (always) -execute run scoreboard players set leaf_distance timber 0 -function timber:algorithm/fungus/leaves/search \ No newline at end of file diff --git a/18-41/data/timber/functions/algorithm/fungus/leaves/destroy.mcfunction b/18-41/data/timber/functions/algorithm/fungus/leaves/destroy.mcfunction deleted file mode 100644 index e5389ec..0000000 --- a/18-41/data/timber/functions/algorithm/fungus/leaves/destroy.mcfunction +++ /dev/null @@ -1,3 +0,0 @@ -execute if score leaf_distance timber <= max_tree_size timber run summon marker ~ ~ ~ {Tags:["timber_cap"]} - -execute if score leaf_distance timber <= max_tree_size timber run function timber:algorithm/fungus/leaves/search \ No newline at end of file diff --git a/18-41/data/timber/functions/algorithm/fungus/leaves/find_other_stems.mcfunction b/18-41/data/timber/functions/algorithm/fungus/leaves/find_other_stems.mcfunction deleted file mode 100644 index 899a15c..0000000 --- a/18-41/data/timber/functions/algorithm/fungus/leaves/find_other_stems.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -# find other stems -execute at @e[type=minecraft:marker,tag=timber_cap] positioned ~ ~ ~1 if predicate timber:block/stem unless entity @e[type=minecraft:marker,tag=timber_stem,distance=...1,sort=arbitrary,limit=1] unless entity @e[type=minecraft:marker,tag=timber_other_stem,distance=...1,sort=arbitrary,limit=1] run summon marker ~ ~ ~ {Tags:["timber_other_stem"]} -execute at @e[type=minecraft:marker,tag=timber_cap] positioned ~ ~ ~-1 if predicate timber:block/stem unless entity @e[type=minecraft:marker,tag=timber_stem,distance=...1,sort=arbitrary,limit=1] unless entity @e[type=minecraft:marker,tag=timber_other_stem,distance=...1,sort=arbitrary,limit=1] run summon marker ~ ~ ~ {Tags:["timber_other_stem"]} -execute at @e[type=minecraft:marker,tag=timber_cap] positioned ~1 ~ ~ if predicate timber:block/stem unless entity @e[type=minecraft:marker,tag=timber_stem,distance=...1,sort=arbitrary,limit=1] unless entity @e[type=minecraft:marker,tag=timber_other_stem,distance=...1,sort=arbitrary,limit=1] run summon marker ~ ~ ~ {Tags:["timber_other_stem"]} -execute at @e[type=minecraft:marker,tag=timber_cap] positioned ~-1 ~ ~ if predicate timber:block/stem unless entity @e[type=minecraft:marker,tag=timber_stem,distance=...1,sort=arbitrary,limit=1] unless entity @e[type=minecraft:marker,tag=timber_other_stem,distance=...1,sort=arbitrary,limit=1] run summon marker ~ ~ ~ {Tags:["timber_other_stem"]} -execute at @e[type=minecraft:marker,tag=timber_cap] positioned ~ ~1 ~ if predicate timber:block/stem unless entity @e[type=minecraft:marker,tag=timber_stem,distance=...1,sort=arbitrary,limit=1] unless entity @e[type=minecraft:marker,tag=timber_other_stem,distance=...1,sort=arbitrary,limit=1] run summon marker ~ ~ ~ {Tags:["timber_other_stem"]} -execute at @e[type=minecraft:marker,tag=timber_cap] positioned ~ ~-1 ~ if predicate timber:block/stem unless entity @e[type=minecraft:marker,tag=timber_stem,distance=...1,sort=arbitrary,limit=1] unless entity @e[type=minecraft:marker,tag=timber_other_stem,distance=...1,sort=arbitrary,limit=1] run summon marker ~ ~ ~ {Tags:["timber_other_stem"]} \ No newline at end of file diff --git a/18-41/data/timber/functions/algorithm/fungus/leaves/search.mcfunction b/18-41/data/timber/functions/algorithm/fungus/leaves/search.mcfunction deleted file mode 100644 index d2a0e7f..0000000 --- a/18-41/data/timber/functions/algorithm/fungus/leaves/search.mcfunction +++ /dev/null @@ -1,18 +0,0 @@ -scoreboard players add leaf_distance timber 1 -# search for next cap -execute positioned ~ ~ ~1 unless entity @e[type=minecraft:marker,tag=timber_cap,distance=...1,sort=arbitrary,limit=1] if predicate timber:block/cap run function timber:algorithm/fungus/leaves/destroy -execute positioned ~ ~ ~-1 unless entity @e[type=minecraft:marker,tag=timber_cap,distance=...1,sort=arbitrary,limit=1] if predicate timber:block/cap run function timber:algorithm/fungus/leaves/destroy -execute positioned ~1 ~ ~ unless entity @e[type=minecraft:marker,tag=timber_cap,distance=...1,sort=arbitrary,limit=1] if predicate timber:block/cap run function timber:algorithm/fungus/leaves/destroy -execute positioned ~-1 ~ ~ unless entity @e[type=minecraft:marker,tag=timber_cap,distance=...1,sort=arbitrary,limit=1] if predicate timber:block/cap run function timber:algorithm/fungus/leaves/destroy -execute positioned ~ ~1 ~ unless entity @e[type=minecraft:marker,tag=timber_cap,distance=...1,sort=arbitrary,limit=1] if predicate timber:block/cap run function timber:algorithm/fungus/leaves/destroy -execute positioned ~ ~-1 ~ unless entity @e[type=minecraft:marker,tag=timber_cap,distance=...1,sort=arbitrary,limit=1] if predicate timber:block/cap run function timber:algorithm/fungus/leaves/destroy - -execute positioned ~ ~-1 ~1 unless entity @e[type=minecraft:marker,tag=timber_cap,distance=...1,sort=arbitrary,limit=1] if predicate timber:block/cap run function timber:algorithm/fungus/leaves/destroy -execute positioned ~ ~-1 ~-1 unless entity @e[type=minecraft:marker,tag=timber_cap,distance=...1,sort=arbitrary,limit=1] if predicate timber:block/cap run function timber:algorithm/fungus/leaves/destroy -execute positioned ~1 ~-1 ~ unless entity @e[type=minecraft:marker,tag=timber_cap,distance=...1,sort=arbitrary,limit=1] if predicate timber:block/cap run function timber:algorithm/fungus/leaves/destroy -execute positioned ~-1 ~-1 ~ unless entity @e[type=minecraft:marker,tag=timber_cap,distance=...1,sort=arbitrary,limit=1] if predicate timber:block/cap run function timber:algorithm/fungus/leaves/destroy - -execute positioned ~1 ~-1 ~1 unless entity @e[type=minecraft:marker,tag=timber_cap,distance=...1,sort=arbitrary,limit=1] if predicate timber:block/cap run function timber:algorithm/fungus/leaves/destroy -execute positioned ~-1 ~-1 ~-1 unless entity @e[type=minecraft:marker,tag=timber_cap,distance=...1,sort=arbitrary,limit=1] if predicate timber:block/cap run function timber:algorithm/fungus/leaves/destroy -execute positioned ~1 ~-1 ~-1 unless entity @e[type=minecraft:marker,tag=timber_cap,distance=...1,sort=arbitrary,limit=1] if predicate timber:block/cap run function timber:algorithm/fungus/leaves/destroy -execute positioned ~-1 ~-1 ~1 unless entity @e[type=minecraft:marker,tag=timber_cap,distance=...1,sort=arbitrary,limit=1] if predicate timber:block/cap run function timber:algorithm/fungus/leaves/destroy \ No newline at end of file diff --git a/18-41/data/timber/functions/algorithm/fungus/run.mcfunction b/18-41/data/timber/functions/algorithm/fungus/run.mcfunction deleted file mode 100644 index 6db2dbe..0000000 --- a/18-41/data/timber/functions/algorithm/fungus/run.mcfunction +++ /dev/null @@ -1,5 +0,0 @@ -# check if item is a stem -summon minecraft:armor_stand ~ ~ ~ {Silent:1b,Invulnerable:1b,Marker:1b,Invisible:1b,Tags:["timber_item_check"]} -data modify entity @e[type=minecraft:armor_stand,tag=timber_item_check,distance=...1,sort=arbitrary,limit=1] HandItems[0] set from entity @e[distance=...1,type=minecraft:item,sort=nearest,limit=1,nbt={Age:0s}] Item -execute if entity @e[type=minecraft:armor_stand,tag=timber_item_check,distance=...1,sort=arbitrary,limit=1,predicate=timber:item/stem] run function timber:algorithm/fungus/chop -kill @e[type=minecraft:armor_stand,tag=timber_item_check,distance=...1,sort=arbitrary,limit=1] \ No newline at end of file diff --git a/18-41/data/timber/functions/algorithm/fungus/search.mcfunction b/18-41/data/timber/functions/algorithm/fungus/search.mcfunction deleted file mode 100644 index 07e10c9..0000000 --- a/18-41/data/timber/functions/algorithm/fungus/search.mcfunction +++ /dev/null @@ -1,24 +0,0 @@ -# search for next log -execute positioned ~ ~1 ~ if predicate timber:block/stem unless entity @e[type=minecraft:marker,tag=timber_stem,distance=...1,sort=arbitrary,limit=1] run function timber:algorithm/fungus/chop -execute positioned ~ ~1 ~1 if predicate timber:block/stem unless entity @e[type=minecraft:marker,tag=timber_stem,distance=...1,sort=arbitrary,limit=1] run function timber:algorithm/fungus/chop -execute positioned ~ ~1 ~-1 if predicate timber:block/stem unless entity @e[type=minecraft:marker,tag=timber_stem,distance=...1,sort=arbitrary,limit=1] run function timber:algorithm/fungus/chop -execute positioned ~1 ~1 ~ if predicate timber:block/stem unless entity @e[type=minecraft:marker,tag=timber_stem,distance=...1,sort=arbitrary,limit=1] run function timber:algorithm/fungus/chop -execute positioned ~1 ~1 ~1 if predicate timber:block/stem unless entity @e[type=minecraft:marker,tag=timber_stem,distance=...1,sort=arbitrary,limit=1] run function timber:algorithm/fungus/chop -execute positioned ~1 ~1 ~-1 if predicate timber:block/stem unless entity @e[type=minecraft:marker,tag=timber_stem,distance=...1,sort=arbitrary,limit=1] run function timber:algorithm/fungus/chop -execute positioned ~-1 ~1 ~ if predicate timber:block/stem unless entity @e[type=minecraft:marker,tag=timber_stem,distance=...1,sort=arbitrary,limit=1] run function timber:algorithm/fungus/chop -execute positioned ~-1 ~1 ~1 if predicate timber:block/stem unless entity @e[type=minecraft:marker,tag=timber_stem,distance=...1,sort=arbitrary,limit=1] run function timber:algorithm/fungus/chop -execute positioned ~-1 ~1 ~-1 if predicate timber:block/stem unless entity @e[type=minecraft:marker,tag=timber_stem,distance=...1,sort=arbitrary,limit=1] run function timber:algorithm/fungus/chop - -execute positioned ~ ~ ~1 if predicate timber:block/stem unless entity @e[type=minecraft:marker,tag=timber_stem,distance=...1,sort=arbitrary,limit=1] run function timber:algorithm/fungus/chop -execute positioned ~ ~ ~-1 if predicate timber:block/stem unless entity @e[type=minecraft:marker,tag=timber_stem,distance=...1,sort=arbitrary,limit=1] run function timber:algorithm/fungus/chop -execute positioned ~1 ~ ~ if predicate timber:block/stem unless entity @e[type=minecraft:marker,tag=timber_stem,distance=...1,sort=arbitrary,limit=1] run function timber:algorithm/fungus/chop -execute positioned ~1 ~ ~1 if predicate timber:block/stem unless entity @e[type=minecraft:marker,tag=timber_stem,distance=...1,sort=arbitrary,limit=1] run function timber:algorithm/fungus/chop -execute positioned ~1 ~ ~-1 if predicate timber:block/stem unless entity @e[type=minecraft:marker,tag=timber_stem,distance=...1,sort=arbitrary,limit=1] run function timber:algorithm/fungus/chop -execute positioned ~-1 ~ ~ if predicate timber:block/stem unless entity @e[type=minecraft:marker,tag=timber_stem,distance=...1,sort=arbitrary,limit=1] run function timber:algorithm/fungus/chop -execute positioned ~-1 ~ ~1 if predicate timber:block/stem unless entity @e[type=minecraft:marker,tag=timber_stem,distance=...1,sort=arbitrary,limit=1] run function timber:algorithm/fungus/chop -execute positioned ~-1 ~ ~-1 if predicate timber:block/stem unless entity @e[type=minecraft:marker,tag=timber_stem,distance=...1,sort=arbitrary,limit=1] run function timber:algorithm/fungus/chop - -execute positioned ~ ~2 ~ if predicate timber:block/stem unless entity @e[type=minecraft:marker,tag=timber_stem,distance=...1,sort=arbitrary,limit=1] run function timber:algorithm/fungus/chop - -# search for next log downwards -execute if score chop_down timber matches 1.. run function timber:algorithm/fungus/search_down \ No newline at end of file diff --git a/18-41/data/timber/functions/algorithm/fungus/search_down.mcfunction b/18-41/data/timber/functions/algorithm/fungus/search_down.mcfunction deleted file mode 100644 index a9edfb2..0000000 --- a/18-41/data/timber/functions/algorithm/fungus/search_down.mcfunction +++ /dev/null @@ -1,10 +0,0 @@ -# search for next log downwards -execute positioned ~ ~-1 ~ if predicate timber:block/stem unless entity @e[type=minecraft:marker,tag=timber_stem,distance=...1,sort=arbitrary,limit=1] run function timber:algorithm/fungus/chop -execute positioned ~ ~-1 ~1 if predicate timber:block/stem unless entity @e[type=minecraft:marker,tag=timber_stem,distance=...1,sort=arbitrary,limit=1] run function timber:algorithm/fungus/chop -execute positioned ~ ~-1 ~-1 if predicate timber:block/stem unless entity @e[type=minecraft:marker,tag=timber_stem,distance=...1,sort=arbitrary,limit=1] run function timber:algorithm/fungus/chop -execute positioned ~1 ~-1 ~ if predicate timber:block/stem unless entity @e[type=minecraft:marker,tag=timber_stem,distance=...1,sort=arbitrary,limit=1] run function timber:algorithm/fungus/chop -execute positioned ~1 ~-1 ~1 if predicate timber:block/stem unless entity @e[type=minecraft:marker,tag=timber_stem,distance=...1,sort=arbitrary,limit=1] run function timber:algorithm/fungus/chop -execute positioned ~1 ~-1 ~-1 if predicate timber:block/stem unless entity @e[type=minecraft:marker,tag=timber_stem,distance=...1,sort=arbitrary,limit=1] run function timber:algorithm/fungus/chop -execute positioned ~-1 ~-1 ~ if predicate timber:block/stem unless entity @e[type=minecraft:marker,tag=timber_stem,distance=...1,sort=arbitrary,limit=1] run function timber:algorithm/fungus/chop -execute positioned ~-1 ~-1 ~1 if predicate timber:block/stem unless entity @e[type=minecraft:marker,tag=timber_stem,distance=...1,sort=arbitrary,limit=1] run function timber:algorithm/fungus/chop -execute positioned ~-1 ~-1 ~-1 if predicate timber:block/stem unless entity @e[type=minecraft:marker,tag=timber_stem,distance=...1,sort=arbitrary,limit=1] run function timber:algorithm/fungus/chop \ No newline at end of file diff --git a/18-41/data/timber/functions/algorithm/fungus/validate_cap.mcfunction b/18-41/data/timber/functions/algorithm/fungus/validate_cap.mcfunction deleted file mode 100644 index e41d9b6..0000000 --- a/18-41/data/timber/functions/algorithm/fungus/validate_cap.mcfunction +++ /dev/null @@ -1,6 +0,0 @@ -# mark leaf that have been checked -execute positioned ~ ~ ~1 unless entity @e[type=minecraft:marker,tag=timber_leaves_found,distance=...1,sort=nearest,limit=1] if predicate timber:block/cap run summon marker ~ ~ ~ {Tags:["timber_leaves_found"]} -execute positioned ~ ~ ~-1 unless entity @e[type=minecraft:marker,tag=timber_leaves_found,distance=...1,sort=nearest,limit=1] if predicate timber:block/cap run summon marker ~ ~ ~ {Tags:["timber_leaves_found"]} -execute positioned ~1 ~ ~ unless entity @e[type=minecraft:marker,tag=timber_leaves_found,distance=...1,sort=nearest,limit=1] if predicate timber:block/cap run summon marker ~ ~ ~ {Tags:["timber_leaves_found"]} -execute positioned ~-1 ~ ~ unless entity @e[type=minecraft:marker,tag=timber_leaves_found,distance=...1,sort=nearest,limit=1] if predicate timber:block/cap run summon marker ~ ~ ~ {Tags:["timber_leaves_found"]} -execute positioned ~ ~ ~ unless entity @e[type=minecraft:marker,tag=timber_leaves_found,distance=...1,sort=nearest,limit=1] if predicate timber:block/cap run summon marker ~ ~ ~ {Tags:["timber_leaves_found"]} \ No newline at end of file diff --git a/18-41/data/timber/functions/algorithm/tree/chop.mcfunction b/18-41/data/timber/functions/algorithm/tree/chop.mcfunction deleted file mode 100644 index f3c591f..0000000 --- a/18-41/data/timber/functions/algorithm/tree/chop.mcfunction +++ /dev/null @@ -1,29 +0,0 @@ -# resets -scoreboard players set durability_okay timber 1 - -# keep track of tree size -scoreboard players add tree_size timber 1 - -# wear out tool -function timber:tool/durability_count - -# tool durability check -execute if score stop_chopping timber matches 1.. unless score unbreakable timber matches 1 run function timber:tool/durability_check - -# mark leaf that have been checked -execute positioned ~ ~ ~1 unless entity @e[type=minecraft:marker,tag=timber_leaves_found,distance=...1,sort=nearest,limit=1] if predicate timber:block/leaf run summon marker ~ ~ ~ {Tags:["timber_leaves_found"]} -execute positioned ~ ~ ~-1 unless entity @e[type=minecraft:marker,tag=timber_leaves_found,distance=...1,sort=nearest,limit=1] if predicate timber:block/leaf run summon marker ~ ~ ~ {Tags:["timber_leaves_found"]} -execute positioned ~1 ~ ~ unless entity @e[type=minecraft:marker,tag=timber_leaves_found,distance=...1,sort=nearest,limit=1] if predicate timber:block/leaf run summon marker ~ ~ ~ {Tags:["timber_leaves_found"]} -execute positioned ~-1 ~ ~ unless entity @e[type=minecraft:marker,tag=timber_leaves_found,distance=...1,sort=nearest,limit=1] if predicate timber:block/leaf run summon marker ~ ~ ~ {Tags:["timber_leaves_found"]} -execute positioned ~ ~1 ~ unless entity @e[type=minecraft:marker,tag=timber_leaves_found,distance=...1,sort=nearest,limit=1] if predicate timber:block/leaf run summon marker ~ ~ ~ {Tags:["timber_leaves_found"]} -execute positioned ~ ~-1 ~ unless entity @e[type=minecraft:marker,tag=timber_leaves_found,distance=...1,sort=nearest,limit=1] if predicate timber:block/leaf run summon marker ~ ~ ~ {Tags:["timber_leaves_found"]} - -# mark block that will get destroyed -execute if score durability_okay timber matches 1.. if score tree_size timber < max_tree_size timber run summon marker ~ ~ ~ {Tags:["timber_log"]} - -# search for next log/root -execute if score durability_okay timber matches 1.. if score tree_size timber < max_tree_size timber run function timber:algorithm/tree/search_1 - -# destroy leaves -execute if score destroy_leaves timber matches 1.. run scoreboard players set leaf_distance timber 0 -execute if score destroy_leaves timber matches 1.. run function timber:algorithm/tree/leaves/search \ No newline at end of file diff --git a/18-41/data/timber/functions/algorithm/tree/chop_roots.mcfunction b/18-41/data/timber/functions/algorithm/tree/chop_roots.mcfunction deleted file mode 100644 index 0b57e1e..0000000 --- a/18-41/data/timber/functions/algorithm/tree/chop_roots.mcfunction +++ /dev/null @@ -1,17 +0,0 @@ -# resets -scoreboard players set durability_okay timber 1 - -# keep track of tree size -scoreboard players add tree_size timber 1 - -# wear out tool -function timber:tool/durability_count - -# tool durability check -execute if score stop_chopping timber matches 1.. unless score unbreakable timber matches 1 run function timber:tool/durability_check - -# mark block that will get destroyed -execute if score durability_okay timber matches 1.. if score tree_size timber < max_tree_size timber run summon marker ~ ~ ~ {Tags:["timber_root"]} - -# search for next log/root -execute if score durability_okay timber matches 1.. if score tree_size timber < max_tree_size timber run function timber:algorithm/tree/search_1 \ No newline at end of file diff --git a/18-41/data/timber/functions/algorithm/tree/leaves/destroy.mcfunction b/18-41/data/timber/functions/algorithm/tree/leaves/destroy.mcfunction deleted file mode 100644 index 500641f..0000000 --- a/18-41/data/timber/functions/algorithm/tree/leaves/destroy.mcfunction +++ /dev/null @@ -1,8 +0,0 @@ -# get leaf distance (in score leaf_distance timber) -function timber:algorithm/tree/leaves/get_distance - -# decide if leaf is closer to the current tree then mark it so that it will get destroyed -execute if score leaf_distance_old timber < leaf_distance timber run summon marker ~ ~ ~ {Tags:["timber_leaf"]} - -# search for next leaf -execute if score leaf_distance_old timber < leaf_distance timber run function timber:algorithm/tree/leaves/search \ No newline at end of file diff --git a/18-41/data/timber/functions/algorithm/tree/leaves/get_distance.mcfunction b/18-41/data/timber/functions/algorithm/tree/leaves/get_distance.mcfunction deleted file mode 100644 index ece384e..0000000 --- a/18-41/data/timber/functions/algorithm/tree/leaves/get_distance.mcfunction +++ /dev/null @@ -1,17 +0,0 @@ -scoreboard players set leaf_distance timber 0 -execute if block ~ ~ ~ #minecraft:leaves[distance=1] run scoreboard players set leaf_distance timber 1 -execute if block ~ ~ ~ #minecraft:leaves[distance=2] run scoreboard players set leaf_distance timber 2 -execute if block ~ ~ ~ #minecraft:leaves[distance=3] run scoreboard players set leaf_distance timber 3 -execute if block ~ ~ ~ #minecraft:leaves[distance=4] run scoreboard players set leaf_distance timber 4 -execute if block ~ ~ ~ #minecraft:leaves[distance=5] run scoreboard players set leaf_distance timber 5 -execute if block ~ ~ ~ #minecraft:leaves[distance=6] run scoreboard players set leaf_distance timber 6 -execute if block ~ ~ ~ #minecraft:leaves[distance=7] run scoreboard players set leaf_distance timber 7 -execute if block ~ ~ ~ #minecraft:leaves[distance=8] run scoreboard players set leaf_distance timber 8 -execute if block ~ ~ ~ #minecraft:leaves[distance=9] run scoreboard players set leaf_distance timber 9 -execute if block ~ ~ ~ #minecraft:leaves[distance=10] run scoreboard players set leaf_distance timber 10 -execute if block ~ ~ ~ #minecraft:leaves[distance=11] run scoreboard players set leaf_distance timber 11 -execute if block ~ ~ ~ #minecraft:leaves[distance=12] run scoreboard players set leaf_distance timber 12 -execute if block ~ ~ ~ #minecraft:leaves[distance=13] run scoreboard players set leaf_distance timber 13 -execute if block ~ ~ ~ #minecraft:leaves[distance=14] run scoreboard players set leaf_distance timber 14 -# always destroy mangrove sapling -execute if block ~ ~ ~ minecraft:mangrove_propagule run scoreboard players set leaf_distance timber 15 \ No newline at end of file diff --git a/18-41/data/timber/functions/algorithm/tree/leaves/search.mcfunction b/18-41/data/timber/functions/algorithm/tree/leaves/search.mcfunction deleted file mode 100644 index 13bbd5e..0000000 --- a/18-41/data/timber/functions/algorithm/tree/leaves/search.mcfunction +++ /dev/null @@ -1,17 +0,0 @@ -# save leaves distance tag at its position -summon marker ~ ~ ~ {Tags:["timber_leaf_distance"]} -scoreboard players operation @e[type=minecraft:marker,tag=timber_leaf_distance,distance=...1,sort=arbitrary,limit=1] timber = leaf_distance timber - -# load leaves distance tag and search for next leaf -scoreboard players operation leaf_distance_old timber = @e[type=minecraft:marker,tag=timber_leaf_distance,distance=...1,sort=arbitrary,limit=1] timber -execute positioned ~ ~ ~1 unless entity @e[type=minecraft:marker,tag=timber_leaf_distance,distance=...1,sort=arbitrary,limit=1] if predicate timber:block/leaf run function timber:algorithm/tree/leaves/destroy -scoreboard players operation leaf_distance_old timber = @e[type=minecraft:marker,tag=timber_leaf_distance,distance=...1,sort=arbitrary,limit=1] timber -execute positioned ~ ~ ~-1 unless entity @e[type=minecraft:marker,tag=timber_leaf_distance,distance=...1,sort=arbitrary,limit=1] if predicate timber:block/leaf run function timber:algorithm/tree/leaves/destroy -scoreboard players operation leaf_distance_old timber = @e[type=minecraft:marker,tag=timber_leaf_distance,distance=...1,sort=arbitrary,limit=1] timber -execute positioned ~1 ~ ~ unless entity @e[type=minecraft:marker,tag=timber_leaf_distance,distance=...1,sort=arbitrary,limit=1] if predicate timber:block/leaf run function timber:algorithm/tree/leaves/destroy -scoreboard players operation leaf_distance_old timber = @e[type=minecraft:marker,tag=timber_leaf_distance,distance=...1,sort=arbitrary,limit=1] timber -execute positioned ~-1 ~ ~ unless entity @e[type=minecraft:marker,tag=timber_leaf_distance,distance=...1,sort=arbitrary,limit=1] if predicate timber:block/leaf run function timber:algorithm/tree/leaves/destroy -scoreboard players operation leaf_distance_old timber = @e[type=minecraft:marker,tag=timber_leaf_distance,distance=...1,sort=arbitrary,limit=1] timber -execute positioned ~ ~1 ~ unless entity @e[type=minecraft:marker,tag=timber_leaf_distance,distance=...1,sort=arbitrary,limit=1] if predicate timber:block/leaf run function timber:algorithm/tree/leaves/destroy -scoreboard players operation leaf_distance_old timber = @e[type=minecraft:marker,tag=timber_leaf_distance,distance=...1,sort=arbitrary,limit=1] timber -execute positioned ~ ~-1 ~ unless entity @e[type=minecraft:marker,tag=timber_leaf_distance,distance=...1,sort=arbitrary,limit=1] if predicate timber:block/leaf run function timber:algorithm/tree/leaves/destroy \ No newline at end of file diff --git a/18-41/data/timber/functions/algorithm/tree/run.mcfunction b/18-41/data/timber/functions/algorithm/tree/run.mcfunction deleted file mode 100644 index 172c583..0000000 --- a/18-41/data/timber/functions/algorithm/tree/run.mcfunction +++ /dev/null @@ -1,6 +0,0 @@ -# check if item is in tag #minecraft:logs -summon minecraft:armor_stand ~ ~ ~ {Silent:1b,Invulnerable:1b,Marker:1b,Invisible:1b,Tags:["timber_item_check"]} -data modify entity @e[type=minecraft:armor_stand,tag=timber_item_check,distance=...1,sort=arbitrary,limit=1] HandItems[0] set from entity @e[type=minecraft:item,distance=...1,sort=nearest,limit=1,nbt={Age:0s}] Item -execute if entity @e[type=minecraft:armor_stand,tag=timber_item_check,distance=...1,sort=arbitrary,limit=1,predicate=timber:item/log] run function timber:algorithm/tree/chop -execute if score start_chop_on_root timber matches 1.. if entity @e[type=minecraft:armor_stand,tag=timber_item_check,distance=...1,sort=arbitrary,limit=1,predicate=timber:item/root] run function timber:algorithm/tree/chop_roots -kill @e[type=minecraft:armor_stand,tag=timber_item_check,distance=...1,sort=arbitrary,limit=1] diff --git a/18-41/data/timber/functions/algorithm/tree/search_1.mcfunction b/18-41/data/timber/functions/algorithm/tree/search_1.mcfunction deleted file mode 100644 index 020d7eb..0000000 --- a/18-41/data/timber/functions/algorithm/tree/search_1.mcfunction +++ /dev/null @@ -1,24 +0,0 @@ -# search for next log -execute positioned ~ ~1 ~ run function timber:algorithm/tree/search_2 -execute positioned ~ ~1 ~1 run function timber:algorithm/tree/search_2 -execute positioned ~ ~1 ~-1 run function timber:algorithm/tree/search_2 -execute positioned ~1 ~1 ~ run function timber:algorithm/tree/search_2 -execute positioned ~1 ~1 ~1 run function timber:algorithm/tree/search_2 -execute positioned ~1 ~1 ~-1 run function timber:algorithm/tree/search_2 -execute positioned ~-1 ~1 ~ run function timber:algorithm/tree/search_2 -execute positioned ~-1 ~1 ~1 run function timber:algorithm/tree/search_2 -execute positioned ~-1 ~1 ~-1 run function timber:algorithm/tree/search_2 - -execute positioned ~ ~ ~1 run function timber:algorithm/tree/search_2 -execute positioned ~ ~ ~-1 run function timber:algorithm/tree/search_2 -execute positioned ~1 ~ ~ run function timber:algorithm/tree/search_2 -execute positioned ~1 ~ ~1 run function timber:algorithm/tree/search_2 -execute positioned ~1 ~ ~-1 run function timber:algorithm/tree/search_2 -execute positioned ~-1 ~ ~ run function timber:algorithm/tree/search_2 -execute positioned ~-1 ~ ~1 run function timber:algorithm/tree/search_2 -execute positioned ~-1 ~ ~-1 run function timber:algorithm/tree/search_2 - -# search for next log downwards -execute if score chop_down timber matches 1.. run function timber:algorithm/tree/search_down -# search for roots downwards -execute unless score chop_down timber matches 1.. if score destroy_roots timber matches 1.. if entity @e[type=minecraft:marker,tag=timber_root,distance=...1,sort=arbitrary,limit=1] run function timber:algorithm/tree/search_down diff --git a/18-41/data/timber/functions/algorithm/tree/search_2.mcfunction b/18-41/data/timber/functions/algorithm/tree/search_2.mcfunction deleted file mode 100644 index 7f48c20..0000000 --- a/18-41/data/timber/functions/algorithm/tree/search_2.mcfunction +++ /dev/null @@ -1,4 +0,0 @@ -# if log -execute if predicate timber:block/log unless entity @e[type=minecraft:marker,tag=timber_log,distance=...1,sort=arbitrary,limit=1] run function timber:algorithm/tree/chop -# if root -execute if block ~ ~ ~ minecraft:mangrove_roots unless entity @e[type=minecraft:marker,tag=timber_root,distance=...1,sort=arbitrary,limit=1] run function timber:algorithm/tree/chop_roots diff --git a/18-41/data/timber/functions/algorithm/tree/search_down.mcfunction b/18-41/data/timber/functions/algorithm/tree/search_down.mcfunction deleted file mode 100644 index aee97f5..0000000 --- a/18-41/data/timber/functions/algorithm/tree/search_down.mcfunction +++ /dev/null @@ -1,10 +0,0 @@ -# search for next log downwards -execute positioned ~ ~-1 ~ run function timber:algorithm/tree/search_2 -execute positioned ~ ~-1 ~1 run function timber:algorithm/tree/search_2 -execute positioned ~ ~-1 ~-1 run function timber:algorithm/tree/search_2 -execute positioned ~1 ~-1 ~ run function timber:algorithm/tree/search_2 -execute positioned ~1 ~-1 ~1 run function timber:algorithm/tree/search_2 -execute positioned ~1 ~-1 ~-1 run function timber:algorithm/tree/search_2 -execute positioned ~-1 ~-1 ~ run function timber:algorithm/tree/search_2 -execute positioned ~-1 ~-1 ~1 run function timber:algorithm/tree/search_2 -execute positioned ~-1 ~-1 ~-1 run function timber:algorithm/tree/search_2 \ No newline at end of file diff --git a/18-41/data/timber/functions/autoplant/loop.mcfunction b/18-41/data/timber/functions/autoplant/loop.mcfunction deleted file mode 100644 index f765e10..0000000 --- a/18-41/data/timber/functions/autoplant/loop.mcfunction +++ /dev/null @@ -1,5 +0,0 @@ -# Loop -execute if score plant_sapling timber matches 1.. run schedule function timber:autoplant/loop 2s replace - -# plant sapling process -execute as @e[type=minecraft:item,tag=!timber_checked,tag=!global.ignore,tag=!global.ignore.kill,sort=random,limit=20,nbt={OnGround:1b}] at @s if block ~ ~-0.5 ~ #minecraft:dirt if block ~ ~0.5 ~ minecraft:air at @s run function timber:autoplant/validate \ No newline at end of file diff --git a/18-41/data/timber/functions/autoplant/mud_fix.mcfunction b/18-41/data/timber/functions/autoplant/mud_fix.mcfunction deleted file mode 100644 index cffee47..0000000 --- a/18-41/data/timber/functions/autoplant/mud_fix.mcfunction +++ /dev/null @@ -1,2 +0,0 @@ -execute at @e[type=minecraft:marker,tag=timber_mud] run setblock ~ ~ ~ minecraft:mud -kill @e[type=minecraft:marker,tag=timber_mud] \ No newline at end of file diff --git a/18-41/data/timber/functions/autoplant/plant.mcfunction b/18-41/data/timber/functions/autoplant/plant.mcfunction deleted file mode 100644 index d701739..0000000 --- a/18-41/data/timber/functions/autoplant/plant.mcfunction +++ /dev/null @@ -1,18 +0,0 @@ -# replace item-sapling with block-sapling (special case for mud, because fallingblock get destroyed. 0.125 is the height when you sink into the mud) -execute unless block ~ ~ ~ minecraft:air run summon marker ~ ~ ~ {Tags:["timber_mud","global.ignore"]} -execute unless block ~ ~ ~ minecraft:air run schedule function timber:autoplant/mud_fix 1t -execute unless block ~ ~ ~ minecraft:air run setblock ~ ~ ~ minecraft:muddy_mangrove_roots -summon minecraft:falling_block ~ ~0.125 ~ {BlockState:{Name:"minecraft:barrier"},Time:1,DropItem:0b,Tags:["timber_item_to_block"]} -execute positioned ~ ~0.125 ~ run data modify entity @e[type=minecraft:falling_block,tag=timber_item_to_block,distance=...1,sort=arbitrary,limit=1] BlockState.Name set from entity @s Item.id - -# on stacks -1 -execute store result score @s timber run data get entity @s Item.count -scoreboard players remove @s timber 1 -execute if score @s timber matches ..0 run kill @s -execute if score @s timber matches 1.. store result entity @s Item.count byte 1 run scoreboard players get @s timber -scoreboard players reset @s timber -# cheap custom server fix -execute if score not_vanilla timber matches 1 run kill @s - -# sapling will be checked every time -tag @s remove timber_checked \ No newline at end of file diff --git a/18-41/data/timber/functions/autoplant/validate.mcfunction b/18-41/data/timber/functions/autoplant/validate.mcfunction deleted file mode 100644 index e84bccf..0000000 --- a/18-41/data/timber/functions/autoplant/validate.mcfunction +++ /dev/null @@ -1,8 +0,0 @@ -# don't check items twice (except saplings, look in plant.mcfunction) -tag @s add timber_checked - -# check if item is in tag #minecraft:sapling -summon minecraft:armor_stand ~ ~ ~ {Silent:1b,Invulnerable:1b,Marker:1b,Invisible:1b,Tags:["timber_item_check"]} -data modify entity @e[type=minecraft:armor_stand,tag=timber_item_check,distance=...1,sort=arbitrary,limit=1] HandItems[0] set from entity @s Item -execute if entity @e[type=minecraft:armor_stand,tag=timber_item_check,distance=...1,sort=arbitrary,limit=1,predicate=timber:item/sapling] run function timber:autoplant/plant -kill @e[type=minecraft:armor_stand,tag=timber_item_check,distance=...1,sort=arbitrary,limit=1] \ No newline at end of file diff --git a/18-41/data/timber/functions/help.mcfunction b/18-41/data/timber/functions/help.mcfunction deleted file mode 100644 index 8f04f36..0000000 --- a/18-41/data/timber/functions/help.mcfunction +++ /dev/null @@ -1,25 +0,0 @@ -tellraw @s ["",{"text":"\u00A7m ","color":"dark_gray"}] - -tellraw @s ["",{"text":" Timber Datapack"}] - -tellraw @s ["",{"text":"\u00A7m ","color":"dark_gray"}] - -tellraw @s ["",{"text":"The Timber Datapack allows you to instantly chop down a tree just by breaking one log with any axe."}] - -tellraw @s ["",{"text":"By default, sneaking while breaking will not chop the tree."}] - -tellraw @s [""] - -tellraw @s ["",{"text":"Every player can use "},{"text":"/trigger TimberToggle","color":"blue","clickEvent":{"action":"suggest_command","value":"/trigger TimberToggle"}},{"text":" to individually turn the datapack on or off."}] - -tellraw @s [""] - -tellraw @s [""," ",{"text":"[click here to check for new updates]","color":"green","clickEvent":{"action":"open_url","value":"https://github.com/Moggla/Timber-Datapack/releases"}}] - -tellraw @s [""] - -tellraw @s [""," ",{"text":"[click here to see the settings]","color":"gold","clickEvent":{"action":"run_command","value":"/function timber:settings/settings1_click"}}] - -tellraw @s ["",{"text":"\u00A7m ","color":"dark_gray"}] - -function timber:settings/end_of_message \ No newline at end of file diff --git a/18-41/data/timber/functions/init.mcfunction b/18-41/data/timber/functions/init.mcfunction deleted file mode 100644 index 1d2cbf0..0000000 --- a/18-41/data/timber/functions/init.mcfunction +++ /dev/null @@ -1,39 +0,0 @@ -# longer maxCommandChainLength -gamerule maxCommandChainLength 1000000000 - -# create scoreboards -scoreboard objectives add timber_w_axe minecraft.used:minecraft.wooden_axe -scoreboard objectives add timber_s_axe minecraft.used:minecraft.stone_axe -scoreboard objectives add timber_i_axe minecraft.used:minecraft.iron_axe -scoreboard objectives add timber_g_axe minecraft.used:minecraft.golden_axe -scoreboard objectives add timber_d_axe minecraft.used:minecraft.diamond_axe -scoreboard objectives add timber_n_axe minecraft.used:minecraft.netherite_axe -scoreboard objectives add timber dummy -scoreboard objectives add TimberToggle trigger {"text":"Timber Toggle","color":"#724E21"} -scoreboard objectives add timber_disabled dummy -scoreboard objectives add timber_prsistent dummy - -# non-overriding default values -function timber:settings/default_values -function timber:tool/durability_max -scoreboard players set running timber 0 - -# start optional loops -execute if score plant_sapling timber matches 1.. run function timber:autoplant/loop -execute if score slow_chop timber matches 1.. run function timber:slow_chop/loop - -# check loottable compatibility of mushroom stem -setblock ~ 255 ~ minecraft:chest -setblock ~ 255 ~1 minecraft:mushroom_stem -loot insert ~ 255 ~ mine ~ 255 ~1 minecraft:diamond_axe -execute unless data block ~ 255 ~ Items[{id:"minecraft:knowledge_book",components:{"minecraft:custom_data":{Tags:["timber_location"]}}}] run tellraw @a[tag=!noTimberWarning] ["",{"text":"Timber Datapack Warning: ","color":"red"},{"text":"Incompatibility with the mushroom_stem loottable of another datapack!"}] -fill ~-1 255 ~-1 ~1 255 ~1 minecraft:air -execute store result score doTileDrops timber run gamerule doTileDrops -execute if score doTileDrops timber matches 0 run tellraw @a[tag=!noTimberWarning] ["",{"text":"Timber Datapack Warning: ","color":"red"},{"text":"This datapack only works when gamerule doTileDrops is set to true!"}] - -# Getting the game version and checking if it is compatible with the datapack https://minecraft.fandom.com/wiki/Data_version -execute store result score game_version timber run data get entity @r DataVersion -execute if score game_version timber matches ..3442 run tellraw @a[tag=!noTimberWarning] ["",{"text":"Timber Datapack Warning: ","color":"red"},{"text":"This datapack is not compatible with all the versions before 1.20!"}] - -# Checking if the server uses a different jar from the vanilla one -function timber:utils/check_server \ No newline at end of file diff --git a/18-41/data/timber/functions/main.mcfunction b/18-41/data/timber/functions/main.mcfunction deleted file mode 100644 index 02ef309..0000000 --- a/18-41/data/timber/functions/main.mcfunction +++ /dev/null @@ -1,11 +0,0 @@ -# loop (not in minecraft:tick in order to stop it after uninstall) -schedule function timber:main 1t - -# if any kind of axe is used -execute as @a[predicate=timber:used_axe,tag=!global.ignore] at @s run function timber:utils/check_max_command_chain_length - -# toggle timber datapack per player -execute as @a[scores={TimberToggle=1..},tag=!global.ignore] run function timber:settings/toggle/timber - -# reset -scoreboard players enable @a TimberToggle \ No newline at end of file diff --git a/18-41/data/timber/functions/run.mcfunction b/18-41/data/timber/functions/run.mcfunction deleted file mode 100644 index 23149f8..0000000 --- a/18-41/data/timber/functions/run.mcfunction +++ /dev/null @@ -1,46 +0,0 @@ -# reset -scoreboard players set leaves_found timber 0 -scoreboard players set tree_size timber 0 -scoreboard players set tree_type timber 0 - -# load settings to other scoreboard (leaves persistent) -execute unless score persistent timber matches 1.. run scoreboard players set @s timber_prsistent 0 -execute if score persistent timber matches 1.. run scoreboard players set @s timber_prsistent 1 - -# search last broken item (place of tree) and set a marker (limit=1 removed, because carpet on log breaks this) -execute at @e[type=minecraft:item,distance=..7,nbt={Age:0s}] run summon marker ~ ~ ~ {Tags:["timber_tree","global.ignore"]} - -# Enchantments randomizer -scoreboard players set unbreaking timber 0 -execute if entity @s[nbt={SelectedItem:{components:{"minecraft:enchantments":{levels:{"minecraft:unbreaking": 1}}}}}] run scoreboard players set unbreaking timber 1 -execute if entity @s[nbt={SelectedItem:{components:{"minecraft:enchantments":{levels:{"minecraft:unbreaking": 2}}}}}] run scoreboard players set unbreaking timber 2 -execute if entity @s[nbt={SelectedItem:{components:{"minecraft:enchantments":{levels:{"minecraft:unbreaking": 3}}}}}] run scoreboard players set unbreaking timber 3 - -# get durability -function timber:tool/durability_get - -# scan through tree - # tree (limit=1,sort=arbitrary removed, because carpet on log breaks this) - execute if score chop_trees timber matches 1.. at @e[type=minecraft:marker,tag=timber_tree,tag=!timber_slow_chop,distance=..7] run function timber:algorithm/tree/run - execute store result score leaves_found timber if entity @e[type=minecraft:marker,tag=timber_leaves_found] - # if tree is valid - execute if score leaves_found timber >= min_leaves_found timber unless score tree_size timber matches 0 run scoreboard players set tree_type timber 1 - - # fungi (only if not tree already) - execute if score tree_type timber matches 0 if score chop_fungi timber matches 1.. run function timber:run_fungi - -# if tree or fungi is valid (tree = 1 | fungus = 2) -execute unless score tree_type timber matches 0 if score tree_size timber <= max_tree_size timber run function timber:validated - -# remove markers -execute if score chop_fungi timber matches 1.. run kill @e[type=minecraft:item,nbt={Item:{components:{"minecraft:custom_data":{Tags:["timber_location"]}}}}] -execute if score chop_fungi timber matches 1.. run clear @s knowledge_book[minecraft:custom_data={Tags:["timber_location"]}] -kill @e[type=minecraft:marker,tag=timber_tree,tag=!timber_slow_chop] -kill @e[type=minecraft:marker,tag=timber_log,tag=!timber_destroy] -kill @e[type=minecraft:marker,tag=timber_leaf,tag=!timber_destroy] -kill @e[type=minecraft:marker,tag=timber_root,tag=!timber_destroy] -kill @e[type=minecraft:marker,tag=timber_stem,tag=!timber_destroy] -kill @e[type=minecraft:marker,tag=timber_cap,tag=!timber_destroy] -kill @e[type=minecraft:marker,tag=timber_leaves_found] -kill @e[type=minecraft:marker,tag=timber_leaf_distance] -kill @e[type=minecraft:marker,tag=timber_other_stem] \ No newline at end of file diff --git a/18-41/data/timber/functions/run_fungi.mcfunction b/18-41/data/timber/functions/run_fungi.mcfunction deleted file mode 100644 index ff8a737..0000000 --- a/18-41/data/timber/functions/run_fungi.mcfunction +++ /dev/null @@ -1,18 +0,0 @@ -# reset -scoreboard players set tree_size timber 0 -kill @e[type=minecraft:marker,tag=timber_leaves_found] -kill @e[type=minecraft:marker,tag=timber_log,tag=!timber_destroy] -kill @e[type=minecraft:marker,tag=timber_leaf,tag=!timber_destroy] - -# get durability -function timber:tool/durability_get - -# fungi -execute at @e[type=minecraft:marker,tag=timber_tree,tag=!timber_slow_chop,distance=..7,sort=arbitrary,limit=1] run function timber:algorithm/fungus/run -execute store result score leaves_found timber if entity @e[type=minecraft:marker,tag=timber_leaves_found] -# if fungi is valid -execute if score leaves_found timber >= min_leaves_found timber unless score tree_size timber matches 0 run scoreboard players set tree_type timber 2 - -# don't destroy the blocks in a radius of 5 blocks from other stems -execute if score tree_type timber matches 2 run function timber:algorithm/fungus/leaves/find_other_stems -execute if score tree_type timber matches 2 at @e[type=minecraft:marker,tag=timber_other_stem] run kill @e[type=minecraft:marker,tag=timber_cap,distance=..5] \ No newline at end of file diff --git a/18-41/data/timber/functions/settings/default_values.mcfunction b/18-41/data/timber/functions/settings/default_values.mcfunction deleted file mode 100644 index 8800ffc..0000000 --- a/18-41/data/timber/functions/settings/default_values.mcfunction +++ /dev/null @@ -1,28 +0,0 @@ -# non-overriding default values -execute unless score standing timber = standing timber run scoreboard players set standing timber 1 -execute unless score sneaking timber = sneaking timber run scoreboard players set sneaking timber 0 -execute unless score destroy_leaves timber = destroy_leaves timber run scoreboard players set destroy_leaves timber 1 -execute unless score plant_sapling timber = plant_sapling timber run scoreboard players set plant_sapling timber 0 -execute unless score destroy_roots timber = destroy_roots timber run scoreboard players set destroy_roots timber 1 -execute unless score start_chop_on_root timber = start_chop_on_root timber run scoreboard players set start_chop_on_root timber 1 -execute unless score slow_chop timber = slow_chop timber run scoreboard players set slow_chop timber 0 -execute unless score chop_down timber = chop_down timber run scoreboard players set chop_down timber 0 -execute unless score stop_chopping timber = stop_chopping timber run scoreboard players set stop_chopping timber 0 -execute unless score wear_out timber = wear_out timber run scoreboard players set wear_out timber 1 -execute unless score drop_loot timber = drop_loot timber run scoreboard players set drop_loot timber 0 -execute unless score hunger timber = hunger timber run scoreboard players set hunger timber 0 -execute unless score stopsound timber = stopsound timber run scoreboard players set stopsound timber 0 - -execute unless score wooden_axe timber = wooden_axe timber run scoreboard players set wooden_axe timber 1 -execute unless score stone_axe timber = stone_axe timber run scoreboard players set stone_axe timber 1 -execute unless score iron_axe timber = iron_axe timber run scoreboard players set iron_axe timber 1 -execute unless score golden_axe timber = golden_axe timber run scoreboard players set golden_axe timber 1 -execute unless score diamond_axe timber = diamond_axe timber run scoreboard players set diamond_axe timber 1 -execute unless score netherite_axe timber = netherite_axe timber run scoreboard players set netherite_axe timber 1 -execute unless score chop_trees timber = chop_trees timber run scoreboard players set chop_trees timber 1 -execute unless score chop_fungi timber = chop_fungi timber run scoreboard players set chop_fungi timber 0 -execute unless score persistent timber = persistent timber run scoreboard players set persistent timber 0 -execute unless score blocks_per_chop timber = blocks_per_chop timber run scoreboard players set blocks_per_chop timber 1 -execute unless score time_between_chops timber = time_between_chops timber run scoreboard players set time_between_chops timber 1 -execute unless score max_tree_size timber = max_tree_size timber run scoreboard players set max_tree_size timber 555 -execute unless score min_leaves_found timber = min_leaves_found timber run scoreboard players set min_leaves_found timber 5 diff --git a/18-41/data/timber/functions/settings/end_of_message.mcfunction b/18-41/data/timber/functions/settings/end_of_message.mcfunction deleted file mode 100644 index 1d5e211..0000000 --- a/18-41/data/timber/functions/settings/end_of_message.mcfunction +++ /dev/null @@ -1,3 +0,0 @@ -execute store result score sendCommandFeedback timber run gamerule sendCommandFeedback -execute if score sendCommandFeedback timber matches 1 run schedule function timber:settings/reset_gamerule 1t -gamerule sendCommandFeedback false \ No newline at end of file diff --git a/18-41/data/timber/functions/settings/reset_gamerule.mcfunction b/18-41/data/timber/functions/settings/reset_gamerule.mcfunction deleted file mode 100644 index b466492..0000000 --- a/18-41/data/timber/functions/settings/reset_gamerule.mcfunction +++ /dev/null @@ -1 +0,0 @@ -gamerule sendCommandFeedback true \ No newline at end of file diff --git a/18-41/data/timber/functions/settings/settings.mcfunction b/18-41/data/timber/functions/settings/settings.mcfunction deleted file mode 100644 index 42d2588..0000000 --- a/18-41/data/timber/functions/settings/settings.mcfunction +++ /dev/null @@ -1 +0,0 @@ -function timber:settings/settings1 \ No newline at end of file diff --git a/18-41/data/timber/functions/settings/settings1.mcfunction b/18-41/data/timber/functions/settings/settings1.mcfunction deleted file mode 100644 index 140256c..0000000 --- a/18-41/data/timber/functions/settings/settings1.mcfunction +++ /dev/null @@ -1,57 +0,0 @@ -tellraw @s ["",{"text":" "}] -tellraw @s ["",{"text":" "}] -tellraw @s ["",{"text":" "}] -tellraw @s ["",{"text":" "}] - -tellraw @s ["",{"text":"\u00A7m ","color":"dark_gray"}] - -tellraw @s ["",{"text":" Timber "},{"text":"/","color":"gray"},{"text":" Global Settings "},{"text":" < ","clickEvent":{"action":"run_command","value":"/function timber:settings/settings1_click"}},{"text":"1/2"},{"text":" > ","clickEvent":{"action":"run_command","value":"/function timber:settings/settings2_click"}}] - -tellraw @s ["",{"text":"\u00A7m ","color":"dark_gray"}] - -execute if score standing timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/standing"}},{"text":" Works When Standing"}] -execute unless score standing timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/standing"}},{"text":" Works When Standing"}] - -execute if score sneaking timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/sneaking"}},{"text":" Works When Sneaking"}] -execute unless score sneaking timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/sneaking"}},{"text":" Works When Sneaking"}] - -execute if score destroy_leaves timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/destroy_leaves"}},{"text":" Destroy Leaves"}] -execute unless score destroy_leaves timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/destroy_leaves"}},{"text":" Destroy Leaves"}] - -execute if score plant_sapling timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/plant_sapling"}},{"text":" Auto Plant Saplings","hoverEvent":{"action":"show_text","value":["",{"text":"All kind of saplings laying on the ground will get planted every 2 seconds."},{"text":"\n\nWhen a sapling get placed on mud, it turns into muddy mangrove roots for a fraction of a second due to datapack limitations.","color":"gray"}]}}] -execute unless score plant_sapling timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/plant_sapling"}},{"text":" Auto Plant Saplings","hoverEvent":{"action":"show_text","value":["",{"text":"All kind of saplings laying on the ground will get planted every 2 seconds."},{"text":"\n\nWhen a sapling get placed on mud, it turns into muddy mangrove roots for a fraction of a second due to datapack limitations.","color":"gray"}]}}] - -execute if score destroy_roots timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/destroy_roots"}},{"text":" Destroy Roots","hoverEvent":{"action":"show_text","value":["",{"text":"Only root blocks which are not in the ground count."}]}}] -execute unless score destroy_roots timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/destroy_roots"}},{"text":" Destroy Roots","hoverEvent":{"action":"show_text","value":["",{"text":"Only root blocks which are not in the ground count."}]}}] - -execute if score start_chop_on_root timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/start_chop_on_root"}},{"text":" Chop Tree When Roots Get Destroyed","hoverEvent":{"action":"show_text","value":["",{"text":"The datapack will chop the tree if you break its root blocks.\nOnly root blocks which are not in the ground count."}]}}] -execute unless score start_chop_on_root timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/start_chop_on_root"}},{"text":" Chop Tree When Roots Get Destroyed","hoverEvent":{"action":"show_text","value":["",{"text":"The datapack will chop the tree if you break its root blocks.\nOnly root blocks which are not in the ground count."}]}}] - -execute if score slow_chop timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/slow_chop"}},{"text":" Chop Slowly","hoverEvent":{"action":"show_text","value":["",{"text":"The tree won't be chopped instantly anymore. Additional options to change the speed of the chopping process can be changed at the next page."},{"text":"\n\nThis option can increase performance.","color":"gray"}]}}] -execute unless score slow_chop timber matches 1.. unless score drop_loot timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/slow_chop"}},{"text":" Chop Slowly","hoverEvent":{"action":"show_text","value":["",{"text":"The tree won't be chopped instantly anymore. Additional options to change the speed of the chopping process can be changed at the next page."},{"text":"\n\nThis option can increase performance.","color":"gray"}]}}] -execute unless score slow_chop timber matches 1.. if score drop_loot timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"#7F2B2B","hoverEvent":{"action":"show_text","value":"Can't be enabled when \"Drop Loot Straight into Inventory\" is enabled."}},{"text":" Chop Slowly","hoverEvent":{"action":"show_text","value":["",{"text":"The tree won't be chopped instantly anymore. Additional options to change the speed of the chopping process can be changed at the next page."},{"text":"\n\nThis option can increase performance.","color":"gray"}]}}] - -execute if score chop_down timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/chop_down"}},{"text":" Enable Chopping Down","hoverEvent":{"action":"show_text","value":"Normally when you cut a tree in the middle only the top part of the tree gets destroyed. But if this option is enabled the bottom part will get destroyed as well."}}] -execute unless score chop_down timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/chop_down"}},{"text":" Enable Chopping Down","hoverEvent":{"action":"show_text","value":"Normally when you cut a tree in the middle only the top part of the tree gets destroyed. But if this option is enabled the bottom part will get destroyed as well."}}] - -execute if score stop_chopping timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/stop_chopping"}},{"text":" Stop Chopping Once Axe Breaks","hoverEvent":{"action":"show_text","value":"Stops the process of chopping the tree as soon as your axe doesn't have any durability left for the whole tree. When disabled, the tree will always be chopped finish."}}] -execute unless score stop_chopping timber matches 1.. if score wear_out timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/stop_chopping"}},{"text":" Stop Chopping Once Axe Breaks","hoverEvent":{"action":"show_text","value":"Stops the process of chopping the tree as soon as your axe doesn't have any durability left for the whole tree. When disabled, the tree will always be chopped finish."}}] -execute unless score stop_chopping timber matches 1.. unless score wear_out timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"#7F2B2B","hoverEvent":{"action":"show_text","value":"Can't be enabled when \"Additionally Wear out Tool Depending on Tree Size\" is disabled."}},{"text":" Stop Chopping Once Axe Breaks","hoverEvent":{"action":"show_text","value":"Stops the process of chopping the tree as soon as your axe doesn't have any durability left for the whole tree. When disabled, the tree will always be chopped finish."}}] - -execute if score wear_out timber matches 1.. unless score stop_chopping timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/wear_out"}},{"text":" Additionally Wear out Tool Depending on Tree Size","hoverEvent":{"action":"show_text","value":"The tool's durability will get decreased by the amount of destroyed logs (or stems). When disabled, only one durability gets deducted."}}] -execute unless score wear_out timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/wear_out"}},{"text":" Additionally Wear out Tool Depending on Tree Size","hoverEvent":{"action":"show_text","value":"The tool's durability will get decreased by the amount of destroyed logs (or stems). When disabled, only one durability gets deducted."}}] -execute if score wear_out timber matches 1.. if score stop_chopping timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"#2B7F2B","hoverEvent":{"action":"show_text","value":"Can't be disabled when \"Stop Chopping Once Axe Breaks\" is enabled."}},{"text":" Additionally Wear out Tool Depending on Tree Size","hoverEvent":{"action":"show_text","value":"The tool's durability will get decreased by the amount of destroyed logs (or stems). When disabled, only one durability gets deducted."}}] - -execute if score drop_loot timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/drop_loot"}},{"text":" Drop Loot Straight into Inventory","hoverEvent":{"action":"show_text","value":["",{"text":"Lets the dropped items of the tree teleport straight into your own inventory. As soon as you have a full inventory the loot gets dropped on the ground."},{"text":"\n\nThis option can increase performance.","color":"gray"}]}}] -execute unless score drop_loot timber matches 1.. unless score slow_chop timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/drop_loot"}},{"text":" Drop Loot Straight into Inventory","hoverEvent":{"action":"show_text","value":["",{"text":"Lets the dropped items of the tree teleport straight into your own inventory. As soon as you have a full inventory the loot gets dropped on the ground."},{"text":"\n\nThis option can increase performance.","color":"gray"}]}}] -execute unless score drop_loot timber matches 1.. if score slow_chop timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"#7F2B2B","hoverEvent":{"action":"show_text","value":"Can't be enabled when \"Chop Slowly\" is enabled."}},{"text":" Drop Loot Straight into Inventory","hoverEvent":{"action":"show_text","value":["",{"text":"Lets the dropped items of the tree teleport straight into your own inventory. As soon as you have a full inventory the loot gets dropped on the ground."},{"text":"\n\nThis option can increase performance.","color":"gray"}]}}] - -execute if score hunger timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/hunger"}},{"text":" Give Additional Hunger Effect","hoverEvent":{"action":"show_text","value":"Gives a hunger effect for 1 second with the same level as the size of the chopped tree."}}] -execute unless score hunger timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/hunger"}},{"text":" Give Additional Hunger Effect","hoverEvent":{"action":"show_text","value":"Gives a hunger effect for 1 second with the same level as the size of the chopped tree."}}] - -execute if score stopsound timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/stopsound"}},{"text":" Stop Loud Breaking Noises","hoverEvent":{"action":"show_text","value":["",{"text":"Suppresses loud breaking noises when chopping a tree."},{"text":"\n\nThis option is","color":"gray"},{"text":" experimental","color":"dark_red"},{"text":" because sometimes it still sounds loud for a fraction of a second.","color":"gray"}]}}] -execute unless score stopsound timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/stopsound"}},{"text":" Stop Loud Breaking Noises","hoverEvent":{"action":"show_text","value":["",{"text":"Suppresses loud breaking noises when chopping a tree."},{"text":"\n\nThis option is","color":"gray"},{"text":" experimental","color":"dark_red"},{"text":" because sometimes it still sounds loud for a fraction of a second.","color":"gray"}]}}] - -tellraw @s ["",{"text":"\u00A7m ","color":"dark_gray"}] - -function timber:settings/end_of_message \ No newline at end of file diff --git a/18-41/data/timber/functions/settings/settings1_click.mcfunction b/18-41/data/timber/functions/settings/settings1_click.mcfunction deleted file mode 100644 index 64625b3..0000000 --- a/18-41/data/timber/functions/settings/settings1_click.mcfunction +++ /dev/null @@ -1,2 +0,0 @@ -function timber:settings/settings -playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.6 .2 \ No newline at end of file diff --git a/18-41/data/timber/functions/settings/settings2.mcfunction b/18-41/data/timber/functions/settings/settings2.mcfunction deleted file mode 100644 index 6f1a745..0000000 --- a/18-41/data/timber/functions/settings/settings2.mcfunction +++ /dev/null @@ -1,55 +0,0 @@ -tellraw @s ["",{"text":" "}] -tellraw @s ["",{"text":" "}] -tellraw @s ["",{"text":" "}] -tellraw @s ["",{"text":" "}] - -tellraw @s ["",{"text":"\u00A7m ","color":"dark_gray"}] - -tellraw @s ["",{"text":" Timber "},{"text":"/","color":"gray"},{"text":" Global Settings "},{"text":" < ","clickEvent":{"action":"run_command","value":"/function timber:settings/settings1_click"}},{"text":"2/2"},{"text":" > ","clickEvent":{"action":"run_command","value":"/function timber:settings/settings2_click"}}] - -tellraw @s ["",{"text":"\u00A7m ","color":"dark_gray"}] - -execute if score wooden_axe timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/wooden_axe"}},{"text":" Enable Wooden Axe"}] -execute unless score wooden_axe timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/wooden_axe"}},{"text":" Enable Wooden Axe"}] - -execute if score stone_axe timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/stone_axe"}},{"text":" Enable Stone Axe"}] -execute unless score stone_axe timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/stone_axe"}},{"text":" Enable Stone Axe"}] - -execute if score iron_axe timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/iron_axe"}},{"text":" Enable Iron Axe"}] -execute unless score iron_axe timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/iron_axe"}},{"text":" Enable Iron Axe"}] - -execute if score golden_axe timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/golden_axe"}},{"text":" Enable Golden Axe"}] -execute unless score golden_axe timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/golden_axe"}},{"text":" Enable Golden Axe"}] - -execute if score diamond_axe timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/diamond_axe"}},{"text":" Enable Diamond Axe"}] -execute unless score diamond_axe timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/diamond_axe"}},{"text":" Enable Diamond Axe"}] - -execute if score netherite_axe timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/netherite_axe"}},{"text":" Enable Netherite Axe"}] -execute unless score netherite_axe timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/netherite_axe"}},{"text":" Enable Netherite Axe"}] - -execute if score chop_trees timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/chop_trees"}},{"text":" Chop Trees","hoverEvent":{"action":"show_text","value":"All kind of trees will get chopped."}}] -execute unless score chop_trees timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/chop_trees"}},{"text":" Chop Trees","hoverEvent":{"action":"show_text","value":"All kind of trees will get chopped."}}] - -execute if score chop_fungi timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/chop_fungi"}},{"text":" Chop Fungi","hoverEvent":{"action":"show_text","value":["",{"text":"Huge fungi (nether trees) as well as huge mushrooms will get chopped."},{"text":"\n\nUse it on your own risk!\nWhen enabled, this datapack can't differentiate between generated fungi and player-placed fungi and therefore can potentially destroy your buildings.","color":"dark_red"}]}}] -execute unless score chop_fungi timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/chop_fungi"}},{"text":" Chop Fungi","hoverEvent":{"action":"show_text","value":["",{"text":"Huge fungi (nether trees) as well as huge mushrooms will get chopped."},{"text":"\n\nUse it on your own risk!\nWhen enabled, this datapack can't differentiate between generated fungi and player-placed fungi and therefore can potentially destroy your buildings.","color":"dark_red"}]}}] - -execute if score persistent timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/persistent"}},{"text":" Destroy Player-Placed Trees","hoverEvent":{"action":"show_text","value":["",{"text":"Player-placed trees are trees made out of player-placed leaves (Look at leaf block with F3 debug screen: persistent = true)."},{"text":"\n\nUse it on your own risk!\nWhen enabled, this datapack can't differentiate between generated trees and player-placed trees anymore and therefore can potentially destroy your buildings.","color":"dark_red"}]}}] -execute unless score persistent timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/persistent"}},{"text":" Destroy Player-Placed Trees","hoverEvent":{"action":"show_text","value":["",{"text":"Player-placed trees are trees made out of player-placed leaves (Look at leaf block with F3 debug screen: persistent = true)."},{"text":"\n\nUse it on your own risk!\nWhen enabled, this datapack can't differentiate between generated trees and player-placed trees anymore and therefore can potentially destroy your buildings.","color":"dark_red"}]}}] - -execute if score blocks_per_chop timber matches 1 if score slow_chop timber matches 1.. run tellraw @s ["",{"text":"[ ✎ ]","color":"gray","clickEvent":{"action":"suggest_command","value":"/scoreboard players set blocks_per_chop timber "},"hoverEvent":{"action":"show_text","value":"Default: 1"}},{"text":" Set Amount of Destroyed Blocks per Chop"},{"text":" (Current: ","color":"gray"},{"score":{"name":"blocks_per_chop","objective":"timber"},"color":"gray"},{"text": ")","color":"gray"}] -execute unless score blocks_per_chop timber matches 1 if score slow_chop timber matches 1.. run tellraw @s ["",{"text":"[ ✎ ]","color":"gray","clickEvent":{"action":"suggest_command","value":"/scoreboard players set blocks_per_chop timber "},"hoverEvent":{"action":"show_text","value":"Default: 1"}},{"text":" Set Amount of Destroyed Blocks per Chop"},{"text":" (Current: ","color":"gray"},{"score":{"name":"blocks_per_chop","objective":"timber"},"color":"gray"},{"text": ")","color":"gray"}] -execute unless score slow_chop timber matches 1.. run tellraw @s ["",{"text":"[ ✎ ]","color":"dark_gray","hoverEvent":{"action":"show_text","value":"Not active when \"Chop Slowly\" is disabled."}},{"text":" Set Amount of Destroyed Blocks per Chop"}] - -execute if score time_between_chops timber matches 1 if score slow_chop timber matches 1.. run tellraw @s ["",{"text":"[ ✎ ]","color":"gray","clickEvent":{"action":"suggest_command","value":"/scoreboard players set time_between_chops timber "},"hoverEvent":{"action":"show_text","value":"Default: 1"}},{"text":" Set Time in Ticks Between Chops"},{"text":" (Current: ","color":"gray"},{"score":{"name":"time_between_chops","objective":"timber"},"color":"gray"},{"text": ")","color":"gray"}] -execute unless score time_between_chops timber matches 1 if score slow_chop timber matches 1.. run tellraw @s ["",{"text":"[ ✎ ]","color":"gray","clickEvent":{"action":"suggest_command","value":"/scoreboard players set time_between_chops timber "},"hoverEvent":{"action":"show_text","value":"Default: 1"}},{"text":" Set Time in Ticks Between Chops"},{"text":" (Current: ","color":"gray"},{"score":{"name":"time_between_chops","objective":"timber"},"color":"gray"},{"text": ")","color":"gray"}] -execute unless score slow_chop timber matches 1.. run tellraw @s ["",{"text":"[ ✎ ]","color":"dark_gray","hoverEvent":{"action":"show_text","value":"Not active when \"Chop Slowly\" is disabled."}},{"text":" Set Time in Ticks Between Chops"}] - -execute if score max_tree_size timber matches 555 run tellraw @s ["",{"text":"[ ✎ ]","color":"gray","clickEvent":{"action":"suggest_command","value":"/scoreboard players set max_tree_size timber "},"hoverEvent":{"action":"show_text","value":"Default: 555"}},{"text":" Set Max. Tree Size","hoverEvent":{"action":"show_text","value":"Trees bigger than this set value won't get further processed."}},{"text":" (Currently: ","color":"gray"},{"score":{"name":"max_tree_size","objective":"timber"},"color":"gray"},{"text": ")","color":"gray"}] -execute unless score max_tree_size timber matches 555 run tellraw @s ["",{"text":"[ ✎ ]","color":"gray","clickEvent":{"action":"suggest_command","value":"/scoreboard players set max_tree_size timber "},"hoverEvent":{"action":"show_text","value":"Default: 555"}},{"text":" Set Max. Tree Size","hoverEvent":{"action":"show_text","value":"Trees bigger than this set value won't get further processed."}},{"text":" (Currently: ","color":"gray"},{"score":{"name":"max_tree_size","objective":"timber"},"color":"gray"},{"text": ")","color":"gray"}] - -execute if score min_leaves_found timber matches 5 run tellraw @s ["",{"text":"[ ✎ ]","color":"gray","clickEvent":{"action":"suggest_command","value":"/scoreboard players set min_leaves_found timber "},"hoverEvent":{"action":"show_text","value":"Default: 5"}},{"text":" Set Min. Leaves to Valid a Tree","hoverEvent":{"action":"show_text","value":"This datapack considers a tree only if it has this set value amount of naturally generated leaves at its trunk.\nFungi will get checked by the amount of cap blocks and additionally a 3×3 big cross on top of the stem."}},{"text":" (Current: ","color":"gray"},{"score":{"name":"min_leaves_found","objective":"timber"},"color":"gray"},{"text": ")","color":"gray"}] -execute unless score min_leaves_found timber matches 5 run tellraw @s ["",{"text":"[ ✎ ]","color":"gray","clickEvent":{"action":"suggest_command","value":"/scoreboard players set min_leaves_found timber "},"hoverEvent":{"action":"show_text","value":"Default: 5"}},{"text":" Set Min. Leaves to Valid a Tree","hoverEvent":{"action":"show_text","value":"This datapack considers a tree only if it has this set value amount of naturally generated leaves at its trunk.\nFungi will get checked by the amount of cap blocks and additionally a 3×3 big cross on top of the stem."}},{"text":" (Current: ","color":"gray"},{"score":{"name":"min_leaves_found","objective":"timber"},"color":"gray"},{"text": ")","color":"gray"}] - -tellraw @s ["",{"text":"\u00A7m ","color":"dark_gray"}] - -function timber:settings/end_of_message \ No newline at end of file diff --git a/18-41/data/timber/functions/settings/settings2_click.mcfunction b/18-41/data/timber/functions/settings/settings2_click.mcfunction deleted file mode 100644 index 44b2ce4..0000000 --- a/18-41/data/timber/functions/settings/settings2_click.mcfunction +++ /dev/null @@ -1,2 +0,0 @@ -function timber:settings/settings2 -playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.6 .2 \ No newline at end of file diff --git a/18-41/data/timber/functions/settings/toggle/chop_down.mcfunction b/18-41/data/timber/functions/settings/toggle/chop_down.mcfunction deleted file mode 100644 index e601c0c..0000000 --- a/18-41/data/timber/functions/settings/toggle/chop_down.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score chop_down timber matches 1.. run scoreboard players set chop_down timber 2 -execute unless score chop_down timber matches 1.. run scoreboard players set chop_down timber 1 -execute if score chop_down timber matches 2.. run scoreboard players set chop_down timber 0 -execute if score chop_down timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score chop_down timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings -function timber:settings/end_of_message \ No newline at end of file diff --git a/18-41/data/timber/functions/settings/toggle/chop_fungi.mcfunction b/18-41/data/timber/functions/settings/toggle/chop_fungi.mcfunction deleted file mode 100644 index 22614b8..0000000 --- a/18-41/data/timber/functions/settings/toggle/chop_fungi.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score chop_fungi timber matches 1.. run scoreboard players set chop_fungi timber 2 -execute unless score chop_fungi timber matches 1.. run scoreboard players set chop_fungi timber 1 -execute if score chop_fungi timber matches 2.. run scoreboard players set chop_fungi timber 0 -execute if score chop_fungi timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score chop_fungi timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings2 -function timber:settings/end_of_message \ No newline at end of file diff --git a/18-41/data/timber/functions/settings/toggle/chop_trees.mcfunction b/18-41/data/timber/functions/settings/toggle/chop_trees.mcfunction deleted file mode 100644 index 19fe627..0000000 --- a/18-41/data/timber/functions/settings/toggle/chop_trees.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score chop_trees timber matches 1.. run scoreboard players set chop_trees timber 2 -execute unless score chop_trees timber matches 1.. run scoreboard players set chop_trees timber 1 -execute if score chop_trees timber matches 2.. run scoreboard players set chop_trees timber 0 -execute if score chop_trees timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score chop_trees timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings2 -function timber:settings/end_of_message \ No newline at end of file diff --git a/18-41/data/timber/functions/settings/toggle/destroy_leaves.mcfunction b/18-41/data/timber/functions/settings/toggle/destroy_leaves.mcfunction deleted file mode 100644 index 346bced..0000000 --- a/18-41/data/timber/functions/settings/toggle/destroy_leaves.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score destroy_leaves timber matches 1.. run scoreboard players set destroy_leaves timber 2 -execute unless score destroy_leaves timber matches 1.. run scoreboard players set destroy_leaves timber 1 -execute if score destroy_leaves timber matches 2.. run scoreboard players set destroy_leaves timber 0 -execute if score destroy_leaves timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score destroy_leaves timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings -function timber:settings/end_of_message \ No newline at end of file diff --git a/18-41/data/timber/functions/settings/toggle/destroy_roots.mcfunction b/18-41/data/timber/functions/settings/toggle/destroy_roots.mcfunction deleted file mode 100644 index 87a6906..0000000 --- a/18-41/data/timber/functions/settings/toggle/destroy_roots.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score destroy_roots timber matches 1.. run scoreboard players set destroy_roots timber 2 -execute unless score destroy_roots timber matches 1.. run scoreboard players set destroy_roots timber 1 -execute if score destroy_roots timber matches 2.. run scoreboard players set destroy_roots timber 0 -execute if score destroy_roots timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score destroy_roots timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings -function timber:settings/end_of_message \ No newline at end of file diff --git a/18-41/data/timber/functions/settings/toggle/diamond_axe.mcfunction b/18-41/data/timber/functions/settings/toggle/diamond_axe.mcfunction deleted file mode 100644 index 83e45d8..0000000 --- a/18-41/data/timber/functions/settings/toggle/diamond_axe.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score diamond_axe timber matches 1.. run scoreboard players set diamond_axe timber 2 -execute unless score diamond_axe timber matches 1.. run scoreboard players set diamond_axe timber 1 -execute if score diamond_axe timber matches 2.. run scoreboard players set diamond_axe timber 0 -execute if score diamond_axe timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score diamond_axe timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings2 -function timber:settings/end_of_message \ No newline at end of file diff --git a/18-41/data/timber/functions/settings/toggle/drop_loot.mcfunction b/18-41/data/timber/functions/settings/toggle/drop_loot.mcfunction deleted file mode 100644 index dc35415..0000000 --- a/18-41/data/timber/functions/settings/toggle/drop_loot.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score drop_loot timber matches 1.. run scoreboard players set drop_loot timber 2 -execute unless score drop_loot timber matches 1.. run scoreboard players set drop_loot timber 1 -execute if score drop_loot timber matches 2.. run scoreboard players set drop_loot timber 0 -execute if score drop_loot timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score drop_loot timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings -function timber:settings/end_of_message \ No newline at end of file diff --git a/18-41/data/timber/functions/settings/toggle/golden_axe.mcfunction b/18-41/data/timber/functions/settings/toggle/golden_axe.mcfunction deleted file mode 100644 index f21f5b8..0000000 --- a/18-41/data/timber/functions/settings/toggle/golden_axe.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score golden_axe timber matches 1.. run scoreboard players set golden_axe timber 2 -execute unless score golden_axe timber matches 1.. run scoreboard players set golden_axe timber 1 -execute if score golden_axe timber matches 2.. run scoreboard players set golden_axe timber 0 -execute if score golden_axe timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score golden_axe timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings2 -function timber:settings/end_of_message \ No newline at end of file diff --git a/18-41/data/timber/functions/settings/toggle/hunger.mcfunction b/18-41/data/timber/functions/settings/toggle/hunger.mcfunction deleted file mode 100644 index b1e9543..0000000 --- a/18-41/data/timber/functions/settings/toggle/hunger.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score hunger timber matches 1.. run scoreboard players set hunger timber 2 -execute unless score hunger timber matches 1.. run scoreboard players set hunger timber 1 -execute if score hunger timber matches 2.. run scoreboard players set hunger timber 0 -execute if score hunger timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score hunger timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings1 -function timber:settings/end_of_message \ No newline at end of file diff --git a/18-41/data/timber/functions/settings/toggle/interfere.mcfunction b/18-41/data/timber/functions/settings/toggle/interfere.mcfunction deleted file mode 100644 index b1270d0..0000000 --- a/18-41/data/timber/functions/settings/toggle/interfere.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score interfere timber matches 1.. run scoreboard players set interfere timber 2 -execute unless score interfere timber matches 1.. run scoreboard players set interfere timber 1 -execute if score interfere timber matches 2.. run scoreboard players set interfere timber 0 -execute if score interfere timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score interfere timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings -function timber:settings/end_of_message \ No newline at end of file diff --git a/18-41/data/timber/functions/settings/toggle/iron_axe.mcfunction b/18-41/data/timber/functions/settings/toggle/iron_axe.mcfunction deleted file mode 100644 index bf08c1f..0000000 --- a/18-41/data/timber/functions/settings/toggle/iron_axe.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score iron_axe timber matches 1.. run scoreboard players set iron_axe timber 2 -execute unless score iron_axe timber matches 1.. run scoreboard players set iron_axe timber 1 -execute if score iron_axe timber matches 2.. run scoreboard players set iron_axe timber 0 -execute if score iron_axe timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score iron_axe timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings2 -function timber:settings/end_of_message \ No newline at end of file diff --git a/18-41/data/timber/functions/settings/toggle/netherite_axe.mcfunction b/18-41/data/timber/functions/settings/toggle/netherite_axe.mcfunction deleted file mode 100644 index 17f6528..0000000 --- a/18-41/data/timber/functions/settings/toggle/netherite_axe.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score netherite_axe timber matches 1.. run scoreboard players set netherite_axe timber 2 -execute unless score netherite_axe timber matches 1.. run scoreboard players set netherite_axe timber 1 -execute if score netherite_axe timber matches 2.. run scoreboard players set netherite_axe timber 0 -execute if score netherite_axe timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score netherite_axe timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings2 -function timber:settings/end_of_message \ No newline at end of file diff --git a/18-41/data/timber/functions/settings/toggle/persistent.mcfunction b/18-41/data/timber/functions/settings/toggle/persistent.mcfunction deleted file mode 100644 index 58bad84..0000000 --- a/18-41/data/timber/functions/settings/toggle/persistent.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score persistent timber matches 1.. run scoreboard players set persistent timber 2 -execute unless score persistent timber matches 1.. run scoreboard players set persistent timber 1 -execute if score persistent timber matches 2.. run scoreboard players set persistent timber 0 -execute if score persistent timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score persistent timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings2 -function timber:settings/end_of_message \ No newline at end of file diff --git a/18-41/data/timber/functions/settings/toggle/plant_sapling.mcfunction b/18-41/data/timber/functions/settings/toggle/plant_sapling.mcfunction deleted file mode 100644 index a6efdc3..0000000 --- a/18-41/data/timber/functions/settings/toggle/plant_sapling.mcfunction +++ /dev/null @@ -1,8 +0,0 @@ -execute if score plant_sapling timber matches 1.. run scoreboard players set plant_sapling timber 2 -execute unless score plant_sapling timber matches 1.. run scoreboard players set plant_sapling timber 1 -execute if score plant_sapling timber matches 2.. run scoreboard players set plant_sapling timber 0 -execute if score plant_sapling timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score plant_sapling timber matches 1 run function timber:autoplant/loop -execute if score plant_sapling timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings1 -function timber:settings/end_of_message \ No newline at end of file diff --git a/18-41/data/timber/functions/settings/toggle/slow_chop.mcfunction b/18-41/data/timber/functions/settings/toggle/slow_chop.mcfunction deleted file mode 100644 index 0a0989c..0000000 --- a/18-41/data/timber/functions/settings/toggle/slow_chop.mcfunction +++ /dev/null @@ -1,8 +0,0 @@ -execute if score slow_chop timber matches 1.. run scoreboard players set slow_chop timber 2 -execute unless score slow_chop timber matches 1.. run scoreboard players set slow_chop timber 1 -execute if score slow_chop timber matches 2.. run scoreboard players set slow_chop timber 0 -execute if score slow_chop timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score slow_chop timber matches 1 run function timber:slow_chop/loop -execute if score slow_chop timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings1 -function timber:settings/end_of_message \ No newline at end of file diff --git a/18-41/data/timber/functions/settings/toggle/sneaking.mcfunction b/18-41/data/timber/functions/settings/toggle/sneaking.mcfunction deleted file mode 100644 index 53f5141..0000000 --- a/18-41/data/timber/functions/settings/toggle/sneaking.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score sneaking timber matches 1.. run scoreboard players set sneaking timber 2 -execute unless score sneaking timber matches 1.. run scoreboard players set sneaking timber 1 -execute if score sneaking timber matches 2.. run scoreboard players set sneaking timber 0 -execute if score sneaking timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score sneaking timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings -function timber:settings/end_of_message \ No newline at end of file diff --git a/18-41/data/timber/functions/settings/toggle/standing.mcfunction b/18-41/data/timber/functions/settings/toggle/standing.mcfunction deleted file mode 100644 index 08c661d..0000000 --- a/18-41/data/timber/functions/settings/toggle/standing.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score standing timber matches 1.. run scoreboard players set standing timber 2 -execute unless score standing timber matches 1.. run scoreboard players set standing timber 1 -execute if score standing timber matches 2.. run scoreboard players set standing timber 0 -execute if score standing timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score standing timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings -function timber:settings/end_of_message \ No newline at end of file diff --git a/18-41/data/timber/functions/settings/toggle/start_chop_on_root.mcfunction b/18-41/data/timber/functions/settings/toggle/start_chop_on_root.mcfunction deleted file mode 100644 index e04a2b7..0000000 --- a/18-41/data/timber/functions/settings/toggle/start_chop_on_root.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score start_chop_on_root timber matches 1.. run scoreboard players set start_chop_on_root timber 2 -execute unless score start_chop_on_root timber matches 1.. run scoreboard players set start_chop_on_root timber 1 -execute if score start_chop_on_root timber matches 2.. run scoreboard players set start_chop_on_root timber 0 -execute if score start_chop_on_root timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score start_chop_on_root timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings -function timber:settings/end_of_message \ No newline at end of file diff --git a/18-41/data/timber/functions/settings/toggle/stone_axe.mcfunction b/18-41/data/timber/functions/settings/toggle/stone_axe.mcfunction deleted file mode 100644 index 641459d..0000000 --- a/18-41/data/timber/functions/settings/toggle/stone_axe.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score stone_axe timber matches 1.. run scoreboard players set stone_axe timber 2 -execute unless score stone_axe timber matches 1.. run scoreboard players set stone_axe timber 1 -execute if score stone_axe timber matches 2.. run scoreboard players set stone_axe timber 0 -execute if score stone_axe timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score stone_axe timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings2 -function timber:settings/end_of_message \ No newline at end of file diff --git a/18-41/data/timber/functions/settings/toggle/stop_chopping.mcfunction b/18-41/data/timber/functions/settings/toggle/stop_chopping.mcfunction deleted file mode 100644 index b13140f..0000000 --- a/18-41/data/timber/functions/settings/toggle/stop_chopping.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score stop_chopping timber matches 1.. run scoreboard players set stop_chopping timber 2 -execute unless score stop_chopping timber matches 1.. run scoreboard players set stop_chopping timber 1 -execute if score stop_chopping timber matches 2.. run scoreboard players set stop_chopping timber 0 -execute if score stop_chopping timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score stop_chopping timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings -function timber:settings/end_of_message \ No newline at end of file diff --git a/18-41/data/timber/functions/settings/toggle/stopsound.mcfunction b/18-41/data/timber/functions/settings/toggle/stopsound.mcfunction deleted file mode 100644 index 9418b18..0000000 --- a/18-41/data/timber/functions/settings/toggle/stopsound.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score stopsound timber matches 1.. run scoreboard players set stopsound timber 2 -execute unless score stopsound timber matches 1.. run scoreboard players set stopsound timber 1 -execute if score stopsound timber matches 2.. run scoreboard players set stopsound timber 0 -execute if score stopsound timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score stopsound timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings -function timber:settings/end_of_message \ No newline at end of file diff --git a/18-41/data/timber/functions/settings/toggle/timber.mcfunction b/18-41/data/timber/functions/settings/toggle/timber.mcfunction deleted file mode 100644 index 0a26b1d..0000000 --- a/18-41/data/timber/functions/settings/toggle/timber.mcfunction +++ /dev/null @@ -1,9 +0,0 @@ -# reset -scoreboard players set @s TimberToggle 0 -# toggle -execute unless score @s timber_disabled matches 1.. run scoreboard players set @s timber_disabled 3 -execute if score @s timber_disabled matches 1 run scoreboard players set @s timber_disabled 0 -execute if score @s timber_disabled matches 3 run scoreboard players set @s timber_disabled 1 -# display on/off status -execute unless score @s[tag=!global.ignore.gui] timber_disabled matches 1.. run tellraw @s ["",{"text":"<< ","color":"dark_gray"},{"text":"✔","color":"green"},{"text":" Timber is now on"},{"text":" >>","color":"dark_gray"}] -execute if score @s[tag=!global.ignore.gui] timber_disabled matches 1.. run tellraw @s ["",{"text":"<< ","color":"dark_gray"},{"text":"❌","color":"red"},{"text":" Timber is now off"},{"text":" >>","color":"dark_gray"}] \ No newline at end of file diff --git a/18-41/data/timber/functions/settings/toggle/wear_out.mcfunction b/18-41/data/timber/functions/settings/toggle/wear_out.mcfunction deleted file mode 100644 index 0bad5ec..0000000 --- a/18-41/data/timber/functions/settings/toggle/wear_out.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score wear_out timber matches 1.. run scoreboard players set wear_out timber 2 -execute unless score wear_out timber matches 1.. run scoreboard players set wear_out timber 1 -execute if score wear_out timber matches 2.. run scoreboard players set wear_out timber 0 -execute if score wear_out timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score wear_out timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings -function timber:settings/end_of_message \ No newline at end of file diff --git a/18-41/data/timber/functions/settings/toggle/wooden_axe.mcfunction b/18-41/data/timber/functions/settings/toggle/wooden_axe.mcfunction deleted file mode 100644 index cb82456..0000000 --- a/18-41/data/timber/functions/settings/toggle/wooden_axe.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score wooden_axe timber matches 1.. run scoreboard players set wooden_axe timber 2 -execute unless score wooden_axe timber matches 1.. run scoreboard players set wooden_axe timber 1 -execute if score wooden_axe timber matches 2.. run scoreboard players set wooden_axe timber 0 -execute if score wooden_axe timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score wooden_axe timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings2 -function timber:settings/end_of_message \ No newline at end of file diff --git a/18-41/data/timber/functions/slow_chop/api.mcfunction b/18-41/data/timber/functions/slow_chop/api.mcfunction deleted file mode 100644 index d27c681..0000000 --- a/18-41/data/timber/functions/slow_chop/api.mcfunction +++ /dev/null @@ -1,6 +0,0 @@ -# api -execute if entity @s[tag=timber_log] run function #timber:api/break_log -execute if entity @s[tag=timber_leaf] run function #timber:api/break_leaf -execute if entity @s[tag=timber_root] run function #timber:api/break_root -execute if entity @s[tag=timber_stem] run function #timber:api/break_stem -execute if entity @s[tag=timber_cap] run function #timber:api/break_cap \ No newline at end of file diff --git a/18-41/data/timber/functions/slow_chop/delete_markers.mcfunction b/18-41/data/timber/functions/slow_chop/delete_markers.mcfunction deleted file mode 100644 index c3375d6..0000000 --- a/18-41/data/timber/functions/slow_chop/delete_markers.mcfunction +++ /dev/null @@ -1,3 +0,0 @@ -# delete tree markers after chopping -kill @e[type=minecraft:marker,tag=timber_slow_chop] -kill @e[type=minecraft:armor_stand,tag=timber_tool] \ No newline at end of file diff --git a/18-41/data/timber/functions/slow_chop/destroy_loop.mcfunction b/18-41/data/timber/functions/slow_chop/destroy_loop.mcfunction deleted file mode 100644 index db43fbd..0000000 --- a/18-41/data/timber/functions/slow_chop/destroy_loop.mcfunction +++ /dev/null @@ -1,25 +0,0 @@ -# loop -$schedule function timber:slow_chop/loop $(time_between_chops)t replace - -# api -$execute at @e[type=minecraft:marker,tag=timber_slow_chop] as @e[type=minecraft:marker,tag=timber_destroy,sort=nearest,limit=$(blocks_per_chop)] at @s run function timber:slow_chop/api - -# destroy n block -$execute at @e[type=minecraft:marker,tag=timber_slow_chop] as @e[type=minecraft:armor_stand,tag=timber_tool,y=-64,distance=...1,sort=arbitrary,limit=1] at @e[type=minecraft:marker,tag=timber_destroy,sort=nearest,limit=$(blocks_per_chop)] run loot spawn ~ ~ ~ mine ~ ~ ~ mainhand - -# destroy animation (doTileDrops can be changed, because datapack wouldn't get to this point otherwise) -gamerule doTileDrops false -$execute at @e[type=minecraft:marker,tag=timber_slow_chop] at @e[type=minecraft:marker,tag=timber_destroy,sort=nearest,limit=$(blocks_per_chop)] run setblock ~ ~ ~ minecraft:air destroy -gamerule doTileDrops true - -# stop sound -$execute at @e[type=minecraft:marker,tag=timber_slow_chop] if score stopsound timber matches 1.. at @e[type=minecraft:marker,tag=timber_destroy,sort=nearest,limit=$(blocks_per_chop)] as @a[distance=..20,tag=!global.ignore.gui] run function timber:utils/stopsound - -# delete location item -$execute at @e[type=minecraft:marker,tag=timber_slow_chop] at @e[type=minecraft:marker,tag=timber_destroy,sort=nearest,limit=$(blocks_per_chop)] run kill @e[type=minecraft:item,distance=..1,nbt={Item:{components:{"minecraft:custom_data":{Tags:["timber_location"]}}}}] - -# remove marker of processed blocks -$execute at @e[type=minecraft:marker,tag=timber_slow_chop] run kill @e[type=minecraft:marker,tag=timber_destroy,sort=nearest,limit=$(blocks_per_chop)] - -# delete tree markers after chopping -execute unless entity @e[type=minecraft:marker,tag=timber_destroy] run function timber:slow_chop/delete_markers \ No newline at end of file diff --git a/18-41/data/timber/functions/slow_chop/loop.mcfunction b/18-41/data/timber/functions/slow_chop/loop.mcfunction deleted file mode 100644 index e6a33b5..0000000 --- a/18-41/data/timber/functions/slow_chop/loop.mcfunction +++ /dev/null @@ -1,4 +0,0 @@ -# destroy loop -execute store result storage timber:slow_chop blocks_per_chop int 1 run scoreboard players get blocks_per_chop timber -execute store result storage timber:slow_chop time_between_chops int 1 run scoreboard players get time_between_chops timber -execute if score slow_chop timber matches 1.. run function timber:slow_chop/destroy_loop with storage timber:slow_chop diff --git a/18-41/data/timber/functions/tool/durability_check.mcfunction b/18-41/data/timber/functions/tool/durability_check.mcfunction deleted file mode 100644 index d6607bf..0000000 --- a/18-41/data/timber/functions/tool/durability_check.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -# check if axe is broken -execute if score @s timber_w_axe matches 1.. unless score durability timber <= #wooden_axe_max_durability timber run scoreboard players set durability_okay timber 0 -execute if score @s timber_s_axe matches 1.. unless score durability timber <= #stone_axe_max_durability timber run scoreboard players set durability_okay timber 0 -execute if score @s timber_i_axe matches 1.. unless score durability timber <= #iron_axe_max_durability timber run scoreboard players set durability_okay timber 0 -execute if score @s timber_g_axe matches 1.. unless score durability timber <= #golden_axe_max_durability timber run scoreboard players set durability_okay timber 0 -execute if score @s timber_d_axe matches 1.. unless score durability timber <= #diamond_axe_max_durability timber run scoreboard players set durability_okay timber 0 -execute if score @s timber_n_axe matches 1.. unless score durability timber <= #netherite_axe_max_durability timber run scoreboard players set durability_okay timber 0 \ No newline at end of file diff --git a/18-41/data/timber/functions/tool/durability_count.mcfunction b/18-41/data/timber/functions/tool/durability_count.mcfunction deleted file mode 100644 index 118f8c3..0000000 --- a/18-41/data/timber/functions/tool/durability_count.mcfunction +++ /dev/null @@ -1,5 +0,0 @@ -# count wear out of tool depending on enchantment -execute if score unbreaking timber matches 0 run scoreboard players add durability timber 1 -execute if score unbreaking timber matches 1 if predicate timber:random_chance/0.5 run scoreboard players add durability timber 1 -execute if score unbreaking timber matches 2 if predicate timber:random_chance/0.333 run scoreboard players add durability timber 1 -execute if score unbreaking timber matches 3.. if predicate timber:random_chance/0.25 run scoreboard players add durability timber 1 \ No newline at end of file diff --git a/18-41/data/timber/functions/tool/durability_get.mcfunction b/18-41/data/timber/functions/tool/durability_get.mcfunction deleted file mode 100644 index d4505c4..0000000 --- a/18-41/data/timber/functions/tool/durability_get.mcfunction +++ /dev/null @@ -1,5 +0,0 @@ -scoreboard players set unbreakable timber 0 -execute store result score durability timber run data get entity @s SelectedItem.components."minecraft:damage" -scoreboard players remove durability timber 1 -execute if entity @s[nbt=!{SelectedItem:{}}] run scoreboard players set durability timber 2147483647 -execute if data entity @s SelectedItem.components."minecraft:unbreakable" run scoreboard players set unbreakable timber 1 \ No newline at end of file diff --git a/18-41/data/timber/functions/tool/durability_max.mcfunction b/18-41/data/timber/functions/tool/durability_max.mcfunction deleted file mode 100644 index 3342e7b..0000000 --- a/18-41/data/timber/functions/tool/durability_max.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -# non-overriding max durability for axes -execute unless score #wooden_axe_max_durability timber = #wooden_axe_max_durability timber run scoreboard players set #wooden_axe_max_durability timber 59 -execute unless score #stone_axe_max_durability timber = #stone_axe_max_durability timber run scoreboard players set #stone_axe_max_durability timber 131 -execute unless score #iron_axe_max_durability timber = #iron_axe_max_durability timber run scoreboard players set #iron_axe_max_durability timber 250 -execute unless score #golden_axe_max_durability timber = #golden_axe_max_durability timber run scoreboard players set #golden_axe_max_durability timber 32 -execute unless score #diamond_axe_max_durability timber = #diamond_axe_max_durability timber run scoreboard players set #diamond_axe_max_durability timber 1561 -execute unless score #netherite_axe_max_durability timber = #netherite_axe_max_durability timber run scoreboard players set #netherite_axe_max_durability timber 2031 \ No newline at end of file diff --git a/18-41/data/timber/functions/tool/wear_out.mcfunction b/18-41/data/timber/functions/tool/wear_out.mcfunction deleted file mode 100644 index c9a6224..0000000 --- a/18-41/data/timber/functions/tool/wear_out.mcfunction +++ /dev/null @@ -1,9 +0,0 @@ -# wear out tool -execute if score @s timber_w_axe matches 1.. run scoreboard players operation damage timber = #wooden_axe_max_durability timber -execute if score @s timber_s_axe matches 1.. run scoreboard players operation damage timber = #stone_axe_max_durability timber -execute if score @s timber_i_axe matches 1.. run scoreboard players operation damage timber = #iron_axe_max_durability timber -execute if score @s timber_g_axe matches 1.. run scoreboard players operation damage timber = #golden_axe_max_durability timber -execute if score @s timber_d_axe matches 1.. run scoreboard players operation damage timber = #diamond_axe_max_durability timber -execute if score @s timber_n_axe matches 1.. run scoreboard players operation damage timber = #netherite_axe_max_durability timber -scoreboard players operation damage timber -= durability timber -item modify entity @s weapon.mainhand timber:wear_out \ No newline at end of file diff --git a/18-41/data/timber/functions/tool/worn_out.mcfunction b/18-41/data/timber/functions/tool/worn_out.mcfunction deleted file mode 100644 index 545717d..0000000 --- a/18-41/data/timber/functions/tool/worn_out.mcfunction +++ /dev/null @@ -1,13 +0,0 @@ -# break axe if worn out -execute if score @s timber_w_axe matches 1.. if score durability timber >= #wooden_axe_max_durability timber run item replace entity @s weapon.mainhand with minecraft:air -execute if score @s timber_w_axe matches 1.. if score durability timber >= #wooden_axe_max_durability timber run playsound minecraft:entity.item.break player @a ~ ~ ~ -execute if score @s timber_s_axe matches 1.. if score durability timber >= #stone_axe_max_durability timber run item replace entity @s weapon.mainhand with minecraft:air -execute if score @s timber_s_axe matches 1.. if score durability timber >= #stone_axe_max_durability timber run playsound minecraft:entity.item.break player @a ~ ~ ~ -execute if score @s timber_i_axe matches 1.. if score durability timber >= #iron_axe_max_durability timber run item replace entity @s weapon.mainhand with minecraft:air -execute if score @s timber_i_axe matches 1.. if score durability timber >= #iron_axe_max_durability timber run playsound minecraft:entity.item.break player @a ~ ~ ~ -execute if score @s timber_g_axe matches 1.. if score durability timber >= #golden_axe_max_durability timber run item replace entity @s weapon.mainhand with minecraft:air -execute if score @s timber_g_axe matches 1.. if score durability timber >= #golden_axe_max_durability timber run playsound minecraft:entity.item.break player @a ~ ~ ~ -execute if score @s timber_d_axe matches 1.. if score durability timber >= #diamond_axe_max_durability timber run item replace entity @s weapon.mainhand with minecraft:air -execute if score @s timber_d_axe matches 1.. if score durability timber >= #diamond_axe_max_durability timber run playsound minecraft:entity.item.break player @a ~ ~ ~ -execute if score @s timber_n_axe matches 1.. if score durability timber >= #netherite_axe_max_durability timber run item replace entity @s weapon.mainhand with minecraft:air -execute if score @s timber_n_axe matches 1.. if score durability timber >= #netherite_axe_max_durability timber run playsound minecraft:entity.item.break player @a ~ ~ ~ \ No newline at end of file diff --git a/18-41/data/timber/functions/uninstall.mcfunction b/18-41/data/timber/functions/uninstall.mcfunction deleted file mode 100644 index 96a2656..0000000 --- a/18-41/data/timber/functions/uninstall.mcfunction +++ /dev/null @@ -1,19 +0,0 @@ -# stop main functions -schedule clear timber:main - -# remove scoreboards -scoreboard objectives remove timber_w_axe -scoreboard objectives remove timber_s_axe -scoreboard objectives remove timber_i_axe -scoreboard objectives remove timber_g_axe -scoreboard objectives remove timber_d_axe -scoreboard objectives remove timber_n_axe -scoreboard objectives remove timber -scoreboard objectives remove TimberToggle -scoreboard objectives remove timber_disabled -scoreboard objectives remove timber_prsistent - -# remove markers -kill @e[type=minecraft:marker,tag=timber_slow_chop] -kill @e[type=minecraft:marker,tag=timber_destroy] -kill @e[type=minecraft:armor_stand,tag=timber_tool] \ No newline at end of file diff --git a/18-41/data/timber/functions/utils/check_max_command_chain_length.mcfunction b/18-41/data/timber/functions/utils/check_max_command_chain_length.mcfunction deleted file mode 100644 index 6f32c3e..0000000 --- a/18-41/data/timber/functions/utils/check_max_command_chain_length.mcfunction +++ /dev/null @@ -1,13 +0,0 @@ -# function didn't end successfully -execute if score running timber matches 1 run scoreboard players set running timber 2 -# normal behavior -execute if score running timber matches 0 run scoreboard players set running timber 1 -execute if score running timber matches 1 run function timber:utils/used_axe -# function didn't end successfully -execute if score running timber matches 2 run execute store result storage timber:max_command_chain_length value int 1 run gamerule maxCommandChainLength -execute if score running timber matches 2 run tellraw @a[tag=!noTimberWarning] ["",{"text":"Timber Datapack Warning: ","color":"red"},{"text":"The datapack wasn't able to finish its functionality to work properly!"},{"text":"\n ─ Please either decrease setting \"Max. Tree Size\" with "},{"text":"\n [ ✎ ]","color":"gray","clickEvent":{"action":"suggest_command","value":"/scoreboard players set max_tree_size timber "},"hoverEvent":{"action":"show_text","value":"Default: 555"}},{"text":" (Currently: ","color":"gray"},{"score":{"name":"max_tree_size","objective":"timber"},"color":"gray"},{"text": ")","color":"gray"},{"text":"."},{"text":"\n ─ Or increase the gamerule \"maxCommandChainLength\" with "},{"text":"\n [ ✎ ]","color":"gray","clickEvent":{"action":"suggest_command","value":"/gamerule maxCommandChainLength "},"hoverEvent":{"action":"show_text","value":"Default: 65536"}},{"text":" (Currently: ","color":"gray"},{"nbt":"value","storage":"timber:max_command_chain_length","color":"gray"},{"text": ")","color":"gray"},{"text":"."}] -execute if score running timber matches 2 run schedule function timber:utils/reset 10t -execute if score running timber matches 2 run scoreboard players set running timber 3 - -# function ended successfully -execute if score running timber matches 1 run scoreboard players set running timber 0 \ No newline at end of file diff --git a/18-41/data/timber/functions/utils/check_server.mcfunction b/18-41/data/timber/functions/utils/check_server.mcfunction deleted file mode 100644 index 7d70177..0000000 --- a/18-41/data/timber/functions/utils/check_server.mcfunction +++ /dev/null @@ -1,8 +0,0 @@ -# load until someone is in the server -execute unless entity @r run schedule function timber:utils/check_server 1s - -# Checking if the server uses a different jar from the vanilla one -execute unless score not_vanilla timber matches 1.. store success score not_vanilla timber run data get entity @r "Spigot.ticksLived" -execute unless score not_vanilla timber matches 1.. store success score not_vanilla timber run data get entity @r "Bukkit.updateLevel" -execute unless score not_vanilla timber matches 1.. store success score not_vanilla timber run data get entity @r "Paper.SpawnReason" -execute if score not_vanilla timber matches 1 run tellraw @a[tag=!noTimberWarning] ["",{"text":"Timber Datapack Warning: ","color":"red"},{"text":"Compatibility in non-vanilla servers is not guaranteed! For more information please visit official download site.","clickEvent":{"action":"open_url","value":"https://github.com/Moggla/Timber-Datapack"}}] \ No newline at end of file diff --git a/18-41/data/timber/functions/utils/hunger.mcfunction b/18-41/data/timber/functions/utils/hunger.mcfunction deleted file mode 100644 index 3d9e3de..0000000 --- a/18-41/data/timber/functions/utils/hunger.mcfunction +++ /dev/null @@ -1,255 +0,0 @@ -execute if score tree_size timber matches 1 run effect give @s minecraft:hunger 1 1 true -execute if score tree_size timber matches 2 run effect give @s minecraft:hunger 1 2 true -execute if score tree_size timber matches 3 run effect give @s minecraft:hunger 1 3 true -execute if score tree_size timber matches 4 run effect give @s minecraft:hunger 1 4 true -execute if score tree_size timber matches 5 run effect give @s minecraft:hunger 1 5 true -execute if score tree_size timber matches 6 run effect give @s minecraft:hunger 1 6 true -execute if score tree_size timber matches 7 run effect give @s minecraft:hunger 1 7 true -execute if score tree_size timber matches 8 run effect give @s minecraft:hunger 1 8 true -execute if score tree_size timber matches 9 run effect give @s minecraft:hunger 1 9 true -execute if score tree_size timber matches 10 run effect give @s minecraft:hunger 1 10 true -execute if score tree_size timber matches 11 run effect give @s minecraft:hunger 1 11 true -execute if score tree_size timber matches 12 run effect give @s minecraft:hunger 1 12 true -execute if score tree_size timber matches 13 run effect give @s minecraft:hunger 1 13 true -execute if score tree_size timber matches 14 run effect give @s minecraft:hunger 1 14 true -execute if score tree_size timber matches 15 run effect give @s minecraft:hunger 1 15 true -execute if score tree_size timber matches 16 run effect give @s minecraft:hunger 1 16 true -execute if score tree_size timber matches 17 run effect give @s minecraft:hunger 1 17 true -execute if score tree_size timber matches 18 run effect give @s minecraft:hunger 1 18 true -execute if score tree_size timber matches 19 run effect give @s minecraft:hunger 1 19 true -execute if score tree_size timber matches 20 run effect give @s minecraft:hunger 1 20 true -execute if score tree_size timber matches 21 run effect give @s minecraft:hunger 1 21 true -execute if score tree_size timber matches 22 run effect give @s minecraft:hunger 1 22 true -execute if score tree_size timber matches 23 run effect give @s minecraft:hunger 1 23 true -execute if score tree_size timber matches 24 run effect give @s minecraft:hunger 1 24 true -execute if score tree_size timber matches 25 run effect give @s minecraft:hunger 1 25 true -execute if score tree_size timber matches 26 run effect give @s minecraft:hunger 1 26 true -execute if score tree_size timber matches 27 run effect give @s minecraft:hunger 1 27 true -execute if score tree_size timber matches 28 run effect give @s minecraft:hunger 1 28 true -execute if score tree_size timber matches 29 run effect give @s minecraft:hunger 1 29 true -execute if score tree_size timber matches 30 run effect give @s minecraft:hunger 1 30 true -execute if score tree_size timber matches 31 run effect give @s minecraft:hunger 1 31 true -execute if score tree_size timber matches 32 run effect give @s minecraft:hunger 1 32 true -execute if score tree_size timber matches 33 run effect give @s minecraft:hunger 1 33 true -execute if score tree_size timber matches 34 run effect give @s minecraft:hunger 1 34 true -execute if score tree_size timber matches 35 run effect give @s minecraft:hunger 1 35 true -execute if score tree_size timber matches 36 run effect give @s minecraft:hunger 1 36 true -execute if score tree_size timber matches 37 run effect give @s minecraft:hunger 1 37 true -execute if score tree_size timber matches 38 run effect give @s minecraft:hunger 1 38 true -execute if score tree_size timber matches 39 run effect give @s minecraft:hunger 1 39 true -execute if score tree_size timber matches 40 run effect give @s minecraft:hunger 1 40 true -execute if score tree_size timber matches 41 run effect give @s minecraft:hunger 1 41 true -execute if score tree_size timber matches 42 run effect give @s minecraft:hunger 1 42 true -execute if score tree_size timber matches 43 run effect give @s minecraft:hunger 1 43 true -execute if score tree_size timber matches 44 run effect give @s minecraft:hunger 1 44 true -execute if score tree_size timber matches 45 run effect give @s minecraft:hunger 1 45 true -execute if score tree_size timber matches 46 run effect give @s minecraft:hunger 1 46 true -execute if score tree_size timber matches 47 run effect give @s minecraft:hunger 1 47 true -execute if score tree_size timber matches 48 run effect give @s minecraft:hunger 1 48 true -execute if score tree_size timber matches 49 run effect give @s minecraft:hunger 1 49 true -execute if score tree_size timber matches 50 run effect give @s minecraft:hunger 1 50 true -execute if score tree_size timber matches 51 run effect give @s minecraft:hunger 1 51 true -execute if score tree_size timber matches 52 run effect give @s minecraft:hunger 1 52 true -execute if score tree_size timber matches 53 run effect give @s minecraft:hunger 1 53 true -execute if score tree_size timber matches 54 run effect give @s minecraft:hunger 1 54 true -execute if score tree_size timber matches 55 run effect give @s minecraft:hunger 1 55 true -execute if score tree_size timber matches 56 run effect give @s minecraft:hunger 1 56 true -execute if score tree_size timber matches 57 run effect give @s minecraft:hunger 1 57 true -execute if score tree_size timber matches 58 run effect give @s minecraft:hunger 1 58 true -execute if score tree_size timber matches 59 run effect give @s minecraft:hunger 1 59 true -execute if score tree_size timber matches 60 run effect give @s minecraft:hunger 1 60 true -execute if score tree_size timber matches 61 run effect give @s minecraft:hunger 1 61 true -execute if score tree_size timber matches 62 run effect give @s minecraft:hunger 1 62 true -execute if score tree_size timber matches 63 run effect give @s minecraft:hunger 1 63 true -execute if score tree_size timber matches 64 run effect give @s minecraft:hunger 1 64 true -execute if score tree_size timber matches 65 run effect give @s minecraft:hunger 1 65 true -execute if score tree_size timber matches 66 run effect give @s minecraft:hunger 1 66 true -execute if score tree_size timber matches 67 run effect give @s minecraft:hunger 1 67 true -execute if score tree_size timber matches 68 run effect give @s minecraft:hunger 1 68 true -execute if score tree_size timber matches 69 run effect give @s minecraft:hunger 1 69 true -execute if score tree_size timber matches 70 run effect give @s minecraft:hunger 1 70 true -execute if score tree_size timber matches 71 run effect give @s minecraft:hunger 1 71 true -execute if score tree_size timber matches 72 run effect give @s minecraft:hunger 1 72 true -execute if score tree_size timber matches 73 run effect give @s minecraft:hunger 1 73 true -execute if score tree_size timber matches 74 run effect give @s minecraft:hunger 1 74 true -execute if score tree_size timber matches 75 run effect give @s minecraft:hunger 1 75 true -execute if score tree_size timber matches 76 run effect give @s minecraft:hunger 1 76 true -execute if score tree_size timber matches 77 run effect give @s minecraft:hunger 1 77 true -execute if score tree_size timber matches 78 run effect give @s minecraft:hunger 1 78 true -execute if score tree_size timber matches 79 run effect give @s minecraft:hunger 1 79 true -execute if score tree_size timber matches 80 run effect give @s minecraft:hunger 1 80 true -execute if score tree_size timber matches 81 run effect give @s minecraft:hunger 1 81 true -execute if score tree_size timber matches 82 run effect give @s minecraft:hunger 1 82 true -execute if score tree_size timber matches 83 run effect give @s minecraft:hunger 1 83 true -execute if score tree_size timber matches 84 run effect give @s minecraft:hunger 1 84 true -execute if score tree_size timber matches 85 run effect give @s minecraft:hunger 1 85 true -execute if score tree_size timber matches 86 run effect give @s minecraft:hunger 1 86 true -execute if score tree_size timber matches 87 run effect give @s minecraft:hunger 1 87 true -execute if score tree_size timber matches 88 run effect give @s minecraft:hunger 1 88 true -execute if score tree_size timber matches 89 run effect give @s minecraft:hunger 1 89 true -execute if score tree_size timber matches 90 run effect give @s minecraft:hunger 1 90 true -execute if score tree_size timber matches 91 run effect give @s minecraft:hunger 1 91 true -execute if score tree_size timber matches 92 run effect give @s minecraft:hunger 1 92 true -execute if score tree_size timber matches 93 run effect give @s minecraft:hunger 1 93 true -execute if score tree_size timber matches 94 run effect give @s minecraft:hunger 1 94 true -execute if score tree_size timber matches 95 run effect give @s minecraft:hunger 1 95 true -execute if score tree_size timber matches 96 run effect give @s minecraft:hunger 1 96 true -execute if score tree_size timber matches 97 run effect give @s minecraft:hunger 1 97 true -execute if score tree_size timber matches 98 run effect give @s minecraft:hunger 1 98 true -execute if score tree_size timber matches 99 run effect give @s minecraft:hunger 1 99 true -execute if score tree_size timber matches 100 run effect give @s minecraft:hunger 1 100 true -execute if score tree_size timber matches 101 run effect give @s minecraft:hunger 1 101 true -execute if score tree_size timber matches 102 run effect give @s minecraft:hunger 1 102 true -execute if score tree_size timber matches 103 run effect give @s minecraft:hunger 1 103 true -execute if score tree_size timber matches 104 run effect give @s minecraft:hunger 1 104 true -execute if score tree_size timber matches 105 run effect give @s minecraft:hunger 1 105 true -execute if score tree_size timber matches 106 run effect give @s minecraft:hunger 1 106 true -execute if score tree_size timber matches 107 run effect give @s minecraft:hunger 1 107 true -execute if score tree_size timber matches 108 run effect give @s minecraft:hunger 1 108 true -execute if score tree_size timber matches 109 run effect give @s minecraft:hunger 1 109 true -execute if score tree_size timber matches 110 run effect give @s minecraft:hunger 1 110 true -execute if score tree_size timber matches 111 run effect give @s minecraft:hunger 1 111 true -execute if score tree_size timber matches 112 run effect give @s minecraft:hunger 1 112 true -execute if score tree_size timber matches 113 run effect give @s minecraft:hunger 1 113 true -execute if score tree_size timber matches 114 run effect give @s minecraft:hunger 1 114 true -execute if score tree_size timber matches 115 run effect give @s minecraft:hunger 1 115 true -execute if score tree_size timber matches 116 run effect give @s minecraft:hunger 1 116 true -execute if score tree_size timber matches 117 run effect give @s minecraft:hunger 1 117 true -execute if score tree_size timber matches 118 run effect give @s minecraft:hunger 1 118 true -execute if score tree_size timber matches 119 run effect give @s minecraft:hunger 1 119 true -execute if score tree_size timber matches 120 run effect give @s minecraft:hunger 1 120 true -execute if score tree_size timber matches 121 run effect give @s minecraft:hunger 1 121 true -execute if score tree_size timber matches 122 run effect give @s minecraft:hunger 1 122 true -execute if score tree_size timber matches 123 run effect give @s minecraft:hunger 1 123 true -execute if score tree_size timber matches 124 run effect give @s minecraft:hunger 1 124 true -execute if score tree_size timber matches 125 run effect give @s minecraft:hunger 1 125 true -execute if score tree_size timber matches 126 run effect give @s minecraft:hunger 1 126 true -execute if score tree_size timber matches 127 run effect give @s minecraft:hunger 1 127 true -execute if score tree_size timber matches 128 run effect give @s minecraft:hunger 1 128 true -execute if score tree_size timber matches 129 run effect give @s minecraft:hunger 1 129 true -execute if score tree_size timber matches 130 run effect give @s minecraft:hunger 1 130 true -execute if score tree_size timber matches 131 run effect give @s minecraft:hunger 1 131 true -execute if score tree_size timber matches 132 run effect give @s minecraft:hunger 1 132 true -execute if score tree_size timber matches 133 run effect give @s minecraft:hunger 1 133 true -execute if score tree_size timber matches 134 run effect give @s minecraft:hunger 1 134 true -execute if score tree_size timber matches 135 run effect give @s minecraft:hunger 1 135 true -execute if score tree_size timber matches 136 run effect give @s minecraft:hunger 1 136 true -execute if score tree_size timber matches 137 run effect give @s minecraft:hunger 1 137 true -execute if score tree_size timber matches 138 run effect give @s minecraft:hunger 1 138 true -execute if score tree_size timber matches 139 run effect give @s minecraft:hunger 1 139 true -execute if score tree_size timber matches 140 run effect give @s minecraft:hunger 1 140 true -execute if score tree_size timber matches 141 run effect give @s minecraft:hunger 1 141 true -execute if score tree_size timber matches 142 run effect give @s minecraft:hunger 1 142 true -execute if score tree_size timber matches 143 run effect give @s minecraft:hunger 1 143 true -execute if score tree_size timber matches 144 run effect give @s minecraft:hunger 1 144 true -execute if score tree_size timber matches 145 run effect give @s minecraft:hunger 1 145 true -execute if score tree_size timber matches 146 run effect give @s minecraft:hunger 1 146 true -execute if score tree_size timber matches 147 run effect give @s minecraft:hunger 1 147 true -execute if score tree_size timber matches 148 run effect give @s minecraft:hunger 1 148 true -execute if score tree_size timber matches 149 run effect give @s minecraft:hunger 1 149 true -execute if score tree_size timber matches 150 run effect give @s minecraft:hunger 1 150 true -execute if score tree_size timber matches 151 run effect give @s minecraft:hunger 1 151 true -execute if score tree_size timber matches 152 run effect give @s minecraft:hunger 1 152 true -execute if score tree_size timber matches 153 run effect give @s minecraft:hunger 1 153 true -execute if score tree_size timber matches 154 run effect give @s minecraft:hunger 1 154 true -execute if score tree_size timber matches 155 run effect give @s minecraft:hunger 1 155 true -execute if score tree_size timber matches 156 run effect give @s minecraft:hunger 1 156 true -execute if score tree_size timber matches 157 run effect give @s minecraft:hunger 1 157 true -execute if score tree_size timber matches 158 run effect give @s minecraft:hunger 1 158 true -execute if score tree_size timber matches 159 run effect give @s minecraft:hunger 1 159 true -execute if score tree_size timber matches 160 run effect give @s minecraft:hunger 1 160 true -execute if score tree_size timber matches 161 run effect give @s minecraft:hunger 1 161 true -execute if score tree_size timber matches 162 run effect give @s minecraft:hunger 1 162 true -execute if score tree_size timber matches 163 run effect give @s minecraft:hunger 1 163 true -execute if score tree_size timber matches 164 run effect give @s minecraft:hunger 1 164 true -execute if score tree_size timber matches 165 run effect give @s minecraft:hunger 1 165 true -execute if score tree_size timber matches 166 run effect give @s minecraft:hunger 1 166 true -execute if score tree_size timber matches 167 run effect give @s minecraft:hunger 1 167 true -execute if score tree_size timber matches 168 run effect give @s minecraft:hunger 1 168 true -execute if score tree_size timber matches 169 run effect give @s minecraft:hunger 1 169 true -execute if score tree_size timber matches 170 run effect give @s minecraft:hunger 1 170 true -execute if score tree_size timber matches 171 run effect give @s minecraft:hunger 1 171 true -execute if score tree_size timber matches 172 run effect give @s minecraft:hunger 1 172 true -execute if score tree_size timber matches 173 run effect give @s minecraft:hunger 1 173 true -execute if score tree_size timber matches 174 run effect give @s minecraft:hunger 1 174 true -execute if score tree_size timber matches 175 run effect give @s minecraft:hunger 1 175 true -execute if score tree_size timber matches 176 run effect give @s minecraft:hunger 1 176 true -execute if score tree_size timber matches 177 run effect give @s minecraft:hunger 1 177 true -execute if score tree_size timber matches 178 run effect give @s minecraft:hunger 1 178 true -execute if score tree_size timber matches 179 run effect give @s minecraft:hunger 1 179 true -execute if score tree_size timber matches 180 run effect give @s minecraft:hunger 1 180 true -execute if score tree_size timber matches 181 run effect give @s minecraft:hunger 1 181 true -execute if score tree_size timber matches 182 run effect give @s minecraft:hunger 1 182 true -execute if score tree_size timber matches 183 run effect give @s minecraft:hunger 1 183 true -execute if score tree_size timber matches 184 run effect give @s minecraft:hunger 1 184 true -execute if score tree_size timber matches 185 run effect give @s minecraft:hunger 1 185 true -execute if score tree_size timber matches 186 run effect give @s minecraft:hunger 1 186 true -execute if score tree_size timber matches 187 run effect give @s minecraft:hunger 1 187 true -execute if score tree_size timber matches 188 run effect give @s minecraft:hunger 1 188 true -execute if score tree_size timber matches 189 run effect give @s minecraft:hunger 1 189 true -execute if score tree_size timber matches 190 run effect give @s minecraft:hunger 1 190 true -execute if score tree_size timber matches 191 run effect give @s minecraft:hunger 1 191 true -execute if score tree_size timber matches 192 run effect give @s minecraft:hunger 1 192 true -execute if score tree_size timber matches 193 run effect give @s minecraft:hunger 1 193 true -execute if score tree_size timber matches 194 run effect give @s minecraft:hunger 1 194 true -execute if score tree_size timber matches 195 run effect give @s minecraft:hunger 1 195 true -execute if score tree_size timber matches 196 run effect give @s minecraft:hunger 1 196 true -execute if score tree_size timber matches 197 run effect give @s minecraft:hunger 1 197 true -execute if score tree_size timber matches 198 run effect give @s minecraft:hunger 1 198 true -execute if score tree_size timber matches 199 run effect give @s minecraft:hunger 1 199 true -execute if score tree_size timber matches 200 run effect give @s minecraft:hunger 1 200 true -execute if score tree_size timber matches 201 run effect give @s minecraft:hunger 1 201 true -execute if score tree_size timber matches 202 run effect give @s minecraft:hunger 1 202 true -execute if score tree_size timber matches 203 run effect give @s minecraft:hunger 1 203 true -execute if score tree_size timber matches 204 run effect give @s minecraft:hunger 1 204 true -execute if score tree_size timber matches 205 run effect give @s minecraft:hunger 1 205 true -execute if score tree_size timber matches 206 run effect give @s minecraft:hunger 1 206 true -execute if score tree_size timber matches 207 run effect give @s minecraft:hunger 1 207 true -execute if score tree_size timber matches 208 run effect give @s minecraft:hunger 1 208 true -execute if score tree_size timber matches 209 run effect give @s minecraft:hunger 1 209 true -execute if score tree_size timber matches 210 run effect give @s minecraft:hunger 1 210 true -execute if score tree_size timber matches 211 run effect give @s minecraft:hunger 1 211 true -execute if score tree_size timber matches 212 run effect give @s minecraft:hunger 1 212 true -execute if score tree_size timber matches 213 run effect give @s minecraft:hunger 1 213 true -execute if score tree_size timber matches 214 run effect give @s minecraft:hunger 1 214 true -execute if score tree_size timber matches 215 run effect give @s minecraft:hunger 1 215 true -execute if score tree_size timber matches 216 run effect give @s minecraft:hunger 1 216 true -execute if score tree_size timber matches 217 run effect give @s minecraft:hunger 1 217 true -execute if score tree_size timber matches 218 run effect give @s minecraft:hunger 1 218 true -execute if score tree_size timber matches 219 run effect give @s minecraft:hunger 1 219 true -execute if score tree_size timber matches 220 run effect give @s minecraft:hunger 1 220 true -execute if score tree_size timber matches 221 run effect give @s minecraft:hunger 1 221 true -execute if score tree_size timber matches 222 run effect give @s minecraft:hunger 1 222 true -execute if score tree_size timber matches 223 run effect give @s minecraft:hunger 1 223 true -execute if score tree_size timber matches 224 run effect give @s minecraft:hunger 1 224 true -execute if score tree_size timber matches 225 run effect give @s minecraft:hunger 1 225 true -execute if score tree_size timber matches 226 run effect give @s minecraft:hunger 1 226 true -execute if score tree_size timber matches 227 run effect give @s minecraft:hunger 1 227 true -execute if score tree_size timber matches 228 run effect give @s minecraft:hunger 1 228 true -execute if score tree_size timber matches 229 run effect give @s minecraft:hunger 1 229 true -execute if score tree_size timber matches 230 run effect give @s minecraft:hunger 1 230 true -execute if score tree_size timber matches 231 run effect give @s minecraft:hunger 1 231 true -execute if score tree_size timber matches 232 run effect give @s minecraft:hunger 1 232 true -execute if score tree_size timber matches 233 run effect give @s minecraft:hunger 1 233 true -execute if score tree_size timber matches 234 run effect give @s minecraft:hunger 1 234 true -execute if score tree_size timber matches 235 run effect give @s minecraft:hunger 1 235 true -execute if score tree_size timber matches 236 run effect give @s minecraft:hunger 1 236 true -execute if score tree_size timber matches 237 run effect give @s minecraft:hunger 1 237 true -execute if score tree_size timber matches 238 run effect give @s minecraft:hunger 1 238 true -execute if score tree_size timber matches 239 run effect give @s minecraft:hunger 1 239 true -execute if score tree_size timber matches 240 run effect give @s minecraft:hunger 1 240 true -execute if score tree_size timber matches 241 run effect give @s minecraft:hunger 1 241 true -execute if score tree_size timber matches 242 run effect give @s minecraft:hunger 1 242 true -execute if score tree_size timber matches 243 run effect give @s minecraft:hunger 1 243 true -execute if score tree_size timber matches 244 run effect give @s minecraft:hunger 1 244 true -execute if score tree_size timber matches 245 run effect give @s minecraft:hunger 1 245 true -execute if score tree_size timber matches 246 run effect give @s minecraft:hunger 1 246 true -execute if score tree_size timber matches 247 run effect give @s minecraft:hunger 1 247 true -execute if score tree_size timber matches 248 run effect give @s minecraft:hunger 1 248 true -execute if score tree_size timber matches 249 run effect give @s minecraft:hunger 1 249 true -execute if score tree_size timber matches 250 run effect give @s minecraft:hunger 1 250 true -execute if score tree_size timber matches 251 run effect give @s minecraft:hunger 1 251 true -execute if score tree_size timber matches 252 run effect give @s minecraft:hunger 1 252 true -execute if score tree_size timber matches 253 run effect give @s minecraft:hunger 1 253 true -execute if score tree_size timber matches 254 run effect give @s minecraft:hunger 1 254 true -execute if score tree_size timber matches 255.. run effect give @s minecraft:hunger 1 255 true \ No newline at end of file diff --git a/18-41/data/timber/functions/utils/reset.mcfunction b/18-41/data/timber/functions/utils/reset.mcfunction deleted file mode 100644 index 3b6df1c..0000000 --- a/18-41/data/timber/functions/utils/reset.mcfunction +++ /dev/null @@ -1,24 +0,0 @@ -scoreboard players set running timber 0 - -# reset scores -scoreboard players set @s timber_w_axe 0 -scoreboard players set @s timber_s_axe 0 -scoreboard players set @s timber_i_axe 0 -scoreboard players set @s timber_g_axe 0 -scoreboard players set @s timber_d_axe 0 -scoreboard players set @s timber_n_axe 0 - -# remove markers -execute if score chop_fungi timber matches 1.. run kill @e[type=minecraft:item,nbt={Item:{components:{"minecraft:custom_data":{Tags:["timber_location"]}}}}] -#execute if score chop_fungi timber matches 1.. run clear @s knowledge_book{Tags:[timber_location]} -kill @e[type=minecraft:marker,tag=timber_tree,tag=!timber_slow_chop] -kill @e[type=minecraft:marker,tag=timber_log,tag=!timber_destroy] -kill @e[type=minecraft:marker,tag=timber_leaf,tag=!timber_destroy] -kill @e[type=minecraft:marker,tag=timber_root,tag=!timber_destroy] -kill @e[type=minecraft:marker,tag=timber_stem,tag=!timber_destroy] -kill @e[type=minecraft:marker,tag=timber_cap,tag=!timber_destroy] -kill @e[type=minecraft:marker,tag=timber_leaves_found] -kill @e[type=minecraft:marker,tag=timber_leaf_distance] -kill @e[type=minecraft:marker,tag=timber_other_stem] - -kill @e[type=minecraft:armor_stand,tag=timber_item_check] \ No newline at end of file diff --git a/18-41/data/timber/functions/utils/sneaking.mcfunction b/18-41/data/timber/functions/utils/sneaking.mcfunction deleted file mode 100644 index 1a6478b..0000000 --- a/18-41/data/timber/functions/utils/sneaking.mcfunction +++ /dev/null @@ -1,3 +0,0 @@ -# standing or sneaking enabled? -execute if score standing timber matches 1.. unless predicate timber:sneaking run function timber:run -execute if score sneaking timber matches 1.. if predicate timber:sneaking run function timber:run \ No newline at end of file diff --git a/18-41/data/timber/functions/utils/stopsound.mcfunction b/18-41/data/timber/functions/utils/stopsound.mcfunction deleted file mode 100644 index b9d5f78..0000000 --- a/18-41/data/timber/functions/utils/stopsound.mcfunction +++ /dev/null @@ -1,6 +0,0 @@ -stopsound @s block minecraft:block.grass.break -stopsound @s block minecraft:block.wood.break -stopsound @s block minecraft:block.wart_block.break -stopsound @s block minecraft:block.shroomlight.break -stopsound @s block minecraft:block.nether_wart.break -stopsound @s block minecraft:block.stem.break \ No newline at end of file diff --git a/18-41/data/timber/functions/utils/tp_item.mcfunction b/18-41/data/timber/functions/utils/tp_item.mcfunction deleted file mode 100644 index a01d4e1..0000000 --- a/18-41/data/timber/functions/utils/tp_item.mcfunction +++ /dev/null @@ -1,9 +0,0 @@ -# tp hand-broken loot into inventory - -tp @e[type=minecraft:item,sort=nearest,limit=1] @s - -#setblock ~ 255 ~ minecraft:yellow_shulker_box -#data modify block ~ 255 ~ Items append from entity @e[type=minecraft:item,sort=nearest,limit=1] Item -#kill @e[type=minecraft:item,sort=nearest,limit=1] -#loot give @s mine ~ 255 ~ minecraft:air{drop_contents:1b} -#setblock ~ 255 ~ minecraft:air \ No newline at end of file diff --git a/18-41/data/timber/functions/utils/used_axe.mcfunction b/18-41/data/timber/functions/utils/used_axe.mcfunction deleted file mode 100644 index 5e7f9b6..0000000 --- a/18-41/data/timber/functions/utils/used_axe.mcfunction +++ /dev/null @@ -1,18 +0,0 @@ -# is this axe enabled? -execute unless score @s timber_disabled matches 1.. if score @s timber_w_axe matches 1.. if score wooden_axe timber matches 1.. run function timber:utils/sneaking -execute unless score @s timber_disabled matches 1.. if score @s timber_s_axe matches 1.. if score stone_axe timber matches 1.. run function timber:utils/sneaking -execute unless score @s timber_disabled matches 1.. if score @s timber_i_axe matches 1.. if score iron_axe timber matches 1.. run function timber:utils/sneaking -execute unless score @s timber_disabled matches 1.. if score @s timber_g_axe matches 1.. if score golden_axe timber matches 1.. run function timber:utils/sneaking -execute unless score @s timber_disabled matches 1.. if score @s timber_d_axe matches 1.. if score diamond_axe timber matches 1.. run function timber:utils/sneaking -execute unless score @s timber_disabled matches 1.. if score @s timber_n_axe matches 1.. if score netherite_axe timber matches 1.. run function timber:utils/sneaking - -# remove mushroom_stem marker -kill @e[type=minecraft:item,distance=..7,nbt={Item:{components:{"minecraft:custom_data":{Tags:["timber_location"]}}}}] - -# reset scores -scoreboard players set @s timber_w_axe 0 -scoreboard players set @s timber_s_axe 0 -scoreboard players set @s timber_i_axe 0 -scoreboard players set @s timber_g_axe 0 -scoreboard players set @s timber_d_axe 0 -scoreboard players set @s timber_n_axe 0 \ No newline at end of file diff --git a/18-41/data/timber/functions/validated.mcfunction b/18-41/data/timber/functions/validated.mcfunction deleted file mode 100644 index b00df5e..0000000 --- a/18-41/data/timber/functions/validated.mcfunction +++ /dev/null @@ -1,51 +0,0 @@ -# when full inventory fix -execute if score drop_loot timber matches 1.. store result score inventory timber run data get entity @s Inventory -execute if score drop_loot timber matches 1.. if entity @s[nbt={Inventory:[{Slot:100b}]}] run scoreboard players remove inventory timber 1 -execute if score drop_loot timber matches 1.. if entity @s[nbt={Inventory:[{Slot:101b}]}] run scoreboard players remove inventory timber 1 -execute if score drop_loot timber matches 1.. if entity @s[nbt={Inventory:[{Slot:102b}]}] run scoreboard players remove inventory timber 1 -execute if score drop_loot timber matches 1.. if entity @s[nbt={Inventory:[{Slot:103b}]}] run scoreboard players remove inventory timber 1 -execute if score drop_loot timber matches 1.. if entity @s[nbt={Inventory:[{Slot:-106b}]}] run scoreboard players remove inventory timber 1 - -# destroy blocks - # mark blocks that need to be destroyed - tag @e[type=minecraft:marker,tag=timber_log] add timber_destroy - execute if score destroy_leaves timber matches 1.. run tag @e[type=minecraft:marker,tag=timber_leaf] add timber_destroy - execute if score destroy_roots timber matches 1.. run tag @e[type=minecraft:marker,tag=timber_root] add timber_destroy - tag @e[type=minecraft:marker,tag=timber_stem] add timber_destroy - tag @e[type=minecraft:marker,tag=timber_cap] add timber_destroy - # api - execute unless score slow_chop timber matches 1.. at @e[type=minecraft:marker,tag=timber_log] run function #timber:api/break_log - execute unless score slow_chop timber matches 1.. at @e[type=minecraft:marker,tag=timber_leaf] run function #timber:api/break_leaf - execute unless score slow_chop timber matches 1.. at @e[type=minecraft:marker,tag=timber_root] run function #timber:api/break_root - execute unless score slow_chop timber matches 1.. at @e[type=minecraft:marker,tag=timber_stem] run function #timber:api/break_stem - execute unless score slow_chop timber matches 1.. at @e[type=minecraft:marker,tag=timber_cap] run function #timber:api/break_cap - # loot - execute unless score slow_chop timber matches 1.. unless score drop_loot timber matches 1.. at @e[type=minecraft:marker,tag=timber_destroy] run loot spawn ~ ~ ~ mine ~ ~ ~ mainhand - execute unless score slow_chop timber matches 1.. if score drop_loot timber matches 1.. unless score inventory timber matches 36.. at @e[type=minecraft:marker,tag=timber_destroy] run loot give @s mine ~ ~ ~ mainhand - execute unless score slow_chop timber matches 1.. if score drop_loot timber matches 1.. if score inventory timber matches 36.. at @e[type=minecraft:marker,tag=timber_destroy] run loot spawn ~ ~ ~ mine ~ ~ ~ mainhand - # destroy animation (doTileDrops can be changed, because datapack wouldn't get to this point otherwise) - gamerule doTileDrops false - execute unless score slow_chop timber matches 1.. at @e[type=minecraft:marker,tag=timber_destroy] run setblock ~ ~ ~ minecraft:air destroy - gamerule doTileDrops true - # remove destroy marker - execute unless score slow_chop timber matches 1.. run kill @e[type=minecraft:marker,tag=timber_destroy] - -# stop sound -execute if score stopsound timber matches 1.. as @a[distance=..20,tag=!global.ignore.gui] run function timber:utils/stopsound - -# save tool for slow chopping process -execute if score slow_chop timber matches 1.. at @e[type=minecraft:marker,tag=timber_tree,tag=!timber_slow_chop,distance=..7,sort=arbitrary,limit=1] run summon minecraft:armor_stand ~ -64 ~ {Silent:1b,Invulnerable:1b,Marker:1b,Invisible:1b,Tags:["timber_tool"],Rotation:[45f],Pose:{RightArm:[0f,270f,0f]}} -execute if score slow_chop timber matches 1.. at @e[type=minecraft:marker,tag=timber_tree,tag=!timber_slow_chop,distance=..7,sort=arbitrary,limit=1] run data modify entity @e[type=minecraft:armor_stand,tag=timber_tool,y=-64,distance=...1,sort=arbitrary,limit=1] HandItems[0] merge from entity @s SelectedItem -execute if score slow_chop timber matches 1.. run tag @e[type=minecraft:marker,tag=timber_tree,tag=!timber_slow_chop,distance=..7,sort=arbitrary,limit=1] add timber_slow_chop - -# tp hand-broken loot into inventory -execute if score drop_loot timber matches 1.. unless score inventory timber matches 36.. at @e[type=minecraft:marker,tag=timber_tree,tag=!timber_slow_chop,distance=..7,sort=arbitrary,limit=1] run function timber:utils/tp_item - -# wear out tool -execute if score wear_out timber matches 1.. unless score unbreakable timber matches 1 run function timber:tool/wear_out - -# break axe if worn out -execute if score wear_out timber matches 1.. unless score unbreakable timber matches 1 run function timber:tool/worn_out - -# give hunger effect -execute if score hunger timber matches 1.. run function timber:utils/hunger \ No newline at end of file diff --git a/18-41/data/timber/item_modifiers/wear_out.json b/18-41/data/timber/item_modifiers/wear_out.json deleted file mode 100644 index 63fd89d..0000000 --- a/18-41/data/timber/item_modifiers/wear_out.json +++ /dev/null @@ -1,152 +0,0 @@ -[ - { - "function": "minecraft:set_damage", - "damage": { - "type": "minecraft:score", - "target": { - "type": "minecraft:fixed", - "name": "damage" - }, - "score": "timber", - "scale": 0.01694915254237288 - }, - "conditions": [ - { - "condition": "minecraft:entity_properties", - "entity": "this", - "predicate": { - "equipment": { - "mainhand": { - "items": "minecraft:wooden_axe" - } - } - } - } - ] - }, - { - "function": "minecraft:set_damage", - "damage": { - "type": "minecraft:score", - "target": { - "type": "minecraft:fixed", - "name": "damage" - }, - "score": "timber", - "scale": 0.007633587786259542 - }, - "conditions": [ - { - "condition": "minecraft:entity_properties", - "entity": "this", - "predicate": { - "equipment": { - "mainhand": { - "items": "minecraft:stone_axe" - } - } - } - } - ] - }, - { - "function": "minecraft:set_damage", - "damage": { - "type": "minecraft:score", - "target": { - "type": "minecraft:fixed", - "name": "damage" - }, - "score": "timber", - "scale": 0.004 - }, - "conditions": [ - { - "condition": "minecraft:entity_properties", - "entity": "this", - "predicate": { - "equipment": { - "mainhand": { - "items": "minecraft:iron_axe" - } - } - } - } - ] - }, - { - "function": "minecraft:set_damage", - "damage": { - "type": "minecraft:score", - "target": { - "type": "minecraft:fixed", - "name": "damage" - }, - "score": "timber", - "scale": 0.03125 - }, - "conditions": [ - { - "condition": "minecraft:entity_properties", - "entity": "this", - "predicate": { - "equipment": { - "mainhand": { - "items": "minecraft:golden_axe" - } - } - } - } - ] - }, - { - "function": "minecraft:set_damage", - "damage": { - "type": "minecraft:score", - "target": { - "type": "minecraft:fixed", - "name": "damage" - }, - "score": "timber", - "scale": 0.0006393861892583121 - }, - "conditions": [ - { - "condition": "minecraft:entity_properties", - "entity": "this", - "predicate": { - "equipment": { - "mainhand": { - "items": "minecraft:diamond_axe" - } - } - } - } - ] - }, - { - "function": "minecraft:set_damage", - "damage": { - "type": "minecraft:score", - "target": { - "type": "minecraft:fixed", - "name": "damage" - }, - "score": "timber", - "scale": 0.0004923682914820286 - }, - "conditions": [ - { - "condition": "minecraft:entity_properties", - "entity": "this", - "predicate": { - "equipment": { - "mainhand": { - "items": "minecraft:netherite_axe" - } - } - } - } - ] - } -] \ No newline at end of file diff --git a/18-41/data/timber/predicates/block/cap.json b/18-41/data/timber/predicates/block/cap.json deleted file mode 100644 index 807ada7..0000000 --- a/18-41/data/timber/predicates/block/cap.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "condition": "minecraft:any_of", - "terms": [ - { - "condition": "minecraft:location_check", - "predicate": { - "block": { - "blocks": "#minecraft:wart_blocks" - } - } - }, - { - "condition": "minecraft:location_check", - "predicate": { - "block": { - "blocks": "minecraft:shroomlight" - } - } - }, - { - "condition": "minecraft:location_check", - "predicate": { - "block": { - "blocks": "minecraft:brown_mushroom_block" - } - } - }, - { - "condition": "minecraft:location_check", - "predicate": { - "block": { - "blocks": "minecraft:red_mushroom_block" - } - } - } - ] -} \ No newline at end of file diff --git a/18-41/data/timber/predicates/block/leaf.json b/18-41/data/timber/predicates/block/leaf.json deleted file mode 100644 index 14d5368..0000000 --- a/18-41/data/timber/predicates/block/leaf.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "condition": "minecraft:any_of", - "terms": [ - { - "condition": "minecraft:location_check", - "predicate": { - "block": { - "blocks": "#minecraft:leaves" - } - } - }, - { - "condition": "minecraft:location_check", - "predicate": { - "block": { - "blocks": "minecraft:mangrove_propagule", - "state": { - "hanging": "true" - } - } - } - } - ] -} \ No newline at end of file diff --git a/18-41/data/timber/predicates/block/log.json b/18-41/data/timber/predicates/block/log.json deleted file mode 100644 index 3b960aa..0000000 --- a/18-41/data/timber/predicates/block/log.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "condition": "minecraft:location_check", - "predicate": { - "block": { - "blocks": "#minecraft:logs" - } - } -} diff --git a/18-41/data/timber/predicates/block/stem.json b/18-41/data/timber/predicates/block/stem.json deleted file mode 100644 index 591f3db..0000000 --- a/18-41/data/timber/predicates/block/stem.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "condition": "minecraft:any_of", - "terms": [ - { - "condition": "minecraft:location_check", - "predicate": { - "block": { - "blocks": "#minecraft:logs" - } - } - }, - { - "condition": "minecraft:location_check", - "predicate": { - "block": { - "blocks": "minecraft:mushroom_stem" - } - } - } - ] -} \ No newline at end of file diff --git a/18-41/data/timber/predicates/item/log.json b/18-41/data/timber/predicates/item/log.json deleted file mode 100644 index 7fea675..0000000 --- a/18-41/data/timber/predicates/item/log.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "condition": "minecraft:entity_properties", - "entity": "this", - "predicate": { - "equipment": { - "mainhand": { - "items": "#minecraft:logs" - } - } - } -} \ No newline at end of file diff --git a/18-41/data/timber/predicates/item/root.json b/18-41/data/timber/predicates/item/root.json deleted file mode 100644 index f746554..0000000 --- a/18-41/data/timber/predicates/item/root.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "condition": "minecraft:entity_properties", - "entity": "this", - "predicate": { - "equipment": { - "mainhand": { - "items": "minecraft:mangrove_roots" - } - } - } -} \ No newline at end of file diff --git a/18-41/data/timber/predicates/item/sapling.json b/18-41/data/timber/predicates/item/sapling.json deleted file mode 100644 index 31bd5c7..0000000 --- a/18-41/data/timber/predicates/item/sapling.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "condition": "minecraft:entity_properties", - "entity": "this", - "predicate": { - "equipment": { - "mainhand": { - "items": "#minecraft:saplings" - } - } - } -} \ No newline at end of file diff --git a/18-41/data/timber/predicates/item/stem.json b/18-41/data/timber/predicates/item/stem.json deleted file mode 100644 index 43b5e29..0000000 --- a/18-41/data/timber/predicates/item/stem.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "condition": "minecraft:any_of", - "terms": [ - { - "condition": "minecraft:entity_properties", - "entity": "this", - "predicate": { - "equipment": { - "mainhand": { - "items": "#minecraft:logs" - } - } - } - }, - { - "condition": "minecraft:entity_properties", - "entity": "this", - "predicate": { - "equipment": { - "mainhand": { - "items": "minecraft:mushroom_stem" - } - } - } - }, - { - "condition": "minecraft:entity_properties", - "entity": "this", - "predicate": { - "equipment": { - "mainhand": { - "items": "minecraft:knowledge_book", - "components": { - "minecraft:custom_data": { - "Tags": [ - "timber_location" - ] - } - } - } - } - } - } - ] -} \ No newline at end of file diff --git a/18-41/data/timber/predicates/random_chance/0.25.json b/18-41/data/timber/predicates/random_chance/0.25.json deleted file mode 100644 index 9740f91..0000000 --- a/18-41/data/timber/predicates/random_chance/0.25.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "condition": "minecraft:random_chance", - "chance": 0.25 - } \ No newline at end of file diff --git a/18-41/data/timber/predicates/random_chance/0.333.json b/18-41/data/timber/predicates/random_chance/0.333.json deleted file mode 100644 index 52e5ecf..0000000 --- a/18-41/data/timber/predicates/random_chance/0.333.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "condition": "minecraft:random_chance", - "chance": 0.333 - } \ No newline at end of file diff --git a/18-41/data/timber/predicates/random_chance/0.5.json b/18-41/data/timber/predicates/random_chance/0.5.json deleted file mode 100644 index 66c2b14..0000000 --- a/18-41/data/timber/predicates/random_chance/0.5.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "condition": "minecraft:random_chance", - "chance": 0.5 - } \ No newline at end of file diff --git a/18-41/data/timber/predicates/sneaking.json b/18-41/data/timber/predicates/sneaking.json deleted file mode 100644 index a41c89c..0000000 --- a/18-41/data/timber/predicates/sneaking.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "condition": "minecraft:entity_properties", - "entity": "this", - "predicate": { - "flags": { - "is_sneaking": true - } - } -} \ No newline at end of file diff --git a/18-41/data/timber/predicates/used_axe.json b/18-41/data/timber/predicates/used_axe.json deleted file mode 100644 index ac00bc4..0000000 --- a/18-41/data/timber/predicates/used_axe.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "condition": "minecraft:any_of", - "terms": [ - { - "condition": "minecraft:entity_scores", - "entity": "this", - "scores": { - "timber_w_axe": { - "min": 1 - } - } - }, - { - "condition": "minecraft:entity_scores", - "entity": "this", - "scores": { - "timber_s_axe": { - "min": 1 - } - } - }, - { - "condition": "minecraft:entity_scores", - "entity": "this", - "scores": { - "timber_i_axe": { - "min": 1 - } - } - }, - { - "condition": "minecraft:entity_scores", - "entity": "this", - "scores": { - "timber_g_axe": { - "min": 1 - } - } - }, - { - "condition": "minecraft:entity_scores", - "entity": "this", - "scores": { - "timber_d_axe": { - "min": 1 - } - } - }, - { - "condition": "minecraft:entity_scores", - "entity": "this", - "scores": { - "timber_n_axe": { - "min": 1 - } - } - } - ] -} \ No newline at end of file diff --git a/18-41/data/timber/tags/functions/api/break_cap.json b/18-41/data/timber/tags/functions/api/break_cap.json deleted file mode 100644 index ab3fce9..0000000 --- a/18-41/data/timber/tags/functions/api/break_cap.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "values":[ - ] -} \ No newline at end of file diff --git a/18-41/data/timber/tags/functions/api/break_leaf.json b/18-41/data/timber/tags/functions/api/break_leaf.json deleted file mode 100644 index ab3fce9..0000000 --- a/18-41/data/timber/tags/functions/api/break_leaf.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "values":[ - ] -} \ No newline at end of file diff --git a/18-41/data/timber/tags/functions/api/break_log.json b/18-41/data/timber/tags/functions/api/break_log.json deleted file mode 100644 index ab3fce9..0000000 --- a/18-41/data/timber/tags/functions/api/break_log.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "values":[ - ] -} \ No newline at end of file diff --git a/18-41/data/timber/tags/functions/api/break_root.json b/18-41/data/timber/tags/functions/api/break_root.json deleted file mode 100644 index ab3fce9..0000000 --- a/18-41/data/timber/tags/functions/api/break_root.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "values":[ - ] -} \ No newline at end of file diff --git a/18-41/data/timber/tags/functions/api/break_stem.json b/18-41/data/timber/tags/functions/api/break_stem.json deleted file mode 100644 index ab3fce9..0000000 --- a/18-41/data/timber/tags/functions/api/break_stem.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "values":[ - ] -} \ No newline at end of file diff --git a/18-41/data/timber/tags/functions/help.json b/18-41/data/timber/tags/functions/help.json deleted file mode 100644 index 082ff6b..0000000 --- a/18-41/data/timber/tags/functions/help.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "values":[ - "timber:help" - ] -} \ No newline at end of file diff --git a/18-41/data/timber/tags/items/axes.json b/18-41/data/timber/tags/items/axes.json deleted file mode 100644 index c496215..0000000 --- a/18-41/data/timber/tags/items/axes.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "values":[ - "minecraft:wooden_axe", - "minecraft:stone_axe", - "minecraft:iron_axe", - "minecraft:golden_axe", - "minecraft:diamond_axe", - "minecraft:netherite_axe" - ] -} \ No newline at end of file diff --git a/18-61/data/timber/function/algorithm/fungus/run.mcfunction b/18-61/data/timber/function/algorithm/fungus/run.mcfunction deleted file mode 100644 index 6db2dbe..0000000 --- a/18-61/data/timber/function/algorithm/fungus/run.mcfunction +++ /dev/null @@ -1,5 +0,0 @@ -# check if item is a stem -summon minecraft:armor_stand ~ ~ ~ {Silent:1b,Invulnerable:1b,Marker:1b,Invisible:1b,Tags:["timber_item_check"]} -data modify entity @e[type=minecraft:armor_stand,tag=timber_item_check,distance=...1,sort=arbitrary,limit=1] HandItems[0] set from entity @e[distance=...1,type=minecraft:item,sort=nearest,limit=1,nbt={Age:0s}] Item -execute if entity @e[type=minecraft:armor_stand,tag=timber_item_check,distance=...1,sort=arbitrary,limit=1,predicate=timber:item/stem] run function timber:algorithm/fungus/chop -kill @e[type=minecraft:armor_stand,tag=timber_item_check,distance=...1,sort=arbitrary,limit=1] \ No newline at end of file diff --git a/18-61/data/timber/function/algorithm/tree/run.mcfunction b/18-61/data/timber/function/algorithm/tree/run.mcfunction deleted file mode 100644 index 172c583..0000000 --- a/18-61/data/timber/function/algorithm/tree/run.mcfunction +++ /dev/null @@ -1,6 +0,0 @@ -# check if item is in tag #minecraft:logs -summon minecraft:armor_stand ~ ~ ~ {Silent:1b,Invulnerable:1b,Marker:1b,Invisible:1b,Tags:["timber_item_check"]} -data modify entity @e[type=minecraft:armor_stand,tag=timber_item_check,distance=...1,sort=arbitrary,limit=1] HandItems[0] set from entity @e[type=minecraft:item,distance=...1,sort=nearest,limit=1,nbt={Age:0s}] Item -execute if entity @e[type=minecraft:armor_stand,tag=timber_item_check,distance=...1,sort=arbitrary,limit=1,predicate=timber:item/log] run function timber:algorithm/tree/chop -execute if score start_chop_on_root timber matches 1.. if entity @e[type=minecraft:armor_stand,tag=timber_item_check,distance=...1,sort=arbitrary,limit=1,predicate=timber:item/root] run function timber:algorithm/tree/chop_roots -kill @e[type=minecraft:armor_stand,tag=timber_item_check,distance=...1,sort=arbitrary,limit=1] diff --git a/18-61/data/timber/function/autoplant/validate.mcfunction b/18-61/data/timber/function/autoplant/validate.mcfunction deleted file mode 100644 index e84bccf..0000000 --- a/18-61/data/timber/function/autoplant/validate.mcfunction +++ /dev/null @@ -1,8 +0,0 @@ -# don't check items twice (except saplings, look in plant.mcfunction) -tag @s add timber_checked - -# check if item is in tag #minecraft:sapling -summon minecraft:armor_stand ~ ~ ~ {Silent:1b,Invulnerable:1b,Marker:1b,Invisible:1b,Tags:["timber_item_check"]} -data modify entity @e[type=minecraft:armor_stand,tag=timber_item_check,distance=...1,sort=arbitrary,limit=1] HandItems[0] set from entity @s Item -execute if entity @e[type=minecraft:armor_stand,tag=timber_item_check,distance=...1,sort=arbitrary,limit=1,predicate=timber:item/sapling] run function timber:autoplant/plant -kill @e[type=minecraft:armor_stand,tag=timber_item_check,distance=...1,sort=arbitrary,limit=1] \ No newline at end of file diff --git a/18-61/data/timber/function/help.mcfunction b/18-61/data/timber/function/help.mcfunction deleted file mode 100644 index 6051b57..0000000 --- a/18-61/data/timber/function/help.mcfunction +++ /dev/null @@ -1,25 +0,0 @@ -tellraw @s ["",{"text":"\u00A7m ","color":"dark_gray"}] - -tellraw @s ["",{"text":" Timber Datapack"}] - -tellraw @s ["",{"text":"\u00A7m ","color":"dark_gray"}] - -tellraw @s ["",{"text":"The Timber Datapack allows you to instantly chop down a tree just by breaking one log with any axe."}] - -tellraw @s ["",{"text":"By default, sneaking while breaking will not chop the tree."}] - -tellraw @s [""] - -tellraw @s ["",{"text":"Every player can use "},{"text":"/trigger TimberToggle","color":"blue","clickEvent":{"action":"suggest_command","value":"/trigger TimberToggle"}},{"text":" to individually turn the datapack on or off."}] - -tellraw @s [""] - -tellraw @s ["",{"text":"Snapshot (work in progress) version"}] - -tellraw @s [""] - -tellraw @s [""," ",{"text":"[click here to see the settings]","color":"gold","clickEvent":{"action":"run_command","value":"/function timber:settings/settings1_click"}}] - -tellraw @s ["",{"text":"\u00A7m ","color":"dark_gray"}] - -function timber:settings/end_of_message \ No newline at end of file diff --git a/18-61/data/timber/function/init.mcfunction b/18-61/data/timber/function/init.mcfunction deleted file mode 100644 index 1d2cbf0..0000000 --- a/18-61/data/timber/function/init.mcfunction +++ /dev/null @@ -1,39 +0,0 @@ -# longer maxCommandChainLength -gamerule maxCommandChainLength 1000000000 - -# create scoreboards -scoreboard objectives add timber_w_axe minecraft.used:minecraft.wooden_axe -scoreboard objectives add timber_s_axe minecraft.used:minecraft.stone_axe -scoreboard objectives add timber_i_axe minecraft.used:minecraft.iron_axe -scoreboard objectives add timber_g_axe minecraft.used:minecraft.golden_axe -scoreboard objectives add timber_d_axe minecraft.used:minecraft.diamond_axe -scoreboard objectives add timber_n_axe minecraft.used:minecraft.netherite_axe -scoreboard objectives add timber dummy -scoreboard objectives add TimberToggle trigger {"text":"Timber Toggle","color":"#724E21"} -scoreboard objectives add timber_disabled dummy -scoreboard objectives add timber_prsistent dummy - -# non-overriding default values -function timber:settings/default_values -function timber:tool/durability_max -scoreboard players set running timber 0 - -# start optional loops -execute if score plant_sapling timber matches 1.. run function timber:autoplant/loop -execute if score slow_chop timber matches 1.. run function timber:slow_chop/loop - -# check loottable compatibility of mushroom stem -setblock ~ 255 ~ minecraft:chest -setblock ~ 255 ~1 minecraft:mushroom_stem -loot insert ~ 255 ~ mine ~ 255 ~1 minecraft:diamond_axe -execute unless data block ~ 255 ~ Items[{id:"minecraft:knowledge_book",components:{"minecraft:custom_data":{Tags:["timber_location"]}}}] run tellraw @a[tag=!noTimberWarning] ["",{"text":"Timber Datapack Warning: ","color":"red"},{"text":"Incompatibility with the mushroom_stem loottable of another datapack!"}] -fill ~-1 255 ~-1 ~1 255 ~1 minecraft:air -execute store result score doTileDrops timber run gamerule doTileDrops -execute if score doTileDrops timber matches 0 run tellraw @a[tag=!noTimberWarning] ["",{"text":"Timber Datapack Warning: ","color":"red"},{"text":"This datapack only works when gamerule doTileDrops is set to true!"}] - -# Getting the game version and checking if it is compatible with the datapack https://minecraft.fandom.com/wiki/Data_version -execute store result score game_version timber run data get entity @r DataVersion -execute if score game_version timber matches ..3442 run tellraw @a[tag=!noTimberWarning] ["",{"text":"Timber Datapack Warning: ","color":"red"},{"text":"This datapack is not compatible with all the versions before 1.20!"}] - -# Checking if the server uses a different jar from the vanilla one -function timber:utils/check_server \ No newline at end of file diff --git a/18-61/data/timber/function/settings/settings1.mcfunction b/18-61/data/timber/function/settings/settings1.mcfunction deleted file mode 100644 index 140256c..0000000 --- a/18-61/data/timber/function/settings/settings1.mcfunction +++ /dev/null @@ -1,57 +0,0 @@ -tellraw @s ["",{"text":" "}] -tellraw @s ["",{"text":" "}] -tellraw @s ["",{"text":" "}] -tellraw @s ["",{"text":" "}] - -tellraw @s ["",{"text":"\u00A7m ","color":"dark_gray"}] - -tellraw @s ["",{"text":" Timber "},{"text":"/","color":"gray"},{"text":" Global Settings "},{"text":" < ","clickEvent":{"action":"run_command","value":"/function timber:settings/settings1_click"}},{"text":"1/2"},{"text":" > ","clickEvent":{"action":"run_command","value":"/function timber:settings/settings2_click"}}] - -tellraw @s ["",{"text":"\u00A7m ","color":"dark_gray"}] - -execute if score standing timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/standing"}},{"text":" Works When Standing"}] -execute unless score standing timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/standing"}},{"text":" Works When Standing"}] - -execute if score sneaking timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/sneaking"}},{"text":" Works When Sneaking"}] -execute unless score sneaking timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/sneaking"}},{"text":" Works When Sneaking"}] - -execute if score destroy_leaves timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/destroy_leaves"}},{"text":" Destroy Leaves"}] -execute unless score destroy_leaves timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/destroy_leaves"}},{"text":" Destroy Leaves"}] - -execute if score plant_sapling timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/plant_sapling"}},{"text":" Auto Plant Saplings","hoverEvent":{"action":"show_text","value":["",{"text":"All kind of saplings laying on the ground will get planted every 2 seconds."},{"text":"\n\nWhen a sapling get placed on mud, it turns into muddy mangrove roots for a fraction of a second due to datapack limitations.","color":"gray"}]}}] -execute unless score plant_sapling timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/plant_sapling"}},{"text":" Auto Plant Saplings","hoverEvent":{"action":"show_text","value":["",{"text":"All kind of saplings laying on the ground will get planted every 2 seconds."},{"text":"\n\nWhen a sapling get placed on mud, it turns into muddy mangrove roots for a fraction of a second due to datapack limitations.","color":"gray"}]}}] - -execute if score destroy_roots timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/destroy_roots"}},{"text":" Destroy Roots","hoverEvent":{"action":"show_text","value":["",{"text":"Only root blocks which are not in the ground count."}]}}] -execute unless score destroy_roots timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/destroy_roots"}},{"text":" Destroy Roots","hoverEvent":{"action":"show_text","value":["",{"text":"Only root blocks which are not in the ground count."}]}}] - -execute if score start_chop_on_root timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/start_chop_on_root"}},{"text":" Chop Tree When Roots Get Destroyed","hoverEvent":{"action":"show_text","value":["",{"text":"The datapack will chop the tree if you break its root blocks.\nOnly root blocks which are not in the ground count."}]}}] -execute unless score start_chop_on_root timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/start_chop_on_root"}},{"text":" Chop Tree When Roots Get Destroyed","hoverEvent":{"action":"show_text","value":["",{"text":"The datapack will chop the tree if you break its root blocks.\nOnly root blocks which are not in the ground count."}]}}] - -execute if score slow_chop timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/slow_chop"}},{"text":" Chop Slowly","hoverEvent":{"action":"show_text","value":["",{"text":"The tree won't be chopped instantly anymore. Additional options to change the speed of the chopping process can be changed at the next page."},{"text":"\n\nThis option can increase performance.","color":"gray"}]}}] -execute unless score slow_chop timber matches 1.. unless score drop_loot timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/slow_chop"}},{"text":" Chop Slowly","hoverEvent":{"action":"show_text","value":["",{"text":"The tree won't be chopped instantly anymore. Additional options to change the speed of the chopping process can be changed at the next page."},{"text":"\n\nThis option can increase performance.","color":"gray"}]}}] -execute unless score slow_chop timber matches 1.. if score drop_loot timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"#7F2B2B","hoverEvent":{"action":"show_text","value":"Can't be enabled when \"Drop Loot Straight into Inventory\" is enabled."}},{"text":" Chop Slowly","hoverEvent":{"action":"show_text","value":["",{"text":"The tree won't be chopped instantly anymore. Additional options to change the speed of the chopping process can be changed at the next page."},{"text":"\n\nThis option can increase performance.","color":"gray"}]}}] - -execute if score chop_down timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/chop_down"}},{"text":" Enable Chopping Down","hoverEvent":{"action":"show_text","value":"Normally when you cut a tree in the middle only the top part of the tree gets destroyed. But if this option is enabled the bottom part will get destroyed as well."}}] -execute unless score chop_down timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/chop_down"}},{"text":" Enable Chopping Down","hoverEvent":{"action":"show_text","value":"Normally when you cut a tree in the middle only the top part of the tree gets destroyed. But if this option is enabled the bottom part will get destroyed as well."}}] - -execute if score stop_chopping timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/stop_chopping"}},{"text":" Stop Chopping Once Axe Breaks","hoverEvent":{"action":"show_text","value":"Stops the process of chopping the tree as soon as your axe doesn't have any durability left for the whole tree. When disabled, the tree will always be chopped finish."}}] -execute unless score stop_chopping timber matches 1.. if score wear_out timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/stop_chopping"}},{"text":" Stop Chopping Once Axe Breaks","hoverEvent":{"action":"show_text","value":"Stops the process of chopping the tree as soon as your axe doesn't have any durability left for the whole tree. When disabled, the tree will always be chopped finish."}}] -execute unless score stop_chopping timber matches 1.. unless score wear_out timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"#7F2B2B","hoverEvent":{"action":"show_text","value":"Can't be enabled when \"Additionally Wear out Tool Depending on Tree Size\" is disabled."}},{"text":" Stop Chopping Once Axe Breaks","hoverEvent":{"action":"show_text","value":"Stops the process of chopping the tree as soon as your axe doesn't have any durability left for the whole tree. When disabled, the tree will always be chopped finish."}}] - -execute if score wear_out timber matches 1.. unless score stop_chopping timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/wear_out"}},{"text":" Additionally Wear out Tool Depending on Tree Size","hoverEvent":{"action":"show_text","value":"The tool's durability will get decreased by the amount of destroyed logs (or stems). When disabled, only one durability gets deducted."}}] -execute unless score wear_out timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/wear_out"}},{"text":" Additionally Wear out Tool Depending on Tree Size","hoverEvent":{"action":"show_text","value":"The tool's durability will get decreased by the amount of destroyed logs (or stems). When disabled, only one durability gets deducted."}}] -execute if score wear_out timber matches 1.. if score stop_chopping timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"#2B7F2B","hoverEvent":{"action":"show_text","value":"Can't be disabled when \"Stop Chopping Once Axe Breaks\" is enabled."}},{"text":" Additionally Wear out Tool Depending on Tree Size","hoverEvent":{"action":"show_text","value":"The tool's durability will get decreased by the amount of destroyed logs (or stems). When disabled, only one durability gets deducted."}}] - -execute if score drop_loot timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/drop_loot"}},{"text":" Drop Loot Straight into Inventory","hoverEvent":{"action":"show_text","value":["",{"text":"Lets the dropped items of the tree teleport straight into your own inventory. As soon as you have a full inventory the loot gets dropped on the ground."},{"text":"\n\nThis option can increase performance.","color":"gray"}]}}] -execute unless score drop_loot timber matches 1.. unless score slow_chop timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/drop_loot"}},{"text":" Drop Loot Straight into Inventory","hoverEvent":{"action":"show_text","value":["",{"text":"Lets the dropped items of the tree teleport straight into your own inventory. As soon as you have a full inventory the loot gets dropped on the ground."},{"text":"\n\nThis option can increase performance.","color":"gray"}]}}] -execute unless score drop_loot timber matches 1.. if score slow_chop timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"#7F2B2B","hoverEvent":{"action":"show_text","value":"Can't be enabled when \"Chop Slowly\" is enabled."}},{"text":" Drop Loot Straight into Inventory","hoverEvent":{"action":"show_text","value":["",{"text":"Lets the dropped items of the tree teleport straight into your own inventory. As soon as you have a full inventory the loot gets dropped on the ground."},{"text":"\n\nThis option can increase performance.","color":"gray"}]}}] - -execute if score hunger timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/hunger"}},{"text":" Give Additional Hunger Effect","hoverEvent":{"action":"show_text","value":"Gives a hunger effect for 1 second with the same level as the size of the chopped tree."}}] -execute unless score hunger timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/hunger"}},{"text":" Give Additional Hunger Effect","hoverEvent":{"action":"show_text","value":"Gives a hunger effect for 1 second with the same level as the size of the chopped tree."}}] - -execute if score stopsound timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/stopsound"}},{"text":" Stop Loud Breaking Noises","hoverEvent":{"action":"show_text","value":["",{"text":"Suppresses loud breaking noises when chopping a tree."},{"text":"\n\nThis option is","color":"gray"},{"text":" experimental","color":"dark_red"},{"text":" because sometimes it still sounds loud for a fraction of a second.","color":"gray"}]}}] -execute unless score stopsound timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/stopsound"}},{"text":" Stop Loud Breaking Noises","hoverEvent":{"action":"show_text","value":["",{"text":"Suppresses loud breaking noises when chopping a tree."},{"text":"\n\nThis option is","color":"gray"},{"text":" experimental","color":"dark_red"},{"text":" because sometimes it still sounds loud for a fraction of a second.","color":"gray"}]}}] - -tellraw @s ["",{"text":"\u00A7m ","color":"dark_gray"}] - -function timber:settings/end_of_message \ No newline at end of file diff --git a/18-61/data/timber/function/settings/settings2.mcfunction b/18-61/data/timber/function/settings/settings2.mcfunction deleted file mode 100644 index 6f1a745..0000000 --- a/18-61/data/timber/function/settings/settings2.mcfunction +++ /dev/null @@ -1,55 +0,0 @@ -tellraw @s ["",{"text":" "}] -tellraw @s ["",{"text":" "}] -tellraw @s ["",{"text":" "}] -tellraw @s ["",{"text":" "}] - -tellraw @s ["",{"text":"\u00A7m ","color":"dark_gray"}] - -tellraw @s ["",{"text":" Timber "},{"text":"/","color":"gray"},{"text":" Global Settings "},{"text":" < ","clickEvent":{"action":"run_command","value":"/function timber:settings/settings1_click"}},{"text":"2/2"},{"text":" > ","clickEvent":{"action":"run_command","value":"/function timber:settings/settings2_click"}}] - -tellraw @s ["",{"text":"\u00A7m ","color":"dark_gray"}] - -execute if score wooden_axe timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/wooden_axe"}},{"text":" Enable Wooden Axe"}] -execute unless score wooden_axe timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/wooden_axe"}},{"text":" Enable Wooden Axe"}] - -execute if score stone_axe timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/stone_axe"}},{"text":" Enable Stone Axe"}] -execute unless score stone_axe timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/stone_axe"}},{"text":" Enable Stone Axe"}] - -execute if score iron_axe timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/iron_axe"}},{"text":" Enable Iron Axe"}] -execute unless score iron_axe timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/iron_axe"}},{"text":" Enable Iron Axe"}] - -execute if score golden_axe timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/golden_axe"}},{"text":" Enable Golden Axe"}] -execute unless score golden_axe timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/golden_axe"}},{"text":" Enable Golden Axe"}] - -execute if score diamond_axe timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/diamond_axe"}},{"text":" Enable Diamond Axe"}] -execute unless score diamond_axe timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/diamond_axe"}},{"text":" Enable Diamond Axe"}] - -execute if score netherite_axe timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/netherite_axe"}},{"text":" Enable Netherite Axe"}] -execute unless score netherite_axe timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/netherite_axe"}},{"text":" Enable Netherite Axe"}] - -execute if score chop_trees timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/chop_trees"}},{"text":" Chop Trees","hoverEvent":{"action":"show_text","value":"All kind of trees will get chopped."}}] -execute unless score chop_trees timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/chop_trees"}},{"text":" Chop Trees","hoverEvent":{"action":"show_text","value":"All kind of trees will get chopped."}}] - -execute if score chop_fungi timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/chop_fungi"}},{"text":" Chop Fungi","hoverEvent":{"action":"show_text","value":["",{"text":"Huge fungi (nether trees) as well as huge mushrooms will get chopped."},{"text":"\n\nUse it on your own risk!\nWhen enabled, this datapack can't differentiate between generated fungi and player-placed fungi and therefore can potentially destroy your buildings.","color":"dark_red"}]}}] -execute unless score chop_fungi timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/chop_fungi"}},{"text":" Chop Fungi","hoverEvent":{"action":"show_text","value":["",{"text":"Huge fungi (nether trees) as well as huge mushrooms will get chopped."},{"text":"\n\nUse it on your own risk!\nWhen enabled, this datapack can't differentiate between generated fungi and player-placed fungi and therefore can potentially destroy your buildings.","color":"dark_red"}]}}] - -execute if score persistent timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/persistent"}},{"text":" Destroy Player-Placed Trees","hoverEvent":{"action":"show_text","value":["",{"text":"Player-placed trees are trees made out of player-placed leaves (Look at leaf block with F3 debug screen: persistent = true)."},{"text":"\n\nUse it on your own risk!\nWhen enabled, this datapack can't differentiate between generated trees and player-placed trees anymore and therefore can potentially destroy your buildings.","color":"dark_red"}]}}] -execute unless score persistent timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/persistent"}},{"text":" Destroy Player-Placed Trees","hoverEvent":{"action":"show_text","value":["",{"text":"Player-placed trees are trees made out of player-placed leaves (Look at leaf block with F3 debug screen: persistent = true)."},{"text":"\n\nUse it on your own risk!\nWhen enabled, this datapack can't differentiate between generated trees and player-placed trees anymore and therefore can potentially destroy your buildings.","color":"dark_red"}]}}] - -execute if score blocks_per_chop timber matches 1 if score slow_chop timber matches 1.. run tellraw @s ["",{"text":"[ ✎ ]","color":"gray","clickEvent":{"action":"suggest_command","value":"/scoreboard players set blocks_per_chop timber "},"hoverEvent":{"action":"show_text","value":"Default: 1"}},{"text":" Set Amount of Destroyed Blocks per Chop"},{"text":" (Current: ","color":"gray"},{"score":{"name":"blocks_per_chop","objective":"timber"},"color":"gray"},{"text": ")","color":"gray"}] -execute unless score blocks_per_chop timber matches 1 if score slow_chop timber matches 1.. run tellraw @s ["",{"text":"[ ✎ ]","color":"gray","clickEvent":{"action":"suggest_command","value":"/scoreboard players set blocks_per_chop timber "},"hoverEvent":{"action":"show_text","value":"Default: 1"}},{"text":" Set Amount of Destroyed Blocks per Chop"},{"text":" (Current: ","color":"gray"},{"score":{"name":"blocks_per_chop","objective":"timber"},"color":"gray"},{"text": ")","color":"gray"}] -execute unless score slow_chop timber matches 1.. run tellraw @s ["",{"text":"[ ✎ ]","color":"dark_gray","hoverEvent":{"action":"show_text","value":"Not active when \"Chop Slowly\" is disabled."}},{"text":" Set Amount of Destroyed Blocks per Chop"}] - -execute if score time_between_chops timber matches 1 if score slow_chop timber matches 1.. run tellraw @s ["",{"text":"[ ✎ ]","color":"gray","clickEvent":{"action":"suggest_command","value":"/scoreboard players set time_between_chops timber "},"hoverEvent":{"action":"show_text","value":"Default: 1"}},{"text":" Set Time in Ticks Between Chops"},{"text":" (Current: ","color":"gray"},{"score":{"name":"time_between_chops","objective":"timber"},"color":"gray"},{"text": ")","color":"gray"}] -execute unless score time_between_chops timber matches 1 if score slow_chop timber matches 1.. run tellraw @s ["",{"text":"[ ✎ ]","color":"gray","clickEvent":{"action":"suggest_command","value":"/scoreboard players set time_between_chops timber "},"hoverEvent":{"action":"show_text","value":"Default: 1"}},{"text":" Set Time in Ticks Between Chops"},{"text":" (Current: ","color":"gray"},{"score":{"name":"time_between_chops","objective":"timber"},"color":"gray"},{"text": ")","color":"gray"}] -execute unless score slow_chop timber matches 1.. run tellraw @s ["",{"text":"[ ✎ ]","color":"dark_gray","hoverEvent":{"action":"show_text","value":"Not active when \"Chop Slowly\" is disabled."}},{"text":" Set Time in Ticks Between Chops"}] - -execute if score max_tree_size timber matches 555 run tellraw @s ["",{"text":"[ ✎ ]","color":"gray","clickEvent":{"action":"suggest_command","value":"/scoreboard players set max_tree_size timber "},"hoverEvent":{"action":"show_text","value":"Default: 555"}},{"text":" Set Max. Tree Size","hoverEvent":{"action":"show_text","value":"Trees bigger than this set value won't get further processed."}},{"text":" (Currently: ","color":"gray"},{"score":{"name":"max_tree_size","objective":"timber"},"color":"gray"},{"text": ")","color":"gray"}] -execute unless score max_tree_size timber matches 555 run tellraw @s ["",{"text":"[ ✎ ]","color":"gray","clickEvent":{"action":"suggest_command","value":"/scoreboard players set max_tree_size timber "},"hoverEvent":{"action":"show_text","value":"Default: 555"}},{"text":" Set Max. Tree Size","hoverEvent":{"action":"show_text","value":"Trees bigger than this set value won't get further processed."}},{"text":" (Currently: ","color":"gray"},{"score":{"name":"max_tree_size","objective":"timber"},"color":"gray"},{"text": ")","color":"gray"}] - -execute if score min_leaves_found timber matches 5 run tellraw @s ["",{"text":"[ ✎ ]","color":"gray","clickEvent":{"action":"suggest_command","value":"/scoreboard players set min_leaves_found timber "},"hoverEvent":{"action":"show_text","value":"Default: 5"}},{"text":" Set Min. Leaves to Valid a Tree","hoverEvent":{"action":"show_text","value":"This datapack considers a tree only if it has this set value amount of naturally generated leaves at its trunk.\nFungi will get checked by the amount of cap blocks and additionally a 3×3 big cross on top of the stem."}},{"text":" (Current: ","color":"gray"},{"score":{"name":"min_leaves_found","objective":"timber"},"color":"gray"},{"text": ")","color":"gray"}] -execute unless score min_leaves_found timber matches 5 run tellraw @s ["",{"text":"[ ✎ ]","color":"gray","clickEvent":{"action":"suggest_command","value":"/scoreboard players set min_leaves_found timber "},"hoverEvent":{"action":"show_text","value":"Default: 5"}},{"text":" Set Min. Leaves to Valid a Tree","hoverEvent":{"action":"show_text","value":"This datapack considers a tree only if it has this set value amount of naturally generated leaves at its trunk.\nFungi will get checked by the amount of cap blocks and additionally a 3×3 big cross on top of the stem."}},{"text":" (Current: ","color":"gray"},{"score":{"name":"min_leaves_found","objective":"timber"},"color":"gray"},{"text": ")","color":"gray"}] - -tellraw @s ["",{"text":"\u00A7m ","color":"dark_gray"}] - -function timber:settings/end_of_message \ No newline at end of file diff --git a/18-61/data/timber/function/settings/toggle/timber.mcfunction b/18-61/data/timber/function/settings/toggle/timber.mcfunction deleted file mode 100644 index 0a26b1d..0000000 --- a/18-61/data/timber/function/settings/toggle/timber.mcfunction +++ /dev/null @@ -1,9 +0,0 @@ -# reset -scoreboard players set @s TimberToggle 0 -# toggle -execute unless score @s timber_disabled matches 1.. run scoreboard players set @s timber_disabled 3 -execute if score @s timber_disabled matches 1 run scoreboard players set @s timber_disabled 0 -execute if score @s timber_disabled matches 3 run scoreboard players set @s timber_disabled 1 -# display on/off status -execute unless score @s[tag=!global.ignore.gui] timber_disabled matches 1.. run tellraw @s ["",{"text":"<< ","color":"dark_gray"},{"text":"✔","color":"green"},{"text":" Timber is now on"},{"text":" >>","color":"dark_gray"}] -execute if score @s[tag=!global.ignore.gui] timber_disabled matches 1.. run tellraw @s ["",{"text":"<< ","color":"dark_gray"},{"text":"❌","color":"red"},{"text":" Timber is now off"},{"text":" >>","color":"dark_gray"}] \ No newline at end of file diff --git a/18-61/data/timber/function/utils/check_max_command_chain_length.mcfunction b/18-61/data/timber/function/utils/check_max_command_chain_length.mcfunction deleted file mode 100644 index 6f32c3e..0000000 --- a/18-61/data/timber/function/utils/check_max_command_chain_length.mcfunction +++ /dev/null @@ -1,13 +0,0 @@ -# function didn't end successfully -execute if score running timber matches 1 run scoreboard players set running timber 2 -# normal behavior -execute if score running timber matches 0 run scoreboard players set running timber 1 -execute if score running timber matches 1 run function timber:utils/used_axe -# function didn't end successfully -execute if score running timber matches 2 run execute store result storage timber:max_command_chain_length value int 1 run gamerule maxCommandChainLength -execute if score running timber matches 2 run tellraw @a[tag=!noTimberWarning] ["",{"text":"Timber Datapack Warning: ","color":"red"},{"text":"The datapack wasn't able to finish its functionality to work properly!"},{"text":"\n ─ Please either decrease setting \"Max. Tree Size\" with "},{"text":"\n [ ✎ ]","color":"gray","clickEvent":{"action":"suggest_command","value":"/scoreboard players set max_tree_size timber "},"hoverEvent":{"action":"show_text","value":"Default: 555"}},{"text":" (Currently: ","color":"gray"},{"score":{"name":"max_tree_size","objective":"timber"},"color":"gray"},{"text": ")","color":"gray"},{"text":"."},{"text":"\n ─ Or increase the gamerule \"maxCommandChainLength\" with "},{"text":"\n [ ✎ ]","color":"gray","clickEvent":{"action":"suggest_command","value":"/gamerule maxCommandChainLength "},"hoverEvent":{"action":"show_text","value":"Default: 65536"}},{"text":" (Currently: ","color":"gray"},{"nbt":"value","storage":"timber:max_command_chain_length","color":"gray"},{"text": ")","color":"gray"},{"text":"."}] -execute if score running timber matches 2 run schedule function timber:utils/reset 10t -execute if score running timber matches 2 run scoreboard players set running timber 3 - -# function ended successfully -execute if score running timber matches 1 run scoreboard players set running timber 0 \ No newline at end of file diff --git a/18-61/data/timber/function/utils/check_server.mcfunction b/18-61/data/timber/function/utils/check_server.mcfunction deleted file mode 100644 index 7d70177..0000000 --- a/18-61/data/timber/function/utils/check_server.mcfunction +++ /dev/null @@ -1,8 +0,0 @@ -# load until someone is in the server -execute unless entity @r run schedule function timber:utils/check_server 1s - -# Checking if the server uses a different jar from the vanilla one -execute unless score not_vanilla timber matches 1.. store success score not_vanilla timber run data get entity @r "Spigot.ticksLived" -execute unless score not_vanilla timber matches 1.. store success score not_vanilla timber run data get entity @r "Bukkit.updateLevel" -execute unless score not_vanilla timber matches 1.. store success score not_vanilla timber run data get entity @r "Paper.SpawnReason" -execute if score not_vanilla timber matches 1 run tellraw @a[tag=!noTimberWarning] ["",{"text":"Timber Datapack Warning: ","color":"red"},{"text":"Compatibility in non-vanilla servers is not guaranteed! For more information please visit official download site.","clickEvent":{"action":"open_url","value":"https://github.com/Moggla/Timber-Datapack"}}] \ No newline at end of file diff --git a/18-61/data/timber/function/validated.mcfunction b/18-61/data/timber/function/validated.mcfunction deleted file mode 100644 index b00df5e..0000000 --- a/18-61/data/timber/function/validated.mcfunction +++ /dev/null @@ -1,51 +0,0 @@ -# when full inventory fix -execute if score drop_loot timber matches 1.. store result score inventory timber run data get entity @s Inventory -execute if score drop_loot timber matches 1.. if entity @s[nbt={Inventory:[{Slot:100b}]}] run scoreboard players remove inventory timber 1 -execute if score drop_loot timber matches 1.. if entity @s[nbt={Inventory:[{Slot:101b}]}] run scoreboard players remove inventory timber 1 -execute if score drop_loot timber matches 1.. if entity @s[nbt={Inventory:[{Slot:102b}]}] run scoreboard players remove inventory timber 1 -execute if score drop_loot timber matches 1.. if entity @s[nbt={Inventory:[{Slot:103b}]}] run scoreboard players remove inventory timber 1 -execute if score drop_loot timber matches 1.. if entity @s[nbt={Inventory:[{Slot:-106b}]}] run scoreboard players remove inventory timber 1 - -# destroy blocks - # mark blocks that need to be destroyed - tag @e[type=minecraft:marker,tag=timber_log] add timber_destroy - execute if score destroy_leaves timber matches 1.. run tag @e[type=minecraft:marker,tag=timber_leaf] add timber_destroy - execute if score destroy_roots timber matches 1.. run tag @e[type=minecraft:marker,tag=timber_root] add timber_destroy - tag @e[type=minecraft:marker,tag=timber_stem] add timber_destroy - tag @e[type=minecraft:marker,tag=timber_cap] add timber_destroy - # api - execute unless score slow_chop timber matches 1.. at @e[type=minecraft:marker,tag=timber_log] run function #timber:api/break_log - execute unless score slow_chop timber matches 1.. at @e[type=minecraft:marker,tag=timber_leaf] run function #timber:api/break_leaf - execute unless score slow_chop timber matches 1.. at @e[type=minecraft:marker,tag=timber_root] run function #timber:api/break_root - execute unless score slow_chop timber matches 1.. at @e[type=minecraft:marker,tag=timber_stem] run function #timber:api/break_stem - execute unless score slow_chop timber matches 1.. at @e[type=minecraft:marker,tag=timber_cap] run function #timber:api/break_cap - # loot - execute unless score slow_chop timber matches 1.. unless score drop_loot timber matches 1.. at @e[type=minecraft:marker,tag=timber_destroy] run loot spawn ~ ~ ~ mine ~ ~ ~ mainhand - execute unless score slow_chop timber matches 1.. if score drop_loot timber matches 1.. unless score inventory timber matches 36.. at @e[type=minecraft:marker,tag=timber_destroy] run loot give @s mine ~ ~ ~ mainhand - execute unless score slow_chop timber matches 1.. if score drop_loot timber matches 1.. if score inventory timber matches 36.. at @e[type=minecraft:marker,tag=timber_destroy] run loot spawn ~ ~ ~ mine ~ ~ ~ mainhand - # destroy animation (doTileDrops can be changed, because datapack wouldn't get to this point otherwise) - gamerule doTileDrops false - execute unless score slow_chop timber matches 1.. at @e[type=minecraft:marker,tag=timber_destroy] run setblock ~ ~ ~ minecraft:air destroy - gamerule doTileDrops true - # remove destroy marker - execute unless score slow_chop timber matches 1.. run kill @e[type=minecraft:marker,tag=timber_destroy] - -# stop sound -execute if score stopsound timber matches 1.. as @a[distance=..20,tag=!global.ignore.gui] run function timber:utils/stopsound - -# save tool for slow chopping process -execute if score slow_chop timber matches 1.. at @e[type=minecraft:marker,tag=timber_tree,tag=!timber_slow_chop,distance=..7,sort=arbitrary,limit=1] run summon minecraft:armor_stand ~ -64 ~ {Silent:1b,Invulnerable:1b,Marker:1b,Invisible:1b,Tags:["timber_tool"],Rotation:[45f],Pose:{RightArm:[0f,270f,0f]}} -execute if score slow_chop timber matches 1.. at @e[type=minecraft:marker,tag=timber_tree,tag=!timber_slow_chop,distance=..7,sort=arbitrary,limit=1] run data modify entity @e[type=minecraft:armor_stand,tag=timber_tool,y=-64,distance=...1,sort=arbitrary,limit=1] HandItems[0] merge from entity @s SelectedItem -execute if score slow_chop timber matches 1.. run tag @e[type=minecraft:marker,tag=timber_tree,tag=!timber_slow_chop,distance=..7,sort=arbitrary,limit=1] add timber_slow_chop - -# tp hand-broken loot into inventory -execute if score drop_loot timber matches 1.. unless score inventory timber matches 36.. at @e[type=minecraft:marker,tag=timber_tree,tag=!timber_slow_chop,distance=..7,sort=arbitrary,limit=1] run function timber:utils/tp_item - -# wear out tool -execute if score wear_out timber matches 1.. unless score unbreakable timber matches 1 run function timber:tool/wear_out - -# break axe if worn out -execute if score wear_out timber matches 1.. unless score unbreakable timber matches 1 run function timber:tool/worn_out - -# give hunger effect -execute if score hunger timber matches 1.. run function timber:utils/hunger \ No newline at end of file diff --git a/18-81/data/timber/function/init.mcfunction b/18-81/data/timber/function/init.mcfunction deleted file mode 100644 index 3bfd75d..0000000 --- a/18-81/data/timber/function/init.mcfunction +++ /dev/null @@ -1,39 +0,0 @@ -# longer maxCommandChainLength -gamerule maxCommandChainLength 1000000000 - -# create scoreboards -scoreboard objectives add timber_w_axe minecraft.used:minecraft.wooden_axe -scoreboard objectives add timber_s_axe minecraft.used:minecraft.stone_axe -scoreboard objectives add timber_i_axe minecraft.used:minecraft.iron_axe -scoreboard objectives add timber_g_axe minecraft.used:minecraft.golden_axe -scoreboard objectives add timber_d_axe minecraft.used:minecraft.diamond_axe -scoreboard objectives add timber_n_axe minecraft.used:minecraft.netherite_axe -scoreboard objectives add timber dummy -scoreboard objectives add TimberToggle trigger {text:"Timber Toggle",color:"#724E21"} -scoreboard objectives add timber_disabled dummy -scoreboard objectives add timber_prsistent dummy - -# non-overriding default values -function timber:settings/default_values -function timber:tool/durability_max -scoreboard players set running timber 0 - -# start optional loops -execute if score plant_sapling timber matches 1.. run function timber:autoplant/loop -execute if score slow_chop timber matches 1.. run function timber:slow_chop/loop - -# check loottable compatibility of mushroom stem -setblock ~ 255 ~ minecraft:chest -setblock ~ 255 ~1 minecraft:mushroom_stem -loot insert ~ 255 ~ mine ~ 255 ~1 minecraft:diamond_axe -execute unless data block ~ 255 ~ Items[{id:"minecraft:knowledge_book",components:{"minecraft:custom_data":{Tags:["timber_location"]}}}] run tellraw @a[tag=!noTimberWarning] ["",{text:"Timber Datapack Warning: ",color:"red"},{text:"Incompatibility with the mushroom_stem loottable of another datapack!"}] -fill ~-1 255 ~-1 ~1 255 ~1 minecraft:air -execute store result score doTileDrops timber run gamerule doTileDrops -execute if score doTileDrops timber matches 0 run tellraw @a[tag=!noTimberWarning] ["",{text:"Timber Datapack Warning: ",color:"red"},{text:"This datapack only works when gamerule doTileDrops is set to true!"}] - -# Getting the game version and checking if it is compatible with the datapack https://minecraft.fandom.com/wiki/Data_version -execute store result score game_version timber run data get entity @r DataVersion -execute if score game_version timber matches ..3442 run tellraw @a[tag=!noTimberWarning] ["",{text:"Timber Datapack Warning: ",color:"red"},{text:"This datapack is not compatible with all the versions before 1.20!"}] - -# Checking if the server uses a different jar from the vanilla one -function timber:utils/check_server \ No newline at end of file diff --git a/18-81/data/timber/function/main.mcfunction b/18-81/data/timber/function/main.mcfunction deleted file mode 100644 index 02ef309..0000000 --- a/18-81/data/timber/function/main.mcfunction +++ /dev/null @@ -1,11 +0,0 @@ -# loop (not in minecraft:tick in order to stop it after uninstall) -schedule function timber:main 1t - -# if any kind of axe is used -execute as @a[predicate=timber:used_axe,tag=!global.ignore] at @s run function timber:utils/check_max_command_chain_length - -# toggle timber datapack per player -execute as @a[scores={TimberToggle=1..},tag=!global.ignore] run function timber:settings/toggle/timber - -# reset -scoreboard players enable @a TimberToggle \ No newline at end of file diff --git a/18-81/data/timber/function/settings/end_of_message.mcfunction b/18-81/data/timber/function/settings/end_of_message.mcfunction deleted file mode 100644 index 1d5e211..0000000 --- a/18-81/data/timber/function/settings/end_of_message.mcfunction +++ /dev/null @@ -1,3 +0,0 @@ -execute store result score sendCommandFeedback timber run gamerule sendCommandFeedback -execute if score sendCommandFeedback timber matches 1 run schedule function timber:settings/reset_gamerule 1t -gamerule sendCommandFeedback false \ No newline at end of file diff --git a/18-81/data/timber/function/settings/reset_gamerule.mcfunction b/18-81/data/timber/function/settings/reset_gamerule.mcfunction deleted file mode 100644 index b466492..0000000 --- a/18-81/data/timber/function/settings/reset_gamerule.mcfunction +++ /dev/null @@ -1 +0,0 @@ -gamerule sendCommandFeedback true \ No newline at end of file diff --git a/18-81/data/timber/function/settings/settings2.mcfunction b/18-81/data/timber/function/settings/settings2.mcfunction deleted file mode 100644 index 6f1a745..0000000 --- a/18-81/data/timber/function/settings/settings2.mcfunction +++ /dev/null @@ -1,55 +0,0 @@ -tellraw @s ["",{"text":" "}] -tellraw @s ["",{"text":" "}] -tellraw @s ["",{"text":" "}] -tellraw @s ["",{"text":" "}] - -tellraw @s ["",{"text":"\u00A7m ","color":"dark_gray"}] - -tellraw @s ["",{"text":" Timber "},{"text":"/","color":"gray"},{"text":" Global Settings "},{"text":" < ","clickEvent":{"action":"run_command","value":"/function timber:settings/settings1_click"}},{"text":"2/2"},{"text":" > ","clickEvent":{"action":"run_command","value":"/function timber:settings/settings2_click"}}] - -tellraw @s ["",{"text":"\u00A7m ","color":"dark_gray"}] - -execute if score wooden_axe timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/wooden_axe"}},{"text":" Enable Wooden Axe"}] -execute unless score wooden_axe timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/wooden_axe"}},{"text":" Enable Wooden Axe"}] - -execute if score stone_axe timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/stone_axe"}},{"text":" Enable Stone Axe"}] -execute unless score stone_axe timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/stone_axe"}},{"text":" Enable Stone Axe"}] - -execute if score iron_axe timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/iron_axe"}},{"text":" Enable Iron Axe"}] -execute unless score iron_axe timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/iron_axe"}},{"text":" Enable Iron Axe"}] - -execute if score golden_axe timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/golden_axe"}},{"text":" Enable Golden Axe"}] -execute unless score golden_axe timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/golden_axe"}},{"text":" Enable Golden Axe"}] - -execute if score diamond_axe timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/diamond_axe"}},{"text":" Enable Diamond Axe"}] -execute unless score diamond_axe timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/diamond_axe"}},{"text":" Enable Diamond Axe"}] - -execute if score netherite_axe timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/netherite_axe"}},{"text":" Enable Netherite Axe"}] -execute unless score netherite_axe timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/netherite_axe"}},{"text":" Enable Netherite Axe"}] - -execute if score chop_trees timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/chop_trees"}},{"text":" Chop Trees","hoverEvent":{"action":"show_text","value":"All kind of trees will get chopped."}}] -execute unless score chop_trees timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/chop_trees"}},{"text":" Chop Trees","hoverEvent":{"action":"show_text","value":"All kind of trees will get chopped."}}] - -execute if score chop_fungi timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/chop_fungi"}},{"text":" Chop Fungi","hoverEvent":{"action":"show_text","value":["",{"text":"Huge fungi (nether trees) as well as huge mushrooms will get chopped."},{"text":"\n\nUse it on your own risk!\nWhen enabled, this datapack can't differentiate between generated fungi and player-placed fungi and therefore can potentially destroy your buildings.","color":"dark_red"}]}}] -execute unless score chop_fungi timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/chop_fungi"}},{"text":" Chop Fungi","hoverEvent":{"action":"show_text","value":["",{"text":"Huge fungi (nether trees) as well as huge mushrooms will get chopped."},{"text":"\n\nUse it on your own risk!\nWhen enabled, this datapack can't differentiate between generated fungi and player-placed fungi and therefore can potentially destroy your buildings.","color":"dark_red"}]}}] - -execute if score persistent timber matches 1.. run tellraw @s ["",{"text":"[ ✔ ]","color":"green","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/persistent"}},{"text":" Destroy Player-Placed Trees","hoverEvent":{"action":"show_text","value":["",{"text":"Player-placed trees are trees made out of player-placed leaves (Look at leaf block with F3 debug screen: persistent = true)."},{"text":"\n\nUse it on your own risk!\nWhen enabled, this datapack can't differentiate between generated trees and player-placed trees anymore and therefore can potentially destroy your buildings.","color":"dark_red"}]}}] -execute unless score persistent timber matches 1.. run tellraw @s ["",{"text":"[ ❌ ]","color":"red","clickEvent":{"action":"run_command","value":"/function timber:settings/toggle/persistent"}},{"text":" Destroy Player-Placed Trees","hoverEvent":{"action":"show_text","value":["",{"text":"Player-placed trees are trees made out of player-placed leaves (Look at leaf block with F3 debug screen: persistent = true)."},{"text":"\n\nUse it on your own risk!\nWhen enabled, this datapack can't differentiate between generated trees and player-placed trees anymore and therefore can potentially destroy your buildings.","color":"dark_red"}]}}] - -execute if score blocks_per_chop timber matches 1 if score slow_chop timber matches 1.. run tellraw @s ["",{"text":"[ ✎ ]","color":"gray","clickEvent":{"action":"suggest_command","value":"/scoreboard players set blocks_per_chop timber "},"hoverEvent":{"action":"show_text","value":"Default: 1"}},{"text":" Set Amount of Destroyed Blocks per Chop"},{"text":" (Current: ","color":"gray"},{"score":{"name":"blocks_per_chop","objective":"timber"},"color":"gray"},{"text": ")","color":"gray"}] -execute unless score blocks_per_chop timber matches 1 if score slow_chop timber matches 1.. run tellraw @s ["",{"text":"[ ✎ ]","color":"gray","clickEvent":{"action":"suggest_command","value":"/scoreboard players set blocks_per_chop timber "},"hoverEvent":{"action":"show_text","value":"Default: 1"}},{"text":" Set Amount of Destroyed Blocks per Chop"},{"text":" (Current: ","color":"gray"},{"score":{"name":"blocks_per_chop","objective":"timber"},"color":"gray"},{"text": ")","color":"gray"}] -execute unless score slow_chop timber matches 1.. run tellraw @s ["",{"text":"[ ✎ ]","color":"dark_gray","hoverEvent":{"action":"show_text","value":"Not active when \"Chop Slowly\" is disabled."}},{"text":" Set Amount of Destroyed Blocks per Chop"}] - -execute if score time_between_chops timber matches 1 if score slow_chop timber matches 1.. run tellraw @s ["",{"text":"[ ✎ ]","color":"gray","clickEvent":{"action":"suggest_command","value":"/scoreboard players set time_between_chops timber "},"hoverEvent":{"action":"show_text","value":"Default: 1"}},{"text":" Set Time in Ticks Between Chops"},{"text":" (Current: ","color":"gray"},{"score":{"name":"time_between_chops","objective":"timber"},"color":"gray"},{"text": ")","color":"gray"}] -execute unless score time_between_chops timber matches 1 if score slow_chop timber matches 1.. run tellraw @s ["",{"text":"[ ✎ ]","color":"gray","clickEvent":{"action":"suggest_command","value":"/scoreboard players set time_between_chops timber "},"hoverEvent":{"action":"show_text","value":"Default: 1"}},{"text":" Set Time in Ticks Between Chops"},{"text":" (Current: ","color":"gray"},{"score":{"name":"time_between_chops","objective":"timber"},"color":"gray"},{"text": ")","color":"gray"}] -execute unless score slow_chop timber matches 1.. run tellraw @s ["",{"text":"[ ✎ ]","color":"dark_gray","hoverEvent":{"action":"show_text","value":"Not active when \"Chop Slowly\" is disabled."}},{"text":" Set Time in Ticks Between Chops"}] - -execute if score max_tree_size timber matches 555 run tellraw @s ["",{"text":"[ ✎ ]","color":"gray","clickEvent":{"action":"suggest_command","value":"/scoreboard players set max_tree_size timber "},"hoverEvent":{"action":"show_text","value":"Default: 555"}},{"text":" Set Max. Tree Size","hoverEvent":{"action":"show_text","value":"Trees bigger than this set value won't get further processed."}},{"text":" (Currently: ","color":"gray"},{"score":{"name":"max_tree_size","objective":"timber"},"color":"gray"},{"text": ")","color":"gray"}] -execute unless score max_tree_size timber matches 555 run tellraw @s ["",{"text":"[ ✎ ]","color":"gray","clickEvent":{"action":"suggest_command","value":"/scoreboard players set max_tree_size timber "},"hoverEvent":{"action":"show_text","value":"Default: 555"}},{"text":" Set Max. Tree Size","hoverEvent":{"action":"show_text","value":"Trees bigger than this set value won't get further processed."}},{"text":" (Currently: ","color":"gray"},{"score":{"name":"max_tree_size","objective":"timber"},"color":"gray"},{"text": ")","color":"gray"}] - -execute if score min_leaves_found timber matches 5 run tellraw @s ["",{"text":"[ ✎ ]","color":"gray","clickEvent":{"action":"suggest_command","value":"/scoreboard players set min_leaves_found timber "},"hoverEvent":{"action":"show_text","value":"Default: 5"}},{"text":" Set Min. Leaves to Valid a Tree","hoverEvent":{"action":"show_text","value":"This datapack considers a tree only if it has this set value amount of naturally generated leaves at its trunk.\nFungi will get checked by the amount of cap blocks and additionally a 3×3 big cross on top of the stem."}},{"text":" (Current: ","color":"gray"},{"score":{"name":"min_leaves_found","objective":"timber"},"color":"gray"},{"text": ")","color":"gray"}] -execute unless score min_leaves_found timber matches 5 run tellraw @s ["",{"text":"[ ✎ ]","color":"gray","clickEvent":{"action":"suggest_command","value":"/scoreboard players set min_leaves_found timber "},"hoverEvent":{"action":"show_text","value":"Default: 5"}},{"text":" Set Min. Leaves to Valid a Tree","hoverEvent":{"action":"show_text","value":"This datapack considers a tree only if it has this set value amount of naturally generated leaves at its trunk.\nFungi will get checked by the amount of cap blocks and additionally a 3×3 big cross on top of the stem."}},{"text":" (Current: ","color":"gray"},{"score":{"name":"min_leaves_found","objective":"timber"},"color":"gray"},{"text": ")","color":"gray"}] - -tellraw @s ["",{"text":"\u00A7m ","color":"dark_gray"}] - -function timber:settings/end_of_message \ No newline at end of file diff --git a/18-81/data/timber/function/slow_chop/destroy_loop.mcfunction b/18-81/data/timber/function/slow_chop/destroy_loop.mcfunction deleted file mode 100644 index db43fbd..0000000 --- a/18-81/data/timber/function/slow_chop/destroy_loop.mcfunction +++ /dev/null @@ -1,25 +0,0 @@ -# loop -$schedule function timber:slow_chop/loop $(time_between_chops)t replace - -# api -$execute at @e[type=minecraft:marker,tag=timber_slow_chop] as @e[type=minecraft:marker,tag=timber_destroy,sort=nearest,limit=$(blocks_per_chop)] at @s run function timber:slow_chop/api - -# destroy n block -$execute at @e[type=minecraft:marker,tag=timber_slow_chop] as @e[type=minecraft:armor_stand,tag=timber_tool,y=-64,distance=...1,sort=arbitrary,limit=1] at @e[type=minecraft:marker,tag=timber_destroy,sort=nearest,limit=$(blocks_per_chop)] run loot spawn ~ ~ ~ mine ~ ~ ~ mainhand - -# destroy animation (doTileDrops can be changed, because datapack wouldn't get to this point otherwise) -gamerule doTileDrops false -$execute at @e[type=minecraft:marker,tag=timber_slow_chop] at @e[type=minecraft:marker,tag=timber_destroy,sort=nearest,limit=$(blocks_per_chop)] run setblock ~ ~ ~ minecraft:air destroy -gamerule doTileDrops true - -# stop sound -$execute at @e[type=minecraft:marker,tag=timber_slow_chop] if score stopsound timber matches 1.. at @e[type=minecraft:marker,tag=timber_destroy,sort=nearest,limit=$(blocks_per_chop)] as @a[distance=..20,tag=!global.ignore.gui] run function timber:utils/stopsound - -# delete location item -$execute at @e[type=minecraft:marker,tag=timber_slow_chop] at @e[type=minecraft:marker,tag=timber_destroy,sort=nearest,limit=$(blocks_per_chop)] run kill @e[type=minecraft:item,distance=..1,nbt={Item:{components:{"minecraft:custom_data":{Tags:["timber_location"]}}}}] - -# remove marker of processed blocks -$execute at @e[type=minecraft:marker,tag=timber_slow_chop] run kill @e[type=minecraft:marker,tag=timber_destroy,sort=nearest,limit=$(blocks_per_chop)] - -# delete tree markers after chopping -execute unless entity @e[type=minecraft:marker,tag=timber_destroy] run function timber:slow_chop/delete_markers \ No newline at end of file diff --git a/18-81/data/timber/function/utils/check_max_command_chain_length.mcfunction b/18-81/data/timber/function/utils/check_max_command_chain_length.mcfunction deleted file mode 100644 index 6f32c3e..0000000 --- a/18-81/data/timber/function/utils/check_max_command_chain_length.mcfunction +++ /dev/null @@ -1,13 +0,0 @@ -# function didn't end successfully -execute if score running timber matches 1 run scoreboard players set running timber 2 -# normal behavior -execute if score running timber matches 0 run scoreboard players set running timber 1 -execute if score running timber matches 1 run function timber:utils/used_axe -# function didn't end successfully -execute if score running timber matches 2 run execute store result storage timber:max_command_chain_length value int 1 run gamerule maxCommandChainLength -execute if score running timber matches 2 run tellraw @a[tag=!noTimberWarning] ["",{"text":"Timber Datapack Warning: ","color":"red"},{"text":"The datapack wasn't able to finish its functionality to work properly!"},{"text":"\n ─ Please either decrease setting \"Max. Tree Size\" with "},{"text":"\n [ ✎ ]","color":"gray","clickEvent":{"action":"suggest_command","value":"/scoreboard players set max_tree_size timber "},"hoverEvent":{"action":"show_text","value":"Default: 555"}},{"text":" (Currently: ","color":"gray"},{"score":{"name":"max_tree_size","objective":"timber"},"color":"gray"},{"text": ")","color":"gray"},{"text":"."},{"text":"\n ─ Or increase the gamerule \"maxCommandChainLength\" with "},{"text":"\n [ ✎ ]","color":"gray","clickEvent":{"action":"suggest_command","value":"/gamerule maxCommandChainLength "},"hoverEvent":{"action":"show_text","value":"Default: 65536"}},{"text":" (Currently: ","color":"gray"},{"nbt":"value","storage":"timber:max_command_chain_length","color":"gray"},{"text": ")","color":"gray"},{"text":"."}] -execute if score running timber matches 2 run schedule function timber:utils/reset 10t -execute if score running timber matches 2 run scoreboard players set running timber 3 - -# function ended successfully -execute if score running timber matches 1 run scoreboard players set running timber 0 \ No newline at end of file diff --git a/18-81/data/timber/function/utils/check_max_command_sequence_length.mcfunction b/18-81/data/timber/function/utils/check_max_command_sequence_length.mcfunction deleted file mode 100644 index e69de29..0000000 diff --git a/18-81/data/timber/function/validated.mcfunction b/18-81/data/timber/function/validated.mcfunction deleted file mode 100644 index 9617429..0000000 --- a/18-81/data/timber/function/validated.mcfunction +++ /dev/null @@ -1,51 +0,0 @@ -# when full inventory fix -execute if score drop_loot timber matches 1.. store result score inventory timber run data get entity @s Inventory -execute if score drop_loot timber matches 1.. if entity @s[nbt={Inventory:[{Slot:100b}]}] run scoreboard players remove inventory timber 1 -execute if score drop_loot timber matches 1.. if entity @s[nbt={Inventory:[{Slot:101b}]}] run scoreboard players remove inventory timber 1 -execute if score drop_loot timber matches 1.. if entity @s[nbt={Inventory:[{Slot:102b}]}] run scoreboard players remove inventory timber 1 -execute if score drop_loot timber matches 1.. if entity @s[nbt={Inventory:[{Slot:103b}]}] run scoreboard players remove inventory timber 1 -execute if score drop_loot timber matches 1.. if entity @s[nbt={Inventory:[{Slot:-106b}]}] run scoreboard players remove inventory timber 1 - -# destroy blocks - # mark blocks that need to be destroyed - tag @e[type=minecraft:marker,tag=timber_log] add timber_destroy - execute if score destroy_leaves timber matches 1.. run tag @e[type=minecraft:marker,tag=timber_leaf] add timber_destroy - execute if score destroy_roots timber matches 1.. run tag @e[type=minecraft:marker,tag=timber_root] add timber_destroy - tag @e[type=minecraft:marker,tag=timber_stem] add timber_destroy - tag @e[type=minecraft:marker,tag=timber_cap] add timber_destroy - # api - execute unless score slow_chop timber matches 1.. at @e[type=minecraft:marker,tag=timber_log] run function #timber:api/break_log - execute unless score slow_chop timber matches 1.. at @e[type=minecraft:marker,tag=timber_leaf] run function #timber:api/break_leaf - execute unless score slow_chop timber matches 1.. at @e[type=minecraft:marker,tag=timber_root] run function #timber:api/break_root - execute unless score slow_chop timber matches 1.. at @e[type=minecraft:marker,tag=timber_stem] run function #timber:api/break_stem - execute unless score slow_chop timber matches 1.. at @e[type=minecraft:marker,tag=timber_cap] run function #timber:api/break_cap - # loot - execute unless score slow_chop timber matches 1.. unless score drop_loot timber matches 1.. at @e[type=minecraft:marker,tag=timber_destroy] run loot spawn ~ ~ ~ mine ~ ~ ~ mainhand - execute unless score slow_chop timber matches 1.. if score drop_loot timber matches 1.. unless score inventory timber matches 36.. at @e[type=minecraft:marker,tag=timber_destroy] run loot give @s mine ~ ~ ~ mainhand - execute unless score slow_chop timber matches 1.. if score drop_loot timber matches 1.. if score inventory timber matches 36.. at @e[type=minecraft:marker,tag=timber_destroy] run loot spawn ~ ~ ~ mine ~ ~ ~ mainhand - # destroy animation (doTileDrops can be changed, because datapack wouldn't get to this point otherwise) - gamerule doTileDrops false - execute unless score slow_chop timber matches 1.. at @e[type=minecraft:marker,tag=timber_destroy] run setblock ~ ~ ~ minecraft:air destroy - gamerule doTileDrops true - # remove destroy marker - execute unless score slow_chop timber matches 1.. run kill @e[type=minecraft:marker,tag=timber_destroy] - -# stop sound -execute if score stopsound timber matches 1.. as @a[distance=..20,tag=!global.ignore.gui] run function timber:utils/stopsound - -# save tool for slow chopping process -execute if score slow_chop timber matches 1.. at @e[type=minecraft:marker,tag=timber_tree,tag=!timber_slow_chop,distance=..7,sort=arbitrary,limit=1] run summon minecraft:armor_stand ~ -64 ~ {Silent:1b,Invulnerable:1b,Marker:1b,Invisible:1b,Tags:["timber_tool"],Rotation:[45f],Pose:{RightArm:[0f,270f,0f]}} -execute if score slow_chop timber matches 1.. at @e[type=minecraft:marker,tag=timber_tree,tag=!timber_slow_chop,distance=..7,sort=arbitrary,limit=1] run data modify entity @e[type=minecraft:armor_stand,tag=timber_tool,y=-64,distance=...1,sort=arbitrary,limit=1] equipment.mainhand merge from entity @s SelectedItem -execute if score slow_chop timber matches 1.. run tag @e[type=minecraft:marker,tag=timber_tree,tag=!timber_slow_chop,distance=..7,sort=arbitrary,limit=1] add timber_slow_chop - -# tp hand-broken loot into inventory -execute if score drop_loot timber matches 1.. unless score inventory timber matches 36.. at @e[type=minecraft:marker,tag=timber_tree,tag=!timber_slow_chop,distance=..7,sort=arbitrary,limit=1] run function timber:utils/tp_item - -# wear out tool -execute if score wear_out timber matches 1.. unless score unbreakable timber matches 1 run function timber:tool/wear_out - -# break axe if worn out -execute if score wear_out timber matches 1.. unless score unbreakable timber matches 1 run function timber:tool/worn_out - -# give hunger effect -execute if score hunger timber matches 1.. run function timber:utils/hunger \ No newline at end of file diff --git a/18-81/data/timber/item_modifier/wear_out.json b/18-81/data/timber/item_modifier/wear_out.json deleted file mode 100644 index 63fd89d..0000000 --- a/18-81/data/timber/item_modifier/wear_out.json +++ /dev/null @@ -1,152 +0,0 @@ -[ - { - "function": "minecraft:set_damage", - "damage": { - "type": "minecraft:score", - "target": { - "type": "minecraft:fixed", - "name": "damage" - }, - "score": "timber", - "scale": 0.01694915254237288 - }, - "conditions": [ - { - "condition": "minecraft:entity_properties", - "entity": "this", - "predicate": { - "equipment": { - "mainhand": { - "items": "minecraft:wooden_axe" - } - } - } - } - ] - }, - { - "function": "minecraft:set_damage", - "damage": { - "type": "minecraft:score", - "target": { - "type": "minecraft:fixed", - "name": "damage" - }, - "score": "timber", - "scale": 0.007633587786259542 - }, - "conditions": [ - { - "condition": "minecraft:entity_properties", - "entity": "this", - "predicate": { - "equipment": { - "mainhand": { - "items": "minecraft:stone_axe" - } - } - } - } - ] - }, - { - "function": "minecraft:set_damage", - "damage": { - "type": "minecraft:score", - "target": { - "type": "minecraft:fixed", - "name": "damage" - }, - "score": "timber", - "scale": 0.004 - }, - "conditions": [ - { - "condition": "minecraft:entity_properties", - "entity": "this", - "predicate": { - "equipment": { - "mainhand": { - "items": "minecraft:iron_axe" - } - } - } - } - ] - }, - { - "function": "minecraft:set_damage", - "damage": { - "type": "minecraft:score", - "target": { - "type": "minecraft:fixed", - "name": "damage" - }, - "score": "timber", - "scale": 0.03125 - }, - "conditions": [ - { - "condition": "minecraft:entity_properties", - "entity": "this", - "predicate": { - "equipment": { - "mainhand": { - "items": "minecraft:golden_axe" - } - } - } - } - ] - }, - { - "function": "minecraft:set_damage", - "damage": { - "type": "minecraft:score", - "target": { - "type": "minecraft:fixed", - "name": "damage" - }, - "score": "timber", - "scale": 0.0006393861892583121 - }, - "conditions": [ - { - "condition": "minecraft:entity_properties", - "entity": "this", - "predicate": { - "equipment": { - "mainhand": { - "items": "minecraft:diamond_axe" - } - } - } - } - ] - }, - { - "function": "minecraft:set_damage", - "damage": { - "type": "minecraft:score", - "target": { - "type": "minecraft:fixed", - "name": "damage" - }, - "score": "timber", - "scale": 0.0004923682914820286 - }, - "conditions": [ - { - "condition": "minecraft:entity_properties", - "entity": "this", - "predicate": { - "equipment": { - "mainhand": { - "items": "minecraft:netherite_axe" - } - } - } - } - ] - } -] \ No newline at end of file diff --git a/18-81/data/timber/predicate/used_axe.json b/18-81/data/timber/predicate/used_axe.json deleted file mode 100644 index ac00bc4..0000000 --- a/18-81/data/timber/predicate/used_axe.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "condition": "minecraft:any_of", - "terms": [ - { - "condition": "minecraft:entity_scores", - "entity": "this", - "scores": { - "timber_w_axe": { - "min": 1 - } - } - }, - { - "condition": "minecraft:entity_scores", - "entity": "this", - "scores": { - "timber_s_axe": { - "min": 1 - } - } - }, - { - "condition": "minecraft:entity_scores", - "entity": "this", - "scores": { - "timber_i_axe": { - "min": 1 - } - } - }, - { - "condition": "minecraft:entity_scores", - "entity": "this", - "scores": { - "timber_g_axe": { - "min": 1 - } - } - }, - { - "condition": "minecraft:entity_scores", - "entity": "this", - "scores": { - "timber_d_axe": { - "min": 1 - } - } - }, - { - "condition": "minecraft:entity_scores", - "entity": "this", - "scores": { - "timber_n_axe": { - "min": 1 - } - } - } - ] -} \ No newline at end of file diff --git a/18-81/data/timber/tags/item/axes.json b/18-81/data/timber/tags/item/axes.json deleted file mode 100644 index c496215..0000000 --- a/18-81/data/timber/tags/item/axes.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "values":[ - "minecraft:wooden_axe", - "minecraft:stone_axe", - "minecraft:iron_axe", - "minecraft:golden_axe", - "minecraft:diamond_axe", - "minecraft:netherite_axe" - ] -} \ No newline at end of file diff --git a/82-88/data/timber/function/init.mcfunction b/82-88/data/timber/function/init.mcfunction deleted file mode 100644 index 9bfeada..0000000 --- a/82-88/data/timber/function/init.mcfunction +++ /dev/null @@ -1,40 +0,0 @@ -# longer maxCommandChainLength -gamerule maxCommandChainLength 1000000000 - -# create scoreboards -scoreboard objectives add timber_w_axe minecraft.used:minecraft.wooden_axe -scoreboard objectives add timber_s_axe minecraft.used:minecraft.stone_axe -scoreboard objectives add timber_c_axe minecraft.used:minecraft.copper_axe -scoreboard objectives add timber_i_axe minecraft.used:minecraft.iron_axe -scoreboard objectives add timber_g_axe minecraft.used:minecraft.golden_axe -scoreboard objectives add timber_d_axe minecraft.used:minecraft.diamond_axe -scoreboard objectives add timber_n_axe minecraft.used:minecraft.netherite_axe -scoreboard objectives add timber dummy -scoreboard objectives add TimberToggle trigger {text:"Timber Toggle",color:"#724E21"} -scoreboard objectives add timber_disabled dummy -scoreboard objectives add timber_prsistent dummy - -# non-overriding default values -function timber:settings/default_values -function timber:tool/durability_max -scoreboard players set running timber 0 - -# start optional loops -execute if score plant_sapling timber matches 1.. run function timber:autoplant/loop -execute if score slow_chop timber matches 1.. run function timber:slow_chop/loop - -# check loottable compatibility of mushroom stem -setblock ~ 255 ~ minecraft:chest -setblock ~ 255 ~1 minecraft:mushroom_stem -loot insert ~ 255 ~ mine ~ 255 ~1 minecraft:diamond_axe -execute unless data block ~ 255 ~ Items[{id:"minecraft:knowledge_book",components:{"minecraft:custom_data":{Tags:["timber_location"]}}}] run tellraw @a[tag=!noTimberWarning] ["",{text:"Timber Datapack Warning: ",color:"red"},{text:"Incompatibility with the mushroom_stem loottable of another datapack!"}] -fill ~-1 255 ~-1 ~1 255 ~1 minecraft:air -execute store result score doTileDrops timber run gamerule doTileDrops -execute if score doTileDrops timber matches 0 run tellraw @a[tag=!noTimberWarning] ["",{text:"Timber Datapack Warning: ",color:"red"},{text:"This datapack only works when gamerule doTileDrops is set to true!"}] - -# Getting the game version and checking if it is compatible with the datapack https://minecraft.fandom.com/wiki/Data_version -execute store result score game_version timber run data get entity @r DataVersion -execute if score game_version timber matches ..3442 run tellraw @a[tag=!noTimberWarning] ["",{text:"Timber Datapack Warning: ",color:"red"},{text:"This datapack is not compatible with all the versions before 1.20!"}] - -# Checking if the server uses a different jar from the vanilla one -function timber:utils/check_server \ No newline at end of file diff --git a/82-88/data/timber/function/main.mcfunction b/82-88/data/timber/function/main.mcfunction deleted file mode 100644 index 02ef309..0000000 --- a/82-88/data/timber/function/main.mcfunction +++ /dev/null @@ -1,11 +0,0 @@ -# loop (not in minecraft:tick in order to stop it after uninstall) -schedule function timber:main 1t - -# if any kind of axe is used -execute as @a[predicate=timber:used_axe,tag=!global.ignore] at @s run function timber:utils/check_max_command_chain_length - -# toggle timber datapack per player -execute as @a[scores={TimberToggle=1..},tag=!global.ignore] run function timber:settings/toggle/timber - -# reset -scoreboard players enable @a TimberToggle \ No newline at end of file diff --git a/82-88/data/timber/function/settings/end_of_message.mcfunction b/82-88/data/timber/function/settings/end_of_message.mcfunction deleted file mode 100644 index 1d5e211..0000000 --- a/82-88/data/timber/function/settings/end_of_message.mcfunction +++ /dev/null @@ -1,3 +0,0 @@ -execute store result score sendCommandFeedback timber run gamerule sendCommandFeedback -execute if score sendCommandFeedback timber matches 1 run schedule function timber:settings/reset_gamerule 1t -gamerule sendCommandFeedback false \ No newline at end of file diff --git a/82-88/data/timber/function/settings/reset_gamerule.mcfunction b/82-88/data/timber/function/settings/reset_gamerule.mcfunction deleted file mode 100644 index b466492..0000000 --- a/82-88/data/timber/function/settings/reset_gamerule.mcfunction +++ /dev/null @@ -1 +0,0 @@ -gamerule sendCommandFeedback true \ No newline at end of file diff --git a/82-88/data/timber/function/slow_chop/destroy_loop.mcfunction b/82-88/data/timber/function/slow_chop/destroy_loop.mcfunction deleted file mode 100644 index db43fbd..0000000 --- a/82-88/data/timber/function/slow_chop/destroy_loop.mcfunction +++ /dev/null @@ -1,25 +0,0 @@ -# loop -$schedule function timber:slow_chop/loop $(time_between_chops)t replace - -# api -$execute at @e[type=minecraft:marker,tag=timber_slow_chop] as @e[type=minecraft:marker,tag=timber_destroy,sort=nearest,limit=$(blocks_per_chop)] at @s run function timber:slow_chop/api - -# destroy n block -$execute at @e[type=minecraft:marker,tag=timber_slow_chop] as @e[type=minecraft:armor_stand,tag=timber_tool,y=-64,distance=...1,sort=arbitrary,limit=1] at @e[type=minecraft:marker,tag=timber_destroy,sort=nearest,limit=$(blocks_per_chop)] run loot spawn ~ ~ ~ mine ~ ~ ~ mainhand - -# destroy animation (doTileDrops can be changed, because datapack wouldn't get to this point otherwise) -gamerule doTileDrops false -$execute at @e[type=minecraft:marker,tag=timber_slow_chop] at @e[type=minecraft:marker,tag=timber_destroy,sort=nearest,limit=$(blocks_per_chop)] run setblock ~ ~ ~ minecraft:air destroy -gamerule doTileDrops true - -# stop sound -$execute at @e[type=minecraft:marker,tag=timber_slow_chop] if score stopsound timber matches 1.. at @e[type=minecraft:marker,tag=timber_destroy,sort=nearest,limit=$(blocks_per_chop)] as @a[distance=..20,tag=!global.ignore.gui] run function timber:utils/stopsound - -# delete location item -$execute at @e[type=minecraft:marker,tag=timber_slow_chop] at @e[type=minecraft:marker,tag=timber_destroy,sort=nearest,limit=$(blocks_per_chop)] run kill @e[type=minecraft:item,distance=..1,nbt={Item:{components:{"minecraft:custom_data":{Tags:["timber_location"]}}}}] - -# remove marker of processed blocks -$execute at @e[type=minecraft:marker,tag=timber_slow_chop] run kill @e[type=minecraft:marker,tag=timber_destroy,sort=nearest,limit=$(blocks_per_chop)] - -# delete tree markers after chopping -execute unless entity @e[type=minecraft:marker,tag=timber_destroy] run function timber:slow_chop/delete_markers \ No newline at end of file diff --git a/82-88/data/timber/function/utils/check_max_command_chain_length.mcfunction b/82-88/data/timber/function/utils/check_max_command_chain_length.mcfunction deleted file mode 100644 index 6f32c3e..0000000 --- a/82-88/data/timber/function/utils/check_max_command_chain_length.mcfunction +++ /dev/null @@ -1,13 +0,0 @@ -# function didn't end successfully -execute if score running timber matches 1 run scoreboard players set running timber 2 -# normal behavior -execute if score running timber matches 0 run scoreboard players set running timber 1 -execute if score running timber matches 1 run function timber:utils/used_axe -# function didn't end successfully -execute if score running timber matches 2 run execute store result storage timber:max_command_chain_length value int 1 run gamerule maxCommandChainLength -execute if score running timber matches 2 run tellraw @a[tag=!noTimberWarning] ["",{"text":"Timber Datapack Warning: ","color":"red"},{"text":"The datapack wasn't able to finish its functionality to work properly!"},{"text":"\n ─ Please either decrease setting \"Max. Tree Size\" with "},{"text":"\n [ ✎ ]","color":"gray","clickEvent":{"action":"suggest_command","value":"/scoreboard players set max_tree_size timber "},"hoverEvent":{"action":"show_text","value":"Default: 555"}},{"text":" (Currently: ","color":"gray"},{"score":{"name":"max_tree_size","objective":"timber"},"color":"gray"},{"text": ")","color":"gray"},{"text":"."},{"text":"\n ─ Or increase the gamerule \"maxCommandChainLength\" with "},{"text":"\n [ ✎ ]","color":"gray","clickEvent":{"action":"suggest_command","value":"/gamerule maxCommandChainLength "},"hoverEvent":{"action":"show_text","value":"Default: 65536"}},{"text":" (Currently: ","color":"gray"},{"nbt":"value","storage":"timber:max_command_chain_length","color":"gray"},{"text": ")","color":"gray"},{"text":"."}] -execute if score running timber matches 2 run schedule function timber:utils/reset 10t -execute if score running timber matches 2 run scoreboard players set running timber 3 - -# function ended successfully -execute if score running timber matches 1 run scoreboard players set running timber 0 \ No newline at end of file diff --git a/82-88/data/timber/function/utils/check_max_command_sequence_length.mcfunction b/82-88/data/timber/function/utils/check_max_command_sequence_length.mcfunction deleted file mode 100644 index e69de29..0000000 diff --git a/82-88/data/timber/function/validated.mcfunction b/82-88/data/timber/function/validated.mcfunction deleted file mode 100644 index 9617429..0000000 --- a/82-88/data/timber/function/validated.mcfunction +++ /dev/null @@ -1,51 +0,0 @@ -# when full inventory fix -execute if score drop_loot timber matches 1.. store result score inventory timber run data get entity @s Inventory -execute if score drop_loot timber matches 1.. if entity @s[nbt={Inventory:[{Slot:100b}]}] run scoreboard players remove inventory timber 1 -execute if score drop_loot timber matches 1.. if entity @s[nbt={Inventory:[{Slot:101b}]}] run scoreboard players remove inventory timber 1 -execute if score drop_loot timber matches 1.. if entity @s[nbt={Inventory:[{Slot:102b}]}] run scoreboard players remove inventory timber 1 -execute if score drop_loot timber matches 1.. if entity @s[nbt={Inventory:[{Slot:103b}]}] run scoreboard players remove inventory timber 1 -execute if score drop_loot timber matches 1.. if entity @s[nbt={Inventory:[{Slot:-106b}]}] run scoreboard players remove inventory timber 1 - -# destroy blocks - # mark blocks that need to be destroyed - tag @e[type=minecraft:marker,tag=timber_log] add timber_destroy - execute if score destroy_leaves timber matches 1.. run tag @e[type=minecraft:marker,tag=timber_leaf] add timber_destroy - execute if score destroy_roots timber matches 1.. run tag @e[type=minecraft:marker,tag=timber_root] add timber_destroy - tag @e[type=minecraft:marker,tag=timber_stem] add timber_destroy - tag @e[type=minecraft:marker,tag=timber_cap] add timber_destroy - # api - execute unless score slow_chop timber matches 1.. at @e[type=minecraft:marker,tag=timber_log] run function #timber:api/break_log - execute unless score slow_chop timber matches 1.. at @e[type=minecraft:marker,tag=timber_leaf] run function #timber:api/break_leaf - execute unless score slow_chop timber matches 1.. at @e[type=minecraft:marker,tag=timber_root] run function #timber:api/break_root - execute unless score slow_chop timber matches 1.. at @e[type=minecraft:marker,tag=timber_stem] run function #timber:api/break_stem - execute unless score slow_chop timber matches 1.. at @e[type=minecraft:marker,tag=timber_cap] run function #timber:api/break_cap - # loot - execute unless score slow_chop timber matches 1.. unless score drop_loot timber matches 1.. at @e[type=minecraft:marker,tag=timber_destroy] run loot spawn ~ ~ ~ mine ~ ~ ~ mainhand - execute unless score slow_chop timber matches 1.. if score drop_loot timber matches 1.. unless score inventory timber matches 36.. at @e[type=minecraft:marker,tag=timber_destroy] run loot give @s mine ~ ~ ~ mainhand - execute unless score slow_chop timber matches 1.. if score drop_loot timber matches 1.. if score inventory timber matches 36.. at @e[type=minecraft:marker,tag=timber_destroy] run loot spawn ~ ~ ~ mine ~ ~ ~ mainhand - # destroy animation (doTileDrops can be changed, because datapack wouldn't get to this point otherwise) - gamerule doTileDrops false - execute unless score slow_chop timber matches 1.. at @e[type=minecraft:marker,tag=timber_destroy] run setblock ~ ~ ~ minecraft:air destroy - gamerule doTileDrops true - # remove destroy marker - execute unless score slow_chop timber matches 1.. run kill @e[type=minecraft:marker,tag=timber_destroy] - -# stop sound -execute if score stopsound timber matches 1.. as @a[distance=..20,tag=!global.ignore.gui] run function timber:utils/stopsound - -# save tool for slow chopping process -execute if score slow_chop timber matches 1.. at @e[type=minecraft:marker,tag=timber_tree,tag=!timber_slow_chop,distance=..7,sort=arbitrary,limit=1] run summon minecraft:armor_stand ~ -64 ~ {Silent:1b,Invulnerable:1b,Marker:1b,Invisible:1b,Tags:["timber_tool"],Rotation:[45f],Pose:{RightArm:[0f,270f,0f]}} -execute if score slow_chop timber matches 1.. at @e[type=minecraft:marker,tag=timber_tree,tag=!timber_slow_chop,distance=..7,sort=arbitrary,limit=1] run data modify entity @e[type=minecraft:armor_stand,tag=timber_tool,y=-64,distance=...1,sort=arbitrary,limit=1] equipment.mainhand merge from entity @s SelectedItem -execute if score slow_chop timber matches 1.. run tag @e[type=minecraft:marker,tag=timber_tree,tag=!timber_slow_chop,distance=..7,sort=arbitrary,limit=1] add timber_slow_chop - -# tp hand-broken loot into inventory -execute if score drop_loot timber matches 1.. unless score inventory timber matches 36.. at @e[type=minecraft:marker,tag=timber_tree,tag=!timber_slow_chop,distance=..7,sort=arbitrary,limit=1] run function timber:utils/tp_item - -# wear out tool -execute if score wear_out timber matches 1.. unless score unbreakable timber matches 1 run function timber:tool/wear_out - -# break axe if worn out -execute if score wear_out timber matches 1.. unless score unbreakable timber matches 1 run function timber:tool/worn_out - -# give hunger effect -execute if score hunger timber matches 1.. run function timber:utils/hunger \ No newline at end of file diff --git a/data/timber/function/init.mcfunction b/data/timber/function/init.mcfunction index 6cf85e8..83b4fdb 100644 --- a/data/timber/function/init.mcfunction +++ b/data/timber/function/init.mcfunction @@ -34,7 +34,7 @@ execute if score block_drops timber matches 0 run tellraw @a[tag=!noTimberWarnin # Getting the game version and checking if it is compatible with the datapack https://minecraft.fandom.com/wiki/Data_version execute store result score game_version timber run data get entity @r DataVersion -execute if score game_version timber matches ..3442 run tellraw @a[tag=!noTimberWarning] ["",{text:"Timber Datapack Warning: ",color:"red"},{text:"This datapack is not compatible with all the versions before 1.20!"}] +execute if score game_version timber matches ..4785 run tellraw @a[tag=!noTimberWarning] ["",{text:"Timber Datapack Warning: ",color:"red"},{text:"This datapack is not compatible with all the versions before 26.1!"}] # Checking if the server uses a different jar from the vanilla one function timber:utils/check_server diff --git a/pack.mcmeta b/pack.mcmeta index b436450..86132f0 100644 --- a/pack.mcmeta +++ b/pack.mcmeta @@ -2,18 +2,12 @@ "pack": { "description": "Timber Datapack allows you to instantly chop down a tree just by breaking one log", "pack_format": 121.0, - "supported_formats": [18, 121.0], - "min_format": 18, + "min_format": 101.0, "max_format": 121.0 }, "overlays": { "entries": [ - {"formats": [121, 121], "directory": "121", "min_format": 121, "max_format": 121}, - {"formats": [82, 88], "directory": "82-88", "min_format": 82, "max_format": 88}, - {"formats": [18, 81], "directory": "18-81", "min_format": 18, "max_format": 81}, - {"formats": [18, 61], "directory": "18-61"}, - {"formats": [18, 41], "directory": "18-41"}, - {"formats": [18, 26], "directory": "18-26"} + {"formats": [121, 121], "directory": "121", "min_format": 121, "max_format": 121} ] } -} \ No newline at end of file +} From 411a90baaea180104d53fa292b97294e317aaf40 Mon Sep 17 00:00:00 2001 From: Flavio Felder Date: Sun, 20 Sep 2026 18:48:44 +0200 Subject: [PATCH 02/27] Add help dialog timber:help now opens a dialog, which is also added to the pause screen. Co-Authored-By: Claude Sonnet 5 --- .../tags/dialog/pause_screen_additions.json | 5 ++ data/timber/dialog/help.json | 53 +++++++++++++++++++ data/timber/function/help.mcfunction | 26 +-------- 3 files changed, 59 insertions(+), 25 deletions(-) create mode 100644 data/minecraft/tags/dialog/pause_screen_additions.json create mode 100644 data/timber/dialog/help.json diff --git a/data/minecraft/tags/dialog/pause_screen_additions.json b/data/minecraft/tags/dialog/pause_screen_additions.json new file mode 100644 index 0000000..54d298d --- /dev/null +++ b/data/minecraft/tags/dialog/pause_screen_additions.json @@ -0,0 +1,5 @@ +{ + "values": [ + "timber:help" + ] +} \ No newline at end of file diff --git a/data/timber/dialog/help.json b/data/timber/dialog/help.json new file mode 100644 index 0000000..4b218e2 --- /dev/null +++ b/data/timber/dialog/help.json @@ -0,0 +1,53 @@ +{ + "type": "minecraft:multi_action", + "title": "Timber Datapack", + "external_title": "Timber", + "body": { + "type": "minecraft:plain_message", + "contents": [ + "", + { + "text": "The Timber Datapack allows you to instantly chop down a tree just by breaking one log with any axe." + }, + "\n\n", + { + "text": "By default, sneaking while breaking will not chop the tree." + }, + "\n\n", + { + "text": "Every player can use " + }, + { + "text": "/trigger TimberToggle", + "color": "blue", + "click_event": { + "action": "suggest_command", + "command": "/trigger TimberToggle" + } + }, + { + "text": " to individually turn the datapack on or off." + } + ], + "width": 300 + }, + "inputs": [], + "can_close_with_escape": true, + "pause": true, + "actions": [ + { + "label": "Check for New Updates", + "action": { + "type": "minecraft:open_url", + "url": "https://github.com/Moggla/Timber-Datapack/releases" + } + }, + { + "label": "Settings", + "action": { + "type": "minecraft:run_command", + "command": "function timber:settings/open" + } + } + ] +} \ No newline at end of file diff --git a/data/timber/function/help.mcfunction b/data/timber/function/help.mcfunction index daa8cab..ce9ee44 100644 --- a/data/timber/function/help.mcfunction +++ b/data/timber/function/help.mcfunction @@ -1,25 +1 @@ -tellraw @s ["",{text:"\u00A7m ",color:"dark_gray"}] - -tellraw @s ["",{text:" Timber Datapack"}] - -tellraw @s ["",{text:"\u00A7m ",color:"dark_gray"}] - -tellraw @s ["",{text:"The Timber Datapack allows you to instantly chop down a tree just by breaking one log with any axe."}] - -tellraw @s ["",{text:"By default, sneaking while breaking will not chop the tree."}] - -tellraw @s [""] - -tellraw @s ["",{text:"Every player can use "},{text:"/trigger TimberToggle",color:"blue",click_event:{action:"suggest_command",command:"/trigger TimberToggle"}},{text:" to individually turn the datapack on or off."}] - -tellraw @s [""] - -tellraw @s [""," ",{text:"[click here to check for new updates]",color:"green",click_event:{action:"open_url",url:"https://github.com/Moggla/Timber-Datapack/releases"}}] - -tellraw @s [""] - -tellraw @s [""," ",{text:"[click here to see the settings]",color:"gold",click_event:{action:"run_command",command:"/function timber:settings/settings1_click"}}] - -tellraw @s ["",{text:"\u00A7m ",color:"dark_gray"}] - -function timber:settings/end_of_message \ No newline at end of file +dialog show @s timber:help From fd0cca4929e2472986d34212a8668e836b9a0184 Mon Sep 17 00:00:00 2001 From: Flavio Felder Date: Sun, 20 Sep 2026 18:48:44 +0200 Subject: [PATCH 03/27] Add settings dialog The dialog is filled with the current scores on every open and writes them back on Done. Chop Slowly / Drop Loot exclusivity and the Stop Chopping -> Wear Out dependency are enforced, and the slow chop and auto plant loops are started when enabled. Closes #17 Co-Authored-By: Claude Sonnet 5 --- data/timber/function/settings/open.mcfunction | 30 ++++++++++++ data/timber/function/settings/set.mcfunction | 46 +++++++++++++++++++ data/timber/function/settings/show.mcfunction | 1 + 3 files changed, 77 insertions(+) create mode 100644 data/timber/function/settings/open.mcfunction create mode 100644 data/timber/function/settings/set.mcfunction create mode 100644 data/timber/function/settings/show.mcfunction diff --git a/data/timber/function/settings/open.mcfunction b/data/timber/function/settings/open.mcfunction new file mode 100644 index 0000000..0969a84 --- /dev/null +++ b/data/timber/function/settings/open.mcfunction @@ -0,0 +1,30 @@ +# builds the settings dialog with the current values and shows it to the player +data modify storage timber:settings dialog set value {"type":"minecraft:notice","title":"Timber Global Settings","external_title":"Timber Settings","inputs":[{"type":"minecraft:boolean","key":"standing","label":["",{"text":"Works When Standing"}],"initial":true,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"sneaking","label":["",{"text":"Works When Sneaking"}],"initial":false,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"destroy_leaves","label":["",{"text":"Destroy Leaves"}],"initial":true,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"plant_sapling","label":["",{"text":"Auto Plant Saplings","hover_event":{"action":"show_text","value":["",{"text":"All kind of saplings laying on the ground will get planted every 2 seconds."},{"text":"\n\nWhen a sapling get placed on mud, it turns into muddy mangrove roots for a fraction of a second due to datapack limitations.","color":"gray"}]}}],"initial":false,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"destroy_roots","label":["",{"text":"Destroy Roots","hover_event":{"action":"show_text","value":["",{"text":"Only root blocks which are not in the ground count."}]}}],"initial":true,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"start_chop_on_root","label":["",{"text":"Chop Tree When Roots Get Destroyed","hover_event":{"action":"show_text","value":["",{"text":"The datapack will chop the tree if you break its root blocks.\nOnly root blocks which are not in the ground count."}]}}],"initial":true,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"slow_chop","label":["",{"text":"Chop Slowly","hover_event":{"action":"show_text","value":["",{"text":"The tree won't be chopped instantly anymore."},{"text":"\n\nThis option can increase performance.","color":"gray"}]}}],"initial":false,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"chop_down","label":["",{"text":"Enable Chopping Down","hover_event":{"action":"show_text","value":"Normally when you cut a tree in the middle only the top part of the tree gets destroyed. But if this option is enabled the bottom part will get destroyed as well."}}],"initial":false,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"stop_chopping","label":["",{"text":"Stop Chopping Once Axe Breaks","hover_event":{"action":"show_text","value":"Stops the process of chopping the tree as soon as your axe doesn't have any durability left for the whole tree. When disabled, the tree will always be chopped finish."}}],"initial":false,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"wear_out","label":["",{"text":"Additionally Wear out Tool Depending on Tree Size","hover_event":{"action":"show_text","value":"The tool's durability will get decreased by the amount of destroyed logs (or stems). When disabled, only one durability gets deducted."}}],"initial":true,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"drop_loot","label":["",{"text":"Drop Loot Straight into Inventory","hover_event":{"action":"show_text","value":["",{"text":"Lets the dropped items of the tree teleport straight into your own inventory. As soon as you have a full inventory the loot gets dropped on the ground."},{"text":"\n\nThis option can increase performance.","color":"gray"}]}}],"initial":false,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"hunger","label":["",{"text":"Give Additional Hunger Effect","hover_event":{"action":"show_text","value":"Gives a hunger effect for 1 second with the same level as the size of the chopped tree."}}],"initial":false,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"stopsound","label":["",{"text":"Stop Loud Breaking Noises","hover_event":{"action":"show_text","value":["",{"text":"Suppresses loud breaking noises when chopping a tree."},{"text":"\n\nThis option is","color":"gray"},{"text":" experimental","color":"dark_red"},{"text":" because sometimes it still sounds loud for a fraction of a second.","color":"gray"}]}}],"initial":false,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"wooden_axe","label":["",{"text":"Enable Wooden Axe"}],"initial":true,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"stone_axe","label":["",{"text":"Enable Stone Axe"}],"initial":true,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"copper_axe","label":["",{"text":"Enable Copper Axe"}],"initial":true,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"iron_axe","label":["",{"text":"Enable Iron Axe"}],"initial":true,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"golden_axe","label":["",{"text":"Enable Golden Axe"}],"initial":true,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"diamond_axe","label":["",{"text":"Enable Diamond Axe"}],"initial":true,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"netherite_axe","label":["",{"text":"Enable Netherite Axe"}],"initial":true,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"chop_trees","label":["",{"text":"Chop Trees","hover_event":{"action":"show_text","value":"All kind of trees will get chopped."}}],"initial":true,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"chop_fungi","label":["",{"text":"Chop Fungi","hover_event":{"action":"show_text","value":["",{"text":"Huge fungi (nether trees) as well as huge mushrooms will get chopped."},{"text":"\n\nUse it on your own risk!\nWhen enabled, this datapack can't differentiate between generated fungi and player-placed fungi and therefore can potentially destroy your buildings.","color":"dark_red"}]}}],"initial":false,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"persistent","label":["",{"text":"Destroy Player-Placed Trees","hover_event":{"action":"show_text","value":["",{"text":"Player-placed trees are trees made out of player-placed leaves (Look at leaf block with F3 debug screen: persistent = true)."},{"text":"\n\nUse it on your own risk!\nWhen enabled, this datapack can't differentiate between generated trees and player-placed trees anymore and therefore can potentially destroy your buildings.","color":"dark_red"}]}}],"initial":false,"on_true":"1","on_false":"0"},{"type":"minecraft:number_range","key":"blocks_per_chop","width":300,"label":["",{"text":"Set Amount of Destroyed Blocks per Chop","hover_event":{"action":"show_text","value":"Only used when \"Chop Slowly\" is enabled."}}],"start":1,"end":100,"step":1,"initial":1},{"type":"minecraft:number_range","key":"time_between_chops","width":300,"label":["",{"text":"Set Time in Ticks Between Chops","hover_event":{"action":"show_text","value":"Only used when \"Chop Slowly\" is enabled."}}],"start":1,"end":100,"step":1,"initial":1},{"type":"minecraft:number_range","key":"max_tree_size","width":300,"label":["",{"text":"Set Max. Tree Size","hover_event":{"action":"show_text","value":["",{"text":"Default: 555"},{"text":"\n\nTrees bigger than this set value won't get further processed."}]}}],"start":0,"end":1000,"step":1,"initial":0},{"type":"minecraft:number_range","key":"min_leaves_found","width":300,"label":["",{"text":"Set Min. Leaves to Valid a Tree","hover_event":{"action":"show_text","value":["",{"text":"Default: 5"},{"text":"\n\nThis datapack considers a tree only if it has this set value amount of naturally generated leaves at its trunk.\nFungi will get checked by the amount of cap blocks and additionally a 3×3 big cross on top of the stem."}]}}],"start":0,"end":10,"step":1,"initial":0}],"can_close_with_escape":true,"pause":true,"after_action":"close","action":{"label":{"translate":"gui.done"},"action":{"type":"minecraft:dynamic/run_command","template":"function timber:settings/set {\"standing\":$(standing),\"sneaking\":$(sneaking),\"destroy_leaves\":$(destroy_leaves),\"plant_sapling\":$(plant_sapling),\"destroy_roots\":$(destroy_roots),\"start_chop_on_root\":$(start_chop_on_root),\"slow_chop\":$(slow_chop),\"chop_down\":$(chop_down),\"stop_chopping\":$(stop_chopping),\"wear_out\":$(wear_out),\"drop_loot\":$(drop_loot),\"hunger\":$(hunger),\"stopsound\":$(stopsound),\"wooden_axe\":$(wooden_axe),\"stone_axe\":$(stone_axe),\"copper_axe\":$(copper_axe),\"iron_axe\":$(iron_axe),\"golden_axe\":$(golden_axe),\"diamond_axe\":$(diamond_axe),\"netherite_axe\":$(netherite_axe),\"chop_trees\":$(chop_trees),\"chop_fungi\":$(chop_fungi),\"persistent\":$(persistent),\"blocks_per_chop\":$(blocks_per_chop),\"time_between_chops\":$(time_between_chops),\"max_tree_size\":$(max_tree_size),\"min_leaves_found\":$(min_leaves_found)}"}}} +execute store result storage timber:settings dialog.inputs[0].initial byte 1 run scoreboard players get standing timber +execute store result storage timber:settings dialog.inputs[1].initial byte 1 run scoreboard players get sneaking timber +execute store result storage timber:settings dialog.inputs[2].initial byte 1 run scoreboard players get destroy_leaves timber +execute store result storage timber:settings dialog.inputs[3].initial byte 1 run scoreboard players get plant_sapling timber +execute store result storage timber:settings dialog.inputs[4].initial byte 1 run scoreboard players get destroy_roots timber +execute store result storage timber:settings dialog.inputs[5].initial byte 1 run scoreboard players get start_chop_on_root timber +execute store result storage timber:settings dialog.inputs[6].initial byte 1 run scoreboard players get slow_chop timber +execute store result storage timber:settings dialog.inputs[7].initial byte 1 run scoreboard players get chop_down timber +execute store result storage timber:settings dialog.inputs[8].initial byte 1 run scoreboard players get stop_chopping timber +execute store result storage timber:settings dialog.inputs[9].initial byte 1 run scoreboard players get wear_out timber +execute store result storage timber:settings dialog.inputs[10].initial byte 1 run scoreboard players get drop_loot timber +execute store result storage timber:settings dialog.inputs[11].initial byte 1 run scoreboard players get hunger timber +execute store result storage timber:settings dialog.inputs[12].initial byte 1 run scoreboard players get stopsound timber +execute store result storage timber:settings dialog.inputs[13].initial byte 1 run scoreboard players get wooden_axe timber +execute store result storage timber:settings dialog.inputs[14].initial byte 1 run scoreboard players get stone_axe timber +execute store result storage timber:settings dialog.inputs[15].initial byte 1 run scoreboard players get copper_axe timber +execute store result storage timber:settings dialog.inputs[16].initial byte 1 run scoreboard players get iron_axe timber +execute store result storage timber:settings dialog.inputs[17].initial byte 1 run scoreboard players get golden_axe timber +execute store result storage timber:settings dialog.inputs[18].initial byte 1 run scoreboard players get diamond_axe timber +execute store result storage timber:settings dialog.inputs[19].initial byte 1 run scoreboard players get netherite_axe timber +execute store result storage timber:settings dialog.inputs[20].initial byte 1 run scoreboard players get chop_trees timber +execute store result storage timber:settings dialog.inputs[21].initial byte 1 run scoreboard players get chop_fungi timber +execute store result storage timber:settings dialog.inputs[22].initial byte 1 run scoreboard players get persistent timber +execute store result storage timber:settings dialog.inputs[23].initial float 1 run scoreboard players get blocks_per_chop timber +execute store result storage timber:settings dialog.inputs[24].initial float 1 run scoreboard players get time_between_chops timber +execute store result storage timber:settings dialog.inputs[25].initial float 1 run scoreboard players get max_tree_size timber +execute store result storage timber:settings dialog.inputs[26].initial float 1 run scoreboard players get min_leaves_found timber +function timber:settings/show with storage timber:settings diff --git a/data/timber/function/settings/set.mcfunction b/data/timber/function/settings/set.mcfunction new file mode 100644 index 0000000..4823564 --- /dev/null +++ b/data/timber/function/settings/set.mcfunction @@ -0,0 +1,46 @@ +# applies the values submitted by the settings dialog +scoreboard players operation #old_slow_chop timber = slow_chop timber +scoreboard players operation #old_plant_sapling timber = plant_sapling timber +scoreboard players operation #old_drop_loot timber = drop_loot timber + +$scoreboard players set standing timber $(standing) +$scoreboard players set sneaking timber $(sneaking) +$scoreboard players set destroy_leaves timber $(destroy_leaves) +$scoreboard players set plant_sapling timber $(plant_sapling) +$scoreboard players set destroy_roots timber $(destroy_roots) +$scoreboard players set start_chop_on_root timber $(start_chop_on_root) +$scoreboard players set slow_chop timber $(slow_chop) +$scoreboard players set chop_down timber $(chop_down) +$scoreboard players set stop_chopping timber $(stop_chopping) +$scoreboard players set wear_out timber $(wear_out) +$scoreboard players set drop_loot timber $(drop_loot) +$scoreboard players set hunger timber $(hunger) +$scoreboard players set stopsound timber $(stopsound) +$scoreboard players set wooden_axe timber $(wooden_axe) +$scoreboard players set stone_axe timber $(stone_axe) +$scoreboard players set copper_axe timber $(copper_axe) +$scoreboard players set iron_axe timber $(iron_axe) +$scoreboard players set golden_axe timber $(golden_axe) +$scoreboard players set diamond_axe timber $(diamond_axe) +$scoreboard players set netherite_axe timber $(netherite_axe) +$scoreboard players set chop_trees timber $(chop_trees) +$scoreboard players set chop_fungi timber $(chop_fungi) +$scoreboard players set persistent timber $(persistent) +$data modify storage timber:settings input.blocks_per_chop set value $(blocks_per_chop) +execute store result score blocks_per_chop timber run data get storage timber:settings input.blocks_per_chop +$data modify storage timber:settings input.time_between_chops set value $(time_between_chops) +execute store result score time_between_chops timber run data get storage timber:settings input.time_between_chops +$data modify storage timber:settings input.max_tree_size set value $(max_tree_size) +execute store result score max_tree_size timber run data get storage timber:settings input.max_tree_size +$data modify storage timber:settings input.min_leaves_found set value $(min_leaves_found) +execute store result score min_leaves_found timber run data get storage timber:settings input.min_leaves_found +data remove storage timber:settings input + +# options which exclude each other: the one that was just enabled wins +execute if score slow_chop timber matches 1 if score drop_loot timber matches 1 if score #old_drop_loot timber matches 1 run scoreboard players set drop_loot timber 0 +execute if score slow_chop timber matches 1 if score drop_loot timber matches 1 unless score #old_drop_loot timber matches 1 run scoreboard players set slow_chop timber 0 +execute if score stop_chopping timber matches 1 run scoreboard players set wear_out timber 1 + +# start optional loops +execute if score slow_chop timber matches 1 unless score #old_slow_chop timber matches 1 run function timber:slow_chop/loop +execute if score plant_sapling timber matches 1 unless score #old_plant_sapling timber matches 1 run function timber:autoplant/loop diff --git a/data/timber/function/settings/show.mcfunction b/data/timber/function/settings/show.mcfunction new file mode 100644 index 0000000..3422c0b --- /dev/null +++ b/data/timber/function/settings/show.mcfunction @@ -0,0 +1 @@ +$dialog show @s $(dialog) From 644699bddca9fcb833303f88fcc22f99074fecb7 Mon Sep 17 00:00:00 2001 From: Flavio Felder Date: Sun, 20 Sep 2026 18:48:44 +0200 Subject: [PATCH 04/27] Remove text based settings menu Replaced by the dialogs. toggle/timber stays for /trigger TimberToggle. Co-Authored-By: Claude Sonnet 5 --- .../function/settings/settings.mcfunction | 1 - .../function/settings/settings1.mcfunction | 57 ------------------ .../settings/settings1_click.mcfunction | 2 - .../function/settings/settings2.mcfunction | 58 ------------------- .../settings/settings2_click.mcfunction | 2 - .../settings/toggle/chop_down.mcfunction | 7 --- .../settings/toggle/chop_fungi.mcfunction | 7 --- .../settings/toggle/chop_trees.mcfunction | 7 --- .../settings/toggle/copper_axe.mcfunction | 7 --- .../settings/toggle/destroy_leaves.mcfunction | 7 --- .../settings/toggle/destroy_roots.mcfunction | 7 --- .../settings/toggle/diamond_axe.mcfunction | 7 --- .../settings/toggle/drop_loot.mcfunction | 7 --- .../settings/toggle/golden_axe.mcfunction | 7 --- .../settings/toggle/hunger.mcfunction | 7 --- .../settings/toggle/interfere.mcfunction | 7 --- .../settings/toggle/iron_axe.mcfunction | 7 --- .../settings/toggle/netherite_axe.mcfunction | 7 --- .../settings/toggle/persistent.mcfunction | 7 --- .../settings/toggle/plant_sapling.mcfunction | 8 --- .../settings/toggle/slow_chop.mcfunction | 8 --- .../settings/toggle/sneaking.mcfunction | 7 --- .../settings/toggle/standing.mcfunction | 7 --- .../toggle/start_chop_on_root.mcfunction | 7 --- .../settings/toggle/stone_axe.mcfunction | 7 --- .../settings/toggle/stop_chopping.mcfunction | 7 --- .../settings/toggle/stopsound.mcfunction | 7 --- .../settings/toggle/wear_out.mcfunction | 7 --- .../settings/toggle/wooden_axe.mcfunction | 7 --- 29 files changed, 290 deletions(-) delete mode 100644 data/timber/function/settings/settings.mcfunction delete mode 100644 data/timber/function/settings/settings1.mcfunction delete mode 100644 data/timber/function/settings/settings1_click.mcfunction delete mode 100644 data/timber/function/settings/settings2.mcfunction delete mode 100644 data/timber/function/settings/settings2_click.mcfunction delete mode 100644 data/timber/function/settings/toggle/chop_down.mcfunction delete mode 100644 data/timber/function/settings/toggle/chop_fungi.mcfunction delete mode 100644 data/timber/function/settings/toggle/chop_trees.mcfunction delete mode 100644 data/timber/function/settings/toggle/copper_axe.mcfunction delete mode 100644 data/timber/function/settings/toggle/destroy_leaves.mcfunction delete mode 100644 data/timber/function/settings/toggle/destroy_roots.mcfunction delete mode 100644 data/timber/function/settings/toggle/diamond_axe.mcfunction delete mode 100644 data/timber/function/settings/toggle/drop_loot.mcfunction delete mode 100644 data/timber/function/settings/toggle/golden_axe.mcfunction delete mode 100644 data/timber/function/settings/toggle/hunger.mcfunction delete mode 100644 data/timber/function/settings/toggle/interfere.mcfunction delete mode 100644 data/timber/function/settings/toggle/iron_axe.mcfunction delete mode 100644 data/timber/function/settings/toggle/netherite_axe.mcfunction delete mode 100644 data/timber/function/settings/toggle/persistent.mcfunction delete mode 100644 data/timber/function/settings/toggle/plant_sapling.mcfunction delete mode 100644 data/timber/function/settings/toggle/slow_chop.mcfunction delete mode 100644 data/timber/function/settings/toggle/sneaking.mcfunction delete mode 100644 data/timber/function/settings/toggle/standing.mcfunction delete mode 100644 data/timber/function/settings/toggle/start_chop_on_root.mcfunction delete mode 100644 data/timber/function/settings/toggle/stone_axe.mcfunction delete mode 100644 data/timber/function/settings/toggle/stop_chopping.mcfunction delete mode 100644 data/timber/function/settings/toggle/stopsound.mcfunction delete mode 100644 data/timber/function/settings/toggle/wear_out.mcfunction delete mode 100644 data/timber/function/settings/toggle/wooden_axe.mcfunction diff --git a/data/timber/function/settings/settings.mcfunction b/data/timber/function/settings/settings.mcfunction deleted file mode 100644 index 42d2588..0000000 --- a/data/timber/function/settings/settings.mcfunction +++ /dev/null @@ -1 +0,0 @@ -function timber:settings/settings1 \ No newline at end of file diff --git a/data/timber/function/settings/settings1.mcfunction b/data/timber/function/settings/settings1.mcfunction deleted file mode 100644 index 3146ddc..0000000 --- a/data/timber/function/settings/settings1.mcfunction +++ /dev/null @@ -1,57 +0,0 @@ -tellraw @s ["",{text:" "}] -tellraw @s ["",{text:" "}] -tellraw @s ["",{text:" "}] -tellraw @s ["",{text:" "}] - -tellraw @s ["",{text:"\u00A7m ",color:"dark_gray"}] - -tellraw @s ["",{text:" Timber "},{text:"/",color:"gray"},{text:" Global Settings "},{text:" < ",click_event:{action:"run_command",command:"/function timber:settings/settings1_click"}},{text:"1/2"},{text:" > ",click_event:{action:"run_command",command:"/function timber:settings/settings2_click"}}] - -tellraw @s ["",{text:"\u00A7m ",color:"dark_gray"}] - -execute if score standing timber matches 1.. run tellraw @s ["",{text:"[ ✔ ]",color:"green",click_event:{action:"run_command",command:"/function timber:settings/toggle/standing"}},{text:" Works When Standing"}] -execute unless score standing timber matches 1.. run tellraw @s ["",{text:"[ ❌ ]",color:"red",click_event:{action:"run_command",command:"/function timber:settings/toggle/standing"}},{text:" Works When Standing"}] - -execute if score sneaking timber matches 1.. run tellraw @s ["",{text:"[ ✔ ]",color:"green",click_event:{action:"run_command",command:"/function timber:settings/toggle/sneaking"}},{text:" Works When Sneaking"}] -execute unless score sneaking timber matches 1.. run tellraw @s ["",{text:"[ ❌ ]",color:"red",click_event:{action:"run_command",command:"/function timber:settings/toggle/sneaking"}},{text:" Works When Sneaking"}] - -execute if score destroy_leaves timber matches 1.. run tellraw @s ["",{text:"[ ✔ ]",color:"green",click_event:{action:"run_command",command:"/function timber:settings/toggle/destroy_leaves"}},{text:" Destroy Leaves"}] -execute unless score destroy_leaves timber matches 1.. run tellraw @s ["",{text:"[ ❌ ]",color:"red",click_event:{action:"run_command",command:"/function timber:settings/toggle/destroy_leaves"}},{text:" Destroy Leaves"}] - -execute if score plant_sapling timber matches 1.. run tellraw @s ["",{text:"[ ✔ ]",color:"green",click_event:{action:"run_command",command:"/function timber:settings/toggle/plant_sapling"}},{text:" Auto Plant Saplings",hover_event:{action:"show_text",value:["",{text:"All kind of saplings laying on the ground will get planted every 2 seconds."},{text:"\n\nWhen a sapling get placed on mud, it turns into muddy mangrove roots for a fraction of a second due to datapack limitations.",color:"gray"}]}}] -execute unless score plant_sapling timber matches 1.. run tellraw @s ["",{text:"[ ❌ ]",color:"red",click_event:{action:"run_command",command:"/function timber:settings/toggle/plant_sapling"}},{text:" Auto Plant Saplings",hover_event:{action:"show_text",value:["",{text:"All kind of saplings laying on the ground will get planted every 2 seconds."},{text:"\n\nWhen a sapling get placed on mud, it turns into muddy mangrove roots for a fraction of a second due to datapack limitations.",color:"gray"}]}}] - -execute if score destroy_roots timber matches 1.. run tellraw @s ["",{text:"[ ✔ ]",color:"green",click_event:{action:"run_command",command:"/function timber:settings/toggle/destroy_roots"}},{text:" Destroy Roots",hover_event:{action:"show_text",value:["",{text:"Only root blocks which are not in the ground count."}]}}] -execute unless score destroy_roots timber matches 1.. run tellraw @s ["",{text:"[ ❌ ]",color:"red",click_event:{action:"run_command",command:"/function timber:settings/toggle/destroy_roots"}},{text:" Destroy Roots",hover_event:{action:"show_text",value:["",{text:"Only root blocks which are not in the ground count."}]}}] - -execute if score start_chop_on_root timber matches 1.. run tellraw @s ["",{text:"[ ✔ ]",color:"green",click_event:{action:"run_command",command:"/function timber:settings/toggle/start_chop_on_root"}},{text:" Chop Tree When Roots Get Destroyed",hover_event:{action:"show_text",value:["",{text:"The datapack will chop the tree if you break its root blocks.\nOnly root blocks which are not in the ground count."}]}}] -execute unless score start_chop_on_root timber matches 1.. run tellraw @s ["",{text:"[ ❌ ]",color:"red",click_event:{action:"run_command",command:"/function timber:settings/toggle/start_chop_on_root"}},{text:" Chop Tree When Roots Get Destroyed",hover_event:{action:"show_text",value:["",{text:"The datapack will chop the tree if you break its root blocks.\nOnly root blocks which are not in the ground count."}]}}] - -execute if score slow_chop timber matches 1.. run tellraw @s ["",{text:"[ ✔ ]",color:"green",click_event:{action:"run_command",command:"/function timber:settings/toggle/slow_chop"}},{text:" Chop Slowly",hover_event:{action:"show_text",value:["",{text:"The tree won't be chopped instantly anymore. Additional options to change the speed of the chopping process can be changed at the next page."},{text:"\n\nThis option can increase performance.",color:"gray"}]}}] -execute unless score slow_chop timber matches 1.. unless score drop_loot timber matches 1.. run tellraw @s ["",{text:"[ ❌ ]",color:"red",click_event:{action:"run_command",command:"/function timber:settings/toggle/slow_chop"}},{text:" Chop Slowly",hover_event:{action:"show_text",value:["",{text:"The tree won't be chopped instantly anymore. Additional options to change the speed of the chopping process can be changed at the next page."},{text:"\n\nThis option can increase performance.",color:"gray"}]}}] -execute unless score slow_chop timber matches 1.. if score drop_loot timber matches 1.. run tellraw @s ["",{text:"[ ❌ ]",color:"#7F2B2B",hover_event:{action:"show_text",value:"Can't be enabled when \"Drop Loot Straight into Inventory\" is enabled."}},{text:" Chop Slowly",hover_event:{action:"show_text",value:["",{text:"The tree won't be chopped instantly anymore. Additional options to change the speed of the chopping process can be changed at the next page."},{text:"\n\nThis option can increase performance.",color:"gray"}]}}] - -execute if score chop_down timber matches 1.. run tellraw @s ["",{text:"[ ✔ ]",color:"green",click_event:{action:"run_command",command:"/function timber:settings/toggle/chop_down"}},{text:" Enable Chopping Down",hover_event:{action:"show_text",value:"Normally when you cut a tree in the middle only the top part of the tree gets destroyed. But if this option is enabled the bottom part will get destroyed as well."}}] -execute unless score chop_down timber matches 1.. run tellraw @s ["",{text:"[ ❌ ]",color:"red",click_event:{action:"run_command",command:"/function timber:settings/toggle/chop_down"}},{text:" Enable Chopping Down",hover_event:{action:"show_text",value:"Normally when you cut a tree in the middle only the top part of the tree gets destroyed. But if this option is enabled the bottom part will get destroyed as well."}}] - -execute if score stop_chopping timber matches 1.. run tellraw @s ["",{text:"[ ✔ ]",color:"green",click_event:{action:"run_command",command:"/function timber:settings/toggle/stop_chopping"}},{text:" Stop Chopping Once Axe Breaks",hover_event:{action:"show_text",value:"Stops the process of chopping the tree as soon as your axe doesn't have any durability left for the whole tree. When disabled, the tree will always be chopped finish."}}] -execute unless score stop_chopping timber matches 1.. if score wear_out timber matches 1.. run tellraw @s ["",{text:"[ ❌ ]",color:"red",click_event:{action:"run_command",command:"/function timber:settings/toggle/stop_chopping"}},{text:" Stop Chopping Once Axe Breaks",hover_event:{action:"show_text",value:"Stops the process of chopping the tree as soon as your axe doesn't have any durability left for the whole tree. When disabled, the tree will always be chopped finish."}}] -execute unless score stop_chopping timber matches 1.. unless score wear_out timber matches 1.. run tellraw @s ["",{text:"[ ❌ ]",color:"#7F2B2B",hover_event:{action:"show_text",value:"Can't be enabled when \"Additionally Wear out Tool Depending on Tree Size\" is disabled."}},{text:" Stop Chopping Once Axe Breaks",hover_event:{action:"show_text",value:"Stops the process of chopping the tree as soon as your axe doesn't have any durability left for the whole tree. When disabled, the tree will always be chopped finish."}}] - -execute if score wear_out timber matches 1.. unless score stop_chopping timber matches 1.. run tellraw @s ["",{text:"[ ✔ ]",color:"green",click_event:{action:"run_command",command:"/function timber:settings/toggle/wear_out"}},{text:" Additionally Wear out Tool Depending on Tree Size",hover_event:{action:"show_text",value:"The tool's durability will get decreased by the amount of destroyed logs (or stems). When disabled, only one durability gets deducted."}}] -execute unless score wear_out timber matches 1.. run tellraw @s ["",{text:"[ ❌ ]",color:"red",click_event:{action:"run_command",command:"/function timber:settings/toggle/wear_out"}},{text:" Additionally Wear out Tool Depending on Tree Size",hover_event:{action:"show_text",value:"The tool's durability will get decreased by the amount of destroyed logs (or stems). When disabled, only one durability gets deducted."}}] -execute if score wear_out timber matches 1.. if score stop_chopping timber matches 1.. run tellraw @s ["",{text:"[ ✔ ]",color:"#2B7F2B",hover_event:{action:"show_text",value:"Can't be disabled when \"Stop Chopping Once Axe Breaks\" is enabled."}},{text:" Additionally Wear out Tool Depending on Tree Size",hover_event:{action:"show_text",value:"The tool's durability will get decreased by the amount of destroyed logs (or stems). When disabled, only one durability gets deducted."}}] - -execute if score drop_loot timber matches 1.. run tellraw @s ["",{text:"[ ✔ ]",color:"green",click_event:{action:"run_command",command:"/function timber:settings/toggle/drop_loot"}},{text:" Drop Loot Straight into Inventory",hover_event:{action:"show_text",value:["",{text:"Lets the dropped items of the tree teleport straight into your own inventory. As soon as you have a full inventory the loot gets dropped on the ground."},{text:"\n\nThis option can increase performance.",color:"gray"}]}}] -execute unless score drop_loot timber matches 1.. unless score slow_chop timber matches 1.. run tellraw @s ["",{text:"[ ❌ ]",color:"red",click_event:{action:"run_command",command:"/function timber:settings/toggle/drop_loot"}},{text:" Drop Loot Straight into Inventory",hover_event:{action:"show_text",value:["",{text:"Lets the dropped items of the tree teleport straight into your own inventory. As soon as you have a full inventory the loot gets dropped on the ground."},{text:"\n\nThis option can increase performance.",color:"gray"}]}}] -execute unless score drop_loot timber matches 1.. if score slow_chop timber matches 1.. run tellraw @s ["",{text:"[ ❌ ]",color:"#7F2B2B",hover_event:{action:"show_text",value:"Can't be enabled when \"Chop Slowly\" is enabled."}},{text:" Drop Loot Straight into Inventory",hover_event:{action:"show_text",value:["",{text:"Lets the dropped items of the tree teleport straight into your own inventory. As soon as you have a full inventory the loot gets dropped on the ground."},{text:"\n\nThis option can increase performance.",color:"gray"}]}}] - -execute if score hunger timber matches 1.. run tellraw @s ["",{text:"[ ✔ ]",color:"green",click_event:{action:"run_command",command:"/function timber:settings/toggle/hunger"}},{text:" Give Additional Hunger Effect",hover_event:{action:"show_text",value:"Gives a hunger effect for 1 second with the same level as the size of the chopped tree."}}] -execute unless score hunger timber matches 1.. run tellraw @s ["",{text:"[ ❌ ]",color:"red",click_event:{action:"run_command",command:"/function timber:settings/toggle/hunger"}},{text:" Give Additional Hunger Effect",hover_event:{action:"show_text",value:"Gives a hunger effect for 1 second with the same level as the size of the chopped tree."}}] - -execute if score stopsound timber matches 1.. run tellraw @s ["",{text:"[ ✔ ]",color:"green",click_event:{action:"run_command",command:"/function timber:settings/toggle/stopsound"}},{text:" Stop Loud Breaking Noises",hover_event:{action:"show_text",value:["",{text:"Suppresses loud breaking noises when chopping a tree."},{text:"\n\nThis option is",color:"gray"},{text:" experimental",color:"dark_red"},{text:" because sometimes it still sounds loud for a fraction of a second.",color:"gray"}]}}] -execute unless score stopsound timber matches 1.. run tellraw @s ["",{text:"[ ❌ ]",color:"red",click_event:{action:"run_command",command:"/function timber:settings/toggle/stopsound"}},{text:" Stop Loud Breaking Noises",hover_event:{action:"show_text",value:["",{text:"Suppresses loud breaking noises when chopping a tree."},{text:"\n\nThis option is",color:"gray"},{text:" experimental",color:"dark_red"},{text:" because sometimes it still sounds loud for a fraction of a second.",color:"gray"}]}}] - -tellraw @s ["",{text:"\u00A7m ",color:"dark_gray"}] - -function timber:settings/end_of_message \ No newline at end of file diff --git a/data/timber/function/settings/settings1_click.mcfunction b/data/timber/function/settings/settings1_click.mcfunction deleted file mode 100644 index 64625b3..0000000 --- a/data/timber/function/settings/settings1_click.mcfunction +++ /dev/null @@ -1,2 +0,0 @@ -function timber:settings/settings -playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.6 .2 \ No newline at end of file diff --git a/data/timber/function/settings/settings2.mcfunction b/data/timber/function/settings/settings2.mcfunction deleted file mode 100644 index 56ab2ae..0000000 --- a/data/timber/function/settings/settings2.mcfunction +++ /dev/null @@ -1,58 +0,0 @@ -tellraw @s ["",{text:" "}] -tellraw @s ["",{text:" "}] -tellraw @s ["",{text:" "}] -tellraw @s ["",{text:" "}] - -tellraw @s ["",{text:"\u00A7m ",color:"dark_gray"}] - -tellraw @s ["",{text:" Timber "},{text:"/",color:"gray"},{text:" Global Settings "},{text:" < ",click_event:{action:"run_command",command:"/function timber:settings/settings1_click"}},{text:"2/2"},{text:" > ",click_event:{action:"run_command",command:"/function timber:settings/settings2_click"}}] - -tellraw @s ["",{text:"\u00A7m ",color:"dark_gray"}] - -execute if score wooden_axe timber matches 1.. run tellraw @s ["",{text:"[ ✔ ]",color:"green",click_event:{action:"run_command",command:"/function timber:settings/toggle/wooden_axe"}},{text:" Enable Wooden Axe"}] -execute unless score wooden_axe timber matches 1.. run tellraw @s ["",{text:"[ ❌ ]",color:"red",click_event:{action:"run_command",command:"/function timber:settings/toggle/wooden_axe"}},{text:" Enable Wooden Axe"}] - -execute if score stone_axe timber matches 1.. run tellraw @s ["",{text:"[ ✔ ]",color:"green",click_event:{action:"run_command",command:"/function timber:settings/toggle/stone_axe"}},{text:" Enable Stone Axe"}] -execute unless score stone_axe timber matches 1.. run tellraw @s ["",{text:"[ ❌ ]",color:"red",click_event:{action:"run_command",command:"/function timber:settings/toggle/stone_axe"}},{text:" Enable Stone Axe"}] - -execute if score copper_axe timber matches 1.. run tellraw @s ["",{text:"[ ✔ ]",color:"green",click_event:{action:"run_command",command:"/function timber:settings/toggle/copper_axe"}},{text:" Enable Copper Axe"}] -execute unless score copper_axe timber matches 1.. run tellraw @s ["",{text:"[ ❌ ]",color:"red",click_event:{action:"run_command",command:"/function timber:settings/toggle/copper_axe"}},{text:" Enable Copper Axe"}] - -execute if score iron_axe timber matches 1.. run tellraw @s ["",{text:"[ ✔ ]",color:"green",click_event:{action:"run_command",command:"/function timber:settings/toggle/iron_axe"}},{text:" Enable Iron Axe"}] -execute unless score iron_axe timber matches 1.. run tellraw @s ["",{text:"[ ❌ ]",color:"red",click_event:{action:"run_command",command:"/function timber:settings/toggle/iron_axe"}},{text:" Enable Iron Axe"}] - -execute if score golden_axe timber matches 1.. run tellraw @s ["",{text:"[ ✔ ]",color:"green",click_event:{action:"run_command",command:"/function timber:settings/toggle/golden_axe"}},{text:" Enable Golden Axe"}] -execute unless score golden_axe timber matches 1.. run tellraw @s ["",{text:"[ ❌ ]",color:"red",click_event:{action:"run_command",command:"/function timber:settings/toggle/golden_axe"}},{text:" Enable Golden Axe"}] - -execute if score diamond_axe timber matches 1.. run tellraw @s ["",{text:"[ ✔ ]",color:"green",click_event:{action:"run_command",command:"/function timber:settings/toggle/diamond_axe"}},{text:" Enable Diamond Axe"}] -execute unless score diamond_axe timber matches 1.. run tellraw @s ["",{text:"[ ❌ ]",color:"red",click_event:{action:"run_command",command:"/function timber:settings/toggle/diamond_axe"}},{text:" Enable Diamond Axe"}] - -execute if score netherite_axe timber matches 1.. run tellraw @s ["",{text:"[ ✔ ]",color:"green",click_event:{action:"run_command",command:"/function timber:settings/toggle/netherite_axe"}},{text:" Enable Netherite Axe"}] -execute unless score netherite_axe timber matches 1.. run tellraw @s ["",{text:"[ ❌ ]",color:"red",click_event:{action:"run_command",command:"/function timber:settings/toggle/netherite_axe"}},{text:" Enable Netherite Axe"}] - -execute if score chop_trees timber matches 1.. run tellraw @s ["",{text:"[ ✔ ]",color:"green",click_event:{action:"run_command",command:"/function timber:settings/toggle/chop_trees"}},{text:" Chop Trees",hover_event:{action:"show_text",value:"All kind of trees will get chopped."}}] -execute unless score chop_trees timber matches 1.. run tellraw @s ["",{text:"[ ❌ ]",color:"red",click_event:{action:"run_command",command:"/function timber:settings/toggle/chop_trees"}},{text:" Chop Trees",hover_event:{action:"show_text",value:"All kind of trees will get chopped."}}] - -execute if score chop_fungi timber matches 1.. run tellraw @s ["",{text:"[ ✔ ]",color:"green",click_event:{action:"run_command",command:"/function timber:settings/toggle/chop_fungi"}},{text:" Chop Fungi",hover_event:{action:"show_text",value:["",{text:"Huge fungi (nether trees) as well as huge mushrooms will get chopped."},{text:"\n\nUse it on your own risk!\nWhen enabled, this datapack can't differentiate between generated fungi and player-placed fungi and therefore can potentially destroy your buildings.",color:"dark_red"}]}}] -execute unless score chop_fungi timber matches 1.. run tellraw @s ["",{text:"[ ❌ ]",color:"red",click_event:{action:"run_command",command:"/function timber:settings/toggle/chop_fungi"}},{text:" Chop Fungi",hover_event:{action:"show_text",value:["",{text:"Huge fungi (nether trees) as well as huge mushrooms will get chopped."},{text:"\n\nUse it on your own risk!\nWhen enabled, this datapack can't differentiate between generated fungi and player-placed fungi and therefore can potentially destroy your buildings.",color:"dark_red"}]}}] - -execute if score persistent timber matches 1.. run tellraw @s ["",{text:"[ ✔ ]",color:"green",click_event:{action:"run_command",command:"/function timber:settings/toggle/persistent"}},{text:" Destroy Player-Placed Trees",hover_event:{action:"show_text",value:["",{text:"Player-placed trees are trees made out of player-placed leaves (Look at leaf block with F3 debug screen: persistent = true)."},{text:"\n\nUse it on your own risk!\nWhen enabled, this datapack can't differentiate between generated trees and player-placed trees anymore and therefore can potentially destroy your buildings.",color:"dark_red"}]}}] -execute unless score persistent timber matches 1.. run tellraw @s ["",{text:"[ ❌ ]",color:"red",click_event:{action:"run_command",command:"/function timber:settings/toggle/persistent"}},{text:" Destroy Player-Placed Trees",hover_event:{action:"show_text",value:["",{text:"Player-placed trees are trees made out of player-placed leaves (Look at leaf block with F3 debug screen: persistent = true)."},{text:"\n\nUse it on your own risk!\nWhen enabled, this datapack can't differentiate between generated trees and player-placed trees anymore and therefore can potentially destroy your buildings.",color:"dark_red"}]}}] - -execute if score blocks_per_chop timber matches 1 if score slow_chop timber matches 1.. run tellraw @s ["",{text:"[ ✎ ]",color:"gray",click_event:{action:"suggest_command",command:"/scoreboard players set blocks_per_chop timber "},hover_event:{action:"show_text",value:"Default: 1"}},{text:" Set Amount of Destroyed Blocks per Chop"},{text:" (Current: ",color:"gray"},{"score":{"name":"blocks_per_chop","objective":"timber"},color:"gray"},{text: ")",color:"gray"}] -execute unless score blocks_per_chop timber matches 1 if score slow_chop timber matches 1.. run tellraw @s ["",{text:"[ ✎ ]",color:"gray",click_event:{action:"suggest_command",command:"/scoreboard players set blocks_per_chop timber "},hover_event:{action:"show_text",value:"Default: 1"}},{text:" Set Amount of Destroyed Blocks per Chop"},{text:" (Current: ",color:"gray"},{"score":{"name":"blocks_per_chop","objective":"timber"},color:"gray"},{text: ")",color:"gray"}] -execute unless score slow_chop timber matches 1.. run tellraw @s ["",{text:"[ ✎ ]",color:"dark_gray",hover_event:{action:"show_text",value:"Not active when \"Chop Slowly\" is disabled."}},{text:" Set Amount of Destroyed Blocks per Chop"}] - -execute if score time_between_chops timber matches 1 if score slow_chop timber matches 1.. run tellraw @s ["",{text:"[ ✎ ]",color:"gray",click_event:{action:"suggest_command",command:"/scoreboard players set time_between_chops timber "},hover_event:{action:"show_text",value:"Default: 1"}},{text:" Set Time in Ticks Between Chops"},{text:" (Current: ",color:"gray"},{"score":{"name":"time_between_chops","objective":"timber"},color:"gray"},{text: ")",color:"gray"}] -execute unless score time_between_chops timber matches 1 if score slow_chop timber matches 1.. run tellraw @s ["",{text:"[ ✎ ]",color:"gray",click_event:{action:"suggest_command",command:"/scoreboard players set time_between_chops timber "},hover_event:{action:"show_text",value:"Default: 1"}},{text:" Set Time in Ticks Between Chops"},{text:" (Current: ",color:"gray"},{"score":{"name":"time_between_chops","objective":"timber"},color:"gray"},{text: ")",color:"gray"}] -execute unless score slow_chop timber matches 1.. run tellraw @s ["",{text:"[ ✎ ]",color:"dark_gray",hover_event:{action:"show_text",value:"Not active when \"Chop Slowly\" is disabled."}},{text:" Set Time in Ticks Between Chops"}] - -execute if score max_tree_size timber matches 555 run tellraw @s ["",{text:"[ ✎ ]",color:"gray",click_event:{action:"suggest_command",command:"/scoreboard players set max_tree_size timber "},hover_event:{action:"show_text",value:"Default: 555"}},{text:" Set Max. Tree Size",hover_event:{action:"show_text",value:"Trees bigger than this set value won't get further processed."}},{text:" (Currently: ",color:"gray"},{"score":{"name":"max_tree_size","objective":"timber"},color:"gray"},{text: ")",color:"gray"}] -execute unless score max_tree_size timber matches 555 run tellraw @s ["",{text:"[ ✎ ]",color:"gray",click_event:{action:"suggest_command",command:"/scoreboard players set max_tree_size timber "},hover_event:{action:"show_text",value:"Default: 555"}},{text:" Set Max. Tree Size",hover_event:{action:"show_text",value:"Trees bigger than this set value won't get further processed."}},{text:" (Currently: ",color:"gray"},{"score":{"name":"max_tree_size","objective":"timber"},color:"gray"},{text: ")",color:"gray"}] - -execute if score min_leaves_found timber matches 5 run tellraw @s ["",{text:"[ ✎ ]",color:"gray",click_event:{action:"suggest_command",command:"/scoreboard players set min_leaves_found timber "},hover_event:{action:"show_text",value:"Default: 5"}},{text:" Set Min. Leaves to Valid a Tree",hover_event:{action:"show_text",value:"This datapack considers a tree only if it has this set value amount of naturally generated leaves at its trunk.\nFungi will get checked by the amount of cap blocks and additionally a 3×3 big cross on top of the stem."}},{text:" (Current: ",color:"gray"},{"score":{"name":"min_leaves_found","objective":"timber"},color:"gray"},{text: ")",color:"gray"}] -execute unless score min_leaves_found timber matches 5 run tellraw @s ["",{text:"[ ✎ ]",color:"gray",click_event:{action:"suggest_command",command:"/scoreboard players set min_leaves_found timber "},hover_event:{action:"show_text",value:"Default: 5"}},{text:" Set Min. Leaves to Valid a Tree",hover_event:{action:"show_text",value:"This datapack considers a tree only if it has this set value amount of naturally generated leaves at its trunk.\nFungi will get checked by the amount of cap blocks and additionally a 3×3 big cross on top of the stem."}},{text:" (Current: ",color:"gray"},{"score":{"name":"min_leaves_found","objective":"timber"},color:"gray"},{text: ")",color:"gray"}] - -tellraw @s ["",{text:"\u00A7m ",color:"dark_gray"}] - -function timber:settings/end_of_message diff --git a/data/timber/function/settings/settings2_click.mcfunction b/data/timber/function/settings/settings2_click.mcfunction deleted file mode 100644 index 44b2ce4..0000000 --- a/data/timber/function/settings/settings2_click.mcfunction +++ /dev/null @@ -1,2 +0,0 @@ -function timber:settings/settings2 -playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.6 .2 \ No newline at end of file diff --git a/data/timber/function/settings/toggle/chop_down.mcfunction b/data/timber/function/settings/toggle/chop_down.mcfunction deleted file mode 100644 index e601c0c..0000000 --- a/data/timber/function/settings/toggle/chop_down.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score chop_down timber matches 1.. run scoreboard players set chop_down timber 2 -execute unless score chop_down timber matches 1.. run scoreboard players set chop_down timber 1 -execute if score chop_down timber matches 2.. run scoreboard players set chop_down timber 0 -execute if score chop_down timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score chop_down timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings -function timber:settings/end_of_message \ No newline at end of file diff --git a/data/timber/function/settings/toggle/chop_fungi.mcfunction b/data/timber/function/settings/toggle/chop_fungi.mcfunction deleted file mode 100644 index 22614b8..0000000 --- a/data/timber/function/settings/toggle/chop_fungi.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score chop_fungi timber matches 1.. run scoreboard players set chop_fungi timber 2 -execute unless score chop_fungi timber matches 1.. run scoreboard players set chop_fungi timber 1 -execute if score chop_fungi timber matches 2.. run scoreboard players set chop_fungi timber 0 -execute if score chop_fungi timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score chop_fungi timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings2 -function timber:settings/end_of_message \ No newline at end of file diff --git a/data/timber/function/settings/toggle/chop_trees.mcfunction b/data/timber/function/settings/toggle/chop_trees.mcfunction deleted file mode 100644 index 19fe627..0000000 --- a/data/timber/function/settings/toggle/chop_trees.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score chop_trees timber matches 1.. run scoreboard players set chop_trees timber 2 -execute unless score chop_trees timber matches 1.. run scoreboard players set chop_trees timber 1 -execute if score chop_trees timber matches 2.. run scoreboard players set chop_trees timber 0 -execute if score chop_trees timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score chop_trees timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings2 -function timber:settings/end_of_message \ No newline at end of file diff --git a/data/timber/function/settings/toggle/copper_axe.mcfunction b/data/timber/function/settings/toggle/copper_axe.mcfunction deleted file mode 100644 index 63fac10..0000000 --- a/data/timber/function/settings/toggle/copper_axe.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score copper_axe timber matches 1.. run scoreboard players set copper_axe timber 2 -execute unless score copper_axe timber matches 1.. run scoreboard players set copper_axe timber 1 -execute if score copper_axe timber matches 2.. run scoreboard players set copper_axe timber 0 -execute if score copper_axe timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score copper_axe timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings2 -function timber:settings/end_of_message diff --git a/data/timber/function/settings/toggle/destroy_leaves.mcfunction b/data/timber/function/settings/toggle/destroy_leaves.mcfunction deleted file mode 100644 index 346bced..0000000 --- a/data/timber/function/settings/toggle/destroy_leaves.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score destroy_leaves timber matches 1.. run scoreboard players set destroy_leaves timber 2 -execute unless score destroy_leaves timber matches 1.. run scoreboard players set destroy_leaves timber 1 -execute if score destroy_leaves timber matches 2.. run scoreboard players set destroy_leaves timber 0 -execute if score destroy_leaves timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score destroy_leaves timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings -function timber:settings/end_of_message \ No newline at end of file diff --git a/data/timber/function/settings/toggle/destroy_roots.mcfunction b/data/timber/function/settings/toggle/destroy_roots.mcfunction deleted file mode 100644 index 87a6906..0000000 --- a/data/timber/function/settings/toggle/destroy_roots.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score destroy_roots timber matches 1.. run scoreboard players set destroy_roots timber 2 -execute unless score destroy_roots timber matches 1.. run scoreboard players set destroy_roots timber 1 -execute if score destroy_roots timber matches 2.. run scoreboard players set destroy_roots timber 0 -execute if score destroy_roots timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score destroy_roots timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings -function timber:settings/end_of_message \ No newline at end of file diff --git a/data/timber/function/settings/toggle/diamond_axe.mcfunction b/data/timber/function/settings/toggle/diamond_axe.mcfunction deleted file mode 100644 index 83e45d8..0000000 --- a/data/timber/function/settings/toggle/diamond_axe.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score diamond_axe timber matches 1.. run scoreboard players set diamond_axe timber 2 -execute unless score diamond_axe timber matches 1.. run scoreboard players set diamond_axe timber 1 -execute if score diamond_axe timber matches 2.. run scoreboard players set diamond_axe timber 0 -execute if score diamond_axe timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score diamond_axe timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings2 -function timber:settings/end_of_message \ No newline at end of file diff --git a/data/timber/function/settings/toggle/drop_loot.mcfunction b/data/timber/function/settings/toggle/drop_loot.mcfunction deleted file mode 100644 index dc35415..0000000 --- a/data/timber/function/settings/toggle/drop_loot.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score drop_loot timber matches 1.. run scoreboard players set drop_loot timber 2 -execute unless score drop_loot timber matches 1.. run scoreboard players set drop_loot timber 1 -execute if score drop_loot timber matches 2.. run scoreboard players set drop_loot timber 0 -execute if score drop_loot timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score drop_loot timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings -function timber:settings/end_of_message \ No newline at end of file diff --git a/data/timber/function/settings/toggle/golden_axe.mcfunction b/data/timber/function/settings/toggle/golden_axe.mcfunction deleted file mode 100644 index f21f5b8..0000000 --- a/data/timber/function/settings/toggle/golden_axe.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score golden_axe timber matches 1.. run scoreboard players set golden_axe timber 2 -execute unless score golden_axe timber matches 1.. run scoreboard players set golden_axe timber 1 -execute if score golden_axe timber matches 2.. run scoreboard players set golden_axe timber 0 -execute if score golden_axe timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score golden_axe timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings2 -function timber:settings/end_of_message \ No newline at end of file diff --git a/data/timber/function/settings/toggle/hunger.mcfunction b/data/timber/function/settings/toggle/hunger.mcfunction deleted file mode 100644 index b1e9543..0000000 --- a/data/timber/function/settings/toggle/hunger.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score hunger timber matches 1.. run scoreboard players set hunger timber 2 -execute unless score hunger timber matches 1.. run scoreboard players set hunger timber 1 -execute if score hunger timber matches 2.. run scoreboard players set hunger timber 0 -execute if score hunger timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score hunger timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings1 -function timber:settings/end_of_message \ No newline at end of file diff --git a/data/timber/function/settings/toggle/interfere.mcfunction b/data/timber/function/settings/toggle/interfere.mcfunction deleted file mode 100644 index b1270d0..0000000 --- a/data/timber/function/settings/toggle/interfere.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score interfere timber matches 1.. run scoreboard players set interfere timber 2 -execute unless score interfere timber matches 1.. run scoreboard players set interfere timber 1 -execute if score interfere timber matches 2.. run scoreboard players set interfere timber 0 -execute if score interfere timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score interfere timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings -function timber:settings/end_of_message \ No newline at end of file diff --git a/data/timber/function/settings/toggle/iron_axe.mcfunction b/data/timber/function/settings/toggle/iron_axe.mcfunction deleted file mode 100644 index bf08c1f..0000000 --- a/data/timber/function/settings/toggle/iron_axe.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score iron_axe timber matches 1.. run scoreboard players set iron_axe timber 2 -execute unless score iron_axe timber matches 1.. run scoreboard players set iron_axe timber 1 -execute if score iron_axe timber matches 2.. run scoreboard players set iron_axe timber 0 -execute if score iron_axe timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score iron_axe timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings2 -function timber:settings/end_of_message \ No newline at end of file diff --git a/data/timber/function/settings/toggle/netherite_axe.mcfunction b/data/timber/function/settings/toggle/netherite_axe.mcfunction deleted file mode 100644 index 17f6528..0000000 --- a/data/timber/function/settings/toggle/netherite_axe.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score netherite_axe timber matches 1.. run scoreboard players set netherite_axe timber 2 -execute unless score netherite_axe timber matches 1.. run scoreboard players set netherite_axe timber 1 -execute if score netherite_axe timber matches 2.. run scoreboard players set netherite_axe timber 0 -execute if score netherite_axe timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score netherite_axe timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings2 -function timber:settings/end_of_message \ No newline at end of file diff --git a/data/timber/function/settings/toggle/persistent.mcfunction b/data/timber/function/settings/toggle/persistent.mcfunction deleted file mode 100644 index 58bad84..0000000 --- a/data/timber/function/settings/toggle/persistent.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score persistent timber matches 1.. run scoreboard players set persistent timber 2 -execute unless score persistent timber matches 1.. run scoreboard players set persistent timber 1 -execute if score persistent timber matches 2.. run scoreboard players set persistent timber 0 -execute if score persistent timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score persistent timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings2 -function timber:settings/end_of_message \ No newline at end of file diff --git a/data/timber/function/settings/toggle/plant_sapling.mcfunction b/data/timber/function/settings/toggle/plant_sapling.mcfunction deleted file mode 100644 index a6efdc3..0000000 --- a/data/timber/function/settings/toggle/plant_sapling.mcfunction +++ /dev/null @@ -1,8 +0,0 @@ -execute if score plant_sapling timber matches 1.. run scoreboard players set plant_sapling timber 2 -execute unless score plant_sapling timber matches 1.. run scoreboard players set plant_sapling timber 1 -execute if score plant_sapling timber matches 2.. run scoreboard players set plant_sapling timber 0 -execute if score plant_sapling timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score plant_sapling timber matches 1 run function timber:autoplant/loop -execute if score plant_sapling timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings1 -function timber:settings/end_of_message \ No newline at end of file diff --git a/data/timber/function/settings/toggle/slow_chop.mcfunction b/data/timber/function/settings/toggle/slow_chop.mcfunction deleted file mode 100644 index 0a0989c..0000000 --- a/data/timber/function/settings/toggle/slow_chop.mcfunction +++ /dev/null @@ -1,8 +0,0 @@ -execute if score slow_chop timber matches 1.. run scoreboard players set slow_chop timber 2 -execute unless score slow_chop timber matches 1.. run scoreboard players set slow_chop timber 1 -execute if score slow_chop timber matches 2.. run scoreboard players set slow_chop timber 0 -execute if score slow_chop timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score slow_chop timber matches 1 run function timber:slow_chop/loop -execute if score slow_chop timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings1 -function timber:settings/end_of_message \ No newline at end of file diff --git a/data/timber/function/settings/toggle/sneaking.mcfunction b/data/timber/function/settings/toggle/sneaking.mcfunction deleted file mode 100644 index 53f5141..0000000 --- a/data/timber/function/settings/toggle/sneaking.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score sneaking timber matches 1.. run scoreboard players set sneaking timber 2 -execute unless score sneaking timber matches 1.. run scoreboard players set sneaking timber 1 -execute if score sneaking timber matches 2.. run scoreboard players set sneaking timber 0 -execute if score sneaking timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score sneaking timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings -function timber:settings/end_of_message \ No newline at end of file diff --git a/data/timber/function/settings/toggle/standing.mcfunction b/data/timber/function/settings/toggle/standing.mcfunction deleted file mode 100644 index 08c661d..0000000 --- a/data/timber/function/settings/toggle/standing.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score standing timber matches 1.. run scoreboard players set standing timber 2 -execute unless score standing timber matches 1.. run scoreboard players set standing timber 1 -execute if score standing timber matches 2.. run scoreboard players set standing timber 0 -execute if score standing timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score standing timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings -function timber:settings/end_of_message \ No newline at end of file diff --git a/data/timber/function/settings/toggle/start_chop_on_root.mcfunction b/data/timber/function/settings/toggle/start_chop_on_root.mcfunction deleted file mode 100644 index e04a2b7..0000000 --- a/data/timber/function/settings/toggle/start_chop_on_root.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score start_chop_on_root timber matches 1.. run scoreboard players set start_chop_on_root timber 2 -execute unless score start_chop_on_root timber matches 1.. run scoreboard players set start_chop_on_root timber 1 -execute if score start_chop_on_root timber matches 2.. run scoreboard players set start_chop_on_root timber 0 -execute if score start_chop_on_root timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score start_chop_on_root timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings -function timber:settings/end_of_message \ No newline at end of file diff --git a/data/timber/function/settings/toggle/stone_axe.mcfunction b/data/timber/function/settings/toggle/stone_axe.mcfunction deleted file mode 100644 index 641459d..0000000 --- a/data/timber/function/settings/toggle/stone_axe.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score stone_axe timber matches 1.. run scoreboard players set stone_axe timber 2 -execute unless score stone_axe timber matches 1.. run scoreboard players set stone_axe timber 1 -execute if score stone_axe timber matches 2.. run scoreboard players set stone_axe timber 0 -execute if score stone_axe timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score stone_axe timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings2 -function timber:settings/end_of_message \ No newline at end of file diff --git a/data/timber/function/settings/toggle/stop_chopping.mcfunction b/data/timber/function/settings/toggle/stop_chopping.mcfunction deleted file mode 100644 index b13140f..0000000 --- a/data/timber/function/settings/toggle/stop_chopping.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score stop_chopping timber matches 1.. run scoreboard players set stop_chopping timber 2 -execute unless score stop_chopping timber matches 1.. run scoreboard players set stop_chopping timber 1 -execute if score stop_chopping timber matches 2.. run scoreboard players set stop_chopping timber 0 -execute if score stop_chopping timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score stop_chopping timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings -function timber:settings/end_of_message \ No newline at end of file diff --git a/data/timber/function/settings/toggle/stopsound.mcfunction b/data/timber/function/settings/toggle/stopsound.mcfunction deleted file mode 100644 index 9418b18..0000000 --- a/data/timber/function/settings/toggle/stopsound.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score stopsound timber matches 1.. run scoreboard players set stopsound timber 2 -execute unless score stopsound timber matches 1.. run scoreboard players set stopsound timber 1 -execute if score stopsound timber matches 2.. run scoreboard players set stopsound timber 0 -execute if score stopsound timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score stopsound timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings -function timber:settings/end_of_message \ No newline at end of file diff --git a/data/timber/function/settings/toggle/wear_out.mcfunction b/data/timber/function/settings/toggle/wear_out.mcfunction deleted file mode 100644 index 0bad5ec..0000000 --- a/data/timber/function/settings/toggle/wear_out.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score wear_out timber matches 1.. run scoreboard players set wear_out timber 2 -execute unless score wear_out timber matches 1.. run scoreboard players set wear_out timber 1 -execute if score wear_out timber matches 2.. run scoreboard players set wear_out timber 0 -execute if score wear_out timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score wear_out timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings -function timber:settings/end_of_message \ No newline at end of file diff --git a/data/timber/function/settings/toggle/wooden_axe.mcfunction b/data/timber/function/settings/toggle/wooden_axe.mcfunction deleted file mode 100644 index cb82456..0000000 --- a/data/timber/function/settings/toggle/wooden_axe.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -execute if score wooden_axe timber matches 1.. run scoreboard players set wooden_axe timber 2 -execute unless score wooden_axe timber matches 1.. run scoreboard players set wooden_axe timber 1 -execute if score wooden_axe timber matches 2.. run scoreboard players set wooden_axe timber 0 -execute if score wooden_axe timber matches 1 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.9 .2 -execute if score wooden_axe timber matches 0 run playsound minecraft:ui.button.click master @s ~ ~ ~ .2 1.3 .2 -function timber:settings/settings2 -function timber:settings/end_of_message \ No newline at end of file From 4107a31aa2037860d5eaa2e0cc89bee43c1945ff Mon Sep 17 00:00:00 2001 From: Flavio Felder Date: Sun, 20 Sep 2026 18:49:21 +0200 Subject: [PATCH 05/27] Remove deprecated overlay formats key Overlay formats is deprecated since pack format 82 and makes the pack fail to load. Co-Authored-By: Claude Sonnet 5 --- pack.mcmeta | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pack.mcmeta b/pack.mcmeta index 86132f0..c9f3a12 100644 --- a/pack.mcmeta +++ b/pack.mcmeta @@ -7,7 +7,7 @@ }, "overlays": { "entries": [ - {"formats": [121, 121], "directory": "121", "min_format": 121, "max_format": 121} + {"directory": "121", "min_format": 121, "max_format": 121} ] } } From 816d428901f643da1794ba40e3108ec9f3bb43cf Mon Sep 17 00:00:00 2001 From: Flavio Felder Date: Sun, 20 Sep 2026 19:03:49 +0200 Subject: [PATCH 06/27] Add toggle button and settings tooltip to help dialog The toggle button runs /trigger TimberToggle, which every player can use. The settings button now hints that operator permissions are required. Co-Authored-By: Claude Sonnet 5 --- data/timber/dialog/help.json | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/data/timber/dialog/help.json b/data/timber/dialog/help.json index 4b218e2..5066485 100644 --- a/data/timber/dialog/help.json +++ b/data/timber/dialog/help.json @@ -36,18 +36,30 @@ "pause": true, "actions": [ { - "label": "Check for New Updates", + "label": "Toggle Timber for Me", + "tooltip": "Turns the datapack on or off for you only.", "action": { - "type": "minecraft:open_url", - "url": "https://github.com/Moggla/Timber-Datapack/releases" + "type": "minecraft:run_command", + "command": "trigger TimberToggle" } }, { "label": "Settings", + "tooltip": { + "text": "Requires operator permissions.", + "color": "gray" + }, "action": { "type": "minecraft:run_command", "command": "function timber:settings/open" } + }, + { + "label": "Check for New Updates", + "action": { + "type": "minecraft:open_url", + "url": "https://github.com/Moggla/Timber-Datapack/releases" + } } ] } \ No newline at end of file From 9d60c24d5301da9e68a8e3495a36da4bcaeb6e89 Mon Sep 17 00:00:00 2001 From: Flavio Felder Date: Sun, 20 Sep 2026 23:20:50 +0200 Subject: [PATCH 07/27] Rebuild the settings as a trigger based button menu The checkbox dialog needed `function timber:settings/set`, which shows a confirmation screen for every click. Dialog buttons now only run `/trigger`, and the datapack reacts to it. Settings menu - Every setting is a button "Name: ON/OFF" with its description as a tooltip. Settings which depend on another one show "Locked" and the reason as a tooltip (Chop Slowly / Loot to Inventory, Stop on Axe Break / Wear Out Tool More). - The menu is built by functions on every open, so it always shows the current values. ON/OFF, Locked and the "Name: value" format use Minecraft's own translations. - The four number settings get their own slider dialog. The value is sent as `1` in a single trigger. - Tools (the axes) is a sub page, everything else is on the main page. Long names are shortened, the full text stays in the tooltip. Triggers - `TimberToggle` stays enabled for everybody. Values above 1 are used by the help dialog (open settings, toggle, close). - `TimberSettings` and the slider triggers are only enabled while the menu is used and disabled again on leaving it or after 5 minutes without a click. - Only players with the tag `timber_admin` can change settings; everybody else gets a message with a clickable `/tag @s add timber_admin`. Help dialog - Buttons: check for updates, settings, toggle Timber for me. - The dialog opened from the pause screen is static and shows a neutral toggle label; the dynamic version shows Timber ON/OFF for the player. Co-Authored-By: Claude Sonnet 5 --- data/timber/dialog/help.json | 34 +++++++++----- data/timber/function/help.mcfunction | 2 +- data/timber/function/init.mcfunction | 12 +++++ data/timber/function/main.mcfunction | 15 +++++- .../function/settings/after_set.mcfunction | 8 ++++ .../function/settings/apply/blocks.mcfunction | 13 ++++++ .../function/settings/apply/leaves.mcfunction | 13 ++++++ .../function/settings/apply/ticks.mcfunction | 13 ++++++ .../settings/apply/treesize.mcfunction | 13 ++++++ data/timber/function/settings/arm.mcfunction | 3 ++ .../function/settings/before_set.mcfunction | 4 ++ .../function/settings/denied.mcfunction | 2 + .../settings/disable_triggers.mcfunction | 7 +++ .../function/settings/dispatch.mcfunction | 37 +++++++++++++++ .../settings/flip/chop_down.mcfunction | 5 ++ .../settings/flip/chop_fungi.mcfunction | 5 ++ .../settings/flip/chop_trees.mcfunction | 5 ++ .../settings/flip/copper_axe.mcfunction | 5 ++ .../settings/flip/destroy_leaves.mcfunction | 5 ++ .../settings/flip/destroy_roots.mcfunction | 5 ++ .../settings/flip/diamond_axe.mcfunction | 5 ++ .../settings/flip/drop_loot.mcfunction | 6 +++ .../settings/flip/golden_axe.mcfunction | 5 ++ .../function/settings/flip/hunger.mcfunction | 5 ++ .../settings/flip/iron_axe.mcfunction | 5 ++ .../settings/flip/netherite_axe.mcfunction | 5 ++ .../settings/flip/persistent.mcfunction | 5 ++ .../settings/flip/plant_sapling.mcfunction | 5 ++ .../settings/flip/slow_chop.mcfunction | 6 +++ .../settings/flip/sneaking.mcfunction | 5 ++ .../settings/flip/standing.mcfunction | 5 ++ .../flip/start_chop_on_root.mcfunction | 5 ++ .../settings/flip/stone_axe.mcfunction | 5 ++ .../settings/flip/stop_chopping.mcfunction | 6 +++ .../settings/flip/stopsound.mcfunction | 5 ++ .../settings/flip/wear_out.mcfunction | 6 +++ .../settings/flip/wooden_axe.mcfunction | 5 ++ .../timber/function/settings/leave.mcfunction | 3 ++ .../function/settings/leave_close.mcfunction | 3 ++ .../settings/locked/blocks.mcfunction | 3 ++ .../function/settings/locked/ticks.mcfunction | 3 ++ .../function/settings/menu_request.mcfunction | 6 +++ data/timber/function/settings/open.mcfunction | 30 ------------ .../function/settings/open/blocks.mcfunction | 7 +++ .../function/settings/open/help.mcfunction | 5 ++ .../function/settings/open/leaves.mcfunction | 6 +++ .../function/settings/open/menu.mcfunction | 45 ++++++++++++++++++ .../function/settings/open/ticks.mcfunction | 7 +++ .../function/settings/open/tools.mcfunction | 18 ++++++++ .../settings/open/treesize.mcfunction | 6 +++ .../function/settings/request.mcfunction | 4 ++ data/timber/function/settings/set.mcfunction | 46 ------------------- .../function/settings/toggle_self.mcfunction | 3 ++ data/timber/function/uninstall.mcfunction | 6 +++ 54 files changed, 406 insertions(+), 90 deletions(-) create mode 100644 data/timber/function/settings/after_set.mcfunction create mode 100644 data/timber/function/settings/apply/blocks.mcfunction create mode 100644 data/timber/function/settings/apply/leaves.mcfunction create mode 100644 data/timber/function/settings/apply/ticks.mcfunction create mode 100644 data/timber/function/settings/apply/treesize.mcfunction create mode 100644 data/timber/function/settings/arm.mcfunction create mode 100644 data/timber/function/settings/before_set.mcfunction create mode 100644 data/timber/function/settings/denied.mcfunction create mode 100644 data/timber/function/settings/disable_triggers.mcfunction create mode 100644 data/timber/function/settings/dispatch.mcfunction create mode 100644 data/timber/function/settings/flip/chop_down.mcfunction create mode 100644 data/timber/function/settings/flip/chop_fungi.mcfunction create mode 100644 data/timber/function/settings/flip/chop_trees.mcfunction create mode 100644 data/timber/function/settings/flip/copper_axe.mcfunction create mode 100644 data/timber/function/settings/flip/destroy_leaves.mcfunction create mode 100644 data/timber/function/settings/flip/destroy_roots.mcfunction create mode 100644 data/timber/function/settings/flip/diamond_axe.mcfunction create mode 100644 data/timber/function/settings/flip/drop_loot.mcfunction create mode 100644 data/timber/function/settings/flip/golden_axe.mcfunction create mode 100644 data/timber/function/settings/flip/hunger.mcfunction create mode 100644 data/timber/function/settings/flip/iron_axe.mcfunction create mode 100644 data/timber/function/settings/flip/netherite_axe.mcfunction create mode 100644 data/timber/function/settings/flip/persistent.mcfunction create mode 100644 data/timber/function/settings/flip/plant_sapling.mcfunction create mode 100644 data/timber/function/settings/flip/slow_chop.mcfunction create mode 100644 data/timber/function/settings/flip/sneaking.mcfunction create mode 100644 data/timber/function/settings/flip/standing.mcfunction create mode 100644 data/timber/function/settings/flip/start_chop_on_root.mcfunction create mode 100644 data/timber/function/settings/flip/stone_axe.mcfunction create mode 100644 data/timber/function/settings/flip/stop_chopping.mcfunction create mode 100644 data/timber/function/settings/flip/stopsound.mcfunction create mode 100644 data/timber/function/settings/flip/wear_out.mcfunction create mode 100644 data/timber/function/settings/flip/wooden_axe.mcfunction create mode 100644 data/timber/function/settings/leave.mcfunction create mode 100644 data/timber/function/settings/leave_close.mcfunction create mode 100644 data/timber/function/settings/locked/blocks.mcfunction create mode 100644 data/timber/function/settings/locked/ticks.mcfunction create mode 100644 data/timber/function/settings/menu_request.mcfunction delete mode 100644 data/timber/function/settings/open.mcfunction create mode 100644 data/timber/function/settings/open/blocks.mcfunction create mode 100644 data/timber/function/settings/open/help.mcfunction create mode 100644 data/timber/function/settings/open/leaves.mcfunction create mode 100644 data/timber/function/settings/open/menu.mcfunction create mode 100644 data/timber/function/settings/open/ticks.mcfunction create mode 100644 data/timber/function/settings/open/tools.mcfunction create mode 100644 data/timber/function/settings/open/treesize.mcfunction create mode 100644 data/timber/function/settings/request.mcfunction delete mode 100644 data/timber/function/settings/set.mcfunction create mode 100644 data/timber/function/settings/toggle_self.mcfunction diff --git a/data/timber/dialog/help.json b/data/timber/dialog/help.json index 5066485..7a4bdb0 100644 --- a/data/timber/dialog/help.json +++ b/data/timber/dialog/help.json @@ -26,40 +26,50 @@ } }, { - "text": " to individually turn the datapack on or off." + "text": " or the button below to individually turn the datapack on or off." } ], "width": 300 }, "inputs": [], "can_close_with_escape": true, - "pause": true, + "pause": false, "actions": [ { - "label": "Toggle Timber for Me", - "tooltip": "Turns the datapack on or off for you only.", + "label": "Check for New Updates", "action": { - "type": "minecraft:run_command", - "command": "trigger TimberToggle" + "type": "minecraft:open_url", + "url": "https://github.com/Moggla/Timber-Datapack/releases" } }, { "label": "Settings", "tooltip": { - "text": "Requires operator permissions.", + "text": "Requires the tag timber_admin.", "color": "gray" }, "action": { "type": "minecraft:run_command", - "command": "function timber:settings/open" + "command": "trigger TimberToggle set 2" } }, { - "label": "Check for New Updates", + "label": "Toggle Timber for Me", + "tooltip": "Turns the datapack on or off for you only.", "action": { - "type": "minecraft:open_url", - "url": "https://github.com/Moggla/Timber-Datapack/releases" + "type": "minecraft:run_command", + "command": "trigger TimberToggle set 3" } } - ] + ], + "after_action": "none", + "exit_action": { + "label": { + "translate": "gui.back" + }, + "action": { + "type": "minecraft:run_command", + "command": "trigger TimberToggle set 4" + } + } } \ No newline at end of file diff --git a/data/timber/function/help.mcfunction b/data/timber/function/help.mcfunction index ce9ee44..21452a5 100644 --- a/data/timber/function/help.mcfunction +++ b/data/timber/function/help.mcfunction @@ -1 +1 @@ -dialog show @s timber:help +function timber:settings/open/help diff --git a/data/timber/function/init.mcfunction b/data/timber/function/init.mcfunction index 83b4fdb..7f36fe3 100644 --- a/data/timber/function/init.mcfunction +++ b/data/timber/function/init.mcfunction @@ -11,9 +11,21 @@ scoreboard objectives add timber_d_axe minecraft.used:minecraft.diamond_axe scoreboard objectives add timber_n_axe minecraft.used:minecraft.netherite_axe scoreboard objectives add timber dummy scoreboard objectives add TimberToggle trigger {text:"Timber Toggle",color:"#724E21"} +scoreboard objectives add TimberSettings trigger {text:"Timber Settings",color:"#724E21"} +scoreboard objectives add TimberSetBlocks trigger +scoreboard objectives add TimberSetTicks trigger +scoreboard objectives add TimberSetTreeSize trigger +scoreboard objectives add TimberSetLeaves trigger +scoreboard objectives add timber_menu dummy scoreboard objectives add timber_disabled dummy scoreboard objectives add timber_prsistent dummy +scoreboard players reset * TimberSettings +scoreboard players reset * TimberSetBlocks +scoreboard players reset * TimberSetTicks +scoreboard players reset * TimberSetTreeSize +scoreboard players reset * TimberSetLeaves + # non-overriding default values function timber:settings/default_values function timber:tool/durability_max diff --git a/data/timber/function/main.mcfunction b/data/timber/function/main.mcfunction index 70b5108..82139cc 100644 --- a/data/timber/function/main.mcfunction +++ b/data/timber/function/main.mcfunction @@ -5,7 +5,20 @@ schedule function timber:main 1t execute as @a[predicate=timber:used_axe,tag=!global.ignore] at @s run function timber:utils/check_max_command_sequence_length # toggle timber datapack per player -execute as @a[scores={TimberToggle=1..},tag=!global.ignore] run function timber:settings/toggle/timber +execute as @a[scores={TimberToggle=1},tag=!global.ignore] run function timber:settings/toggle/timber +execute as @a[scores={TimberToggle=2..},tag=!global.ignore] run function timber:settings/menu_request + +# open the settings dialog (the dialog button can only run /trigger without a confirmation screen) +execute as @a[scores={TimberSettings=1..},tag=!global.ignore] run function timber:settings/request +execute as @a[scores={TimberSetBlocks=1..},tag=!global.ignore] run function timber:settings/apply/blocks +execute as @a[scores={TimberSetTicks=1..},tag=!global.ignore] run function timber:settings/apply/ticks +execute as @a[scores={TimberSetTreeSize=1..},tag=!global.ignore] run function timber:settings/apply/treesize +execute as @a[scores={TimberSetLeaves=1..},tag=!global.ignore] run function timber:settings/apply/leaves + +# the triggers of the settings menu are only enabled while the menu is used +scoreboard players add @a[scores={timber_menu=1..}] timber_menu 1 +execute as @a[scores={timber_menu=6000..}] run function timber:settings/disable_triggers + # reset scoreboard players enable @a TimberToggle \ No newline at end of file diff --git a/data/timber/function/settings/after_set.mcfunction b/data/timber/function/settings/after_set.mcfunction new file mode 100644 index 0000000..24cb9b6 --- /dev/null +++ b/data/timber/function/settings/after_set.mcfunction @@ -0,0 +1,8 @@ +# options which exclude each other: the one that was just enabled wins +execute if score slow_chop timber matches 1 if score drop_loot timber matches 1 if score #old_drop_loot timber matches 1 run scoreboard players set drop_loot timber 0 +execute if score slow_chop timber matches 1 if score drop_loot timber matches 1 unless score #old_drop_loot timber matches 1 run scoreboard players set slow_chop timber 0 +execute if score stop_chopping timber matches 1 run scoreboard players set wear_out timber 1 + +# start optional loops +execute if score slow_chop timber matches 1 unless score #old_slow_chop timber matches 1 run function timber:slow_chop/loop +execute if score plant_sapling timber matches 1 unless score #old_plant_sapling timber matches 1 run function timber:autoplant/loop diff --git a/data/timber/function/settings/apply/blocks.mcfunction b/data/timber/function/settings/apply/blocks.mcfunction new file mode 100644 index 0000000..ce3ed53 --- /dev/null +++ b/data/timber/function/settings/apply/blocks.mcfunction @@ -0,0 +1,13 @@ +# applies the value of the "Blocks per Chop" slider: 1 followed by the value +scoreboard players operation #x timber = @s TimberSetBlocks +scoreboard players reset @s TimberSetBlocks +scoreboard players set #p timber 1 +execute if score #x timber matches 10..19 run scoreboard players set #p timber 10 +execute if score #x timber matches 100..199 run scoreboard players set #p timber 100 +execute if score #x timber matches 1000..1999 run scoreboard players set #p timber 1000 +execute if score #x timber matches 10000..19999 run scoreboard players set #p timber 10000 +scoreboard players operation #x timber -= #p timber +scoreboard players operation blocks_per_chop timber = #x timber +function timber:settings/after_set +function timber:settings/arm +function timber:settings/open/menu diff --git a/data/timber/function/settings/apply/leaves.mcfunction b/data/timber/function/settings/apply/leaves.mcfunction new file mode 100644 index 0000000..a4c17c2 --- /dev/null +++ b/data/timber/function/settings/apply/leaves.mcfunction @@ -0,0 +1,13 @@ +# applies the value of the "Min. Leaves" slider: 1 followed by the value +scoreboard players operation #x timber = @s TimberSetLeaves +scoreboard players reset @s TimberSetLeaves +scoreboard players set #p timber 1 +execute if score #x timber matches 10..19 run scoreboard players set #p timber 10 +execute if score #x timber matches 100..199 run scoreboard players set #p timber 100 +execute if score #x timber matches 1000..1999 run scoreboard players set #p timber 1000 +execute if score #x timber matches 10000..19999 run scoreboard players set #p timber 10000 +scoreboard players operation #x timber -= #p timber +scoreboard players operation min_leaves_found timber = #x timber +function timber:settings/after_set +function timber:settings/arm +function timber:settings/open/menu diff --git a/data/timber/function/settings/apply/ticks.mcfunction b/data/timber/function/settings/apply/ticks.mcfunction new file mode 100644 index 0000000..1e3d97a --- /dev/null +++ b/data/timber/function/settings/apply/ticks.mcfunction @@ -0,0 +1,13 @@ +# applies the value of the "Ticks Between Chops" slider: 1 followed by the value +scoreboard players operation #x timber = @s TimberSetTicks +scoreboard players reset @s TimberSetTicks +scoreboard players set #p timber 1 +execute if score #x timber matches 10..19 run scoreboard players set #p timber 10 +execute if score #x timber matches 100..199 run scoreboard players set #p timber 100 +execute if score #x timber matches 1000..1999 run scoreboard players set #p timber 1000 +execute if score #x timber matches 10000..19999 run scoreboard players set #p timber 10000 +scoreboard players operation #x timber -= #p timber +scoreboard players operation time_between_chops timber = #x timber +function timber:settings/after_set +function timber:settings/arm +function timber:settings/open/menu diff --git a/data/timber/function/settings/apply/treesize.mcfunction b/data/timber/function/settings/apply/treesize.mcfunction new file mode 100644 index 0000000..b40d653 --- /dev/null +++ b/data/timber/function/settings/apply/treesize.mcfunction @@ -0,0 +1,13 @@ +# applies the value of the "Max. Tree Size" slider: 1 followed by the value +scoreboard players operation #x timber = @s TimberSetTreeSize +scoreboard players reset @s TimberSetTreeSize +scoreboard players set #p timber 1 +execute if score #x timber matches 10..19 run scoreboard players set #p timber 10 +execute if score #x timber matches 100..199 run scoreboard players set #p timber 100 +execute if score #x timber matches 1000..1999 run scoreboard players set #p timber 1000 +execute if score #x timber matches 10000..19999 run scoreboard players set #p timber 10000 +scoreboard players operation #x timber -= #p timber +scoreboard players operation max_tree_size timber = #x timber +function timber:settings/after_set +function timber:settings/arm +function timber:settings/open/menu diff --git a/data/timber/function/settings/arm.mcfunction b/data/timber/function/settings/arm.mcfunction new file mode 100644 index 0000000..5627222 --- /dev/null +++ b/data/timber/function/settings/arm.mcfunction @@ -0,0 +1,3 @@ +# enable the triggers of the settings menu and start the inactivity timer +scoreboard players enable @s TimberSettings +scoreboard players set @s timber_menu 1 diff --git a/data/timber/function/settings/before_set.mcfunction b/data/timber/function/settings/before_set.mcfunction new file mode 100644 index 0000000..d0bfc54 --- /dev/null +++ b/data/timber/function/settings/before_set.mcfunction @@ -0,0 +1,4 @@ +# remember values which trigger something when they change +scoreboard players operation #old_slow_chop timber = slow_chop timber +scoreboard players operation #old_plant_sapling timber = plant_sapling timber +scoreboard players operation #old_drop_loot timber = drop_loot timber diff --git a/data/timber/function/settings/denied.mcfunction b/data/timber/function/settings/denied.mcfunction new file mode 100644 index 0000000..8cec250 --- /dev/null +++ b/data/timber/function/settings/denied.mcfunction @@ -0,0 +1,2 @@ +tellraw @s ["",{text:"Timber: ",color:"red"},{text:"Only Timber admins can change the settings. Operators can click "},{text:"[ /tag @s add timber_admin ]",color:"gray",click_event:{action:"suggest_command",command:"/tag @s add timber_admin"},hover_event:{action:"show_text",value:"Click to put the command into the chat. Only operators are allowed to run it."}},{text:" to become a Timber admin."}] +dialog clear @s diff --git a/data/timber/function/settings/disable_triggers.mcfunction b/data/timber/function/settings/disable_triggers.mcfunction new file mode 100644 index 0000000..e12aca4 --- /dev/null +++ b/data/timber/function/settings/disable_triggers.mcfunction @@ -0,0 +1,7 @@ +# disable the triggers of the settings menu again +scoreboard players reset @s TimberSettings +scoreboard players reset @s TimberSetBlocks +scoreboard players reset @s TimberSetTicks +scoreboard players reset @s TimberSetTreeSize +scoreboard players reset @s TimberSetLeaves +scoreboard players reset @s timber_menu diff --git a/data/timber/function/settings/dispatch.mcfunction b/data/timber/function/settings/dispatch.mcfunction new file mode 100644 index 0000000..a1f5f4b --- /dev/null +++ b/data/timber/function/settings/dispatch.mcfunction @@ -0,0 +1,37 @@ +# #req: 1 = menu, 2-4 = page, 6-9 = slider, 98 = back to the help, 100+ = toggle a setting (only for admins with the tag timber_admin) +execute unless entity @s[tag=timber_admin] run return run function timber:settings/denied + +execute if score #req timber matches 98 run return run function timber:settings/leave + +# the triggers stay enabled while the menu is used +function timber:settings/arm + +execute if score #req timber matches 1..2 run function timber:settings/open/menu +execute if score #req timber matches 3 run function timber:settings/open/tools +execute if score #req timber matches 6 run function timber:settings/open/blocks +execute if score #req timber matches 7 run function timber:settings/open/ticks +execute if score #req timber matches 8 run function timber:settings/open/treesize +execute if score #req timber matches 9 run function timber:settings/open/leaves +execute if score #req timber matches 100 run function timber:settings/flip/standing +execute if score #req timber matches 101 run function timber:settings/flip/sneaking +execute if score #req timber matches 102 run function timber:settings/flip/destroy_leaves +execute if score #req timber matches 103 run function timber:settings/flip/plant_sapling +execute if score #req timber matches 104 run function timber:settings/flip/destroy_roots +execute if score #req timber matches 105 run function timber:settings/flip/start_chop_on_root +execute if score #req timber matches 106 run function timber:settings/flip/slow_chop +execute if score #req timber matches 107 run function timber:settings/flip/chop_down +execute if score #req timber matches 108 run function timber:settings/flip/stop_chopping +execute if score #req timber matches 109 run function timber:settings/flip/wear_out +execute if score #req timber matches 110 run function timber:settings/flip/drop_loot +execute if score #req timber matches 111 run function timber:settings/flip/hunger +execute if score #req timber matches 112 run function timber:settings/flip/stopsound +execute if score #req timber matches 113 run function timber:settings/flip/chop_trees +execute if score #req timber matches 114 run function timber:settings/flip/chop_fungi +execute if score #req timber matches 115 run function timber:settings/flip/persistent +execute if score #req timber matches 116 run function timber:settings/flip/wooden_axe +execute if score #req timber matches 117 run function timber:settings/flip/golden_axe +execute if score #req timber matches 118 run function timber:settings/flip/stone_axe +execute if score #req timber matches 119 run function timber:settings/flip/copper_axe +execute if score #req timber matches 120 run function timber:settings/flip/iron_axe +execute if score #req timber matches 121 run function timber:settings/flip/diamond_axe +execute if score #req timber matches 122 run function timber:settings/flip/netherite_axe diff --git a/data/timber/function/settings/flip/chop_down.mcfunction b/data/timber/function/settings/flip/chop_down.mcfunction new file mode 100644 index 0000000..0af8acd --- /dev/null +++ b/data/timber/function/settings/flip/chop_down.mcfunction @@ -0,0 +1,5 @@ +# toggles "Chop Down" +function timber:settings/before_set +execute store success score chop_down timber unless score chop_down timber matches 1 +function timber:settings/after_set +function timber:settings/open/menu diff --git a/data/timber/function/settings/flip/chop_fungi.mcfunction b/data/timber/function/settings/flip/chop_fungi.mcfunction new file mode 100644 index 0000000..c30fee6 --- /dev/null +++ b/data/timber/function/settings/flip/chop_fungi.mcfunction @@ -0,0 +1,5 @@ +# toggles "Chop Fungi" +function timber:settings/before_set +execute store success score chop_fungi timber unless score chop_fungi timber matches 1 +function timber:settings/after_set +function timber:settings/open/menu diff --git a/data/timber/function/settings/flip/chop_trees.mcfunction b/data/timber/function/settings/flip/chop_trees.mcfunction new file mode 100644 index 0000000..3a399d0 --- /dev/null +++ b/data/timber/function/settings/flip/chop_trees.mcfunction @@ -0,0 +1,5 @@ +# toggles "Chop Trees" +function timber:settings/before_set +execute store success score chop_trees timber unless score chop_trees timber matches 1 +function timber:settings/after_set +function timber:settings/open/menu diff --git a/data/timber/function/settings/flip/copper_axe.mcfunction b/data/timber/function/settings/flip/copper_axe.mcfunction new file mode 100644 index 0000000..79235e9 --- /dev/null +++ b/data/timber/function/settings/flip/copper_axe.mcfunction @@ -0,0 +1,5 @@ +# toggles "Enable Copper Axe" +function timber:settings/before_set +execute store success score copper_axe timber unless score copper_axe timber matches 1 +function timber:settings/after_set +function timber:settings/open/tools diff --git a/data/timber/function/settings/flip/destroy_leaves.mcfunction b/data/timber/function/settings/flip/destroy_leaves.mcfunction new file mode 100644 index 0000000..2e7b70b --- /dev/null +++ b/data/timber/function/settings/flip/destroy_leaves.mcfunction @@ -0,0 +1,5 @@ +# toggles "Destroy Leaves" +function timber:settings/before_set +execute store success score destroy_leaves timber unless score destroy_leaves timber matches 1 +function timber:settings/after_set +function timber:settings/open/menu diff --git a/data/timber/function/settings/flip/destroy_roots.mcfunction b/data/timber/function/settings/flip/destroy_roots.mcfunction new file mode 100644 index 0000000..f8aa6e3 --- /dev/null +++ b/data/timber/function/settings/flip/destroy_roots.mcfunction @@ -0,0 +1,5 @@ +# toggles "Destroy Roots" +function timber:settings/before_set +execute store success score destroy_roots timber unless score destroy_roots timber matches 1 +function timber:settings/after_set +function timber:settings/open/menu diff --git a/data/timber/function/settings/flip/diamond_axe.mcfunction b/data/timber/function/settings/flip/diamond_axe.mcfunction new file mode 100644 index 0000000..f38a48f --- /dev/null +++ b/data/timber/function/settings/flip/diamond_axe.mcfunction @@ -0,0 +1,5 @@ +# toggles "Enable Diamond Axe" +function timber:settings/before_set +execute store success score diamond_axe timber unless score diamond_axe timber matches 1 +function timber:settings/after_set +function timber:settings/open/tools diff --git a/data/timber/function/settings/flip/drop_loot.mcfunction b/data/timber/function/settings/flip/drop_loot.mcfunction new file mode 100644 index 0000000..f35b11b --- /dev/null +++ b/data/timber/function/settings/flip/drop_loot.mcfunction @@ -0,0 +1,6 @@ +# toggles "Loot to Inventory" +execute if score slow_chop timber matches 1 run return run function timber:settings/open/menu +function timber:settings/before_set +execute store success score drop_loot timber unless score drop_loot timber matches 1 +function timber:settings/after_set +function timber:settings/open/menu diff --git a/data/timber/function/settings/flip/golden_axe.mcfunction b/data/timber/function/settings/flip/golden_axe.mcfunction new file mode 100644 index 0000000..f3fc533 --- /dev/null +++ b/data/timber/function/settings/flip/golden_axe.mcfunction @@ -0,0 +1,5 @@ +# toggles "Enable Golden Axe" +function timber:settings/before_set +execute store success score golden_axe timber unless score golden_axe timber matches 1 +function timber:settings/after_set +function timber:settings/open/tools diff --git a/data/timber/function/settings/flip/hunger.mcfunction b/data/timber/function/settings/flip/hunger.mcfunction new file mode 100644 index 0000000..cfb0f77 --- /dev/null +++ b/data/timber/function/settings/flip/hunger.mcfunction @@ -0,0 +1,5 @@ +# toggles "Hunger Effect" +function timber:settings/before_set +execute store success score hunger timber unless score hunger timber matches 1 +function timber:settings/after_set +function timber:settings/open/menu diff --git a/data/timber/function/settings/flip/iron_axe.mcfunction b/data/timber/function/settings/flip/iron_axe.mcfunction new file mode 100644 index 0000000..79c589a --- /dev/null +++ b/data/timber/function/settings/flip/iron_axe.mcfunction @@ -0,0 +1,5 @@ +# toggles "Enable Iron Axe" +function timber:settings/before_set +execute store success score iron_axe timber unless score iron_axe timber matches 1 +function timber:settings/after_set +function timber:settings/open/tools diff --git a/data/timber/function/settings/flip/netherite_axe.mcfunction b/data/timber/function/settings/flip/netherite_axe.mcfunction new file mode 100644 index 0000000..c01cac9 --- /dev/null +++ b/data/timber/function/settings/flip/netherite_axe.mcfunction @@ -0,0 +1,5 @@ +# toggles "Enable Netherite Axe" +function timber:settings/before_set +execute store success score netherite_axe timber unless score netherite_axe timber matches 1 +function timber:settings/after_set +function timber:settings/open/tools diff --git a/data/timber/function/settings/flip/persistent.mcfunction b/data/timber/function/settings/flip/persistent.mcfunction new file mode 100644 index 0000000..97139a1 --- /dev/null +++ b/data/timber/function/settings/flip/persistent.mcfunction @@ -0,0 +1,5 @@ +# toggles "Player-Placed Trees" +function timber:settings/before_set +execute store success score persistent timber unless score persistent timber matches 1 +function timber:settings/after_set +function timber:settings/open/menu diff --git a/data/timber/function/settings/flip/plant_sapling.mcfunction b/data/timber/function/settings/flip/plant_sapling.mcfunction new file mode 100644 index 0000000..d0fd46b --- /dev/null +++ b/data/timber/function/settings/flip/plant_sapling.mcfunction @@ -0,0 +1,5 @@ +# toggles "Auto Plant Saplings" +function timber:settings/before_set +execute store success score plant_sapling timber unless score plant_sapling timber matches 1 +function timber:settings/after_set +function timber:settings/open/menu diff --git a/data/timber/function/settings/flip/slow_chop.mcfunction b/data/timber/function/settings/flip/slow_chop.mcfunction new file mode 100644 index 0000000..c8da263 --- /dev/null +++ b/data/timber/function/settings/flip/slow_chop.mcfunction @@ -0,0 +1,6 @@ +# toggles "Chop Slowly" +execute if score drop_loot timber matches 1 run return run function timber:settings/open/menu +function timber:settings/before_set +execute store success score slow_chop timber unless score slow_chop timber matches 1 +function timber:settings/after_set +function timber:settings/open/menu diff --git a/data/timber/function/settings/flip/sneaking.mcfunction b/data/timber/function/settings/flip/sneaking.mcfunction new file mode 100644 index 0000000..bf0902f --- /dev/null +++ b/data/timber/function/settings/flip/sneaking.mcfunction @@ -0,0 +1,5 @@ +# toggles "Works When Sneaking" +function timber:settings/before_set +execute store success score sneaking timber unless score sneaking timber matches 1 +function timber:settings/after_set +function timber:settings/open/menu diff --git a/data/timber/function/settings/flip/standing.mcfunction b/data/timber/function/settings/flip/standing.mcfunction new file mode 100644 index 0000000..8a05b44 --- /dev/null +++ b/data/timber/function/settings/flip/standing.mcfunction @@ -0,0 +1,5 @@ +# toggles "Works When Standing" +function timber:settings/before_set +execute store success score standing timber unless score standing timber matches 1 +function timber:settings/after_set +function timber:settings/open/menu diff --git a/data/timber/function/settings/flip/start_chop_on_root.mcfunction b/data/timber/function/settings/flip/start_chop_on_root.mcfunction new file mode 100644 index 0000000..f64b133 --- /dev/null +++ b/data/timber/function/settings/flip/start_chop_on_root.mcfunction @@ -0,0 +1,5 @@ +# toggles "Chop on Root Break" +function timber:settings/before_set +execute store success score start_chop_on_root timber unless score start_chop_on_root timber matches 1 +function timber:settings/after_set +function timber:settings/open/menu diff --git a/data/timber/function/settings/flip/stone_axe.mcfunction b/data/timber/function/settings/flip/stone_axe.mcfunction new file mode 100644 index 0000000..75fea06 --- /dev/null +++ b/data/timber/function/settings/flip/stone_axe.mcfunction @@ -0,0 +1,5 @@ +# toggles "Enable Stone Axe" +function timber:settings/before_set +execute store success score stone_axe timber unless score stone_axe timber matches 1 +function timber:settings/after_set +function timber:settings/open/tools diff --git a/data/timber/function/settings/flip/stop_chopping.mcfunction b/data/timber/function/settings/flip/stop_chopping.mcfunction new file mode 100644 index 0000000..0cb5f7c --- /dev/null +++ b/data/timber/function/settings/flip/stop_chopping.mcfunction @@ -0,0 +1,6 @@ +# toggles "Stop on Axe Break" +execute unless score wear_out timber matches 1 run return run function timber:settings/open/menu +function timber:settings/before_set +execute store success score stop_chopping timber unless score stop_chopping timber matches 1 +function timber:settings/after_set +function timber:settings/open/menu diff --git a/data/timber/function/settings/flip/stopsound.mcfunction b/data/timber/function/settings/flip/stopsound.mcfunction new file mode 100644 index 0000000..73ad6f5 --- /dev/null +++ b/data/timber/function/settings/flip/stopsound.mcfunction @@ -0,0 +1,5 @@ +# toggles "Mute Breaking Sounds" +function timber:settings/before_set +execute store success score stopsound timber unless score stopsound timber matches 1 +function timber:settings/after_set +function timber:settings/open/menu diff --git a/data/timber/function/settings/flip/wear_out.mcfunction b/data/timber/function/settings/flip/wear_out.mcfunction new file mode 100644 index 0000000..065e63e --- /dev/null +++ b/data/timber/function/settings/flip/wear_out.mcfunction @@ -0,0 +1,6 @@ +# toggles "Wear Out Tool More" +execute if score stop_chopping timber matches 1 run return run function timber:settings/open/menu +function timber:settings/before_set +execute store success score wear_out timber unless score wear_out timber matches 1 +function timber:settings/after_set +function timber:settings/open/menu diff --git a/data/timber/function/settings/flip/wooden_axe.mcfunction b/data/timber/function/settings/flip/wooden_axe.mcfunction new file mode 100644 index 0000000..d2ecbae --- /dev/null +++ b/data/timber/function/settings/flip/wooden_axe.mcfunction @@ -0,0 +1,5 @@ +# toggles "Enable Wooden Axe" +function timber:settings/before_set +execute store success score wooden_axe timber unless score wooden_axe timber matches 1 +function timber:settings/after_set +function timber:settings/open/tools diff --git a/data/timber/function/settings/leave.mcfunction b/data/timber/function/settings/leave.mcfunction new file mode 100644 index 0000000..f34f935 --- /dev/null +++ b/data/timber/function/settings/leave.mcfunction @@ -0,0 +1,3 @@ +# back to the help dialog +function timber:settings/disable_triggers +function timber:settings/open/help diff --git a/data/timber/function/settings/leave_close.mcfunction b/data/timber/function/settings/leave_close.mcfunction new file mode 100644 index 0000000..edba3eb --- /dev/null +++ b/data/timber/function/settings/leave_close.mcfunction @@ -0,0 +1,3 @@ +# close the dialog +function timber:settings/disable_triggers +dialog clear @s diff --git a/data/timber/function/settings/locked/blocks.mcfunction b/data/timber/function/settings/locked/blocks.mcfunction new file mode 100644 index 0000000..9d811d6 --- /dev/null +++ b/data/timber/function/settings/locked/blocks.mcfunction @@ -0,0 +1,3 @@ +# shown instead of the slider while Chop Slowly is disabled +data modify storage timber:settings dialog set value {"type":"minecraft:multi_action","title":"Timber Settings / Blocks per Chop","external_title":"Timber Blocks per Chop","inputs":[],"can_close_with_escape":true,"pause":false,"after_action":"none","columns":1,"actions":[{"label":{"translate":"options.generic_value","with":[{"text":"Set Amount of Destroyed Blocks per Chop"},{"translate":"filled_map.locked","color":"gray"}]},"width":300,"tooltip":["",{"text":"Only used when \"Chop Slowly\" is enabled.","color":"red"}]}],"exit_action":{"label":{"translate":"gui.back"},"width":300,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 1"}}} +function timber:settings/show with storage timber:settings diff --git a/data/timber/function/settings/locked/ticks.mcfunction b/data/timber/function/settings/locked/ticks.mcfunction new file mode 100644 index 0000000..dbc5779 --- /dev/null +++ b/data/timber/function/settings/locked/ticks.mcfunction @@ -0,0 +1,3 @@ +# shown instead of the slider while Chop Slowly is disabled +data modify storage timber:settings dialog set value {"type":"minecraft:multi_action","title":"Timber Settings / Ticks Between Chops","external_title":"Timber Ticks Between Chops","inputs":[],"can_close_with_escape":true,"pause":false,"after_action":"none","columns":1,"actions":[{"label":{"translate":"options.generic_value","with":[{"text":"Set Time in Ticks Between Chops"},{"translate":"filled_map.locked","color":"gray"}]},"width":300,"tooltip":["",{"text":"Only used when \"Chop Slowly\" is enabled.","color":"red"}]}],"exit_action":{"label":{"translate":"gui.back"},"width":300,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 1"}}} +function timber:settings/show with storage timber:settings diff --git a/data/timber/function/settings/menu_request.mcfunction b/data/timber/function/settings/menu_request.mcfunction new file mode 100644 index 0000000..b9adc50 --- /dev/null +++ b/data/timber/function/settings/menu_request.mcfunction @@ -0,0 +1,6 @@ +# trigger TimberToggle with a value > 1 (buttons of the help dialog) +scoreboard players operation #req timber = @s TimberToggle +scoreboard players set @s TimberToggle 0 +execute if score #req timber matches 3 run return run function timber:settings/toggle_self +execute if score #req timber matches 4 run return run function timber:settings/leave_close +execute if score #req timber matches 2 run function timber:settings/dispatch diff --git a/data/timber/function/settings/open.mcfunction b/data/timber/function/settings/open.mcfunction deleted file mode 100644 index 0969a84..0000000 --- a/data/timber/function/settings/open.mcfunction +++ /dev/null @@ -1,30 +0,0 @@ -# builds the settings dialog with the current values and shows it to the player -data modify storage timber:settings dialog set value {"type":"minecraft:notice","title":"Timber Global Settings","external_title":"Timber Settings","inputs":[{"type":"minecraft:boolean","key":"standing","label":["",{"text":"Works When Standing"}],"initial":true,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"sneaking","label":["",{"text":"Works When Sneaking"}],"initial":false,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"destroy_leaves","label":["",{"text":"Destroy Leaves"}],"initial":true,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"plant_sapling","label":["",{"text":"Auto Plant Saplings","hover_event":{"action":"show_text","value":["",{"text":"All kind of saplings laying on the ground will get planted every 2 seconds."},{"text":"\n\nWhen a sapling get placed on mud, it turns into muddy mangrove roots for a fraction of a second due to datapack limitations.","color":"gray"}]}}],"initial":false,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"destroy_roots","label":["",{"text":"Destroy Roots","hover_event":{"action":"show_text","value":["",{"text":"Only root blocks which are not in the ground count."}]}}],"initial":true,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"start_chop_on_root","label":["",{"text":"Chop Tree When Roots Get Destroyed","hover_event":{"action":"show_text","value":["",{"text":"The datapack will chop the tree if you break its root blocks.\nOnly root blocks which are not in the ground count."}]}}],"initial":true,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"slow_chop","label":["",{"text":"Chop Slowly","hover_event":{"action":"show_text","value":["",{"text":"The tree won't be chopped instantly anymore."},{"text":"\n\nThis option can increase performance.","color":"gray"}]}}],"initial":false,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"chop_down","label":["",{"text":"Enable Chopping Down","hover_event":{"action":"show_text","value":"Normally when you cut a tree in the middle only the top part of the tree gets destroyed. But if this option is enabled the bottom part will get destroyed as well."}}],"initial":false,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"stop_chopping","label":["",{"text":"Stop Chopping Once Axe Breaks","hover_event":{"action":"show_text","value":"Stops the process of chopping the tree as soon as your axe doesn't have any durability left for the whole tree. When disabled, the tree will always be chopped finish."}}],"initial":false,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"wear_out","label":["",{"text":"Additionally Wear out Tool Depending on Tree Size","hover_event":{"action":"show_text","value":"The tool's durability will get decreased by the amount of destroyed logs (or stems). When disabled, only one durability gets deducted."}}],"initial":true,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"drop_loot","label":["",{"text":"Drop Loot Straight into Inventory","hover_event":{"action":"show_text","value":["",{"text":"Lets the dropped items of the tree teleport straight into your own inventory. As soon as you have a full inventory the loot gets dropped on the ground."},{"text":"\n\nThis option can increase performance.","color":"gray"}]}}],"initial":false,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"hunger","label":["",{"text":"Give Additional Hunger Effect","hover_event":{"action":"show_text","value":"Gives a hunger effect for 1 second with the same level as the size of the chopped tree."}}],"initial":false,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"stopsound","label":["",{"text":"Stop Loud Breaking Noises","hover_event":{"action":"show_text","value":["",{"text":"Suppresses loud breaking noises when chopping a tree."},{"text":"\n\nThis option is","color":"gray"},{"text":" experimental","color":"dark_red"},{"text":" because sometimes it still sounds loud for a fraction of a second.","color":"gray"}]}}],"initial":false,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"wooden_axe","label":["",{"text":"Enable Wooden Axe"}],"initial":true,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"stone_axe","label":["",{"text":"Enable Stone Axe"}],"initial":true,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"copper_axe","label":["",{"text":"Enable Copper Axe"}],"initial":true,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"iron_axe","label":["",{"text":"Enable Iron Axe"}],"initial":true,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"golden_axe","label":["",{"text":"Enable Golden Axe"}],"initial":true,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"diamond_axe","label":["",{"text":"Enable Diamond Axe"}],"initial":true,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"netherite_axe","label":["",{"text":"Enable Netherite Axe"}],"initial":true,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"chop_trees","label":["",{"text":"Chop Trees","hover_event":{"action":"show_text","value":"All kind of trees will get chopped."}}],"initial":true,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"chop_fungi","label":["",{"text":"Chop Fungi","hover_event":{"action":"show_text","value":["",{"text":"Huge fungi (nether trees) as well as huge mushrooms will get chopped."},{"text":"\n\nUse it on your own risk!\nWhen enabled, this datapack can't differentiate between generated fungi and player-placed fungi and therefore can potentially destroy your buildings.","color":"dark_red"}]}}],"initial":false,"on_true":"1","on_false":"0"},{"type":"minecraft:boolean","key":"persistent","label":["",{"text":"Destroy Player-Placed Trees","hover_event":{"action":"show_text","value":["",{"text":"Player-placed trees are trees made out of player-placed leaves (Look at leaf block with F3 debug screen: persistent = true)."},{"text":"\n\nUse it on your own risk!\nWhen enabled, this datapack can't differentiate between generated trees and player-placed trees anymore and therefore can potentially destroy your buildings.","color":"dark_red"}]}}],"initial":false,"on_true":"1","on_false":"0"},{"type":"minecraft:number_range","key":"blocks_per_chop","width":300,"label":["",{"text":"Set Amount of Destroyed Blocks per Chop","hover_event":{"action":"show_text","value":"Only used when \"Chop Slowly\" is enabled."}}],"start":1,"end":100,"step":1,"initial":1},{"type":"minecraft:number_range","key":"time_between_chops","width":300,"label":["",{"text":"Set Time in Ticks Between Chops","hover_event":{"action":"show_text","value":"Only used when \"Chop Slowly\" is enabled."}}],"start":1,"end":100,"step":1,"initial":1},{"type":"minecraft:number_range","key":"max_tree_size","width":300,"label":["",{"text":"Set Max. Tree Size","hover_event":{"action":"show_text","value":["",{"text":"Default: 555"},{"text":"\n\nTrees bigger than this set value won't get further processed."}]}}],"start":0,"end":1000,"step":1,"initial":0},{"type":"minecraft:number_range","key":"min_leaves_found","width":300,"label":["",{"text":"Set Min. Leaves to Valid a Tree","hover_event":{"action":"show_text","value":["",{"text":"Default: 5"},{"text":"\n\nThis datapack considers a tree only if it has this set value amount of naturally generated leaves at its trunk.\nFungi will get checked by the amount of cap blocks and additionally a 3×3 big cross on top of the stem."}]}}],"start":0,"end":10,"step":1,"initial":0}],"can_close_with_escape":true,"pause":true,"after_action":"close","action":{"label":{"translate":"gui.done"},"action":{"type":"minecraft:dynamic/run_command","template":"function timber:settings/set {\"standing\":$(standing),\"sneaking\":$(sneaking),\"destroy_leaves\":$(destroy_leaves),\"plant_sapling\":$(plant_sapling),\"destroy_roots\":$(destroy_roots),\"start_chop_on_root\":$(start_chop_on_root),\"slow_chop\":$(slow_chop),\"chop_down\":$(chop_down),\"stop_chopping\":$(stop_chopping),\"wear_out\":$(wear_out),\"drop_loot\":$(drop_loot),\"hunger\":$(hunger),\"stopsound\":$(stopsound),\"wooden_axe\":$(wooden_axe),\"stone_axe\":$(stone_axe),\"copper_axe\":$(copper_axe),\"iron_axe\":$(iron_axe),\"golden_axe\":$(golden_axe),\"diamond_axe\":$(diamond_axe),\"netherite_axe\":$(netherite_axe),\"chop_trees\":$(chop_trees),\"chop_fungi\":$(chop_fungi),\"persistent\":$(persistent),\"blocks_per_chop\":$(blocks_per_chop),\"time_between_chops\":$(time_between_chops),\"max_tree_size\":$(max_tree_size),\"min_leaves_found\":$(min_leaves_found)}"}}} -execute store result storage timber:settings dialog.inputs[0].initial byte 1 run scoreboard players get standing timber -execute store result storage timber:settings dialog.inputs[1].initial byte 1 run scoreboard players get sneaking timber -execute store result storage timber:settings dialog.inputs[2].initial byte 1 run scoreboard players get destroy_leaves timber -execute store result storage timber:settings dialog.inputs[3].initial byte 1 run scoreboard players get plant_sapling timber -execute store result storage timber:settings dialog.inputs[4].initial byte 1 run scoreboard players get destroy_roots timber -execute store result storage timber:settings dialog.inputs[5].initial byte 1 run scoreboard players get start_chop_on_root timber -execute store result storage timber:settings dialog.inputs[6].initial byte 1 run scoreboard players get slow_chop timber -execute store result storage timber:settings dialog.inputs[7].initial byte 1 run scoreboard players get chop_down timber -execute store result storage timber:settings dialog.inputs[8].initial byte 1 run scoreboard players get stop_chopping timber -execute store result storage timber:settings dialog.inputs[9].initial byte 1 run scoreboard players get wear_out timber -execute store result storage timber:settings dialog.inputs[10].initial byte 1 run scoreboard players get drop_loot timber -execute store result storage timber:settings dialog.inputs[11].initial byte 1 run scoreboard players get hunger timber -execute store result storage timber:settings dialog.inputs[12].initial byte 1 run scoreboard players get stopsound timber -execute store result storage timber:settings dialog.inputs[13].initial byte 1 run scoreboard players get wooden_axe timber -execute store result storage timber:settings dialog.inputs[14].initial byte 1 run scoreboard players get stone_axe timber -execute store result storage timber:settings dialog.inputs[15].initial byte 1 run scoreboard players get copper_axe timber -execute store result storage timber:settings dialog.inputs[16].initial byte 1 run scoreboard players get iron_axe timber -execute store result storage timber:settings dialog.inputs[17].initial byte 1 run scoreboard players get golden_axe timber -execute store result storage timber:settings dialog.inputs[18].initial byte 1 run scoreboard players get diamond_axe timber -execute store result storage timber:settings dialog.inputs[19].initial byte 1 run scoreboard players get netherite_axe timber -execute store result storage timber:settings dialog.inputs[20].initial byte 1 run scoreboard players get chop_trees timber -execute store result storage timber:settings dialog.inputs[21].initial byte 1 run scoreboard players get chop_fungi timber -execute store result storage timber:settings dialog.inputs[22].initial byte 1 run scoreboard players get persistent timber -execute store result storage timber:settings dialog.inputs[23].initial float 1 run scoreboard players get blocks_per_chop timber -execute store result storage timber:settings dialog.inputs[24].initial float 1 run scoreboard players get time_between_chops timber -execute store result storage timber:settings dialog.inputs[25].initial float 1 run scoreboard players get max_tree_size timber -execute store result storage timber:settings dialog.inputs[26].initial float 1 run scoreboard players get min_leaves_found timber -function timber:settings/show with storage timber:settings diff --git a/data/timber/function/settings/open/blocks.mcfunction b/data/timber/function/settings/open/blocks.mcfunction new file mode 100644 index 0000000..41138b4 --- /dev/null +++ b/data/timber/function/settings/open/blocks.mcfunction @@ -0,0 +1,7 @@ +# opens the "Blocks per Chop" slider with the current value +# only reachable for admins, so the trigger gets enabled only for them +execute unless score slow_chop timber matches 1 run return run function timber:settings/locked/blocks +scoreboard players enable @s TimberSetBlocks +data modify storage timber:settings dialog set value {"type":"minecraft:multi_action","title":"Timber Settings / Blocks per Chop","external_title":"Timber Blocks per Chop","body":{"type":"minecraft:plain_message","contents":["",{"text":"Amount of blocks which get destroyed per chop.\nOnly used when \"Chop Slowly\" is enabled."},"\n\n",{"text":"Default: 1","color":"gray"}],"width":300},"inputs":[{"type":"minecraft:number_range","key":"blocks_per_chop","width":300,"label":["",{"text":"Set Amount of Destroyed Blocks per Chop","hover_event":{"action":"show_text","value":"Only used when \"Chop Slowly\" is enabled."}}],"start":1,"end":100,"step":1,"initial":1}],"can_close_with_escape":true,"pause":false,"after_action":"none","columns":1,"actions":[{"label":"Save","width":300,"action":{"type":"minecraft:dynamic/run_command","template":"trigger TimberSetBlocks set 1$(blocks_per_chop)"}}],"exit_action":{"label":{"translate":"gui.back"},"width":300,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 1"}}} +execute store result storage timber:settings dialog.inputs[0].initial float 1 run scoreboard players get blocks_per_chop timber +function timber:settings/show with storage timber:settings diff --git a/data/timber/function/settings/open/help.mcfunction b/data/timber/function/settings/open/help.mcfunction new file mode 100644 index 0000000..2c3ee5f --- /dev/null +++ b/data/timber/function/settings/open/help.mcfunction @@ -0,0 +1,5 @@ +# opens the help dialog; the last button shows whether Timber is on or off for the player +data modify storage timber:settings dialog set value {"type":"minecraft:multi_action","title":"Timber Datapack","external_title":"Timber","body":{"type":"minecraft:plain_message","contents":["",{"text":"The Timber Datapack allows you to instantly chop down a tree just by breaking one log with any axe."},"\n\n",{"text":"By default, sneaking while breaking will not chop the tree."},"\n\n",{"text":"Every player can use "},{"text":"/trigger TimberToggle","color":"blue","click_event":{"action":"suggest_command","command":"/trigger TimberToggle"}},{"text":" or the button below to individually turn the datapack on or off."}],"width":300},"inputs":[],"can_close_with_escape":true,"pause":false,"actions":[{"label":"Check for New Updates","action":{"type":"minecraft:open_url","url":"https://github.com/Moggla/Timber-Datapack/releases"}},{"label":"Settings","tooltip":{"text":"Requires the tag timber_admin.","color":"gray"},"action":{"type":"minecraft:run_command","command":"trigger TimberToggle set 2"}}],"after_action":"none","exit_action":{"label":{"translate":"gui.back"},"action":{"type":"minecraft:run_command","command":"trigger TimberToggle set 4"}}} +execute unless score @s timber_disabled matches 1.. run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Timber"},{"translate":"options.on","color":"green"}]},"tooltip":"Turns the datapack on or off for you only.","action":{"type":"minecraft:run_command","command":"trigger TimberToggle set 3"}} +execute if score @s timber_disabled matches 1.. run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Timber"},{"translate":"options.off","color":"red"}]},"tooltip":"Turns the datapack on or off for you only.","action":{"type":"minecraft:run_command","command":"trigger TimberToggle set 3"}} +function timber:settings/show with storage timber:settings diff --git a/data/timber/function/settings/open/leaves.mcfunction b/data/timber/function/settings/open/leaves.mcfunction new file mode 100644 index 0000000..468e806 --- /dev/null +++ b/data/timber/function/settings/open/leaves.mcfunction @@ -0,0 +1,6 @@ +# opens the "Min. Leaves" slider with the current value +# only reachable for admins, so the trigger gets enabled only for them +scoreboard players enable @s TimberSetLeaves +data modify storage timber:settings dialog set value {"type":"minecraft:multi_action","title":"Timber Settings / Min. Leaves","external_title":"Timber Min. Leaves","body":{"type":"minecraft:plain_message","contents":["",{"text":"This datapack considers a tree only if it has this set value amount of naturally generated leaves at its trunk.\nFungi will get checked by the amount of cap blocks and additionally a 3×3 big cross on top of the stem."},"\n\n",{"text":"Default: 5","color":"gray"}],"width":300},"inputs":[{"type":"minecraft:number_range","key":"min_leaves_found","width":300,"label":["",{"text":"Set Min. Leaves to Valid a Tree","hover_event":{"action":"show_text","value":["",{"text":"Default: 5"},{"text":"\n\nThis datapack considers a tree only if it has this set value amount of naturally generated leaves at its trunk.\nFungi will get checked by the amount of cap blocks and additionally a 3×3 big cross on top of the stem."}]}}],"start":0,"end":10,"step":1,"initial":0}],"can_close_with_escape":true,"pause":false,"after_action":"none","columns":1,"actions":[{"label":"Save","width":300,"action":{"type":"minecraft:dynamic/run_command","template":"trigger TimberSetLeaves set 1$(min_leaves_found)"}}],"exit_action":{"label":{"translate":"gui.back"},"width":300,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 1"}}} +execute store result storage timber:settings dialog.inputs[0].initial float 1 run scoreboard players get min_leaves_found timber +function timber:settings/show with storage timber:settings diff --git a/data/timber/function/settings/open/menu.mcfunction b/data/timber/function/settings/open/menu.mcfunction new file mode 100644 index 0000000..b45e4cf --- /dev/null +++ b/data/timber/function/settings/open/menu.mcfunction @@ -0,0 +1,45 @@ +# opens the "Settings" settings page with the current values +# only reachable for admins +data modify storage timber:settings dialog set value {"type":"minecraft:multi_action","title":"Timber Settings","external_title":"Timber Settings","body":{"type":"minecraft:plain_message","contents":"Changes are saved for the whole server.","width":300},"inputs":[],"can_close_with_escape":true,"pause":false,"after_action":"none","columns":2,"actions":[],"exit_action":{"label":{"translate":"gui.back"},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 98"}}} +execute if score standing timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Works When Standing"},{"translate":"options.on","color":"green"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 100"}} +execute unless score standing timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Works When Standing"},{"translate":"options.off","color":"red"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 100"}} +execute if score sneaking timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Works When Sneaking"},{"translate":"options.on","color":"green"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 101"}} +execute unless score sneaking timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Works When Sneaking"},{"translate":"options.off","color":"red"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 101"}} +execute if score destroy_leaves timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Destroy Leaves"},{"translate":"options.on","color":"green"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 102"}} +execute unless score destroy_leaves timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Destroy Leaves"},{"translate":"options.off","color":"red"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 102"}} +execute if score plant_sapling timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Auto Plant Saplings"},{"translate":"options.on","color":"green"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 103"},"tooltip":["",{"text":"All kind of saplings laying on the ground will get planted every 2 seconds."},{"text":"\n\nWhen a sapling get placed on mud, it turns into muddy mangrove roots for a fraction of a second due to datapack limitations.","color":"gray"}]} +execute unless score plant_sapling timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Auto Plant Saplings"},{"translate":"options.off","color":"red"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 103"},"tooltip":["",{"text":"All kind of saplings laying on the ground will get planted every 2 seconds."},{"text":"\n\nWhen a sapling get placed on mud, it turns into muddy mangrove roots for a fraction of a second due to datapack limitations.","color":"gray"}]} +execute if score destroy_roots timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Destroy Roots"},{"translate":"options.on","color":"green"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 104"},"tooltip":["",{"text":"Only root blocks which are not in the ground count."}]} +execute unless score destroy_roots timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Destroy Roots"},{"translate":"options.off","color":"red"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 104"},"tooltip":["",{"text":"Only root blocks which are not in the ground count."}]} +execute if score start_chop_on_root timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Chop on Root Break"},{"translate":"options.on","color":"green"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 105"},"tooltip":["",{"text":"The datapack will chop the tree if you break its root blocks.\nOnly root blocks which are not in the ground count."}]} +execute unless score start_chop_on_root timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Chop on Root Break"},{"translate":"options.off","color":"red"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 105"},"tooltip":["",{"text":"The datapack will chop the tree if you break its root blocks.\nOnly root blocks which are not in the ground count."}]} +execute if score drop_loot timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Chop Slowly"},{"translate":"filled_map.locked","color":"gray"}]},"width":150,"tooltip":["",{"text":"Can't be enabled when \"Loot to Inventory\" is enabled.","color":"red"}]} +execute unless score drop_loot timber matches 1 if score slow_chop timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Chop Slowly"},{"translate":"options.on","color":"green"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 106"},"tooltip":["",{"text":"The tree won't be chopped instantly anymore."},{"text":"\n\nThis option can increase performance.","color":"gray"}]} +execute unless score drop_loot timber matches 1 unless score slow_chop timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Chop Slowly"},{"translate":"options.off","color":"red"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 106"},"tooltip":["",{"text":"The tree won't be chopped instantly anymore."},{"text":"\n\nThis option can increase performance.","color":"gray"}]} +execute if score chop_down timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Chop Down"},{"translate":"options.on","color":"green"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 107"},"tooltip":"Normally when you cut a tree in the middle only the top part of the tree gets destroyed. But if this option is enabled the bottom part will get destroyed as well."} +execute unless score chop_down timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Chop Down"},{"translate":"options.off","color":"red"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 107"},"tooltip":"Normally when you cut a tree in the middle only the top part of the tree gets destroyed. But if this option is enabled the bottom part will get destroyed as well."} +execute unless score wear_out timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Stop on Axe Break"},{"translate":"filled_map.locked","color":"gray"}]},"width":150,"tooltip":["",{"text":"Can't be enabled when \"Wear Out Tool More\" is disabled.","color":"red"}]} +execute if score wear_out timber matches 1 if score stop_chopping timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Stop on Axe Break"},{"translate":"options.on","color":"green"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 108"},"tooltip":"Stops the process of chopping the tree as soon as your axe doesn't have any durability left for the whole tree. When disabled, the tree will always be chopped finish."} +execute if score wear_out timber matches 1 unless score stop_chopping timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Stop on Axe Break"},{"translate":"options.off","color":"red"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 108"},"tooltip":"Stops the process of chopping the tree as soon as your axe doesn't have any durability left for the whole tree. When disabled, the tree will always be chopped finish."} +execute if score stop_chopping timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Wear Out Tool More"},{"translate":"filled_map.locked","color":"gray"}]},"width":150,"tooltip":["",{"text":"Can't be disabled when \"Stop on Axe Break\" is enabled.","color":"red"}]} +execute unless score stop_chopping timber matches 1 if score wear_out timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Wear Out Tool More"},{"translate":"options.on","color":"green"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 109"},"tooltip":"The tool's durability will get decreased by the amount of destroyed logs (or stems). When disabled, only one durability gets deducted."} +execute unless score stop_chopping timber matches 1 unless score wear_out timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Wear Out Tool More"},{"translate":"options.off","color":"red"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 109"},"tooltip":"The tool's durability will get decreased by the amount of destroyed logs (or stems). When disabled, only one durability gets deducted."} +execute if score slow_chop timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Loot to Inventory"},{"translate":"filled_map.locked","color":"gray"}]},"width":150,"tooltip":["",{"text":"Can't be enabled when \"Chop Slowly\" is enabled.","color":"red"}]} +execute unless score slow_chop timber matches 1 if score drop_loot timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Loot to Inventory"},{"translate":"options.on","color":"green"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 110"},"tooltip":["",{"text":"Lets the dropped items of the tree teleport straight into your own inventory. As soon as you have a full inventory the loot gets dropped on the ground."},{"text":"\n\nThis option can increase performance.","color":"gray"}]} +execute unless score slow_chop timber matches 1 unless score drop_loot timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Loot to Inventory"},{"translate":"options.off","color":"red"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 110"},"tooltip":["",{"text":"Lets the dropped items of the tree teleport straight into your own inventory. As soon as you have a full inventory the loot gets dropped on the ground."},{"text":"\n\nThis option can increase performance.","color":"gray"}]} +execute if score hunger timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Hunger Effect"},{"translate":"options.on","color":"green"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 111"},"tooltip":"Gives a hunger effect for 1 second with the same level as the size of the chopped tree."} +execute unless score hunger timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Hunger Effect"},{"translate":"options.off","color":"red"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 111"},"tooltip":"Gives a hunger effect for 1 second with the same level as the size of the chopped tree."} +execute if score stopsound timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Mute Breaking Sounds"},{"translate":"options.on","color":"green"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 112"},"tooltip":["",{"text":"Suppresses loud breaking noises when chopping a tree."},{"text":"\n\nThis option is","color":"gray"},{"text":" experimental","color":"dark_red"},{"text":" because sometimes it still sounds loud for a fraction of a second.","color":"gray"}]} +execute unless score stopsound timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Mute Breaking Sounds"},{"translate":"options.off","color":"red"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 112"},"tooltip":["",{"text":"Suppresses loud breaking noises when chopping a tree."},{"text":"\n\nThis option is","color":"gray"},{"text":" experimental","color":"dark_red"},{"text":" because sometimes it still sounds loud for a fraction of a second.","color":"gray"}]} +execute if score persistent timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Player-Placed Trees"},{"translate":"options.on","color":"green"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 115"},"tooltip":["",{"text":"Player-placed trees are trees made out of player-placed leaves (Look at leaf block with F3 debug screen: persistent = true)."},{"text":"\n\nUse it on your own risk!\nWhen enabled, this datapack can't differentiate between generated trees and player-placed trees anymore and therefore can potentially destroy your buildings.","color":"dark_red"}]} +execute unless score persistent timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Player-Placed Trees"},{"translate":"options.off","color":"red"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 115"},"tooltip":["",{"text":"Player-placed trees are trees made out of player-placed leaves (Look at leaf block with F3 debug screen: persistent = true)."},{"text":"\n\nUse it on your own risk!\nWhen enabled, this datapack can't differentiate between generated trees and player-placed trees anymore and therefore can potentially destroy your buildings.","color":"dark_red"}]} +execute if score chop_trees timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Chop Trees"},{"translate":"options.on","color":"green"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 113"},"tooltip":"All kind of trees will get chopped."} +execute unless score chop_trees timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Chop Trees"},{"translate":"options.off","color":"red"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 113"},"tooltip":"All kind of trees will get chopped."} +execute if score chop_fungi timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Chop Fungi"},{"translate":"options.on","color":"green"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 114"},"tooltip":["",{"text":"Huge fungi (nether trees) as well as huge mushrooms will get chopped."},{"text":"\n\nUse it on your own risk!\nWhen enabled, this datapack can't differentiate between generated fungi and player-placed fungi and therefore can potentially destroy your buildings.","color":"dark_red"}]} +execute unless score chop_fungi timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Chop Fungi"},{"translate":"options.off","color":"red"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 114"},"tooltip":["",{"text":"Huge fungi (nether trees) as well as huge mushrooms will get chopped."},{"text":"\n\nUse it on your own risk!\nWhen enabled, this datapack can't differentiate between generated fungi and player-placed fungi and therefore can potentially destroy your buildings.","color":"dark_red"}]} +data modify storage timber:settings dialog.actions append value {"label":"Blocks per Chop","width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 6"},"tooltip":"Amount of blocks which get destroyed per chop.\nOnly used when \"Chop Slowly\" is enabled."} +data modify storage timber:settings dialog.actions append value {"label":"Ticks Between Chops","width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 7"},"tooltip":"Time in ticks between two chops.\nOnly used when \"Chop Slowly\" is enabled."} +data modify storage timber:settings dialog.actions append value {"label":"Max. Tree Size","width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 8"},"tooltip":"Trees bigger than this set value won't get further processed."} +data modify storage timber:settings dialog.actions append value {"label":"Min. Leaves","width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 9"},"tooltip":"This datapack considers a tree only if it has this set value amount of naturally generated leaves at its trunk.\nFungi will get checked by the amount of cap blocks and additionally a 3×3 big cross on top of the stem."} +data modify storage timber:settings dialog.actions append value {"label":"Tools","width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 3"},"tooltip":"Axes"} +function timber:settings/show with storage timber:settings diff --git a/data/timber/function/settings/open/ticks.mcfunction b/data/timber/function/settings/open/ticks.mcfunction new file mode 100644 index 0000000..c778f8a --- /dev/null +++ b/data/timber/function/settings/open/ticks.mcfunction @@ -0,0 +1,7 @@ +# opens the "Ticks Between Chops" slider with the current value +# only reachable for admins, so the trigger gets enabled only for them +execute unless score slow_chop timber matches 1 run return run function timber:settings/locked/ticks +scoreboard players enable @s TimberSetTicks +data modify storage timber:settings dialog set value {"type":"minecraft:multi_action","title":"Timber Settings / Ticks Between Chops","external_title":"Timber Ticks Between Chops","body":{"type":"minecraft:plain_message","contents":["",{"text":"Time in ticks between two chops.\nOnly used when \"Chop Slowly\" is enabled."},"\n\n",{"text":"Default: 1","color":"gray"}],"width":300},"inputs":[{"type":"minecraft:number_range","key":"time_between_chops","width":300,"label":["",{"text":"Set Time in Ticks Between Chops","hover_event":{"action":"show_text","value":"Only used when \"Chop Slowly\" is enabled."}}],"start":1,"end":100,"step":1,"initial":1}],"can_close_with_escape":true,"pause":false,"after_action":"none","columns":1,"actions":[{"label":"Save","width":300,"action":{"type":"minecraft:dynamic/run_command","template":"trigger TimberSetTicks set 1$(time_between_chops)"}}],"exit_action":{"label":{"translate":"gui.back"},"width":300,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 1"}}} +execute store result storage timber:settings dialog.inputs[0].initial float 1 run scoreboard players get time_between_chops timber +function timber:settings/show with storage timber:settings diff --git a/data/timber/function/settings/open/tools.mcfunction b/data/timber/function/settings/open/tools.mcfunction new file mode 100644 index 0000000..611a153 --- /dev/null +++ b/data/timber/function/settings/open/tools.mcfunction @@ -0,0 +1,18 @@ +# opens the "Tools" settings page with the current values +# only reachable for admins +data modify storage timber:settings dialog set value {"type":"minecraft:multi_action","title":"Timber Settings / Tools","external_title":"Timber Tools","inputs":[],"can_close_with_escape":true,"pause":false,"after_action":"none","columns":2,"actions":[],"exit_action":{"label":{"translate":"gui.back"},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 1"}}} +execute if score wooden_axe timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Enable Wooden Axe"},{"translate":"options.on","color":"green"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 116"}} +execute unless score wooden_axe timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Enable Wooden Axe"},{"translate":"options.off","color":"red"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 116"}} +execute if score golden_axe timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Enable Golden Axe"},{"translate":"options.on","color":"green"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 117"}} +execute unless score golden_axe timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Enable Golden Axe"},{"translate":"options.off","color":"red"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 117"}} +execute if score stone_axe timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Enable Stone Axe"},{"translate":"options.on","color":"green"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 118"}} +execute unless score stone_axe timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Enable Stone Axe"},{"translate":"options.off","color":"red"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 118"}} +execute if score copper_axe timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Enable Copper Axe"},{"translate":"options.on","color":"green"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 119"}} +execute unless score copper_axe timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Enable Copper Axe"},{"translate":"options.off","color":"red"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 119"}} +execute if score iron_axe timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Enable Iron Axe"},{"translate":"options.on","color":"green"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 120"}} +execute unless score iron_axe timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Enable Iron Axe"},{"translate":"options.off","color":"red"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 120"}} +execute if score diamond_axe timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Enable Diamond Axe"},{"translate":"options.on","color":"green"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 121"}} +execute unless score diamond_axe timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Enable Diamond Axe"},{"translate":"options.off","color":"red"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 121"}} +execute if score netherite_axe timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Enable Netherite Axe"},{"translate":"options.on","color":"green"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 122"}} +execute unless score netherite_axe timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Enable Netherite Axe"},{"translate":"options.off","color":"red"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 122"}} +function timber:settings/show with storage timber:settings diff --git a/data/timber/function/settings/open/treesize.mcfunction b/data/timber/function/settings/open/treesize.mcfunction new file mode 100644 index 0000000..bf16513 --- /dev/null +++ b/data/timber/function/settings/open/treesize.mcfunction @@ -0,0 +1,6 @@ +# opens the "Max. Tree Size" slider with the current value +# only reachable for admins, so the trigger gets enabled only for them +scoreboard players enable @s TimberSetTreeSize +data modify storage timber:settings dialog set value {"type":"minecraft:multi_action","title":"Timber Settings / Max. Tree Size","external_title":"Timber Max. Tree Size","body":{"type":"minecraft:plain_message","contents":["",{"text":"Trees bigger than this set value won't get further processed."},"\n\n",{"text":"Default: 555","color":"gray"}],"width":300},"inputs":[{"type":"minecraft:number_range","key":"max_tree_size","width":300,"label":["",{"text":"Set Max. Tree Size","hover_event":{"action":"show_text","value":["",{"text":"Default: 555"},{"text":"\n\nTrees bigger than this set value won't get further processed."}]}}],"start":0,"end":1000,"step":1,"initial":0}],"can_close_with_escape":true,"pause":false,"after_action":"none","columns":1,"actions":[{"label":"Save","width":300,"action":{"type":"minecraft:dynamic/run_command","template":"trigger TimberSetTreeSize set 1$(max_tree_size)"}}],"exit_action":{"label":{"translate":"gui.back"},"width":300,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 1"}}} +execute store result storage timber:settings dialog.inputs[0].initial float 1 run scoreboard players get max_tree_size timber +function timber:settings/show with storage timber:settings diff --git a/data/timber/function/settings/request.mcfunction b/data/timber/function/settings/request.mcfunction new file mode 100644 index 0000000..f57cc3d --- /dev/null +++ b/data/timber/function/settings/request.mcfunction @@ -0,0 +1,4 @@ +# trigger TimberSettings +scoreboard players operation #req timber = @s TimberSettings +scoreboard players reset @s TimberSettings +function timber:settings/dispatch diff --git a/data/timber/function/settings/set.mcfunction b/data/timber/function/settings/set.mcfunction deleted file mode 100644 index 4823564..0000000 --- a/data/timber/function/settings/set.mcfunction +++ /dev/null @@ -1,46 +0,0 @@ -# applies the values submitted by the settings dialog -scoreboard players operation #old_slow_chop timber = slow_chop timber -scoreboard players operation #old_plant_sapling timber = plant_sapling timber -scoreboard players operation #old_drop_loot timber = drop_loot timber - -$scoreboard players set standing timber $(standing) -$scoreboard players set sneaking timber $(sneaking) -$scoreboard players set destroy_leaves timber $(destroy_leaves) -$scoreboard players set plant_sapling timber $(plant_sapling) -$scoreboard players set destroy_roots timber $(destroy_roots) -$scoreboard players set start_chop_on_root timber $(start_chop_on_root) -$scoreboard players set slow_chop timber $(slow_chop) -$scoreboard players set chop_down timber $(chop_down) -$scoreboard players set stop_chopping timber $(stop_chopping) -$scoreboard players set wear_out timber $(wear_out) -$scoreboard players set drop_loot timber $(drop_loot) -$scoreboard players set hunger timber $(hunger) -$scoreboard players set stopsound timber $(stopsound) -$scoreboard players set wooden_axe timber $(wooden_axe) -$scoreboard players set stone_axe timber $(stone_axe) -$scoreboard players set copper_axe timber $(copper_axe) -$scoreboard players set iron_axe timber $(iron_axe) -$scoreboard players set golden_axe timber $(golden_axe) -$scoreboard players set diamond_axe timber $(diamond_axe) -$scoreboard players set netherite_axe timber $(netherite_axe) -$scoreboard players set chop_trees timber $(chop_trees) -$scoreboard players set chop_fungi timber $(chop_fungi) -$scoreboard players set persistent timber $(persistent) -$data modify storage timber:settings input.blocks_per_chop set value $(blocks_per_chop) -execute store result score blocks_per_chop timber run data get storage timber:settings input.blocks_per_chop -$data modify storage timber:settings input.time_between_chops set value $(time_between_chops) -execute store result score time_between_chops timber run data get storage timber:settings input.time_between_chops -$data modify storage timber:settings input.max_tree_size set value $(max_tree_size) -execute store result score max_tree_size timber run data get storage timber:settings input.max_tree_size -$data modify storage timber:settings input.min_leaves_found set value $(min_leaves_found) -execute store result score min_leaves_found timber run data get storage timber:settings input.min_leaves_found -data remove storage timber:settings input - -# options which exclude each other: the one that was just enabled wins -execute if score slow_chop timber matches 1 if score drop_loot timber matches 1 if score #old_drop_loot timber matches 1 run scoreboard players set drop_loot timber 0 -execute if score slow_chop timber matches 1 if score drop_loot timber matches 1 unless score #old_drop_loot timber matches 1 run scoreboard players set slow_chop timber 0 -execute if score stop_chopping timber matches 1 run scoreboard players set wear_out timber 1 - -# start optional loops -execute if score slow_chop timber matches 1 unless score #old_slow_chop timber matches 1 run function timber:slow_chop/loop -execute if score plant_sapling timber matches 1 unless score #old_plant_sapling timber matches 1 run function timber:autoplant/loop diff --git a/data/timber/function/settings/toggle_self.mcfunction b/data/timber/function/settings/toggle_self.mcfunction new file mode 100644 index 0000000..ff98e67 --- /dev/null +++ b/data/timber/function/settings/toggle_self.mcfunction @@ -0,0 +1,3 @@ +# toggles Timber for the player and shows the help dialog again +function timber:settings/toggle/timber +function timber:settings/open/help diff --git a/data/timber/function/uninstall.mcfunction b/data/timber/function/uninstall.mcfunction index a76ceb7..bce2e88 100644 --- a/data/timber/function/uninstall.mcfunction +++ b/data/timber/function/uninstall.mcfunction @@ -11,6 +11,12 @@ scoreboard objectives remove timber_d_axe scoreboard objectives remove timber_n_axe scoreboard objectives remove timber scoreboard objectives remove TimberToggle +scoreboard objectives remove TimberSettings +scoreboard objectives remove TimberSetBlocks +scoreboard objectives remove TimberSetTicks +scoreboard objectives remove TimberSetTreeSize +scoreboard objectives remove TimberSetLeaves +scoreboard objectives remove timber_menu scoreboard objectives remove timber_disabled scoreboard objectives remove timber_prsistent From 9de4a18e3ce879b5a91ac4513e97b7ca33e19804 Mon Sep 17 00:00:00 2001 From: Flavio Felder Date: Sun, 20 Sep 2026 23:42:44 +0200 Subject: [PATCH 08/27] Update README screenshots and world options steps Co-Authored-By: Claude Sonnet 5 --- .github/resources/help.png | Bin 39109 -> 63136 bytes .github/resources/settings1.png | Bin 52080 -> 78911 bytes .github/resources/settings2.png | Bin 49622 -> 48643 bytes README.md | 7 ++++--- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/resources/help.png b/.github/resources/help.png index f11ce6c7d4b3d4616759fb012e7ce2a9eef90ea5..0ce827943b7f55e5f1020fbb8dc43f54a8ef930f 100644 GIT binary patch literal 63136 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 diff --git a/.github/resources/settings1.png b/.github/resources/settings1.png index 8593fa8d04d901cd1c7cc2bcf1bc364b8e7fc246..f94389b85da92a9d20a12afc56e7eaf83ba8fb1f 100644 GIT binary patch literal 78911 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 H|PeQbF} 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 N`<#Dd|8V^B{{T9>MqB^@ diff --git a/.github/resources/settings2.png b/.github/resources/settings2.png index c0f0467cded7536959ee06db95d5963033f14039..ae4e97e2e130cf875c766f1a570bbb805b1a7334 100644 GIT binary patch literal 48643 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 L=hv*iojCvBg=bkB diff --git a/README.md b/README.md index 54272e7..568e931 100644 --- a/README.md +++ b/README.md @@ -36,9 +36,10 @@ To get descriptions and settings for this datapack use: If you don't have access to commands in your singleplayer world 1. Pause the game -2. Click on Open to LAN -3. In the options, change **Allow Cheats**: OFF to ON -4. Click on Start LAN World +2. Click on `World Options...` +3. Toggle `LAN` to `ON` +4. Toggle `Command Access` to `ON` +5. Click on `Apply Changes`
From 490035ff148cb0a616e9ba979e0114cce4c9d9f8 Mon Sep 17 00:00:00 2001 From: Flavio Felder Date: Mon, 21 Sep 2026 00:25:16 +0200 Subject: [PATCH 09/27] Mention F3+F6 looking_at_block_state in player-placed trees tooltip Co-Authored-By: Claude Sonnet 5 --- data/timber/function/settings/open/menu.mcfunction | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/timber/function/settings/open/menu.mcfunction b/data/timber/function/settings/open/menu.mcfunction index b45e4cf..b27b578 100644 --- a/data/timber/function/settings/open/menu.mcfunction +++ b/data/timber/function/settings/open/menu.mcfunction @@ -31,8 +31,8 @@ execute if score hunger timber matches 1 run data modify storage timber:settings execute unless score hunger timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Hunger Effect"},{"translate":"options.off","color":"red"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 111"},"tooltip":"Gives a hunger effect for 1 second with the same level as the size of the chopped tree."} execute if score stopsound timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Mute Breaking Sounds"},{"translate":"options.on","color":"green"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 112"},"tooltip":["",{"text":"Suppresses loud breaking noises when chopping a tree."},{"text":"\n\nThis option is","color":"gray"},{"text":" experimental","color":"dark_red"},{"text":" because sometimes it still sounds loud for a fraction of a second.","color":"gray"}]} execute unless score stopsound timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Mute Breaking Sounds"},{"translate":"options.off","color":"red"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 112"},"tooltip":["",{"text":"Suppresses loud breaking noises when chopping a tree."},{"text":"\n\nThis option is","color":"gray"},{"text":" experimental","color":"dark_red"},{"text":" because sometimes it still sounds loud for a fraction of a second.","color":"gray"}]} -execute if score persistent timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Player-Placed Trees"},{"translate":"options.on","color":"green"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 115"},"tooltip":["",{"text":"Player-placed trees are trees made out of player-placed leaves (Look at leaf block with F3 debug screen: persistent = true)."},{"text":"\n\nUse it on your own risk!\nWhen enabled, this datapack can't differentiate between generated trees and player-placed trees anymore and therefore can potentially destroy your buildings.","color":"dark_red"}]} -execute unless score persistent timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Player-Placed Trees"},{"translate":"options.off","color":"red"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 115"},"tooltip":["",{"text":"Player-placed trees are trees made out of player-placed leaves (Look at leaf block with F3 debug screen: persistent = true)."},{"text":"\n\nUse it on your own risk!\nWhen enabled, this datapack can't differentiate between generated trees and player-placed trees anymore and therefore can potentially destroy your buildings.","color":"dark_red"}]} +execute if score persistent timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Player-Placed Trees"},{"translate":"options.on","color":"green"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 115"},"tooltip":["",{"text":"Player-placed trees are trees made out of player-placed leaves (Look at leaf block with F3 debug screen: persistent = true. Requires enabling looking_at_block_state via F3+F6)."},{"text":"\n\nUse it on your own risk!\nWhen enabled, this datapack can't differentiate between generated trees and player-placed trees anymore and therefore can potentially destroy your buildings.","color":"dark_red"}]} +execute unless score persistent timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Player-Placed Trees"},{"translate":"options.off","color":"red"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 115"},"tooltip":["",{"text":"Player-placed trees are trees made out of player-placed leaves (Look at leaf block with F3 debug screen: persistent = true. Requires enabling looking_at_block_state via F3+F6)."},{"text":"\n\nUse it on your own risk!\nWhen enabled, this datapack can't differentiate between generated trees and player-placed trees anymore and therefore can potentially destroy your buildings.","color":"dark_red"}]} execute if score chop_trees timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Chop Trees"},{"translate":"options.on","color":"green"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 113"},"tooltip":"All kind of trees will get chopped."} execute unless score chop_trees timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Chop Trees"},{"translate":"options.off","color":"red"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 113"},"tooltip":"All kind of trees will get chopped."} execute if score chop_fungi timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Chop Fungi"},{"translate":"options.on","color":"green"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 114"},"tooltip":["",{"text":"Huge fungi (nether trees) as well as huge mushrooms will get chopped."},{"text":"\n\nUse it on your own risk!\nWhen enabled, this datapack can't differentiate between generated fungi and player-placed fungi and therefore can potentially destroy your buildings.","color":"dark_red"}]} From 1cf58823efaff2da2c173d3cc7d3bda179428df1 Mon Sep 17 00:00:00 2001 From: Flavio Felder Date: Mon, 21 Sep 2026 00:57:08 +0200 Subject: [PATCH 10/27] Add PackTest CI workflows and first test - Add tests datapack with a test that mines the bottom log of an oak tree with a fake player and expects the whole tree to be chopped - Add test workflow for pull requests and pushes to main, run on Minecraft 26.1.2 and 26.3, with a "Test result" job to require in the branch ruleset - Add weekly latest workflow that tests newer Minecraft releases and opens an issue with the result - Add script to set up the Fabric server, Fabric API and PackTest Co-Authored-By: Claude Sonnet 5 --- .github/scripts/setup-test-server.sh | 37 ++++++ .github/workflows/latest.yml | 109 ++++++++++++++++++ .github/workflows/test.yml | 48 ++++++++ .../timber_test/test/chop_oak_tree.mcfunction | 26 +++++ tests/pack.mcmeta | 8 ++ 5 files changed, 228 insertions(+) create mode 100644 .github/scripts/setup-test-server.sh create mode 100644 .github/workflows/latest.yml create mode 100644 .github/workflows/test.yml create mode 100644 tests/data/timber_test/test/chop_oak_tree.mcfunction create mode 100644 tests/pack.mcmeta diff --git a/.github/scripts/setup-test-server.sh b/.github/scripts/setup-test-server.sh new file mode 100644 index 0000000..1365bad --- /dev/null +++ b/.github/scripts/setup-test-server.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +# Sets up a Fabric server with Fabric API and PackTest for the given Minecraft version in the current directory. +# Exit code 3 means the tooling is not available for that version (yet), any other non-zero code is a real error. +set -euo pipefail + +MC="$1" +UA="Moggla/Timber-Datapack CI" + +# prints the download url of the newest release of a Modrinth project for $MC, or nothing if there is none +modrinth_url() { + curl -fsSL -A "$UA" -G "https://api.modrinth.com/v2/project/$1/version" \ + --data-urlencode "game_versions=[\"$MC\"]" \ + --data-urlencode 'loaders=["fabric"]' \ + | jq -r '[.[] | select(.version_type == "release")][0].files[0].url // empty' +} + +FABRIC_API=$(modrinth_url fabric-api) +PACKTEST=$(modrinth_url packtest) +LOADER=$(curl -fsSL https://meta.fabricmc.net/v2/versions/loader | jq -r '[.[] | select(.stable)][0].version') +INSTALLER=$(curl -fsSL https://meta.fabricmc.net/v2/versions/installer | jq -r '[.[] | select(.stable)][0].version') +# the meta api answers unknown versions with an error or an empty list +SERVER=$(curl -sSL "https://meta.fabricmc.net/v2/versions/loader/$MC" | jq -r 'if type == "array" and length > 0 then "yes" else empty end' 2>/dev/null || true) + +MISSING="" +[ -n "$SERVER" ] || MISSING="$MISSING Fabric server," +[ -n "$FABRIC_API" ] || MISSING="$MISSING Fabric API," +[ -n "$PACKTEST" ] || MISSING="$MISSING PackTest," +if [ -n "$MISSING" ]; then + echo "Not available for Minecraft $MC yet:${MISSING%,}" >&2 + exit 3 +fi + +mkdir -p mods +curl -fsSL -o server.jar "https://meta.fabricmc.net/v2/versions/loader/$MC/$LOADER/$INSTALLER/server/jar" +curl -fsSL -o mods/fabric-api.jar "$FABRIC_API" +curl -fsSL -o mods/packtest.jar "$PACKTEST" +echo "eula=true" > eula.txt diff --git a/.github/workflows/latest.yml b/.github/workflows/latest.yml new file mode 100644 index 0000000..3d99483 --- /dev/null +++ b/.github/workflows/latest.yml @@ -0,0 +1,109 @@ +name: Latest Minecraft + +# Checks whether the datapack works on a Minecraft release newer than the ones in the test matrix +# and opens an issue with the result, so that nobody has to look at the runs. +# Not a required check: Fabric, Fabric API and PackTest usually lag a few days behind a new release. +on: + schedule: + - cron: "0 6 * * 1" + workflow_dispatch: + +permissions: + contents: read + issues: write + +jobs: + latest: + name: Latest Minecraft release + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Find latest release + id: version + run: | + MANIFEST=$(curl -fsSL https://piston-meta.mojang.com/mc/game/version_manifest_v2.json) + MC=$(jq -r '.latest.release' <<< "$MANIFEST") + URL=$(jq -r --arg id "$MC" '.versions[] | select(.id == $id) | .url' <<< "$MANIFEST") + JAVA=$(curl -fsSL "$URL" | jq -r '.javaVersion.majorVersion') + + # highest version that the test matrix already covers + SUPPORTED=$(sed -n 's/.*minecraft: "\([^"]*\)".*/\1/p' .github/workflows/test.yml | sort -V | tail -n 1) + + echo "mc=$MC" >> "$GITHUB_OUTPUT" + echo "java=$JAVA" >> "$GITHUB_OUTPUT" + if [ "$MC" != "$SUPPORTED" ] && [ "$(printf '%s\n%s\n' "$SUPPORTED" "$MC" | sort -V | tail -n 1)" = "$MC" ]; then + echo "newer=true" >> "$GITHUB_OUTPUT" + echo "Latest Minecraft release $MC is newer than the supported $SUPPORTED (Java $JAVA)" + else + echo "newer=false" >> "$GITHUB_OUTPUT" + echo "Latest Minecraft release $MC is already covered by the test matrix (highest: $SUPPORTED)." | tee -a "$GITHUB_STEP_SUMMARY" + fi + + - uses: actions/setup-java@v4 + if: steps.version.outputs.newer == 'true' + with: + distribution: temurin + java-version: ${{ steps.version.outputs.java }} + + - name: Set up Fabric server, Fabric API and PackTest + id: setup + if: steps.version.outputs.newer == 'true' + run: | + set +e + bash .github/scripts/setup-test-server.sh "${{ steps.version.outputs.mc }}" + CODE=$? + if [ $CODE -eq 3 ]; then + echo "ready=false" >> "$GITHUB_OUTPUT" + echo "Minecraft ${{ steps.version.outputs.mc }} can't be tested yet, the test tooling isn't available for it. Trying again next time." | tee -a "$GITHUB_STEP_SUMMARY" + exit 0 + fi + [ $CODE -eq 0 ] || exit $CODE + echo "ready=true" >> "$GITHUB_OUTPUT" + + - name: Install datapacks + if: steps.setup.outputs.ready == 'true' + run: | + mkdir -p world/datapacks/timber world/datapacks/timber_test + cp -r data pack.mcmeta 121 world/datapacks/timber/ + cp -r tests/. world/datapacks/timber_test/ + + - name: Run tests + id: tests + if: steps.setup.outputs.ready == 'true' + continue-on-error: true + run: java -Xmx2G -Dpacktest.auto -Dpacktest.auto.annotations -jar server.jar nogui + + - name: Open an issue with the result + if: steps.setup.outputs.ready == 'true' + env: + GH_TOKEN: ${{ github.token }} + MC: ${{ steps.version.outputs.mc }} + OUTCOME: ${{ steps.tests.outcome }} + RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + run: | + if [ "$OUTCOME" = "success" ]; then + TITLE="Minecraft $MC works with the datapack" + BODY="The tests pass on Minecraft $MC ([run]($RUN_URL)). + + To support it: + - bump \`max_format\` in \`pack.mcmeta\` + - add \`$MC\` to the matrix in \`.github/workflows/test.yml\` + - release the update" + else + TITLE="Minecraft $MC breaks the datapack" + BODY="The tests fail on Minecraft $MC ([run]($RUN_URL)). See the annotations of the run for the failed tests." + fi + + # closed issues count too, so a handled version doesn't get reported again + EXISTING=$(gh issue list --state all --search "\"$TITLE\" in:title" --json title --jq "[.[] | select(.title == \"$TITLE\")] | length") + if [ "$EXISTING" -gt 0 ]; then + echo "Issue \"$TITLE\" already exists." | tee -a "$GITHUB_STEP_SUMMARY" + else + gh issue create --title "$TITLE" --body "$BODY" --label "CI/CD" + echo "Opened issue \"$TITLE\"." | tee -a "$GITHUB_STEP_SUMMARY" + fi + + - name: Fail if the tests failed + if: steps.tests.outcome == 'failure' + run: exit 1 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..64bbc6d --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,48 @@ +name: Test + +on: + pull_request: + push: + branches: [main] + +jobs: + test: + name: Minecraft ${{ matrix.minecraft }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - minecraft: "26.1.2" + java: "25" + - minecraft: "26.3" + java: "25" + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: ${{ matrix.java }} + + - name: Set up Fabric server, Fabric API and PackTest + run: bash .github/scripts/setup-test-server.sh "${{ matrix.minecraft }}" + + - name: Install datapacks + run: | + mkdir -p world/datapacks/timber world/datapacks/timber_test + cp -r data pack.mcmeta 121 world/datapacks/timber/ + cp -r tests/. world/datapacks/timber_test/ + + - name: Run tests + run: java -Xmx2G -Dpacktest.auto -Dpacktest.auto.annotations -jar server.jar nogui + + # single check to require in the branch ruleset, so changing the matrix doesn't need a ruleset update + test-result: + name: Test result + needs: test + if: always() + runs-on: ubuntu-latest + steps: + - name: Fail if any test job failed or was cancelled + run: test "${{ needs.test.result }}" = "success" diff --git a/tests/data/timber_test/test/chop_oak_tree.mcfunction b/tests/data/timber_test/test/chop_oak_tree.mcfunction new file mode 100644 index 0000000..cccc070 --- /dev/null +++ b/tests/data/timber_test/test/chop_oak_tree.mcfunction @@ -0,0 +1,26 @@ +#> Mining the bottom log of an oak tree with an axe chops down the whole tree +# @timeout 200 +# @skyaccess true + +# platform +fill ~-3 ~-1 ~-4 ~5 ~-1 ~4 minecraft:dirt + +# oak tree: 5 logs high with leaves around the top +fill ~2 ~ ~ ~2 ~4 ~ minecraft:oak_log +fill ~1 ~3 ~-2 ~3 ~4 ~2 minecraft:oak_leaves replace minecraft:air +assert block ~2 ~4 ~ minecraft:oak_log +assert block ~1 ~3 ~ minecraft:oak_leaves + +# player mines the bottom log with an axe +dummy tester spawn +tp @e[type=minecraft:player,name=tester,limit=1] ~0.5 ~ ~0.5 +item replace entity @e[type=minecraft:player,name=tester,limit=1] weapon.mainhand with minecraft:iron_axe +dummy tester mine ~2 ~ ~ + +# the top log can only disappear when the whole tree is chopped +await delay 2s +assert not block ~2 ~4 ~ minecraft:oak_log +assert not block ~2 ~1 ~ minecraft:oak_log +assert not block ~2 ~3 ~ minecraft:oak_log +assert not block ~1 ~3 ~ minecraft:oak_leaves +assert not block ~3 ~4 ~ minecraft:oak_leaves diff --git a/tests/pack.mcmeta b/tests/pack.mcmeta new file mode 100644 index 0000000..26312cd --- /dev/null +++ b/tests/pack.mcmeta @@ -0,0 +1,8 @@ +{ + "pack": { + "description": "Tests for Timber Datapack (run with PackTest)", + "pack_format": 121.0, + "min_format": 101.0, + "max_format": 121.0 + } +} From b7c467b8f0d34dbde4ddb9966c3732dccc29d8db Mon Sep 17 00:00:00 2001 From: Flavio Felder Date: Mon, 21 Sep 2026 10:55:08 +0200 Subject: [PATCH 11/27] Update README --- README.md | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 568e931..3fd0e1a 100644 --- a/README.md +++ b/README.md @@ -17,12 +17,12 @@ Optionally, you can adjust everything as you wish. ³ All kind of custom trees like from **BiomesO'Plenty**, **The Biome Overhaul**, **Terrestria and Traverse**. # Preview - + - - + -*Enable the option "Chop Fungi" +> [!NOTE] +> Enable the option `Chop Fungi` # Settings To get descriptions and settings for this datapack use: @@ -61,13 +61,14 @@ Every player can individually turn the datapack ON or OFF by using: ## About This Advanced Manual will help you to add or change certain elements of this datapack which require editing of your side. -> [!IMPORTANT] -> In order to add or change the following you have to put the content of the datapack (Timber.zip) in a folder called "Timber". + +> [!IMPORTANT] +> In order to add or change the following you have to put the content of the datapack (`Timber.zip`) in a folder called `Timber`. > There are directories to different type of files. Open them with any kind of text editor. Below the directories is the code which you have to insert or replace (it's stated which one of these). ## Debugging -If you have difficulties or errors in trying to change the timber datapack, then please enable the output log (Minecraft Launcher → Settings → General → Enable "Open output log when Minecraft: Java Edition starts"). +If you have difficulties or errors in trying to change the timber datapack, then please enable the output log (Minecraft Launcher → Settings → General → Enable `Open output log when Minecraft: Java Edition starts`). ## Add modded axes @@ -75,14 +76,14 @@ If you have difficulties or errors in trying to change the timber datapack, then (Small note: Your modded axe will break after or below zero durability. This shouldn't be a problem. It's not really worth to implement a fix, because you're only able to chop a few more logs) Get the name of your modded axe: -Go ingame and type "/scoreboard objectives add x minecraft.used:" in chat. Now search for your modded axe there. -In my example it's called "**supertools.emeraldaxe**". +Go ingame and type `/scoreboard objectives add x minecraft.used:` in chat. Now search for your modded axe there. +In my example it's called `supertools.emeraldaxe`. Give your modded axe a nickname: -In my case a gave him the name "**timber_emera_axe**". +In my case a gave him the name `timber_emera_axe`. This is technically the name of your axe used by the datapack. You can name it however you like just look that you write it everywhere the same and that you follow these points: -Always use the prefix "timber_" -The max. length of the nickname is "timber_123456789" +Always use the prefix `timber_` +The max. length of the nickname is `timber_123456789` Don't use names twice for other axes! `Timber/data/timber/functions/init.mcfunctions` @@ -135,7 +136,7 @@ The content of the upper part of this file should look like this. `Timber/data/timber/tags/items/axes.json` -The content should look like this. Don't forget to add the "," after the netherite_axe and to change the ":" between supertools and emeraldaxe! +The content should look like this. Don't forget to add the `,` after the `netherite_axe` and to change the `:` between `supertools` and `emeraldaxe`! ```json {   "values":[ @@ -155,15 +156,16 @@ The content should look like this. Don't forget to add the "," after the netheri # Installation instructions - Download the Datapack - Start Minecraft (Java Edition) -- Click on "Singleplayer" +- Click on `Singleplayer` - Select your world in which to install this Datapack (Don't click on play!) -- Click on "Edit" -- Click on "Open World Folder" -- Place the .zip file inside the **datapacks** folder (Don't unzip it!) -- Join/Rejoin the world or use /reload +- Click on `Edit` +- Click on `Open World Folder` +- Place the `.zip` file inside the `datapacks` folder (Don't unzip it!) +- Join/Rejoin the world or use `/reload` - Finished! Enjoy! -Note: It does not work in creative mode! +> [!NOTE] +> It does not work in creative mode! # Server Compatibility Other Minecraft servers, unlike Vanilla or Realm, often do not fully support Datapacks. From be63cc7c55dceb43a53121d21c16d6c99059e006 Mon Sep 17 00:00:00 2001 From: Flavio Felder Date: Mon, 21 Sep 2026 12:56:48 +0200 Subject: [PATCH 12/27] Build tests on real worldgen trees with shared helpers The oak test now generates a vanilla tree with place feature and checks the whole area instead of single blocks. Shared setup lives in helper functions under timber_test:. Leaves left with distance=7 only warn: the test logs a TIMBER-WARNING line and the workflow shows it as a GitHub warning. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/test.yml | 13 ++++++++- .../function/assert_no_tree.mcfunction | 18 ++++++++++++ .../timber_test/function/count_d7.mcfunction | 6 ++++ .../timber_test/function/platform.mcfunction | 2 ++ .../timber_test/function/player.mcfunction | 5 ++++ .../data/timber_test/function/tree.mcfunction | 4 +++ .../timber_test/function/warn_d7.mcfunction | 5 ++++ .../timber_test/test/chop_oak_tree.mcfunction | 29 +++++++------------ 8 files changed, 63 insertions(+), 19 deletions(-) create mode 100644 tests/data/timber_test/function/assert_no_tree.mcfunction create mode 100644 tests/data/timber_test/function/count_d7.mcfunction create mode 100644 tests/data/timber_test/function/platform.mcfunction create mode 100644 tests/data/timber_test/function/player.mcfunction create mode 100644 tests/data/timber_test/function/tree.mcfunction create mode 100644 tests/data/timber_test/function/warn_d7.mcfunction diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 64bbc6d..0b428f9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -35,7 +35,18 @@ jobs: cp -r tests/. world/datapacks/timber_test/ - name: Run tests - run: java -Xmx2G -Dpacktest.auto -Dpacktest.auto.annotations -jar server.jar nogui + run: | + set -o pipefail + java -Xmx2G -Dpacktest.auto -Dpacktest.auto.annotations -jar server.jar nogui | tee server-output.log + + # tests can log a line with TIMBER-WARNING and still pass, show those as warnings so they don't go unnoticed + - name: Report test warnings + if: always() + run: | + grep -h 'TIMBER-WARNING' server-output.log | sed 's/.*TIMBER-WARNING //' | while read -r line; do + echo "::warning title=Test warning (Minecraft ${{ matrix.minecraft }})::$line" + echo "- Minecraft ${{ matrix.minecraft }}: $line" >> "$GITHUB_STEP_SUMMARY" + done || true # single check to require in the branch ruleset, so changing the matrix doesn't need a ruleset update test-result: diff --git a/tests/data/timber_test/function/assert_no_tree.mcfunction b/tests/data/timber_test/function/assert_no_tree.mcfunction new file mode 100644 index 0000000..a94ada5 --- /dev/null +++ b/tests/data/timber_test/function/assert_no_tree.mcfunction @@ -0,0 +1,18 @@ +#> Fails the test if any log or normal leaf is left around the tree. Leaves with distance=7 only produce a warning (see count_d7). +#> Replaces logs and leaves with air while counting, so run it last. +#> @param test name of the test, for the warning +scoreboard objectives add timber_test dummy +execute store result score #d7_after timber_test run clone ~1 ~ ~1 ~15 ~20 ~15 ~1 ~60 ~1 filtered #minecraft:leaves[distance=7] +fill ~1 ~60 ~1 ~15 ~80 ~15 minecraft:air +fill ~1 ~ ~1 ~15 ~20 ~15 minecraft:air replace #minecraft:leaves[distance=7] +execute store result score #logs timber_test run fill ~1 ~ ~1 ~15 ~20 ~15 minecraft:air replace #minecraft:logs +execute store result score #leaves timber_test run fill ~1 ~ ~1 ~15 ~20 ~15 minecraft:air replace #minecraft:leaves + +# warning: tests still pass, the marker is picked up by the CI workflow +$data modify storage timber_test:tmp test set value "$(test)" +execute store result storage timber_test:tmp before int 1 run scoreboard players get #d7_before timber_test +execute store result storage timber_test:tmp after int 1 run scoreboard players get #d7_after timber_test +execute if score #d7_after timber_test matches 1.. run function timber_test:warn_d7 with storage timber_test:tmp + +assert score #logs timber_test matches 0 +assert score #leaves timber_test matches 0 diff --git a/tests/data/timber_test/function/count_d7.mcfunction b/tests/data/timber_test/function/count_d7.mcfunction new file mode 100644 index 0000000..8534221 --- /dev/null +++ b/tests/data/timber_test/function/count_d7.mcfunction @@ -0,0 +1,6 @@ +#> Counts the leaves with distance=7 around the tree into #d7_before (call before chopping), doesn't change the tree. +#> Leaves without a log in range show up in the test environment but not in a normal world, the timber datapack ignores them. +#> Counting works by cloning them out of the way (clone reports the number of copied blocks), the copies are removed again. +scoreboard objectives add timber_test dummy +execute store result score #d7_before timber_test run clone ~1 ~ ~1 ~15 ~20 ~15 ~1 ~60 ~1 filtered #minecraft:leaves[distance=7] +fill ~1 ~60 ~1 ~15 ~80 ~15 minecraft:air diff --git a/tests/data/timber_test/function/platform.mcfunction b/tests/data/timber_test/function/platform.mcfunction new file mode 100644 index 0000000..d7b3cf6 --- /dev/null +++ b/tests/data/timber_test/function/platform.mcfunction @@ -0,0 +1,2 @@ +#> Flat dirt platform for a test, relative to the test origin +fill ~-2 ~-1 ~-2 ~16 ~-1 ~16 minecraft:dirt diff --git a/tests/data/timber_test/function/player.mcfunction b/tests/data/timber_test/function/player.mcfunction new file mode 100644 index 0000000..9d9b215 --- /dev/null +++ b/tests/data/timber_test/function/player.mcfunction @@ -0,0 +1,5 @@ +#> Spawns the dummy player "tester" next to the tree base at ~8 ~ ~8 and gives them an axe +#> @param axe item id, e.g. "minecraft:iron_axe" +dummy tester spawn +tp @e[type=minecraft:player,name=tester,limit=1] ~7.5 ~ ~8.5 +$item replace entity @e[type=minecraft:player,name=tester,limit=1] weapon.mainhand with $(axe) diff --git a/tests/data/timber_test/function/tree.mcfunction b/tests/data/timber_test/function/tree.mcfunction new file mode 100644 index 0000000..76b6be2 --- /dev/null +++ b/tests/data/timber_test/function/tree.mcfunction @@ -0,0 +1,4 @@ +#> Generates a vanilla worldgen tree with its trunk base at ~8 ~ ~8 +#> Not closer to the origin: the tree can't grow into the origin column (PackTest keeps something there), place feature fails silently +#> @param feature configured feature id, e.g. "minecraft:oak" +$place feature $(feature) ~8 ~ ~8 diff --git a/tests/data/timber_test/function/warn_d7.mcfunction b/tests/data/timber_test/function/warn_d7.mcfunction new file mode 100644 index 0000000..195ff7e --- /dev/null +++ b/tests/data/timber_test/function/warn_d7.mcfunction @@ -0,0 +1,5 @@ +#> Warns about leaves that were left standing. +#> Without a real player (CI) it says it in the server log, where the workflow turns lines with TIMBER-WARNING into GitHub warnings. +#> Real players get an orange chat message instead (tellraw isn't logged, say can't be colored). +$execute unless entity @a[name=!tester] run say TIMBER-WARNING $(test): $(after) leaves with distance=7 were left standing ($(before) before chopping) +$tellraw @a[name=!tester] {"text":"Test warning $(test): $(after) leaves with distance=7 were left standing ($(before) before chopping)","color":"gold"} diff --git a/tests/data/timber_test/test/chop_oak_tree.mcfunction b/tests/data/timber_test/test/chop_oak_tree.mcfunction index cccc070..75ef359 100644 --- a/tests/data/timber_test/test/chop_oak_tree.mcfunction +++ b/tests/data/timber_test/test/chop_oak_tree.mcfunction @@ -2,25 +2,18 @@ # @timeout 200 # @skyaccess true -# platform -fill ~-3 ~-1 ~-4 ~5 ~-1 ~4 minecraft:dirt +function timber_test:platform +function timber_test:tree {feature:"minecraft:oak"} +assert block ~8 ~ ~8 #minecraft:logs +assert block ~8 ~2 ~8 #minecraft:logs -# oak tree: 5 logs high with leaves around the top -fill ~2 ~ ~ ~2 ~4 ~ minecraft:oak_log -fill ~1 ~3 ~-2 ~3 ~4 ~2 minecraft:oak_leaves replace minecraft:air -assert block ~2 ~4 ~ minecraft:oak_log -assert block ~1 ~3 ~ minecraft:oak_leaves +# let the leaves settle before the tree is chopped +await delay 1s +function timber_test:count_d7 -# player mines the bottom log with an axe -dummy tester spawn -tp @e[type=minecraft:player,name=tester,limit=1] ~0.5 ~ ~0.5 -item replace entity @e[type=minecraft:player,name=tester,limit=1] weapon.mainhand with minecraft:iron_axe -dummy tester mine ~2 ~ ~ +function timber_test:player {axe:"minecraft:iron_axe"} +dummy tester mine ~8 ~ ~8 -# the top log can only disappear when the whole tree is chopped +# leaves are checked only after the chop had time to finish await delay 2s -assert not block ~2 ~4 ~ minecraft:oak_log -assert not block ~2 ~1 ~ minecraft:oak_log -assert not block ~2 ~3 ~ minecraft:oak_log -assert not block ~1 ~3 ~ minecraft:oak_leaves -assert not block ~3 ~4 ~ minecraft:oak_leaves +function timber_test:assert_no_tree {test:"chop_oak_tree"} From e4f275b2735b1e58ec3727772c2d5b6222ba6a87 Mon Sep 17 00:00:00 2001 From: Flavio Felder Date: Mon, 21 Sep 2026 13:04:11 +0200 Subject: [PATCH 13/27] Isolate tests with an arena template and per-test dummies Tests run in parallel next to each other. Each one now uses the 20x24x20 timber_test:arena template and its own tagged dummy player. Adds a birch test. Co-Authored-By: Claude Sonnet 5 --- .../data/timber_test/function/mine.mcfunction | 3 +++ .../timber_test/function/platform.mcfunction | 4 ++-- .../timber_test/function/player.mcfunction | 11 ++++++---- .../timber_test/function/warn_d7.mcfunction | 4 ++-- tests/data/timber_test/structure/arena.nbt | Bin 0 -> 114 bytes .../test/chop_birch_tree.mcfunction | 20 ++++++++++++++++++ .../timber_test/test/chop_oak_tree.mcfunction | 5 +++-- 7 files changed, 37 insertions(+), 10 deletions(-) create mode 100644 tests/data/timber_test/function/mine.mcfunction create mode 100644 tests/data/timber_test/structure/arena.nbt create mode 100644 tests/data/timber_test/test/chop_birch_tree.mcfunction diff --git a/tests/data/timber_test/function/mine.mcfunction b/tests/data/timber_test/function/mine.mcfunction new file mode 100644 index 0000000..51b49b5 --- /dev/null +++ b/tests/data/timber_test/function/mine.mcfunction @@ -0,0 +1,3 @@ +#> The dummy mines the block at the tree base (~8 ~ ~8) +#> @param name dummy name +$dummy $(name) mine ~8 ~ ~8 diff --git a/tests/data/timber_test/function/platform.mcfunction b/tests/data/timber_test/function/platform.mcfunction index d7b3cf6..d4e6b30 100644 --- a/tests/data/timber_test/function/platform.mcfunction +++ b/tests/data/timber_test/function/platform.mcfunction @@ -1,2 +1,2 @@ -#> Flat dirt platform for a test, relative to the test origin -fill ~-2 ~-1 ~-2 ~16 ~-1 ~16 minecraft:dirt +#> Flat dirt platform for a test, relative to the test origin (the arena template is 20x24x20) +fill ~ ~-1 ~ ~19 ~-1 ~19 minecraft:dirt diff --git a/tests/data/timber_test/function/player.mcfunction b/tests/data/timber_test/function/player.mcfunction index 9d9b215..7453793 100644 --- a/tests/data/timber_test/function/player.mcfunction +++ b/tests/data/timber_test/function/player.mcfunction @@ -1,5 +1,8 @@ -#> Spawns the dummy player "tester" next to the tree base at ~8 ~ ~8 and gives them an axe +#> Spawns a dummy player next to the tree base at ~8 ~ ~8 and gives them an axe +#> Every test needs its own dummy name, tests run in parallel +#> @param name dummy name, unique per test #> @param axe item id, e.g. "minecraft:iron_axe" -dummy tester spawn -tp @e[type=minecraft:player,name=tester,limit=1] ~7.5 ~ ~8.5 -$item replace entity @e[type=minecraft:player,name=tester,limit=1] weapon.mainhand with $(axe) +$dummy $(name) spawn +$tag @e[type=minecraft:player,name=$(name),limit=1] add timber_test_dummy +$tp @e[type=minecraft:player,name=$(name),limit=1] ~7.5 ~ ~8.5 +$item replace entity @e[type=minecraft:player,name=$(name),limit=1] weapon.mainhand with $(axe) diff --git a/tests/data/timber_test/function/warn_d7.mcfunction b/tests/data/timber_test/function/warn_d7.mcfunction index 195ff7e..65081bb 100644 --- a/tests/data/timber_test/function/warn_d7.mcfunction +++ b/tests/data/timber_test/function/warn_d7.mcfunction @@ -1,5 +1,5 @@ #> Warns about leaves that were left standing. #> Without a real player (CI) it says it in the server log, where the workflow turns lines with TIMBER-WARNING into GitHub warnings. #> Real players get an orange chat message instead (tellraw isn't logged, say can't be colored). -$execute unless entity @a[name=!tester] run say TIMBER-WARNING $(test): $(after) leaves with distance=7 were left standing ($(before) before chopping) -$tellraw @a[name=!tester] {"text":"Test warning $(test): $(after) leaves with distance=7 were left standing ($(before) before chopping)","color":"gold"} +$execute unless entity @a[tag=!timber_test_dummy] run say TIMBER-WARNING $(test): $(after) leaves with distance=7 were left standing ($(before) before chopping) +$tellraw @a[tag=!timber_test_dummy] {"text":"Test warning $(test): $(after) leaves with distance=7 were left standing ($(before) before chopping)","color":"gold"} diff --git a/tests/data/timber_test/structure/arena.nbt b/tests/data/timber_test/structure/arena.nbt new file mode 100644 index 0000000000000000000000000000000000000000..b7048456d198eeaf6fb1a1792f2af0f3b5bb01d9 GIT binary patch literal 114 zcmb2|=3oE;CN9-(Lp}!w0oVNkIu|<*MPA8TT6e-jEn<`P)Mm^1cYmBbeO^0`RU#!$ z&X3V$mxKKM1v_3Qgsy8+Q{B|*=T_YH^y Mining the bottom log of a birch tree with an axe chops down the whole tree +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:birch"} +assert block ~8 ~ ~8 #minecraft:logs +assert block ~8 ~2 ~8 #minecraft:logs + +# let the leaves settle before the tree is chopped +await delay 1s +function timber_test:count_d7 + +function timber_test:player {name:"birch_tester",axe:"minecraft:iron_axe"} +function timber_test:mine {name:"birch_tester"} + +# leaves are checked only after the chop had time to finish +await delay 2s +function timber_test:assert_no_tree {test:"chop_birch_tree"} diff --git a/tests/data/timber_test/test/chop_oak_tree.mcfunction b/tests/data/timber_test/test/chop_oak_tree.mcfunction index 75ef359..b8cd7a7 100644 --- a/tests/data/timber_test/test/chop_oak_tree.mcfunction +++ b/tests/data/timber_test/test/chop_oak_tree.mcfunction @@ -1,4 +1,5 @@ #> Mining the bottom log of an oak tree with an axe chops down the whole tree +# @template timber_test:arena # @timeout 200 # @skyaccess true @@ -11,8 +12,8 @@ assert block ~8 ~2 ~8 #minecraft:logs await delay 1s function timber_test:count_d7 -function timber_test:player {axe:"minecraft:iron_axe"} -dummy tester mine ~8 ~ ~8 +function timber_test:player {name:"oak_tester",axe:"minecraft:iron_axe"} +function timber_test:mine {name:"oak_tester"} # leaves are checked only after the chop had time to finish await delay 2s From bf3292c181fef9d7c8ea0cc73b0dd065f856d6fb Mon Sep 17 00:00:00 2001 From: Flavio Felder Date: Mon, 21 Sep 2026 13:10:08 +0200 Subject: [PATCH 14/27] Wait for the datapack to finish instead of a fixed delay Tests wait until the base log is gone, give the datapack two ticks to react and then wait until no marker is left in the arena. await does not work inside a called function, so the three lines are repeated in each test. Co-Authored-By: Claude Sonnet 5 --- .../data/timber_test/test/chop_birch_tree.mcfunction | 11 +++++++++-- tests/data/timber_test/test/chop_oak_tree.mcfunction | 11 +++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/tests/data/timber_test/test/chop_birch_tree.mcfunction b/tests/data/timber_test/test/chop_birch_tree.mcfunction index 0916451..6e8640f 100644 --- a/tests/data/timber_test/test/chop_birch_tree.mcfunction +++ b/tests/data/timber_test/test/chop_birch_tree.mcfunction @@ -15,6 +15,13 @@ function timber_test:count_d7 function timber_test:player {name:"birch_tester",axe:"minecraft:iron_axe"} function timber_test:mine {name:"birch_tester"} -# leaves are checked only after the chop had time to finish -await delay 2s +# wait until the datapack is done with the tree +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the base log is gone: the dummy hit the tree +# 2. two ticks pass: the datapack reacts to the mined block in the next tick +# 3. no marker is left in the arena: chopping and slow chop delete their markers when they are done +await not block ~8 ~ ~8 #minecraft:logs +await delay 2t +await not entity @e[type=minecraft:marker,dx=19,dy=23,dz=19] + function timber_test:assert_no_tree {test:"chop_birch_tree"} diff --git a/tests/data/timber_test/test/chop_oak_tree.mcfunction b/tests/data/timber_test/test/chop_oak_tree.mcfunction index b8cd7a7..2365221 100644 --- a/tests/data/timber_test/test/chop_oak_tree.mcfunction +++ b/tests/data/timber_test/test/chop_oak_tree.mcfunction @@ -15,6 +15,13 @@ function timber_test:count_d7 function timber_test:player {name:"oak_tester",axe:"minecraft:iron_axe"} function timber_test:mine {name:"oak_tester"} -# leaves are checked only after the chop had time to finish -await delay 2s +# wait until the datapack is done with the tree +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the base log is gone: the dummy hit the tree +# 2. two ticks pass: the datapack reacts to the mined block in the next tick +# 3. no marker is left in the arena: chopping and slow chop delete their markers when they are done +await not block ~8 ~ ~8 #minecraft:logs +await delay 2t +await not entity @e[type=minecraft:marker,dx=19,dy=23,dz=19] + function timber_test:assert_no_tree {test:"chop_oak_tree"} From cdcba58afce450c9c383e0486731814252dc472f Mon Sep 17 00:00:00 2001 From: Flavio Felder Date: Mon, 21 Sep 2026 13:53:29 +0200 Subject: [PATCH 15/27] Add chop tests for 15 vanilla tree types Every test generates the tree with place feature in a 40x40x40 arena, lets the dummy mine the base log and checks that no log or leaf is left. The base check only looks at the base log, acacia and azalea trunks lean away early. Checks run in two slabs because a command may only touch 32768 blocks. Co-Authored-By: Claude Sonnet 5 --- .../function/assert_no_tree.mcfunction | 21 +++++++++----- .../timber_test/function/count_d7.mcfunction | 9 ++++-- .../data/timber_test/function/mine.mcfunction | 4 +-- .../timber_test/function/platform.mcfunction | 4 +-- .../timber_test/function/player.mcfunction | 6 ++-- .../data/timber_test/function/tree.mcfunction | 4 +-- tests/data/timber_test/structure/arena.nbt | Bin 114 -> 112 bytes .../test/chop_acacia_tree.mcfunction | 26 ++++++++++++++++++ .../test/chop_azalea_tree.mcfunction | 26 ++++++++++++++++++ .../test/chop_birch_tree.mcfunction | 11 ++++---- .../test/chop_cherry_tree.mcfunction | 26 ++++++++++++++++++ .../test/chop_dark_oak_tree.mcfunction | 26 ++++++++++++++++++ .../test/chop_fancy_oak_tree.mcfunction | 26 ++++++++++++++++++ .../test/chop_jungle_tree.mcfunction | 26 ++++++++++++++++++ .../test/chop_mega_jungle_tree.mcfunction | 26 ++++++++++++++++++ .../test/chop_mega_pine_tree.mcfunction | 26 ++++++++++++++++++ .../test/chop_mega_spruce_tree.mcfunction | 26 ++++++++++++++++++ .../timber_test/test/chop_oak_tree.mcfunction | 11 ++++---- .../test/chop_pale_oak_tree.mcfunction | 26 ++++++++++++++++++ .../test/chop_pine_tree.mcfunction | 26 ++++++++++++++++++ .../test/chop_spruce_tree.mcfunction | 26 ++++++++++++++++++ .../test/chop_swamp_oak_tree.mcfunction | 26 ++++++++++++++++++ 22 files changed, 377 insertions(+), 31 deletions(-) create mode 100644 tests/data/timber_test/test/chop_acacia_tree.mcfunction create mode 100644 tests/data/timber_test/test/chop_azalea_tree.mcfunction create mode 100644 tests/data/timber_test/test/chop_cherry_tree.mcfunction create mode 100644 tests/data/timber_test/test/chop_dark_oak_tree.mcfunction create mode 100644 tests/data/timber_test/test/chop_fancy_oak_tree.mcfunction create mode 100644 tests/data/timber_test/test/chop_jungle_tree.mcfunction create mode 100644 tests/data/timber_test/test/chop_mega_jungle_tree.mcfunction create mode 100644 tests/data/timber_test/test/chop_mega_pine_tree.mcfunction create mode 100644 tests/data/timber_test/test/chop_mega_spruce_tree.mcfunction create mode 100644 tests/data/timber_test/test/chop_pale_oak_tree.mcfunction create mode 100644 tests/data/timber_test/test/chop_pine_tree.mcfunction create mode 100644 tests/data/timber_test/test/chop_spruce_tree.mcfunction create mode 100644 tests/data/timber_test/test/chop_swamp_oak_tree.mcfunction diff --git a/tests/data/timber_test/function/assert_no_tree.mcfunction b/tests/data/timber_test/function/assert_no_tree.mcfunction index a94ada5..adc6f72 100644 --- a/tests/data/timber_test/function/assert_no_tree.mcfunction +++ b/tests/data/timber_test/function/assert_no_tree.mcfunction @@ -1,12 +1,19 @@ -#> Fails the test if any log or normal leaf is left around the tree. Leaves with distance=7 only produce a warning (see count_d7). -#> Replaces logs and leaves with air while counting, so run it last. +#> Fails the test if any log or normal leaf is left in the arena. Leaves with distance=7 only produce a warning (see count_d7). +#> Replaces logs and leaves with air while counting, so run it last. The arena is handled in two slabs, a command may only touch 32768 blocks. #> @param test name of the test, for the warning scoreboard objectives add timber_test dummy -execute store result score #d7_after timber_test run clone ~1 ~ ~1 ~15 ~20 ~15 ~1 ~60 ~1 filtered #minecraft:leaves[distance=7] -fill ~1 ~60 ~1 ~15 ~80 ~15 minecraft:air -fill ~1 ~ ~1 ~15 ~20 ~15 minecraft:air replace #minecraft:leaves[distance=7] -execute store result score #logs timber_test run fill ~1 ~ ~1 ~15 ~20 ~15 minecraft:air replace #minecraft:logs -execute store result score #leaves timber_test run fill ~1 ~ ~1 ~15 ~20 ~15 minecraft:air replace #minecraft:leaves +execute store result score #d7_after timber_test run clone ~0 ~0 ~0 ~39 ~19 ~39 ~0 ~64 ~0 filtered #minecraft:leaves[distance=7] +execute store result score #d7_b timber_test run clone ~0 ~20 ~0 ~39 ~39 ~39 ~0 ~64 ~0 filtered #minecraft:leaves[distance=7] +scoreboard players operation #d7_after timber_test += #d7_b timber_test +fill ~0 ~64 ~0 ~39 ~83 ~39 minecraft:air +fill ~0 ~0 ~0 ~39 ~19 ~39 minecraft:air replace #minecraft:leaves[distance=7] +fill ~0 ~20 ~0 ~39 ~39 ~39 minecraft:air replace #minecraft:leaves[distance=7] +execute store result score #logs timber_test run fill ~0 ~0 ~0 ~39 ~19 ~39 minecraft:air replace #minecraft:logs +execute store result score #logs_b timber_test run fill ~0 ~20 ~0 ~39 ~39 ~39 minecraft:air replace #minecraft:logs +scoreboard players operation #logs timber_test += #logs_b timber_test +execute store result score #leaves timber_test run fill ~0 ~0 ~0 ~39 ~19 ~39 minecraft:air replace #minecraft:leaves +execute store result score #leaves_b timber_test run fill ~0 ~20 ~0 ~39 ~39 ~39 minecraft:air replace #minecraft:leaves +scoreboard players operation #leaves timber_test += #leaves_b timber_test # warning: tests still pass, the marker is picked up by the CI workflow $data modify storage timber_test:tmp test set value "$(test)" diff --git a/tests/data/timber_test/function/count_d7.mcfunction b/tests/data/timber_test/function/count_d7.mcfunction index 8534221..1bd4b88 100644 --- a/tests/data/timber_test/function/count_d7.mcfunction +++ b/tests/data/timber_test/function/count_d7.mcfunction @@ -1,6 +1,9 @@ -#> Counts the leaves with distance=7 around the tree into #d7_before (call before chopping), doesn't change the tree. +#> Counts the leaves with distance=7 in the arena into #d7_before (call before chopping), doesn't change the tree. #> Leaves without a log in range show up in the test environment but not in a normal world, the timber datapack ignores them. #> Counting works by cloning them out of the way (clone reports the number of copied blocks), the copies are removed again. +#> The arena is counted in two slabs, a command may only touch 32768 blocks. scoreboard objectives add timber_test dummy -execute store result score #d7_before timber_test run clone ~1 ~ ~1 ~15 ~20 ~15 ~1 ~60 ~1 filtered #minecraft:leaves[distance=7] -fill ~1 ~60 ~1 ~15 ~80 ~15 minecraft:air +execute store result score #d7_before timber_test run clone ~0 ~0 ~0 ~39 ~19 ~39 ~0 ~64 ~0 filtered #minecraft:leaves[distance=7] +execute store result score #d7_b timber_test run clone ~0 ~20 ~0 ~39 ~39 ~39 ~0 ~64 ~0 filtered #minecraft:leaves[distance=7] +scoreboard players operation #d7_before timber_test += #d7_b timber_test +fill ~0 ~64 ~0 ~39 ~83 ~39 minecraft:air diff --git a/tests/data/timber_test/function/mine.mcfunction b/tests/data/timber_test/function/mine.mcfunction index 51b49b5..8c3d36a 100644 --- a/tests/data/timber_test/function/mine.mcfunction +++ b/tests/data/timber_test/function/mine.mcfunction @@ -1,3 +1,3 @@ -#> The dummy mines the block at the tree base (~8 ~ ~8) +#> The dummy mines the block at the tree base (~20 ~ ~20) #> @param name dummy name -$dummy $(name) mine ~8 ~ ~8 +$dummy $(name) mine ~20 ~ ~20 diff --git a/tests/data/timber_test/function/platform.mcfunction b/tests/data/timber_test/function/platform.mcfunction index d4e6b30..9695489 100644 --- a/tests/data/timber_test/function/platform.mcfunction +++ b/tests/data/timber_test/function/platform.mcfunction @@ -1,2 +1,2 @@ -#> Flat dirt platform for a test, relative to the test origin (the arena template is 20x24x20) -fill ~ ~-1 ~ ~19 ~-1 ~19 minecraft:dirt +#> Flat dirt platform for a test, relative to the test origin (the arena template is 40x40x40) +fill ~ ~-1 ~ ~39 ~-1 ~39 minecraft:dirt diff --git a/tests/data/timber_test/function/player.mcfunction b/tests/data/timber_test/function/player.mcfunction index 7453793..331f591 100644 --- a/tests/data/timber_test/function/player.mcfunction +++ b/tests/data/timber_test/function/player.mcfunction @@ -1,8 +1,8 @@ -#> Spawns a dummy player next to the tree base at ~8 ~ ~8 and gives them an axe -#> Every test needs its own dummy name, tests run in parallel +#> Spawns a dummy player next to the tree base at ~20 ~ ~20 and gives them an axe +#> Every test needs its own dummy name, tests run in parallel. Player names have at most 16 characters, longer ones silently fail to spawn. #> @param name dummy name, unique per test #> @param axe item id, e.g. "minecraft:iron_axe" $dummy $(name) spawn $tag @e[type=minecraft:player,name=$(name),limit=1] add timber_test_dummy -$tp @e[type=minecraft:player,name=$(name),limit=1] ~7.5 ~ ~8.5 +$tp @e[type=minecraft:player,name=$(name),limit=1] ~19.5 ~ ~20.5 $item replace entity @e[type=minecraft:player,name=$(name),limit=1] weapon.mainhand with $(axe) diff --git a/tests/data/timber_test/function/tree.mcfunction b/tests/data/timber_test/function/tree.mcfunction index 76b6be2..6be187b 100644 --- a/tests/data/timber_test/function/tree.mcfunction +++ b/tests/data/timber_test/function/tree.mcfunction @@ -1,4 +1,4 @@ -#> Generates a vanilla worldgen tree with its trunk base at ~8 ~ ~8 +#> Generates a vanilla worldgen tree with its trunk base at ~20 ~ ~20, the middle of the arena #> Not closer to the origin: the tree can't grow into the origin column (PackTest keeps something there), place feature fails silently #> @param feature configured feature id, e.g. "minecraft:oak" -$place feature $(feature) ~8 ~ ~8 +$place feature $(feature) ~20 ~ ~20 diff --git a/tests/data/timber_test/structure/arena.nbt b/tests/data/timber_test/structure/arena.nbt index b7048456d198eeaf6fb1a1792f2af0f3b5bb01d9..e8a5fd1ed09c07ac65d50121d4db67cfbaa7341b 100644 GIT binary patch literal 112 zcmb2|=3oE;CN9(NjeHIc0<80QJm@$U{)266)XMlxlX&~?Z|0f*r;)1<%$p2HCN4>;`Ysv QkKM#vZod5LGetmq0I@DEJpcdz literal 114 zcmb2|=3oE;CN9-(Lp}!w0oVNkIu|<*MPA8TT6e-jEn<`P)Mm^1cYmBbeO^0`RU#!$ z&X3V$mxKKM1v_3Qgsy8+Q{B|*=T_YH^y Mining the bottom log of an acacia tree with an axe chops down the whole tree +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:acacia"} +assert block ~20 ~ ~20 #minecraft:logs + +# let the leaves settle before the tree is chopped +await delay 1s +function timber_test:count_d7 + +function timber_test:player {name:"acacia_t",axe:"minecraft:iron_axe"} +function timber_test:mine {name:"acacia_t"} + +# wait until the datapack is done with the tree +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the base log is gone: the dummy hit the tree +# 2. two ticks pass: the datapack reacts to the mined block in the next tick +# 3. no marker is left in the arena: chopping and slow chop delete their markers when they are done +await not block ~20 ~ ~20 #minecraft:logs +await delay 2t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_no_tree {test:"chop_acacia_tree"} diff --git a/tests/data/timber_test/test/chop_azalea_tree.mcfunction b/tests/data/timber_test/test/chop_azalea_tree.mcfunction new file mode 100644 index 0000000..ee0b4a6 --- /dev/null +++ b/tests/data/timber_test/test/chop_azalea_tree.mcfunction @@ -0,0 +1,26 @@ +#> Mining the bottom log of an azalea tree with an axe chops down the whole tree +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:azalea_tree"} +assert block ~20 ~ ~20 #minecraft:logs + +# let the leaves settle before the tree is chopped +await delay 1s +function timber_test:count_d7 + +function timber_test:player {name:"azalea_t",axe:"minecraft:iron_axe"} +function timber_test:mine {name:"azalea_t"} + +# wait until the datapack is done with the tree +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the base log is gone: the dummy hit the tree +# 2. two ticks pass: the datapack reacts to the mined block in the next tick +# 3. no marker is left in the arena: chopping and slow chop delete their markers when they are done +await not block ~20 ~ ~20 #minecraft:logs +await delay 2t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_no_tree {test:"chop_azalea_tree"} diff --git a/tests/data/timber_test/test/chop_birch_tree.mcfunction b/tests/data/timber_test/test/chop_birch_tree.mcfunction index 6e8640f..6c792b6 100644 --- a/tests/data/timber_test/test/chop_birch_tree.mcfunction +++ b/tests/data/timber_test/test/chop_birch_tree.mcfunction @@ -5,23 +5,22 @@ function timber_test:platform function timber_test:tree {feature:"minecraft:birch"} -assert block ~8 ~ ~8 #minecraft:logs -assert block ~8 ~2 ~8 #minecraft:logs +assert block ~20 ~ ~20 #minecraft:logs # let the leaves settle before the tree is chopped await delay 1s function timber_test:count_d7 -function timber_test:player {name:"birch_tester",axe:"minecraft:iron_axe"} -function timber_test:mine {name:"birch_tester"} +function timber_test:player {name:"birch_t",axe:"minecraft:iron_axe"} +function timber_test:mine {name:"birch_t"} # wait until the datapack is done with the tree # (await doesn't work inside a called function, so this block is repeated in every test) # 1. the base log is gone: the dummy hit the tree # 2. two ticks pass: the datapack reacts to the mined block in the next tick # 3. no marker is left in the arena: chopping and slow chop delete their markers when they are done -await not block ~8 ~ ~8 #minecraft:logs +await not block ~20 ~ ~20 #minecraft:logs await delay 2t -await not entity @e[type=minecraft:marker,dx=19,dy=23,dz=19] +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] function timber_test:assert_no_tree {test:"chop_birch_tree"} diff --git a/tests/data/timber_test/test/chop_cherry_tree.mcfunction b/tests/data/timber_test/test/chop_cherry_tree.mcfunction new file mode 100644 index 0000000..0653e51 --- /dev/null +++ b/tests/data/timber_test/test/chop_cherry_tree.mcfunction @@ -0,0 +1,26 @@ +#> Mining the bottom log of a cherry tree with an axe chops down the whole tree +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:cherry"} +assert block ~20 ~ ~20 #minecraft:logs + +# let the leaves settle before the tree is chopped +await delay 1s +function timber_test:count_d7 + +function timber_test:player {name:"cherry_t",axe:"minecraft:iron_axe"} +function timber_test:mine {name:"cherry_t"} + +# wait until the datapack is done with the tree +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the base log is gone: the dummy hit the tree +# 2. two ticks pass: the datapack reacts to the mined block in the next tick +# 3. no marker is left in the arena: chopping and slow chop delete their markers when they are done +await not block ~20 ~ ~20 #minecraft:logs +await delay 2t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_no_tree {test:"chop_cherry_tree"} diff --git a/tests/data/timber_test/test/chop_dark_oak_tree.mcfunction b/tests/data/timber_test/test/chop_dark_oak_tree.mcfunction new file mode 100644 index 0000000..ff9329e --- /dev/null +++ b/tests/data/timber_test/test/chop_dark_oak_tree.mcfunction @@ -0,0 +1,26 @@ +#> Mining the bottom log of a 2x2 dark oak tree with an axe chops down the whole tree +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:dark_oak"} +assert block ~20 ~ ~20 #minecraft:logs + +# let the leaves settle before the tree is chopped +await delay 1s +function timber_test:count_d7 + +function timber_test:player {name:"dark_oak_t",axe:"minecraft:iron_axe"} +function timber_test:mine {name:"dark_oak_t"} + +# wait until the datapack is done with the tree +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the base log is gone: the dummy hit the tree +# 2. two ticks pass: the datapack reacts to the mined block in the next tick +# 3. no marker is left in the arena: chopping and slow chop delete their markers when they are done +await not block ~20 ~ ~20 #minecraft:logs +await delay 2t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_no_tree {test:"chop_dark_oak_tree"} diff --git a/tests/data/timber_test/test/chop_fancy_oak_tree.mcfunction b/tests/data/timber_test/test/chop_fancy_oak_tree.mcfunction new file mode 100644 index 0000000..b1a58b5 --- /dev/null +++ b/tests/data/timber_test/test/chop_fancy_oak_tree.mcfunction @@ -0,0 +1,26 @@ +#> Mining the bottom log of a fancy oak tree with an axe chops down the whole tree +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:fancy_oak"} +assert block ~20 ~ ~20 #minecraft:logs + +# let the leaves settle before the tree is chopped +await delay 1s +function timber_test:count_d7 + +function timber_test:player {name:"fancy_oak_t",axe:"minecraft:iron_axe"} +function timber_test:mine {name:"fancy_oak_t"} + +# wait until the datapack is done with the tree +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the base log is gone: the dummy hit the tree +# 2. two ticks pass: the datapack reacts to the mined block in the next tick +# 3. no marker is left in the arena: chopping and slow chop delete their markers when they are done +await not block ~20 ~ ~20 #minecraft:logs +await delay 2t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_no_tree {test:"chop_fancy_oak_tree"} diff --git a/tests/data/timber_test/test/chop_jungle_tree.mcfunction b/tests/data/timber_test/test/chop_jungle_tree.mcfunction new file mode 100644 index 0000000..47683f2 --- /dev/null +++ b/tests/data/timber_test/test/chop_jungle_tree.mcfunction @@ -0,0 +1,26 @@ +#> Mining the bottom log of a jungle tree with an axe chops down the whole tree +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:jungle_tree"} +assert block ~20 ~ ~20 #minecraft:logs + +# let the leaves settle before the tree is chopped +await delay 1s +function timber_test:count_d7 + +function timber_test:player {name:"jungle_t",axe:"minecraft:iron_axe"} +function timber_test:mine {name:"jungle_t"} + +# wait until the datapack is done with the tree +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the base log is gone: the dummy hit the tree +# 2. two ticks pass: the datapack reacts to the mined block in the next tick +# 3. no marker is left in the arena: chopping and slow chop delete their markers when they are done +await not block ~20 ~ ~20 #minecraft:logs +await delay 2t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_no_tree {test:"chop_jungle_tree"} diff --git a/tests/data/timber_test/test/chop_mega_jungle_tree.mcfunction b/tests/data/timber_test/test/chop_mega_jungle_tree.mcfunction new file mode 100644 index 0000000..619308d --- /dev/null +++ b/tests/data/timber_test/test/chop_mega_jungle_tree.mcfunction @@ -0,0 +1,26 @@ +#> Mining the bottom log of a 2x2 jungle tree with an axe chops down the whole tree +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:mega_jungle_tree"} +assert block ~20 ~ ~20 #minecraft:logs + +# let the leaves settle before the tree is chopped +await delay 1s +function timber_test:count_d7 + +function timber_test:player {name:"mega_jungle_t",axe:"minecraft:iron_axe"} +function timber_test:mine {name:"mega_jungle_t"} + +# wait until the datapack is done with the tree +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the base log is gone: the dummy hit the tree +# 2. two ticks pass: the datapack reacts to the mined block in the next tick +# 3. no marker is left in the arena: chopping and slow chop delete their markers when they are done +await not block ~20 ~ ~20 #minecraft:logs +await delay 2t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_no_tree {test:"chop_mega_jungle_tree"} diff --git a/tests/data/timber_test/test/chop_mega_pine_tree.mcfunction b/tests/data/timber_test/test/chop_mega_pine_tree.mcfunction new file mode 100644 index 0000000..2c766af --- /dev/null +++ b/tests/data/timber_test/test/chop_mega_pine_tree.mcfunction @@ -0,0 +1,26 @@ +#> Mining the bottom log of a 2x2 pine tree with an axe chops down the whole tree +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:mega_pine"} +assert block ~20 ~ ~20 #minecraft:logs + +# let the leaves settle before the tree is chopped +await delay 1s +function timber_test:count_d7 + +function timber_test:player {name:"mega_pine_t",axe:"minecraft:iron_axe"} +function timber_test:mine {name:"mega_pine_t"} + +# wait until the datapack is done with the tree +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the base log is gone: the dummy hit the tree +# 2. two ticks pass: the datapack reacts to the mined block in the next tick +# 3. no marker is left in the arena: chopping and slow chop delete their markers when they are done +await not block ~20 ~ ~20 #minecraft:logs +await delay 2t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_no_tree {test:"chop_mega_pine_tree"} diff --git a/tests/data/timber_test/test/chop_mega_spruce_tree.mcfunction b/tests/data/timber_test/test/chop_mega_spruce_tree.mcfunction new file mode 100644 index 0000000..bfd0760 --- /dev/null +++ b/tests/data/timber_test/test/chop_mega_spruce_tree.mcfunction @@ -0,0 +1,26 @@ +#> Mining the bottom log of a 2x2 spruce tree with an axe chops down the whole tree +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:mega_spruce"} +assert block ~20 ~ ~20 #minecraft:logs + +# let the leaves settle before the tree is chopped +await delay 1s +function timber_test:count_d7 + +function timber_test:player {name:"mega_spruce_t",axe:"minecraft:iron_axe"} +function timber_test:mine {name:"mega_spruce_t"} + +# wait until the datapack is done with the tree +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the base log is gone: the dummy hit the tree +# 2. two ticks pass: the datapack reacts to the mined block in the next tick +# 3. no marker is left in the arena: chopping and slow chop delete their markers when they are done +await not block ~20 ~ ~20 #minecraft:logs +await delay 2t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_no_tree {test:"chop_mega_spruce_tree"} diff --git a/tests/data/timber_test/test/chop_oak_tree.mcfunction b/tests/data/timber_test/test/chop_oak_tree.mcfunction index 2365221..289242d 100644 --- a/tests/data/timber_test/test/chop_oak_tree.mcfunction +++ b/tests/data/timber_test/test/chop_oak_tree.mcfunction @@ -5,23 +5,22 @@ function timber_test:platform function timber_test:tree {feature:"minecraft:oak"} -assert block ~8 ~ ~8 #minecraft:logs -assert block ~8 ~2 ~8 #minecraft:logs +assert block ~20 ~ ~20 #minecraft:logs # let the leaves settle before the tree is chopped await delay 1s function timber_test:count_d7 -function timber_test:player {name:"oak_tester",axe:"minecraft:iron_axe"} -function timber_test:mine {name:"oak_tester"} +function timber_test:player {name:"oak_t",axe:"minecraft:iron_axe"} +function timber_test:mine {name:"oak_t"} # wait until the datapack is done with the tree # (await doesn't work inside a called function, so this block is repeated in every test) # 1. the base log is gone: the dummy hit the tree # 2. two ticks pass: the datapack reacts to the mined block in the next tick # 3. no marker is left in the arena: chopping and slow chop delete their markers when they are done -await not block ~8 ~ ~8 #minecraft:logs +await not block ~20 ~ ~20 #minecraft:logs await delay 2t -await not entity @e[type=minecraft:marker,dx=19,dy=23,dz=19] +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] function timber_test:assert_no_tree {test:"chop_oak_tree"} diff --git a/tests/data/timber_test/test/chop_pale_oak_tree.mcfunction b/tests/data/timber_test/test/chop_pale_oak_tree.mcfunction new file mode 100644 index 0000000..9be8a63 --- /dev/null +++ b/tests/data/timber_test/test/chop_pale_oak_tree.mcfunction @@ -0,0 +1,26 @@ +#> Mining the bottom log of a 2x2 pale oak tree with an axe chops down the whole tree +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:pale_oak"} +assert block ~20 ~ ~20 #minecraft:logs + +# let the leaves settle before the tree is chopped +await delay 1s +function timber_test:count_d7 + +function timber_test:player {name:"pale_oak_t",axe:"minecraft:iron_axe"} +function timber_test:mine {name:"pale_oak_t"} + +# wait until the datapack is done with the tree +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the base log is gone: the dummy hit the tree +# 2. two ticks pass: the datapack reacts to the mined block in the next tick +# 3. no marker is left in the arena: chopping and slow chop delete their markers when they are done +await not block ~20 ~ ~20 #minecraft:logs +await delay 2t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_no_tree {test:"chop_pale_oak_tree"} diff --git a/tests/data/timber_test/test/chop_pine_tree.mcfunction b/tests/data/timber_test/test/chop_pine_tree.mcfunction new file mode 100644 index 0000000..acd9595 --- /dev/null +++ b/tests/data/timber_test/test/chop_pine_tree.mcfunction @@ -0,0 +1,26 @@ +#> Mining the bottom log of a pine tree with an axe chops down the whole tree +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:pine"} +assert block ~20 ~ ~20 #minecraft:logs + +# let the leaves settle before the tree is chopped +await delay 1s +function timber_test:count_d7 + +function timber_test:player {name:"pine_t",axe:"minecraft:iron_axe"} +function timber_test:mine {name:"pine_t"} + +# wait until the datapack is done with the tree +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the base log is gone: the dummy hit the tree +# 2. two ticks pass: the datapack reacts to the mined block in the next tick +# 3. no marker is left in the arena: chopping and slow chop delete their markers when they are done +await not block ~20 ~ ~20 #minecraft:logs +await delay 2t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_no_tree {test:"chop_pine_tree"} diff --git a/tests/data/timber_test/test/chop_spruce_tree.mcfunction b/tests/data/timber_test/test/chop_spruce_tree.mcfunction new file mode 100644 index 0000000..8a2f47c --- /dev/null +++ b/tests/data/timber_test/test/chop_spruce_tree.mcfunction @@ -0,0 +1,26 @@ +#> Mining the bottom log of a spruce tree with an axe chops down the whole tree +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:spruce"} +assert block ~20 ~ ~20 #minecraft:logs + +# let the leaves settle before the tree is chopped +await delay 1s +function timber_test:count_d7 + +function timber_test:player {name:"spruce_t",axe:"minecraft:iron_axe"} +function timber_test:mine {name:"spruce_t"} + +# wait until the datapack is done with the tree +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the base log is gone: the dummy hit the tree +# 2. two ticks pass: the datapack reacts to the mined block in the next tick +# 3. no marker is left in the arena: chopping and slow chop delete their markers when they are done +await not block ~20 ~ ~20 #minecraft:logs +await delay 2t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_no_tree {test:"chop_spruce_tree"} diff --git a/tests/data/timber_test/test/chop_swamp_oak_tree.mcfunction b/tests/data/timber_test/test/chop_swamp_oak_tree.mcfunction new file mode 100644 index 0000000..e49db82 --- /dev/null +++ b/tests/data/timber_test/test/chop_swamp_oak_tree.mcfunction @@ -0,0 +1,26 @@ +#> Mining the bottom log of a swamp oak tree with an axe chops down the whole tree +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:swamp_oak"} +assert block ~20 ~ ~20 #minecraft:logs + +# let the leaves settle before the tree is chopped +await delay 1s +function timber_test:count_d7 + +function timber_test:player {name:"swamp_oak_t",axe:"minecraft:iron_axe"} +function timber_test:mine {name:"swamp_oak_t"} + +# wait until the datapack is done with the tree +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the base log is gone: the dummy hit the tree +# 2. two ticks pass: the datapack reacts to the mined block in the next tick +# 3. no marker is left in the arena: chopping and slow chop delete their markers when they are done +await not block ~20 ~ ~20 #minecraft:logs +await delay 2t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_no_tree {test:"chop_swamp_oak_tree"} From f4f528eb551e3c1a4fef6fd98e1e3e70555f996b Mon Sep 17 00:00:00 2001 From: Flavio Felder Date: Mon, 21 Sep 2026 17:25:32 +0200 Subject: [PATCH 16/27] Test mangroves with a finder for the lowest log and warnings for known gaps The tall mangrove leans, its trunk does not start above the place where it is generated. The tests now search the whole arena for the lowest log and put the dummy next to it (the datapack only looks 7 blocks around the player). Tree generation retries because place feature sometimes fails for tall mangroves. Trees with fewer than min_leaves_found leaves next to a trunk must not be chopped and give a warning. A chop that starts at a mangrove log can leave parts standing (chop_down is off), that only warns too. Scores that were read after an await are now per test, parallel tests overwrote each other. Adds a debug mode (#debug) that leaves the arena untouched to look at it. Co-Authored-By: Claude Sonnet 5 --- .../function/assert_no_roots.mcfunction | 12 +++ .../function/assert_no_tree.mcfunction | 18 ++-- .../function/assert_not_chopped.mcfunction | 18 ++++ .../function/count_adjacent.mcfunction | 30 +++++++ .../timber_test/function/count_d7.mcfunction | 10 ++- .../function/count_logs.mcfunction | 7 ++ .../function/debug_report.mcfunction | 18 ++++ .../function/find_lowest_log.mcfunction | 10 +++ .../timber_test/function/mine_here.mcfunction | 15 ++++ .../timber_test/function/player.mcfunction | 2 + .../function/report_leftovers.mcfunction | 41 +++++++++ .../function/scan_next_x.mcfunction | 4 + .../function/scan_next_y.mcfunction | 6 ++ .../function/scan_next_z.mcfunction | 5 ++ .../timber_test/function/scan_step.mcfunction | 7 ++ .../data/timber_test/function/tree.mcfunction | 10 ++- .../function/warn_few_leaves.mcfunction | 3 + .../timber_test/function/warn_left.mcfunction | 3 + .../test/chop_acacia_tree.mcfunction | 2 +- .../test/chop_azalea_tree.mcfunction | 2 +- .../test/chop_birch_tree.mcfunction | 2 +- .../test/chop_cherry_tree.mcfunction | 2 +- .../test/chop_dark_oak_tree.mcfunction | 2 +- .../test/chop_fancy_oak_tree.mcfunction | 2 +- .../test/chop_jungle_tree.mcfunction | 2 +- .../test/chop_mangrove_log_tree.mcfunction | 35 ++++++++ .../test/chop_mangrove_roots_tree.mcfunction | 83 +++++++++++++++++++ .../test/chop_mega_jungle_tree.mcfunction | 2 +- .../test/chop_mega_pine_tree.mcfunction | 2 +- .../test/chop_mega_spruce_tree.mcfunction | 2 +- .../timber_test/test/chop_oak_tree.mcfunction | 2 +- .../test/chop_pale_oak_tree.mcfunction | 2 +- .../test/chop_pine_tree.mcfunction | 2 +- .../test/chop_spruce_tree.mcfunction | 2 +- .../test/chop_swamp_oak_tree.mcfunction | 2 +- .../chop_tall_mangrove_roots_tree.mcfunction | 83 +++++++++++++++++++ .../test/chop_tall_mangrove_tree.mcfunction | 35 ++++++++ 37 files changed, 457 insertions(+), 28 deletions(-) create mode 100644 tests/data/timber_test/function/assert_no_roots.mcfunction create mode 100644 tests/data/timber_test/function/assert_not_chopped.mcfunction create mode 100644 tests/data/timber_test/function/count_adjacent.mcfunction create mode 100644 tests/data/timber_test/function/count_logs.mcfunction create mode 100644 tests/data/timber_test/function/debug_report.mcfunction create mode 100644 tests/data/timber_test/function/find_lowest_log.mcfunction create mode 100644 tests/data/timber_test/function/mine_here.mcfunction create mode 100644 tests/data/timber_test/function/report_leftovers.mcfunction create mode 100644 tests/data/timber_test/function/scan_next_x.mcfunction create mode 100644 tests/data/timber_test/function/scan_next_y.mcfunction create mode 100644 tests/data/timber_test/function/scan_next_z.mcfunction create mode 100644 tests/data/timber_test/function/scan_step.mcfunction create mode 100644 tests/data/timber_test/function/warn_few_leaves.mcfunction create mode 100644 tests/data/timber_test/function/warn_left.mcfunction create mode 100644 tests/data/timber_test/test/chop_mangrove_log_tree.mcfunction create mode 100644 tests/data/timber_test/test/chop_mangrove_roots_tree.mcfunction create mode 100644 tests/data/timber_test/test/chop_tall_mangrove_roots_tree.mcfunction create mode 100644 tests/data/timber_test/test/chop_tall_mangrove_tree.mcfunction diff --git a/tests/data/timber_test/function/assert_no_roots.mcfunction b/tests/data/timber_test/function/assert_no_roots.mcfunction new file mode 100644 index 0000000..9d0ac9d --- /dev/null +++ b/tests/data/timber_test/function/assert_no_roots.mcfunction @@ -0,0 +1,12 @@ +#> Fails the test if any mangrove root is left in the arena. Replaces them with air while counting, so run it last. +#> (#minecraft:mangrove_logs does not contain the roots.) The arena is handled in two slabs, a command may only touch 32768 blocks. +scoreboard objectives add timber_test dummy +execute if score #debug timber_test matches 1 run return 0 +execute store result score #roots timber_test run fill ~0 ~0 ~0 ~39 ~19 ~39 minecraft:air replace minecraft:mangrove_roots +execute store result score #roots_b timber_test run fill ~0 ~20 ~0 ~39 ~39 ~39 minecraft:air replace minecraft:mangrove_roots +scoreboard players operation #roots timber_test += #roots_b timber_test +execute store result score #roots_b timber_test run fill ~0 ~0 ~0 ~39 ~19 ~39 minecraft:air replace minecraft:muddy_mangrove_roots +scoreboard players operation #roots timber_test += #roots_b timber_test +execute store result score #roots_b timber_test run fill ~0 ~20 ~0 ~39 ~39 ~39 minecraft:air replace minecraft:muddy_mangrove_roots +scoreboard players operation #roots timber_test += #roots_b timber_test +assert score #roots timber_test matches 0 diff --git a/tests/data/timber_test/function/assert_no_tree.mcfunction b/tests/data/timber_test/function/assert_no_tree.mcfunction index adc6f72..2594960 100644 --- a/tests/data/timber_test/function/assert_no_tree.mcfunction +++ b/tests/data/timber_test/function/assert_no_tree.mcfunction @@ -1,23 +1,25 @@ #> Fails the test if any log or normal leaf is left in the arena. Leaves with distance=7 only produce a warning (see count_d7). #> Replaces logs and leaves with air while counting, so run it last. The arena is handled in two slabs, a command may only touch 32768 blocks. -#> @param test name of the test, for the warning +#> With #debug set (see debug_report) it only reports and leaves everything as it is. +#> @param test name of the test, count_d7 has to be called with the same name scoreboard objectives add timber_test dummy +execute if score #debug timber_test matches 1 run function timber_test:debug_report execute store result score #d7_after timber_test run clone ~0 ~0 ~0 ~39 ~19 ~39 ~0 ~64 ~0 filtered #minecraft:leaves[distance=7] -execute store result score #d7_b timber_test run clone ~0 ~20 ~0 ~39 ~39 ~39 ~0 ~64 ~0 filtered #minecraft:leaves[distance=7] -scoreboard players operation #d7_after timber_test += #d7_b timber_test +execute store result score #dtmp timber_test run clone ~0 ~20 ~0 ~39 ~39 ~39 ~0 ~64 ~0 filtered #minecraft:leaves[distance=7] +scoreboard players operation #d7_after timber_test += #dtmp timber_test fill ~0 ~64 ~0 ~39 ~83 ~39 minecraft:air fill ~0 ~0 ~0 ~39 ~19 ~39 minecraft:air replace #minecraft:leaves[distance=7] fill ~0 ~20 ~0 ~39 ~39 ~39 minecraft:air replace #minecraft:leaves[distance=7] execute store result score #logs timber_test run fill ~0 ~0 ~0 ~39 ~19 ~39 minecraft:air replace #minecraft:logs -execute store result score #logs_b timber_test run fill ~0 ~20 ~0 ~39 ~39 ~39 minecraft:air replace #minecraft:logs -scoreboard players operation #logs timber_test += #logs_b timber_test +execute store result score #dtmp timber_test run fill ~0 ~20 ~0 ~39 ~39 ~39 minecraft:air replace #minecraft:logs +scoreboard players operation #logs timber_test += #dtmp timber_test execute store result score #leaves timber_test run fill ~0 ~0 ~0 ~39 ~19 ~39 minecraft:air replace #minecraft:leaves -execute store result score #leaves_b timber_test run fill ~0 ~20 ~0 ~39 ~39 ~39 minecraft:air replace #minecraft:leaves -scoreboard players operation #leaves timber_test += #leaves_b timber_test +execute store result score #dtmp timber_test run fill ~0 ~20 ~0 ~39 ~39 ~39 minecraft:air replace #minecraft:leaves +scoreboard players operation #leaves timber_test += #dtmp timber_test # warning: tests still pass, the marker is picked up by the CI workflow $data modify storage timber_test:tmp test set value "$(test)" -execute store result storage timber_test:tmp before int 1 run scoreboard players get #d7_before timber_test +$execute store result storage timber_test:tmp before int 1 run scoreboard players get #d7_$(test) timber_test execute store result storage timber_test:tmp after int 1 run scoreboard players get #d7_after timber_test execute if score #d7_after timber_test matches 1.. run function timber_test:warn_d7 with storage timber_test:tmp diff --git a/tests/data/timber_test/function/assert_not_chopped.mcfunction b/tests/data/timber_test/function/assert_not_chopped.mcfunction new file mode 100644 index 0000000..e7247e9 --- /dev/null +++ b/tests/data/timber_test/function/assert_not_chopped.mcfunction @@ -0,0 +1,18 @@ +#> For a tree the datapack is not allowed to chop (too few leaves next to the trunk): fails the test if it chopped anyway, +#> i.e. if more than the one mined log is gone. Then warns, because every tree should be choppable, this is a known gap. +#> Replaces logs with air while counting, so run it last. +#> @param test name of the test, count_logs and count_adjacent have to be called with the same name +scoreboard objectives add timber_test dummy +execute store result score #logs1 timber_test run fill ~0 ~0 ~0 ~39 ~19 ~39 minecraft:air replace #minecraft:logs +execute store result score #dtmp timber_test run fill ~0 ~20 ~0 ~39 ~39 ~39 minecraft:air replace #minecraft:logs +scoreboard players operation #logs1 timber_test += #dtmp timber_test + +# warning first, the CI workflow picks it up from the log +$data modify storage timber_test:tmp test set value "$(test)" +$execute store result storage timber_test:tmp adj int 1 run scoreboard players get #adj_$(test) timber_test +execute store result storage timber_test:tmp min int 1 run scoreboard players get min_leaves_found timber +function timber_test:warn_few_leaves with storage timber_test:tmp + +# the one mined log may be gone, nothing else +scoreboard players add #logs1 timber_test 1 +$assert score #logs1 timber_test >= #logs0_$(test) timber_test diff --git a/tests/data/timber_test/function/count_adjacent.mcfunction b/tests/data/timber_test/function/count_adjacent.mcfunction new file mode 100644 index 0000000..12b657f --- /dev/null +++ b/tests/data/timber_test/function/count_adjacent.mcfunction @@ -0,0 +1,30 @@ +#> Counts the leaves that touch a log on one of their 6 sides into #adj_, this is the number the datapack compares with min_leaves_found. +#> Doesn't change the tree. The leaves are copied far above the arena, then the logs are copied over them shifted by one block in every +#> direction: every leaf a log lands on is next to a log, what is left of the copy is not (checked against oak: 13, same as the datapack). +#> @param test name of the test +scoreboard objectives add timber_test dummy +fill ~0 ~64 ~0 ~39 ~83 ~39 minecraft:air +fill ~0 ~84 ~0 ~39 ~103 ~39 minecraft:air +execute store result score #ta timber_test run clone ~0 ~0 ~0 ~39 ~19 ~39 ~0 ~64 ~0 filtered #minecraft:leaves +execute store result score #tb timber_test run clone ~0 ~20 ~0 ~39 ~39 ~39 ~0 ~84 ~0 filtered #minecraft:leaves +clone ~0 ~0 ~0 ~39 ~19 ~39 ~1 ~64 ~0 filtered #minecraft:logs +clone ~0 ~20 ~0 ~39 ~39 ~39 ~1 ~84 ~0 filtered #minecraft:logs +clone ~0 ~0 ~0 ~39 ~19 ~39 ~-1 ~64 ~0 filtered #minecraft:logs +clone ~0 ~20 ~0 ~39 ~39 ~39 ~-1 ~84 ~0 filtered #minecraft:logs +clone ~0 ~0 ~0 ~39 ~19 ~39 ~0 ~65 ~0 filtered #minecraft:logs +clone ~0 ~20 ~0 ~39 ~39 ~39 ~0 ~85 ~0 filtered #minecraft:logs +clone ~0 ~0 ~0 ~39 ~19 ~39 ~0 ~63 ~0 filtered #minecraft:logs +clone ~0 ~20 ~0 ~39 ~39 ~39 ~0 ~83 ~0 filtered #minecraft:logs +clone ~0 ~0 ~0 ~39 ~19 ~39 ~0 ~64 ~1 filtered #minecraft:logs +clone ~0 ~20 ~0 ~39 ~39 ~39 ~0 ~84 ~1 filtered #minecraft:logs +clone ~0 ~0 ~0 ~39 ~19 ~39 ~0 ~64 ~-1 filtered #minecraft:logs +clone ~0 ~20 ~0 ~39 ~39 ~39 ~0 ~84 ~-1 filtered #minecraft:logs +execute store result score #ra timber_test run fill ~0 ~64 ~0 ~39 ~83 ~39 minecraft:air replace #minecraft:leaves +execute store result score #rb timber_test run fill ~0 ~84 ~0 ~39 ~103 ~39 minecraft:air replace #minecraft:leaves +scoreboard players operation #atmp timber_test = #ta timber_test +scoreboard players operation #atmp timber_test += #tb timber_test +scoreboard players operation #atmp timber_test -= #ra timber_test +scoreboard players operation #atmp timber_test -= #rb timber_test +$scoreboard players operation #adj_$(test) timber_test = #atmp timber_test +fill ~0 ~64 ~0 ~39 ~83 ~39 minecraft:air +fill ~0 ~84 ~0 ~39 ~103 ~39 minecraft:air diff --git a/tests/data/timber_test/function/count_d7.mcfunction b/tests/data/timber_test/function/count_d7.mcfunction index 1bd4b88..7c351e9 100644 --- a/tests/data/timber_test/function/count_d7.mcfunction +++ b/tests/data/timber_test/function/count_d7.mcfunction @@ -1,9 +1,11 @@ -#> Counts the leaves with distance=7 in the arena into #d7_before (call before chopping), doesn't change the tree. +#> Counts the leaves with distance=7 in the arena into #d7_ (call before chopping), doesn't change the tree. #> Leaves without a log in range show up in the test environment but not in a normal world, the timber datapack ignores them. #> Counting works by cloning them out of the way (clone reports the number of copied blocks), the copies are removed again. #> The arena is counted in two slabs, a command may only touch 32768 blocks. +#> The score is named after the test: tests run in parallel and would overwrite a shared one before it is read. +#> @param test name of the test scoreboard objectives add timber_test dummy -execute store result score #d7_before timber_test run clone ~0 ~0 ~0 ~39 ~19 ~39 ~0 ~64 ~0 filtered #minecraft:leaves[distance=7] -execute store result score #d7_b timber_test run clone ~0 ~20 ~0 ~39 ~39 ~39 ~0 ~64 ~0 filtered #minecraft:leaves[distance=7] -scoreboard players operation #d7_before timber_test += #d7_b timber_test +$execute store result score #d7_$(test) timber_test run clone ~0 ~0 ~0 ~39 ~19 ~39 ~0 ~64 ~0 filtered #minecraft:leaves[distance=7] +execute store result score #dtmp timber_test run clone ~0 ~20 ~0 ~39 ~39 ~39 ~0 ~64 ~0 filtered #minecraft:leaves[distance=7] +$scoreboard players operation #d7_$(test) timber_test += #dtmp timber_test fill ~0 ~64 ~0 ~39 ~83 ~39 minecraft:air diff --git a/tests/data/timber_test/function/count_logs.mcfunction b/tests/data/timber_test/function/count_logs.mcfunction new file mode 100644 index 0000000..d6fd469 --- /dev/null +++ b/tests/data/timber_test/function/count_logs.mcfunction @@ -0,0 +1,7 @@ +#> Counts the logs in the arena into #logs0_, doesn't change the tree (see count_d7 for how and why per test). +#> @param test name of the test +scoreboard objectives add timber_test dummy +$execute store result score #logs0_$(test) timber_test run clone ~0 ~0 ~0 ~39 ~19 ~39 ~0 ~64 ~0 filtered #minecraft:logs +execute store result score #dtmp timber_test run clone ~0 ~20 ~0 ~39 ~39 ~39 ~0 ~64 ~0 filtered #minecraft:logs +$scoreboard players operation #logs0_$(test) timber_test += #dtmp timber_test +fill ~0 ~64 ~0 ~39 ~83 ~39 minecraft:air diff --git a/tests/data/timber_test/function/debug_report.mcfunction b/tests/data/timber_test/function/debug_report.mcfunction new file mode 100644 index 0000000..c806684 --- /dev/null +++ b/tests/data/timber_test/function/debug_report.mcfunction @@ -0,0 +1,18 @@ +#> Debug mode: reports what is left and fails the test without touching a single block, so the arena can be looked at. +#> Switch it on with /scoreboard players set #debug timber_test 1 and off with ... 0 (create the objective first: /scoreboard objectives add timber_test dummy). +#> Counting works by cloning the blocks far above the arena (clone reports the number of copied blocks), the copies are removed again. +#> The arena is counted in two slabs, a command may only touch 32768 blocks. +execute store result score #dbg_logs timber_test run clone ~0 ~0 ~0 ~39 ~19 ~39 ~0 ~64 ~0 filtered #minecraft:logs +execute store result score #dbg_b timber_test run clone ~0 ~20 ~0 ~39 ~39 ~39 ~0 ~64 ~0 filtered #minecraft:logs +scoreboard players operation #dbg_logs timber_test += #dbg_b timber_test +execute store result score #dbg_leaves timber_test run clone ~0 ~0 ~0 ~39 ~19 ~39 ~0 ~64 ~0 filtered #minecraft:leaves +execute store result score #dbg_b timber_test run clone ~0 ~20 ~0 ~39 ~39 ~39 ~0 ~64 ~0 filtered #minecraft:leaves +scoreboard players operation #dbg_leaves timber_test += #dbg_b timber_test +execute store result score #dbg_d7 timber_test run clone ~0 ~0 ~0 ~39 ~19 ~39 ~0 ~64 ~0 filtered #minecraft:leaves[distance=7] +execute store result score #dbg_b timber_test run clone ~0 ~20 ~0 ~39 ~39 ~39 ~0 ~64 ~0 filtered #minecraft:leaves[distance=7] +scoreboard players operation #dbg_d7 timber_test += #dbg_b timber_test +execute store result score #dbg_roots timber_test run clone ~0 ~0 ~0 ~39 ~19 ~39 ~0 ~64 ~0 filtered minecraft:mangrove_roots +execute store result score #dbg_b timber_test run clone ~0 ~20 ~0 ~39 ~39 ~39 ~0 ~64 ~0 filtered minecraft:mangrove_roots +scoreboard players operation #dbg_roots timber_test += #dbg_b timber_test +fill ~0 ~64 ~0 ~39 ~83 ~39 minecraft:air +fail [{"text":"DEBUG mode, nothing was removed. Left standing: logs "},{"score":{"name":"#dbg_logs","objective":"timber_test"}},{"text":", leaves "},{"score":{"name":"#dbg_leaves","objective":"timber_test"}},{"text":" (distance=7: "},{"score":{"name":"#dbg_d7","objective":"timber_test"}},{"text":"), mangrove roots "},{"score":{"name":"#dbg_roots","objective":"timber_test"}}] diff --git a/tests/data/timber_test/function/find_lowest_log.mcfunction b/tests/data/timber_test/function/find_lowest_log.mcfunction new file mode 100644 index 0000000..b1082b2 --- /dev/null +++ b/tests/data/timber_test/function/find_lowest_log.mcfunction @@ -0,0 +1,10 @@ +#> Finds the lowest log of the whole tree, not the lowest one in the middle column: mangroves lean, their trunk doesn't start above the +#> place where the tree was generated. Mining a log further up chops upwards only (chop_down is off), the logs below would stay. +#> (The marker sits in the middle of the block: a marker on the lower edge of the selector box isn't counted as inside.) +#> A marker with the tag tb_scan walks through the arena from the bottom (the whole 40x40 arena, y 0 to 39), scan_step stops on the first log. +#> The marker stays on that log, mine_here uses it. Kill it afterwards, it would keep the marker check at the end of the test from finishing. +scoreboard players set #sx timber_test 0 +scoreboard players set #sy timber_test 0 +scoreboard players set #sz timber_test 0 +summon minecraft:marker ~0.5 ~0.5 ~0.5 {Tags:["tb_scan"]} +execute as @e[type=minecraft:marker,tag=tb_scan,dx=39,dy=39,dz=39] at @s store result score #found timber_test run function timber_test:scan_step diff --git a/tests/data/timber_test/function/mine_here.mcfunction b/tests/data/timber_test/function/mine_here.mcfunction new file mode 100644 index 0000000..9b12c63 --- /dev/null +++ b/tests/data/timber_test/function/mine_here.mcfunction @@ -0,0 +1,15 @@ +#> Run as and at the tb_scan marker standing on the log to mine: puts the dummy next to that log and lets it mine it. +#> The datapack looks for the dropped item within 7 blocks of the player, a dummy standing on the ground is too far away from a log +#> high up. The dummy floats (gravity 0) on a free side of the log, the first of +x, -x, +z, -z that is air. +#> @param name dummy name +$attribute @e[type=minecraft:player,name=$(name),limit=1] minecraft:gravity base set 0 +scoreboard players set #placed timber_test 0 +$execute unless score #placed timber_test matches 1 if block ~1 ~ ~ minecraft:air run tp @e[type=minecraft:player,name=$(name),limit=1] ~1 ~-0.5 ~ +execute unless score #placed timber_test matches 1 if block ~1 ~ ~ minecraft:air run scoreboard players set #placed timber_test 1 +$execute unless score #placed timber_test matches 1 if block ~-1 ~ ~ minecraft:air run tp @e[type=minecraft:player,name=$(name),limit=1] ~-1 ~-0.5 ~ +execute unless score #placed timber_test matches 1 if block ~-1 ~ ~ minecraft:air run scoreboard players set #placed timber_test 1 +$execute unless score #placed timber_test matches 1 if block ~ ~ ~1 minecraft:air run tp @e[type=minecraft:player,name=$(name),limit=1] ~ ~-0.5 ~1 +execute unless score #placed timber_test matches 1 if block ~ ~ ~1 minecraft:air run scoreboard players set #placed timber_test 1 +$execute unless score #placed timber_test matches 1 if block ~ ~ ~-1 minecraft:air run tp @e[type=minecraft:player,name=$(name),limit=1] ~ ~-0.5 ~-1 +execute unless score #placed timber_test matches 1 if block ~ ~ ~-1 minecraft:air run scoreboard players set #placed timber_test 1 +$dummy $(name) mine ~ ~ ~ diff --git a/tests/data/timber_test/function/player.mcfunction b/tests/data/timber_test/function/player.mcfunction index 331f591..e2b7312 100644 --- a/tests/data/timber_test/function/player.mcfunction +++ b/tests/data/timber_test/function/player.mcfunction @@ -6,3 +6,5 @@ $dummy $(name) spawn $tag @e[type=minecraft:player,name=$(name),limit=1] add timber_test_dummy $tp @e[type=minecraft:player,name=$(name),limit=1] ~19.5 ~ ~20.5 $item replace entity @e[type=minecraft:player,name=$(name),limit=1] weapon.mainhand with $(axe) +#> A tall mangrove trunk starts higher than the default reach of 4.5 blocks +$attribute @e[type=minecraft:player,name=$(name),limit=1] minecraft:block_interaction_range base set 30 diff --git a/tests/data/timber_test/function/report_leftovers.mcfunction b/tests/data/timber_test/function/report_leftovers.mcfunction new file mode 100644 index 0000000..d7bc94b --- /dev/null +++ b/tests/data/timber_test/function/report_leftovers.mcfunction @@ -0,0 +1,41 @@ +#> Like assert_no_tree, but nothing fails: leftovers, and a tree that wasn't chopped at all, only warn. +#> For chops that start at a mangrove log. The datapack searches upwards and sideways from the mined log, downwards only with chop_down, +#> so a branch that bends down again can stay, and if the logs it reaches touch fewer than min_leaves_found leaves it chops nothing at all. +#> That is a gap (the goal is to chop every tree), it is reported, not failed. Nothing can be asserted here that always holds. +#> Replaces logs and leaves with air while counting, so run it last. Two slabs, a command may only touch 32768 blocks. +#> @param test name of the test, count_d7 and count_logs have to be called with the same name +scoreboard objectives add timber_test dummy +execute store result score #d7_after timber_test run clone ~0 ~0 ~0 ~39 ~19 ~39 ~0 ~64 ~0 filtered #minecraft:leaves[distance=7] +execute store result score #dtmp timber_test run clone ~0 ~20 ~0 ~39 ~39 ~39 ~0 ~64 ~0 filtered #minecraft:leaves[distance=7] +scoreboard players operation #d7_after timber_test += #dtmp timber_test +fill ~0 ~64 ~0 ~39 ~83 ~39 minecraft:air +fill ~0 ~0 ~0 ~39 ~19 ~39 minecraft:air replace #minecraft:leaves[distance=7] +fill ~0 ~20 ~0 ~39 ~39 ~39 minecraft:air replace #minecraft:leaves[distance=7] +execute store result score #logs timber_test run fill ~0 ~0 ~0 ~39 ~19 ~39 minecraft:air replace #minecraft:logs +execute store result score #dtmp timber_test run fill ~0 ~20 ~0 ~39 ~39 ~39 minecraft:air replace #minecraft:logs +scoreboard players operation #logs timber_test += #dtmp timber_test +execute store result score #leaves timber_test run fill ~0 ~0 ~0 ~39 ~19 ~39 minecraft:air replace #minecraft:leaves +execute store result score #dtmp timber_test run fill ~0 ~20 ~0 ~39 ~39 ~39 minecraft:air replace #minecraft:leaves +scoreboard players operation #leaves timber_test += #dtmp timber_test + +execute store result score #roots timber_test run fill ~0 ~0 ~0 ~39 ~19 ~39 minecraft:air replace minecraft:mangrove_roots +execute store result score #dtmp timber_test run fill ~0 ~20 ~0 ~39 ~39 ~39 minecraft:air replace minecraft:mangrove_roots +scoreboard players operation #roots timber_test += #dtmp timber_test +execute store result score #dtmp timber_test run fill ~0 ~0 ~0 ~39 ~19 ~39 minecraft:air replace minecraft:muddy_mangrove_roots +scoreboard players operation #roots timber_test += #dtmp timber_test +execute store result score #dtmp timber_test run fill ~0 ~20 ~0 ~39 ~39 ~39 minecraft:air replace minecraft:muddy_mangrove_roots +scoreboard players operation #roots timber_test += #dtmp timber_test + +# warnings +$data modify storage timber_test:tmp test set value "$(test)" +$execute store result storage timber_test:tmp before int 1 run scoreboard players get #d7_$(test) timber_test +execute store result storage timber_test:tmp after int 1 run scoreboard players get #d7_after timber_test +execute if score #d7_after timber_test matches 1.. run function timber_test:warn_d7 with storage timber_test:tmp +$execute store result storage timber_test:tmp total int 1 run scoreboard players get #logs0_$(test) timber_test +execute store result storage timber_test:tmp logs int 1 run scoreboard players get #logs timber_test +execute store result storage timber_test:tmp leaves int 1 run scoreboard players get #leaves timber_test +execute store result storage timber_test:tmp roots int 1 run scoreboard players get #roots timber_test +scoreboard players operation #left timber_test = #logs timber_test +scoreboard players operation #left timber_test += #leaves timber_test +scoreboard players operation #left timber_test += #roots timber_test +execute unless score #left timber_test matches 0 run function timber_test:warn_left with storage timber_test:tmp diff --git a/tests/data/timber_test/function/scan_next_x.mcfunction b/tests/data/timber_test/function/scan_next_x.mcfunction new file mode 100644 index 0000000..4b0e88b --- /dev/null +++ b/tests/data/timber_test/function/scan_next_x.mcfunction @@ -0,0 +1,4 @@ +scoreboard players add #sx timber_test 1 +tp @s ~1 ~ ~ +# after the tp the position of this execution is still the old one, take the new one +return run execute at @s run function timber_test:scan_step diff --git a/tests/data/timber_test/function/scan_next_y.mcfunction b/tests/data/timber_test/function/scan_next_y.mcfunction new file mode 100644 index 0000000..a3fbe2e --- /dev/null +++ b/tests/data/timber_test/function/scan_next_y.mcfunction @@ -0,0 +1,6 @@ +scoreboard players set #sx timber_test 0 +scoreboard players set #sz timber_test 0 +scoreboard players add #sy timber_test 1 +tp @s ~-39 ~1 ~-39 +# after the tp the position of this execution is still the old one, take the new one +return run execute at @s run function timber_test:scan_step diff --git a/tests/data/timber_test/function/scan_next_z.mcfunction b/tests/data/timber_test/function/scan_next_z.mcfunction new file mode 100644 index 0000000..ab6d0aa --- /dev/null +++ b/tests/data/timber_test/function/scan_next_z.mcfunction @@ -0,0 +1,5 @@ +scoreboard players set #sx timber_test 0 +scoreboard players add #sz timber_test 1 +tp @s ~-39 ~ ~1 +# after the tp the position of this execution is still the old one, take the new one +return run execute at @s run function timber_test:scan_step diff --git a/tests/data/timber_test/function/scan_step.mcfunction b/tests/data/timber_test/function/scan_step.mcfunction new file mode 100644 index 0000000..4ee2e1d --- /dev/null +++ b/tests/data/timber_test/function/scan_step.mcfunction @@ -0,0 +1,7 @@ +#> Looks at the block under the scanner, moves on if it is no log. Returns 1 if a log was found, 0 if the whole area was searched. +#> return run function is a tail call, the walk through up to 64000 blocks (it stops at the first log, that is after a few layers) doesn't pile up nested calls. +execute if block ~ ~ ~ #minecraft:logs run return 1 +execute if score #sx timber_test matches ..38 run return run function timber_test:scan_next_x +execute if score #sz timber_test matches ..38 run return run function timber_test:scan_next_z +execute if score #sy timber_test matches ..38 run return run function timber_test:scan_next_y +return 0 diff --git a/tests/data/timber_test/function/tree.mcfunction b/tests/data/timber_test/function/tree.mcfunction index 6be187b..23ad6b2 100644 --- a/tests/data/timber_test/function/tree.mcfunction +++ b/tests/data/timber_test/function/tree.mcfunction @@ -1,4 +1,12 @@ #> Generates a vanilla worldgen tree with its trunk base at ~20 ~ ~20, the middle of the arena #> Not closer to the origin: the tree can't grow into the origin column (PackTest keeps something there), place feature fails silently +#> Some trees (tall mangrove in about 1 of 12) can't be placed, place feature then reports a failure and changes nothing: try up to 5 times #> @param feature configured feature id, e.g. "minecraft:oak" -$place feature $(feature) ~20 ~ ~20 +scoreboard objectives add timber_test dummy +scoreboard players set #tree_ok timber_test 0 +$execute store success score #tree_ok timber_test run place feature $(feature) ~20 ~ ~20 +$execute unless score #tree_ok timber_test matches 1 store success score #tree_ok timber_test run place feature $(feature) ~20 ~ ~20 +$execute unless score #tree_ok timber_test matches 1 store success score #tree_ok timber_test run place feature $(feature) ~20 ~ ~20 +$execute unless score #tree_ok timber_test matches 1 store success score #tree_ok timber_test run place feature $(feature) ~20 ~ ~20 +$execute unless score #tree_ok timber_test matches 1 store success score #tree_ok timber_test run place feature $(feature) ~20 ~ ~20 +assert score #tree_ok timber_test matches 1 diff --git a/tests/data/timber_test/function/warn_few_leaves.mcfunction b/tests/data/timber_test/function/warn_few_leaves.mcfunction new file mode 100644 index 0000000..55413d9 --- /dev/null +++ b/tests/data/timber_test/function/warn_few_leaves.mcfunction @@ -0,0 +1,3 @@ +#> Warns that a tree was not chopped because too few leaves touch its trunk. Same two paths as warn_d7 (log line for the CI, orange chat for players). +$execute unless entity @a[tag=!timber_test_dummy] run say TIMBER-WARNING $(test): tree was not chopped, only $(adj) leaves next to the trunk (min_leaves_found is $(min)), every tree should be choppable +$tellraw @a[tag=!timber_test_dummy] {"text":"Test warning $(test): tree was not chopped, only $(adj) leaves next to the trunk (min_leaves_found is $(min)), every tree should be choppable","color":"gold"} diff --git a/tests/data/timber_test/function/warn_left.mcfunction b/tests/data/timber_test/function/warn_left.mcfunction new file mode 100644 index 0000000..56808b5 --- /dev/null +++ b/tests/data/timber_test/function/warn_left.mcfunction @@ -0,0 +1,3 @@ +#> Warns that a chop left logs or leaves standing. Same two paths as warn_d7 (log line for the CI, orange chat for players). +$execute unless entity @a[tag=!timber_test_dummy] run say TIMBER-WARNING $(test): chop left $(logs) of $(total) logs, $(roots) roots and $(leaves) leaves standing, every tree should be chopped completely +$tellraw @a[tag=!timber_test_dummy] {"text":"Test warning $(test): chop left $(logs) of $(total) logs, $(roots) roots and $(leaves) leaves standing, every tree should be chopped completely","color":"gold"} diff --git a/tests/data/timber_test/test/chop_acacia_tree.mcfunction b/tests/data/timber_test/test/chop_acacia_tree.mcfunction index 06d71c4..354d580 100644 --- a/tests/data/timber_test/test/chop_acacia_tree.mcfunction +++ b/tests/data/timber_test/test/chop_acacia_tree.mcfunction @@ -9,7 +9,7 @@ assert block ~20 ~ ~20 #minecraft:logs # let the leaves settle before the tree is chopped await delay 1s -function timber_test:count_d7 +function timber_test:count_d7 {test:"chop_acacia_tree"} function timber_test:player {name:"acacia_t",axe:"minecraft:iron_axe"} function timber_test:mine {name:"acacia_t"} diff --git a/tests/data/timber_test/test/chop_azalea_tree.mcfunction b/tests/data/timber_test/test/chop_azalea_tree.mcfunction index ee0b4a6..3e2824c 100644 --- a/tests/data/timber_test/test/chop_azalea_tree.mcfunction +++ b/tests/data/timber_test/test/chop_azalea_tree.mcfunction @@ -9,7 +9,7 @@ assert block ~20 ~ ~20 #minecraft:logs # let the leaves settle before the tree is chopped await delay 1s -function timber_test:count_d7 +function timber_test:count_d7 {test:"chop_azalea_tree"} function timber_test:player {name:"azalea_t",axe:"minecraft:iron_axe"} function timber_test:mine {name:"azalea_t"} diff --git a/tests/data/timber_test/test/chop_birch_tree.mcfunction b/tests/data/timber_test/test/chop_birch_tree.mcfunction index 6c792b6..dda4125 100644 --- a/tests/data/timber_test/test/chop_birch_tree.mcfunction +++ b/tests/data/timber_test/test/chop_birch_tree.mcfunction @@ -9,7 +9,7 @@ assert block ~20 ~ ~20 #minecraft:logs # let the leaves settle before the tree is chopped await delay 1s -function timber_test:count_d7 +function timber_test:count_d7 {test:"chop_birch_tree"} function timber_test:player {name:"birch_t",axe:"minecraft:iron_axe"} function timber_test:mine {name:"birch_t"} diff --git a/tests/data/timber_test/test/chop_cherry_tree.mcfunction b/tests/data/timber_test/test/chop_cherry_tree.mcfunction index 0653e51..0258b16 100644 --- a/tests/data/timber_test/test/chop_cherry_tree.mcfunction +++ b/tests/data/timber_test/test/chop_cherry_tree.mcfunction @@ -9,7 +9,7 @@ assert block ~20 ~ ~20 #minecraft:logs # let the leaves settle before the tree is chopped await delay 1s -function timber_test:count_d7 +function timber_test:count_d7 {test:"chop_cherry_tree"} function timber_test:player {name:"cherry_t",axe:"minecraft:iron_axe"} function timber_test:mine {name:"cherry_t"} diff --git a/tests/data/timber_test/test/chop_dark_oak_tree.mcfunction b/tests/data/timber_test/test/chop_dark_oak_tree.mcfunction index ff9329e..56a3fe7 100644 --- a/tests/data/timber_test/test/chop_dark_oak_tree.mcfunction +++ b/tests/data/timber_test/test/chop_dark_oak_tree.mcfunction @@ -9,7 +9,7 @@ assert block ~20 ~ ~20 #minecraft:logs # let the leaves settle before the tree is chopped await delay 1s -function timber_test:count_d7 +function timber_test:count_d7 {test:"chop_dark_oak_tree"} function timber_test:player {name:"dark_oak_t",axe:"minecraft:iron_axe"} function timber_test:mine {name:"dark_oak_t"} diff --git a/tests/data/timber_test/test/chop_fancy_oak_tree.mcfunction b/tests/data/timber_test/test/chop_fancy_oak_tree.mcfunction index b1a58b5..e04137c 100644 --- a/tests/data/timber_test/test/chop_fancy_oak_tree.mcfunction +++ b/tests/data/timber_test/test/chop_fancy_oak_tree.mcfunction @@ -9,7 +9,7 @@ assert block ~20 ~ ~20 #minecraft:logs # let the leaves settle before the tree is chopped await delay 1s -function timber_test:count_d7 +function timber_test:count_d7 {test:"chop_fancy_oak_tree"} function timber_test:player {name:"fancy_oak_t",axe:"minecraft:iron_axe"} function timber_test:mine {name:"fancy_oak_t"} diff --git a/tests/data/timber_test/test/chop_jungle_tree.mcfunction b/tests/data/timber_test/test/chop_jungle_tree.mcfunction index 47683f2..20e7236 100644 --- a/tests/data/timber_test/test/chop_jungle_tree.mcfunction +++ b/tests/data/timber_test/test/chop_jungle_tree.mcfunction @@ -9,7 +9,7 @@ assert block ~20 ~ ~20 #minecraft:logs # let the leaves settle before the tree is chopped await delay 1s -function timber_test:count_d7 +function timber_test:count_d7 {test:"chop_jungle_tree"} function timber_test:player {name:"jungle_t",axe:"minecraft:iron_axe"} function timber_test:mine {name:"jungle_t"} diff --git a/tests/data/timber_test/test/chop_mangrove_log_tree.mcfunction b/tests/data/timber_test/test/chop_mangrove_log_tree.mcfunction new file mode 100644 index 0000000..9402f3e --- /dev/null +++ b/tests/data/timber_test/test/chop_mangrove_log_tree.mcfunction @@ -0,0 +1,35 @@ +#> Mining the lowest log of a mangrove tree chops down the whole tree including its roots +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:mangrove"} + +# let the leaves settle before the tree is chopped +await delay 1s +function timber_test:count_d7 {test:"chop_mangrove_log_tree"} +function timber_test:count_adjacent {test:"chop_mangrove_log_tree"} +function timber_test:count_logs {test:"chop_mangrove_log_tree"} + +function timber_test:player {name:"mangrove_log_t",axe:"minecraft:iron_axe"} +# mine the lowest log of the whole tree, from a spot next to it (see find_lowest_log and mine_here for why) +function timber_test:find_lowest_log +execute as @e[type=minecraft:marker,tag=tb_scan,dx=39,dy=39,dz=39] at @s run function timber_test:mine_here {name:"mangrove_log_t"} +kill @e[type=minecraft:marker,tag=tb_scan,dx=39,dy=39,dz=39] + +# wait until the datapack is done with the tree +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the mined block dropped as an item: the dummy hit the tree (the base can be a root here, and any item would do for other things lying around, so wait for this one) +# 2. two ticks pass: the datapack reacts to the mined block in the next tick +# 3. no marker is left in the arena: chopping and slow chop delete their markers when they are done +await entity @e[type=minecraft:item,dx=39,dy=39,dz=39,nbt={Item:{id:"minecraft:mangrove_log"}}] +await delay 2t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +# the datapack only chops a tree with at least min_leaves_found leaves next to a trunk. Some mangroves have fewer (measured with +# 40 trees each: mangrove 15 %, tall mangrove 10 % below 5, the tall one down to 0). That is a gap in the datapack, the goal is to +# chop every tree, so such a tree doesn't fail the test but must not be chopped and gives a warning +# a chop from a log can leave parts of the tree standing or chop nothing (it searches upwards and sideways, see report_leftovers): warning, no failure +execute if score #adj_chop_mangrove_log_tree timber_test >= min_leaves_found timber run function timber_test:report_leftovers {test:"chop_mangrove_log_tree"} +execute if score #adj_chop_mangrove_log_tree timber_test < min_leaves_found timber run function timber_test:assert_not_chopped {test:"chop_mangrove_log_tree"} diff --git a/tests/data/timber_test/test/chop_mangrove_roots_tree.mcfunction b/tests/data/timber_test/test/chop_mangrove_roots_tree.mcfunction new file mode 100644 index 0000000..019f235 --- /dev/null +++ b/tests/data/timber_test/test/chop_mangrove_roots_tree.mcfunction @@ -0,0 +1,83 @@ +#> Mining a root of a mangrove tree chops down the whole tree (start_chop_on_root) +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:mangrove"} + +# let the leaves settle before the tree is chopped +await delay 1s +function timber_test:count_d7 {test:"chop_mangrove_roots_tree"} +function timber_test:count_adjacent {test:"chop_mangrove_roots_tree"} +function timber_test:count_logs {test:"chop_mangrove_roots_tree"} + +function timber_test:player {name:"mangrove_root_t",axe:"minecraft:iron_axe"} +# mine the lowest root of the trunk column (first match from the bottom) +scoreboard players set #mined timber_test 0 +execute if block ~20 ~0 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy mangrove_root_t mine ~20 ~0 ~20 +execute if block ~20 ~0 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~1 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy mangrove_root_t mine ~20 ~1 ~20 +execute if block ~20 ~1 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~2 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy mangrove_root_t mine ~20 ~2 ~20 +execute if block ~20 ~2 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~3 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy mangrove_root_t mine ~20 ~3 ~20 +execute if block ~20 ~3 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~4 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy mangrove_root_t mine ~20 ~4 ~20 +execute if block ~20 ~4 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~5 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy mangrove_root_t mine ~20 ~5 ~20 +execute if block ~20 ~5 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~6 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy mangrove_root_t mine ~20 ~6 ~20 +execute if block ~20 ~6 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~7 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy mangrove_root_t mine ~20 ~7 ~20 +execute if block ~20 ~7 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~8 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy mangrove_root_t mine ~20 ~8 ~20 +execute if block ~20 ~8 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~9 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy mangrove_root_t mine ~20 ~9 ~20 +execute if block ~20 ~9 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~10 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy mangrove_root_t mine ~20 ~10 ~20 +execute if block ~20 ~10 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~11 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy mangrove_root_t mine ~20 ~11 ~20 +execute if block ~20 ~11 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~12 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy mangrove_root_t mine ~20 ~12 ~20 +execute if block ~20 ~12 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~13 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy mangrove_root_t mine ~20 ~13 ~20 +execute if block ~20 ~13 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~14 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy mangrove_root_t mine ~20 ~14 ~20 +execute if block ~20 ~14 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~15 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy mangrove_root_t mine ~20 ~15 ~20 +execute if block ~20 ~15 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~16 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy mangrove_root_t mine ~20 ~16 ~20 +execute if block ~20 ~16 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~17 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy mangrove_root_t mine ~20 ~17 ~20 +execute if block ~20 ~17 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~18 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy mangrove_root_t mine ~20 ~18 ~20 +execute if block ~20 ~18 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~19 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy mangrove_root_t mine ~20 ~19 ~20 +execute if block ~20 ~19 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~20 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy mangrove_root_t mine ~20 ~20 ~20 +execute if block ~20 ~20 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~21 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy mangrove_root_t mine ~20 ~21 ~20 +execute if block ~20 ~21 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~22 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy mangrove_root_t mine ~20 ~22 ~20 +execute if block ~20 ~22 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~23 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy mangrove_root_t mine ~20 ~23 ~20 +execute if block ~20 ~23 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~24 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy mangrove_root_t mine ~20 ~24 ~20 +execute if block ~20 ~24 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 + +# wait until the datapack is done with the tree +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the mined block dropped as an item: the dummy hit the tree (the base can be a root here, and any item would do for other things lying around, so wait for this one) +# 2. two ticks pass: the datapack reacts to the mined block in the next tick +# 3. no marker is left in the arena: chopping and slow chop delete their markers when they are done +await entity @e[type=minecraft:item,dx=39,dy=39,dz=39,nbt={Item:{id:"minecraft:mangrove_roots"}}] +await delay 2t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +# the datapack only chops a tree with at least min_leaves_found leaves next to a trunk. Some mangroves have fewer (measured with +# 40 trees each: mangrove 15 %, tall mangrove 10 % below 5, the tall one down to 0). That is a gap in the datapack, the goal is to +# chop every tree, so such a tree doesn't fail the test but must not be chopped and gives a warning +execute if score #adj_chop_mangrove_roots_tree timber_test >= min_leaves_found timber run function timber_test:assert_no_roots +execute if score #adj_chop_mangrove_roots_tree timber_test >= min_leaves_found timber run function timber_test:assert_no_tree {test:"chop_mangrove_roots_tree"} +execute if score #adj_chop_mangrove_roots_tree timber_test < min_leaves_found timber run function timber_test:assert_not_chopped {test:"chop_mangrove_roots_tree"} diff --git a/tests/data/timber_test/test/chop_mega_jungle_tree.mcfunction b/tests/data/timber_test/test/chop_mega_jungle_tree.mcfunction index 619308d..bac793f 100644 --- a/tests/data/timber_test/test/chop_mega_jungle_tree.mcfunction +++ b/tests/data/timber_test/test/chop_mega_jungle_tree.mcfunction @@ -9,7 +9,7 @@ assert block ~20 ~ ~20 #minecraft:logs # let the leaves settle before the tree is chopped await delay 1s -function timber_test:count_d7 +function timber_test:count_d7 {test:"chop_mega_jungle_tree"} function timber_test:player {name:"mega_jungle_t",axe:"minecraft:iron_axe"} function timber_test:mine {name:"mega_jungle_t"} diff --git a/tests/data/timber_test/test/chop_mega_pine_tree.mcfunction b/tests/data/timber_test/test/chop_mega_pine_tree.mcfunction index 2c766af..9689980 100644 --- a/tests/data/timber_test/test/chop_mega_pine_tree.mcfunction +++ b/tests/data/timber_test/test/chop_mega_pine_tree.mcfunction @@ -9,7 +9,7 @@ assert block ~20 ~ ~20 #minecraft:logs # let the leaves settle before the tree is chopped await delay 1s -function timber_test:count_d7 +function timber_test:count_d7 {test:"chop_mega_pine_tree"} function timber_test:player {name:"mega_pine_t",axe:"minecraft:iron_axe"} function timber_test:mine {name:"mega_pine_t"} diff --git a/tests/data/timber_test/test/chop_mega_spruce_tree.mcfunction b/tests/data/timber_test/test/chop_mega_spruce_tree.mcfunction index bfd0760..ec6831a 100644 --- a/tests/data/timber_test/test/chop_mega_spruce_tree.mcfunction +++ b/tests/data/timber_test/test/chop_mega_spruce_tree.mcfunction @@ -9,7 +9,7 @@ assert block ~20 ~ ~20 #minecraft:logs # let the leaves settle before the tree is chopped await delay 1s -function timber_test:count_d7 +function timber_test:count_d7 {test:"chop_mega_spruce_tree"} function timber_test:player {name:"mega_spruce_t",axe:"minecraft:iron_axe"} function timber_test:mine {name:"mega_spruce_t"} diff --git a/tests/data/timber_test/test/chop_oak_tree.mcfunction b/tests/data/timber_test/test/chop_oak_tree.mcfunction index 289242d..7150a2f 100644 --- a/tests/data/timber_test/test/chop_oak_tree.mcfunction +++ b/tests/data/timber_test/test/chop_oak_tree.mcfunction @@ -9,7 +9,7 @@ assert block ~20 ~ ~20 #minecraft:logs # let the leaves settle before the tree is chopped await delay 1s -function timber_test:count_d7 +function timber_test:count_d7 {test:"chop_oak_tree"} function timber_test:player {name:"oak_t",axe:"minecraft:iron_axe"} function timber_test:mine {name:"oak_t"} diff --git a/tests/data/timber_test/test/chop_pale_oak_tree.mcfunction b/tests/data/timber_test/test/chop_pale_oak_tree.mcfunction index 9be8a63..99b8c12 100644 --- a/tests/data/timber_test/test/chop_pale_oak_tree.mcfunction +++ b/tests/data/timber_test/test/chop_pale_oak_tree.mcfunction @@ -9,7 +9,7 @@ assert block ~20 ~ ~20 #minecraft:logs # let the leaves settle before the tree is chopped await delay 1s -function timber_test:count_d7 +function timber_test:count_d7 {test:"chop_pale_oak_tree"} function timber_test:player {name:"pale_oak_t",axe:"minecraft:iron_axe"} function timber_test:mine {name:"pale_oak_t"} diff --git a/tests/data/timber_test/test/chop_pine_tree.mcfunction b/tests/data/timber_test/test/chop_pine_tree.mcfunction index acd9595..1a1ea76 100644 --- a/tests/data/timber_test/test/chop_pine_tree.mcfunction +++ b/tests/data/timber_test/test/chop_pine_tree.mcfunction @@ -9,7 +9,7 @@ assert block ~20 ~ ~20 #minecraft:logs # let the leaves settle before the tree is chopped await delay 1s -function timber_test:count_d7 +function timber_test:count_d7 {test:"chop_pine_tree"} function timber_test:player {name:"pine_t",axe:"minecraft:iron_axe"} function timber_test:mine {name:"pine_t"} diff --git a/tests/data/timber_test/test/chop_spruce_tree.mcfunction b/tests/data/timber_test/test/chop_spruce_tree.mcfunction index 8a2f47c..1f3fe06 100644 --- a/tests/data/timber_test/test/chop_spruce_tree.mcfunction +++ b/tests/data/timber_test/test/chop_spruce_tree.mcfunction @@ -9,7 +9,7 @@ assert block ~20 ~ ~20 #minecraft:logs # let the leaves settle before the tree is chopped await delay 1s -function timber_test:count_d7 +function timber_test:count_d7 {test:"chop_spruce_tree"} function timber_test:player {name:"spruce_t",axe:"minecraft:iron_axe"} function timber_test:mine {name:"spruce_t"} diff --git a/tests/data/timber_test/test/chop_swamp_oak_tree.mcfunction b/tests/data/timber_test/test/chop_swamp_oak_tree.mcfunction index e49db82..8368550 100644 --- a/tests/data/timber_test/test/chop_swamp_oak_tree.mcfunction +++ b/tests/data/timber_test/test/chop_swamp_oak_tree.mcfunction @@ -9,7 +9,7 @@ assert block ~20 ~ ~20 #minecraft:logs # let the leaves settle before the tree is chopped await delay 1s -function timber_test:count_d7 +function timber_test:count_d7 {test:"chop_swamp_oak_tree"} function timber_test:player {name:"swamp_oak_t",axe:"minecraft:iron_axe"} function timber_test:mine {name:"swamp_oak_t"} diff --git a/tests/data/timber_test/test/chop_tall_mangrove_roots_tree.mcfunction b/tests/data/timber_test/test/chop_tall_mangrove_roots_tree.mcfunction new file mode 100644 index 0000000..4a582f3 --- /dev/null +++ b/tests/data/timber_test/test/chop_tall_mangrove_roots_tree.mcfunction @@ -0,0 +1,83 @@ +#> Mining a root of a tall mangrove tree chops down the whole tree (start_chop_on_root) +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:tall_mangrove"} + +# let the leaves settle before the tree is chopped +await delay 1s +function timber_test:count_d7 {test:"chop_tall_mangrove_roots_tree"} +function timber_test:count_adjacent {test:"chop_tall_mangrove_roots_tree"} +function timber_test:count_logs {test:"chop_tall_mangrove_roots_tree"} + +function timber_test:player {name:"tall_mg_root_t",axe:"minecraft:iron_axe"} +# mine the lowest root of the trunk column (first match from the bottom) +scoreboard players set #mined timber_test 0 +execute if block ~20 ~0 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy tall_mg_root_t mine ~20 ~0 ~20 +execute if block ~20 ~0 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~1 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy tall_mg_root_t mine ~20 ~1 ~20 +execute if block ~20 ~1 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~2 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy tall_mg_root_t mine ~20 ~2 ~20 +execute if block ~20 ~2 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~3 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy tall_mg_root_t mine ~20 ~3 ~20 +execute if block ~20 ~3 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~4 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy tall_mg_root_t mine ~20 ~4 ~20 +execute if block ~20 ~4 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~5 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy tall_mg_root_t mine ~20 ~5 ~20 +execute if block ~20 ~5 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~6 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy tall_mg_root_t mine ~20 ~6 ~20 +execute if block ~20 ~6 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~7 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy tall_mg_root_t mine ~20 ~7 ~20 +execute if block ~20 ~7 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~8 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy tall_mg_root_t mine ~20 ~8 ~20 +execute if block ~20 ~8 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~9 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy tall_mg_root_t mine ~20 ~9 ~20 +execute if block ~20 ~9 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~10 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy tall_mg_root_t mine ~20 ~10 ~20 +execute if block ~20 ~10 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~11 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy tall_mg_root_t mine ~20 ~11 ~20 +execute if block ~20 ~11 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~12 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy tall_mg_root_t mine ~20 ~12 ~20 +execute if block ~20 ~12 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~13 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy tall_mg_root_t mine ~20 ~13 ~20 +execute if block ~20 ~13 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~14 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy tall_mg_root_t mine ~20 ~14 ~20 +execute if block ~20 ~14 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~15 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy tall_mg_root_t mine ~20 ~15 ~20 +execute if block ~20 ~15 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~16 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy tall_mg_root_t mine ~20 ~16 ~20 +execute if block ~20 ~16 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~17 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy tall_mg_root_t mine ~20 ~17 ~20 +execute if block ~20 ~17 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~18 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy tall_mg_root_t mine ~20 ~18 ~20 +execute if block ~20 ~18 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~19 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy tall_mg_root_t mine ~20 ~19 ~20 +execute if block ~20 ~19 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~20 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy tall_mg_root_t mine ~20 ~20 ~20 +execute if block ~20 ~20 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~21 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy tall_mg_root_t mine ~20 ~21 ~20 +execute if block ~20 ~21 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~22 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy tall_mg_root_t mine ~20 ~22 ~20 +execute if block ~20 ~22 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~23 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy tall_mg_root_t mine ~20 ~23 ~20 +execute if block ~20 ~23 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 +execute if block ~20 ~24 ~20 minecraft:mangrove_roots unless score #mined timber_test matches 1 run dummy tall_mg_root_t mine ~20 ~24 ~20 +execute if block ~20 ~24 ~20 minecraft:mangrove_roots run scoreboard players set #mined timber_test 1 + +# wait until the datapack is done with the tree +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the mined block dropped as an item: the dummy hit the tree (the base can be a root here, and any item would do for other things lying around, so wait for this one) +# 2. two ticks pass: the datapack reacts to the mined block in the next tick +# 3. no marker is left in the arena: chopping and slow chop delete their markers when they are done +await entity @e[type=minecraft:item,dx=39,dy=39,dz=39,nbt={Item:{id:"minecraft:mangrove_roots"}}] +await delay 2t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +# the datapack only chops a tree with at least min_leaves_found leaves next to a trunk. Some mangroves have fewer (measured with +# 40 trees each: mangrove 15 %, tall mangrove 10 % below 5, the tall one down to 0). That is a gap in the datapack, the goal is to +# chop every tree, so such a tree doesn't fail the test but must not be chopped and gives a warning +execute if score #adj_chop_tall_mangrove_roots_tree timber_test >= min_leaves_found timber run function timber_test:assert_no_roots +execute if score #adj_chop_tall_mangrove_roots_tree timber_test >= min_leaves_found timber run function timber_test:assert_no_tree {test:"chop_tall_mangrove_roots_tree"} +execute if score #adj_chop_tall_mangrove_roots_tree timber_test < min_leaves_found timber run function timber_test:assert_not_chopped {test:"chop_tall_mangrove_roots_tree"} diff --git a/tests/data/timber_test/test/chop_tall_mangrove_tree.mcfunction b/tests/data/timber_test/test/chop_tall_mangrove_tree.mcfunction new file mode 100644 index 0000000..d091aeb --- /dev/null +++ b/tests/data/timber_test/test/chop_tall_mangrove_tree.mcfunction @@ -0,0 +1,35 @@ +#> Mining the lowest log of a tall mangrove tree chops down the whole tree including its roots +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:tall_mangrove"} + +# let the leaves settle before the tree is chopped +await delay 1s +function timber_test:count_d7 {test:"chop_tall_mangrove_tree"} +function timber_test:count_adjacent {test:"chop_tall_mangrove_tree"} +function timber_test:count_logs {test:"chop_tall_mangrove_tree"} + +function timber_test:player {name:"tall_mangrove_t",axe:"minecraft:iron_axe"} +# mine the lowest log of the whole tree, from a spot next to it (see find_lowest_log and mine_here for why) +function timber_test:find_lowest_log +execute as @e[type=minecraft:marker,tag=tb_scan,dx=39,dy=39,dz=39] at @s run function timber_test:mine_here {name:"tall_mangrove_t"} +kill @e[type=minecraft:marker,tag=tb_scan,dx=39,dy=39,dz=39] + +# wait until the datapack is done with the tree +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the mined block dropped as an item: the dummy hit the tree (the base can be a root here, and any item would do for other things lying around, so wait for this one) +# 2. two ticks pass: the datapack reacts to the mined block in the next tick +# 3. no marker is left in the arena: chopping and slow chop delete their markers when they are done +await entity @e[type=minecraft:item,dx=39,dy=39,dz=39,nbt={Item:{id:"minecraft:mangrove_log"}}] +await delay 2t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +# the datapack only chops a tree with at least min_leaves_found leaves next to a trunk. Some mangroves have fewer (measured with +# 40 trees each: mangrove 15 %, tall mangrove 10 % below 5, the tall one down to 0). That is a gap in the datapack, the goal is to +# chop every tree, so such a tree doesn't fail the test but must not be chopped and gives a warning +# a chop from a log can leave parts of the tree standing or chop nothing (it searches upwards and sideways, see report_leftovers): warning, no failure +execute if score #adj_chop_tall_mangrove_tree timber_test >= min_leaves_found timber run function timber_test:report_leftovers {test:"chop_tall_mangrove_tree"} +execute if score #adj_chop_tall_mangrove_tree timber_test < min_leaves_found timber run function timber_test:assert_not_chopped {test:"chop_tall_mangrove_tree"} From 78f95d55211e574778fbb0921331b3dbcaa2937d Mon Sep 17 00:00:00 2001 From: Flavio Felder Date: Mon, 21 Sep 2026 18:52:18 +0200 Subject: [PATCH 17/27] Add tests for nether fungi and huge mushrooms Six tests: crimson and warped fungus (grown and planted variant) and the huge red and brown mushroom. chop_fungi is off by default, so the tests switch it on; it is global and the tests run in parallel, so its users are counted and it is switched back when the last one is done. Nether fungi need nylium under them, place feature fails silently otherwise. With chop_fungi on, every chop ends with a kill of all marker items of mushroom stems in the world, a chop in the same tick can delete the marker of another mushroom. The mushroom tests therefore run later and one after the other. Stems have to be gone, cap blocks that are left only warn. Co-Authored-By: Claude Sonnet 5 --- .../function/assert_no_fungus.mcfunction | 37 +++++++++++++++++++ .../timber_test/function/warn_caps.mcfunction | 3 ++ .../chop_brown_mushroom_fungus.mcfunction | 37 +++++++++++++++++++ .../test/chop_crimson_fungus.mcfunction | 37 +++++++++++++++++++ .../chop_crimson_planted_fungus.mcfunction | 37 +++++++++++++++++++ .../test/chop_red_mushroom_fungus.mcfunction | 37 +++++++++++++++++++ .../test/chop_warped_fungus.mcfunction | 37 +++++++++++++++++++ .../chop_warped_planted_fungus.mcfunction | 37 +++++++++++++++++++ 8 files changed, 262 insertions(+) create mode 100644 tests/data/timber_test/function/assert_no_fungus.mcfunction create mode 100644 tests/data/timber_test/function/warn_caps.mcfunction create mode 100644 tests/data/timber_test/test/chop_brown_mushroom_fungus.mcfunction create mode 100644 tests/data/timber_test/test/chop_crimson_fungus.mcfunction create mode 100644 tests/data/timber_test/test/chop_crimson_planted_fungus.mcfunction create mode 100644 tests/data/timber_test/test/chop_red_mushroom_fungus.mcfunction create mode 100644 tests/data/timber_test/test/chop_warped_fungus.mcfunction create mode 100644 tests/data/timber_test/test/chop_warped_planted_fungus.mcfunction diff --git a/tests/data/timber_test/function/assert_no_fungus.mcfunction b/tests/data/timber_test/function/assert_no_fungus.mcfunction new file mode 100644 index 0000000..ea037a7 --- /dev/null +++ b/tests/data/timber_test/function/assert_no_fungus.mcfunction @@ -0,0 +1,37 @@ +#> Fails the test if a stem of a fungus is left in the arena, cap blocks that are left only warn. +#> Stems: all logs (crimson and warped stems are logs) and mushroom_stem. Caps: warts, shroomlight, red and brown mushroom blocks. +#> Replaces them with air while counting, so run it last. The arena is handled in two slabs, a command may only touch 32768 blocks. +#> @param test name of the test +scoreboard objectives add timber_test dummy +scoreboard players set #stems timber_test 0 +scoreboard players set #caps timber_test 0 +execute store result score #dtmp timber_test run fill ~0 ~0 ~0 ~39 ~19 ~39 minecraft:air replace #minecraft:logs +scoreboard players operation #stems timber_test += #dtmp timber_test +execute store result score #dtmp timber_test run fill ~0 ~20 ~0 ~39 ~39 ~39 minecraft:air replace #minecraft:logs +scoreboard players operation #stems timber_test += #dtmp timber_test +execute store result score #dtmp timber_test run fill ~0 ~0 ~0 ~39 ~19 ~39 minecraft:air replace minecraft:mushroom_stem +scoreboard players operation #stems timber_test += #dtmp timber_test +execute store result score #dtmp timber_test run fill ~0 ~20 ~0 ~39 ~39 ~39 minecraft:air replace minecraft:mushroom_stem +scoreboard players operation #stems timber_test += #dtmp timber_test +execute store result score #dtmp timber_test run fill ~0 ~0 ~0 ~39 ~19 ~39 minecraft:air replace #minecraft:wart_blocks +scoreboard players operation #caps timber_test += #dtmp timber_test +execute store result score #dtmp timber_test run fill ~0 ~20 ~0 ~39 ~39 ~39 minecraft:air replace #minecraft:wart_blocks +scoreboard players operation #caps timber_test += #dtmp timber_test +execute store result score #dtmp timber_test run fill ~0 ~0 ~0 ~39 ~19 ~39 minecraft:air replace minecraft:shroomlight +scoreboard players operation #caps timber_test += #dtmp timber_test +execute store result score #dtmp timber_test run fill ~0 ~20 ~0 ~39 ~39 ~39 minecraft:air replace minecraft:shroomlight +scoreboard players operation #caps timber_test += #dtmp timber_test +execute store result score #dtmp timber_test run fill ~0 ~0 ~0 ~39 ~19 ~39 minecraft:air replace minecraft:red_mushroom_block +scoreboard players operation #caps timber_test += #dtmp timber_test +execute store result score #dtmp timber_test run fill ~0 ~20 ~0 ~39 ~39 ~39 minecraft:air replace minecraft:red_mushroom_block +scoreboard players operation #caps timber_test += #dtmp timber_test +execute store result score #dtmp timber_test run fill ~0 ~0 ~0 ~39 ~19 ~39 minecraft:air replace minecraft:brown_mushroom_block +scoreboard players operation #caps timber_test += #dtmp timber_test +execute store result score #dtmp timber_test run fill ~0 ~20 ~0 ~39 ~39 ~39 minecraft:air replace minecraft:brown_mushroom_block +scoreboard players operation #caps timber_test += #dtmp timber_test + +# cap blocks that are left standing only warn (the goal is to chop everything, this is a gap), stems have to be gone +$data modify storage timber_test:tmp test set value "$(test)" +execute store result storage timber_test:tmp caps int 1 run scoreboard players get #caps timber_test +execute unless score #caps timber_test matches 0 run function timber_test:warn_caps with storage timber_test:tmp +assert score #stems timber_test matches 0 diff --git a/tests/data/timber_test/function/warn_caps.mcfunction b/tests/data/timber_test/function/warn_caps.mcfunction new file mode 100644 index 0000000..019b05f --- /dev/null +++ b/tests/data/timber_test/function/warn_caps.mcfunction @@ -0,0 +1,3 @@ +#> Warns that a fungus was chopped but cap blocks are left standing. Same two paths as warn_d7 (log line for the CI, orange chat for players). +$execute unless entity @a[tag=!timber_test_dummy] run say TIMBER-WARNING $(test): $(caps) cap blocks were left standing, every fungus should be chopped completely +$tellraw @a[tag=!timber_test_dummy] {"text":"Test warning $(test): $(caps) cap blocks were left standing, every fungus should be chopped completely","color":"gold"} diff --git a/tests/data/timber_test/test/chop_brown_mushroom_fungus.mcfunction b/tests/data/timber_test/test/chop_brown_mushroom_fungus.mcfunction new file mode 100644 index 0000000..48b835f --- /dev/null +++ b/tests/data/timber_test/test/chop_brown_mushroom_fungus.mcfunction @@ -0,0 +1,37 @@ +#> Mining the bottom stem of a huge brown mushroom with an axe chops down the whole fungus (chop_fungi is on) +# @template timber_test:arena +# @timeout 400 +# @skyaccess true + +function timber_test:platform +# nether fungi only grow on nylium, without it place feature fails without a message +setblock ~20 ~-1 ~20 minecraft:mycelium +function timber_test:tree {feature:"minecraft:huge_brown_mushroom"} +assert block ~20 ~ ~20 minecraft:mushroom_stem + +# chop_fungi is off by default. It is a global setting and the tests run in parallel: the users are counted, it is switched +# back only when the last one is done +scoreboard players add #fungi_users timber_test 1 +scoreboard players set chop_fungi timber 1 + +await delay 1s +# With chop_fungi on, every chop of the datapack ends with a kill of ALL marker items of mushroom stems in the world (no distance +# limit). A chop in the same tick that runs first deletes the marker of a mushroom, which then isn't chopped. So the fungus tests +# wait until the trees are done and chop one after the other, 60 ticks after the first fungus test. +await delay 160t +function timber_test:player {name:"brown_mush_t",axe:"minecraft:iron_axe"} +function timber_test:mine {name:"brown_mush_t"} + +# wait until the datapack is done with the fungus +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the base stem is gone: the dummy hit the fungus +# 2. five ticks pass: the datapack reacts to the mined block a tick or two later, a mushroom stem breaks at once with an axe +# 3. no marker is left in the arena: chopping and slow chop delete their markers when they are done +await not block ~20 ~ ~20 minecraft:mushroom_stem +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +scoreboard players remove #fungi_users timber_test 1 +execute if score #fungi_users timber_test matches ..0 run scoreboard players set chop_fungi timber 0 + +function timber_test:assert_no_fungus {test:"chop_brown_mushroom_fungus"} diff --git a/tests/data/timber_test/test/chop_crimson_fungus.mcfunction b/tests/data/timber_test/test/chop_crimson_fungus.mcfunction new file mode 100644 index 0000000..111d8d5 --- /dev/null +++ b/tests/data/timber_test/test/chop_crimson_fungus.mcfunction @@ -0,0 +1,37 @@ +#> Mining the bottom stem of a crimson fungus with an axe chops down the whole fungus (chop_fungi is on) +# @template timber_test:arena +# @timeout 400 +# @skyaccess true + +function timber_test:platform +# nether fungi only grow on nylium, without it place feature fails without a message +setblock ~20 ~-1 ~20 minecraft:crimson_nylium +function timber_test:tree {feature:"minecraft:crimson_fungus"} +assert block ~20 ~ ~20 #minecraft:logs + +# chop_fungi is off by default. It is a global setting and the tests run in parallel: the users are counted, it is switched +# back only when the last one is done +scoreboard players add #fungi_users timber_test 1 +scoreboard players set chop_fungi timber 1 + +await delay 1s +# With chop_fungi on, every chop of the datapack ends with a kill of ALL marker items of mushroom stems in the world (no distance +# limit). A chop in the same tick that runs first deletes the marker of a mushroom, which then isn't chopped. So the fungus tests +# wait until the trees are done and chop one after the other, 0 ticks after the first fungus test. +await delay 100t +function timber_test:player {name:"crimson_t",axe:"minecraft:iron_axe"} +function timber_test:mine {name:"crimson_t"} + +# wait until the datapack is done with the fungus +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the base stem is gone: the dummy hit the fungus +# 2. five ticks pass: the datapack reacts to the mined block a tick or two later, a mushroom stem breaks at once with an axe +# 3. no marker is left in the arena: chopping and slow chop delete their markers when they are done +await not block ~20 ~ ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +scoreboard players remove #fungi_users timber_test 1 +execute if score #fungi_users timber_test matches ..0 run scoreboard players set chop_fungi timber 0 + +function timber_test:assert_no_fungus {test:"chop_crimson_fungus"} diff --git a/tests/data/timber_test/test/chop_crimson_planted_fungus.mcfunction b/tests/data/timber_test/test/chop_crimson_planted_fungus.mcfunction new file mode 100644 index 0000000..7dc5de3 --- /dev/null +++ b/tests/data/timber_test/test/chop_crimson_planted_fungus.mcfunction @@ -0,0 +1,37 @@ +#> Mining the bottom stem of a crimson fungus grown from a planted one with an axe chops down the whole fungus (chop_fungi is on) +# @template timber_test:arena +# @timeout 400 +# @skyaccess true + +function timber_test:platform +# nether fungi only grow on nylium, without it place feature fails without a message +setblock ~20 ~-1 ~20 minecraft:crimson_nylium +function timber_test:tree {feature:"minecraft:crimson_fungus_planted"} +assert block ~20 ~ ~20 #minecraft:logs + +# chop_fungi is off by default. It is a global setting and the tests run in parallel: the users are counted, it is switched +# back only when the last one is done +scoreboard players add #fungi_users timber_test 1 +scoreboard players set chop_fungi timber 1 + +await delay 1s +# With chop_fungi on, every chop of the datapack ends with a kill of ALL marker items of mushroom stems in the world (no distance +# limit). A chop in the same tick that runs first deletes the marker of a mushroom, which then isn't chopped. So the fungus tests +# wait until the trees are done and chop one after the other, 24 ticks after the first fungus test. +await delay 124t +function timber_test:player {name:"crimson_pl_t",axe:"minecraft:iron_axe"} +function timber_test:mine {name:"crimson_pl_t"} + +# wait until the datapack is done with the fungus +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the base stem is gone: the dummy hit the fungus +# 2. five ticks pass: the datapack reacts to the mined block a tick or two later, a mushroom stem breaks at once with an axe +# 3. no marker is left in the arena: chopping and slow chop delete their markers when they are done +await not block ~20 ~ ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +scoreboard players remove #fungi_users timber_test 1 +execute if score #fungi_users timber_test matches ..0 run scoreboard players set chop_fungi timber 0 + +function timber_test:assert_no_fungus {test:"chop_crimson_planted_fungus"} diff --git a/tests/data/timber_test/test/chop_red_mushroom_fungus.mcfunction b/tests/data/timber_test/test/chop_red_mushroom_fungus.mcfunction new file mode 100644 index 0000000..1a6640c --- /dev/null +++ b/tests/data/timber_test/test/chop_red_mushroom_fungus.mcfunction @@ -0,0 +1,37 @@ +#> Mining the bottom stem of a huge red mushroom with an axe chops down the whole fungus (chop_fungi is on) +# @template timber_test:arena +# @timeout 400 +# @skyaccess true + +function timber_test:platform +# nether fungi only grow on nylium, without it place feature fails without a message +setblock ~20 ~-1 ~20 minecraft:mycelium +function timber_test:tree {feature:"minecraft:huge_red_mushroom"} +assert block ~20 ~ ~20 minecraft:mushroom_stem + +# chop_fungi is off by default. It is a global setting and the tests run in parallel: the users are counted, it is switched +# back only when the last one is done +scoreboard players add #fungi_users timber_test 1 +scoreboard players set chop_fungi timber 1 + +await delay 1s +# With chop_fungi on, every chop of the datapack ends with a kill of ALL marker items of mushroom stems in the world (no distance +# limit). A chop in the same tick that runs first deletes the marker of a mushroom, which then isn't chopped. So the fungus tests +# wait until the trees are done and chop one after the other, 48 ticks after the first fungus test. +await delay 148t +function timber_test:player {name:"red_mush_t",axe:"minecraft:iron_axe"} +function timber_test:mine {name:"red_mush_t"} + +# wait until the datapack is done with the fungus +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the base stem is gone: the dummy hit the fungus +# 2. five ticks pass: the datapack reacts to the mined block a tick or two later, a mushroom stem breaks at once with an axe +# 3. no marker is left in the arena: chopping and slow chop delete their markers when they are done +await not block ~20 ~ ~20 minecraft:mushroom_stem +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +scoreboard players remove #fungi_users timber_test 1 +execute if score #fungi_users timber_test matches ..0 run scoreboard players set chop_fungi timber 0 + +function timber_test:assert_no_fungus {test:"chop_red_mushroom_fungus"} diff --git a/tests/data/timber_test/test/chop_warped_fungus.mcfunction b/tests/data/timber_test/test/chop_warped_fungus.mcfunction new file mode 100644 index 0000000..bb28fad --- /dev/null +++ b/tests/data/timber_test/test/chop_warped_fungus.mcfunction @@ -0,0 +1,37 @@ +#> Mining the bottom stem of a warped fungus with an axe chops down the whole fungus (chop_fungi is on) +# @template timber_test:arena +# @timeout 400 +# @skyaccess true + +function timber_test:platform +# nether fungi only grow on nylium, without it place feature fails without a message +setblock ~20 ~-1 ~20 minecraft:warped_nylium +function timber_test:tree {feature:"minecraft:warped_fungus"} +assert block ~20 ~ ~20 #minecraft:logs + +# chop_fungi is off by default. It is a global setting and the tests run in parallel: the users are counted, it is switched +# back only when the last one is done +scoreboard players add #fungi_users timber_test 1 +scoreboard players set chop_fungi timber 1 + +await delay 1s +# With chop_fungi on, every chop of the datapack ends with a kill of ALL marker items of mushroom stems in the world (no distance +# limit). A chop in the same tick that runs first deletes the marker of a mushroom, which then isn't chopped. So the fungus tests +# wait until the trees are done and chop one after the other, 12 ticks after the first fungus test. +await delay 112t +function timber_test:player {name:"warped_t",axe:"minecraft:iron_axe"} +function timber_test:mine {name:"warped_t"} + +# wait until the datapack is done with the fungus +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the base stem is gone: the dummy hit the fungus +# 2. five ticks pass: the datapack reacts to the mined block a tick or two later, a mushroom stem breaks at once with an axe +# 3. no marker is left in the arena: chopping and slow chop delete their markers when they are done +await not block ~20 ~ ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +scoreboard players remove #fungi_users timber_test 1 +execute if score #fungi_users timber_test matches ..0 run scoreboard players set chop_fungi timber 0 + +function timber_test:assert_no_fungus {test:"chop_warped_fungus"} diff --git a/tests/data/timber_test/test/chop_warped_planted_fungus.mcfunction b/tests/data/timber_test/test/chop_warped_planted_fungus.mcfunction new file mode 100644 index 0000000..8e55f8d --- /dev/null +++ b/tests/data/timber_test/test/chop_warped_planted_fungus.mcfunction @@ -0,0 +1,37 @@ +#> Mining the bottom stem of a warped fungus grown from a planted one with an axe chops down the whole fungus (chop_fungi is on) +# @template timber_test:arena +# @timeout 400 +# @skyaccess true + +function timber_test:platform +# nether fungi only grow on nylium, without it place feature fails without a message +setblock ~20 ~-1 ~20 minecraft:warped_nylium +function timber_test:tree {feature:"minecraft:warped_fungus_planted"} +assert block ~20 ~ ~20 #minecraft:logs + +# chop_fungi is off by default. It is a global setting and the tests run in parallel: the users are counted, it is switched +# back only when the last one is done +scoreboard players add #fungi_users timber_test 1 +scoreboard players set chop_fungi timber 1 + +await delay 1s +# With chop_fungi on, every chop of the datapack ends with a kill of ALL marker items of mushroom stems in the world (no distance +# limit). A chop in the same tick that runs first deletes the marker of a mushroom, which then isn't chopped. So the fungus tests +# wait until the trees are done and chop one after the other, 36 ticks after the first fungus test. +await delay 136t +function timber_test:player {name:"warped_pl_t",axe:"minecraft:iron_axe"} +function timber_test:mine {name:"warped_pl_t"} + +# wait until the datapack is done with the fungus +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the base stem is gone: the dummy hit the fungus +# 2. five ticks pass: the datapack reacts to the mined block a tick or two later, a mushroom stem breaks at once with an axe +# 3. no marker is left in the arena: chopping and slow chop delete their markers when they are done +await not block ~20 ~ ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +scoreboard players remove #fungi_users timber_test 1 +execute if score #fungi_users timber_test matches ..0 run scoreboard players set chop_fungi timber 0 + +function timber_test:assert_no_fungus {test:"chop_warped_planted_fungus"} From 0a85c4aae5f7d2ba01e9ffef71339479ce61a497 Mon Sep 17 00:00:00 2001 From: Flavio Felder Date: Mon, 21 Sep 2026 19:01:00 +0200 Subject: [PATCH 18/27] Add negative tests: structures the datapack must not chop A hut and a stack of logs without leaves, and a column with 4 leaves next to it are not chopped, the same column with 5 leaves is (min_leaves_found is 5), the same setup with one leaf more or less shows the rule holds at its edge. Five leaves placed by a player (persistent) next to a stack of logs are chopped anyway: the README says only natural leaves count, the leaf predicate does not check that and the persistent setting is not read anywhere. That is a known gap, the test warns instead of failing. Co-Authored-By: Claude Sonnet 5 --- .../assert_only_mined_gone.mcfunction | 9 ++++++ .../report_chopped_structure.mcfunction | 12 +++++++ .../warn_chopped_structure.mcfunction | 3 ++ .../test/chopped_five_leaves.mcfunction | 32 +++++++++++++++++++ .../test/not_chopped_four_leaves.mcfunction | 29 +++++++++++++++++ .../test/not_chopped_log_hut.mcfunction | 23 +++++++++++++ .../test/not_chopped_log_pile.mcfunction | 23 +++++++++++++ .../placed_leaves_should_not_count.mcfunction | 31 ++++++++++++++++++ 8 files changed, 162 insertions(+) create mode 100644 tests/data/timber_test/function/assert_only_mined_gone.mcfunction create mode 100644 tests/data/timber_test/function/report_chopped_structure.mcfunction create mode 100644 tests/data/timber_test/function/warn_chopped_structure.mcfunction create mode 100644 tests/data/timber_test/test/chopped_five_leaves.mcfunction create mode 100644 tests/data/timber_test/test/not_chopped_four_leaves.mcfunction create mode 100644 tests/data/timber_test/test/not_chopped_log_hut.mcfunction create mode 100644 tests/data/timber_test/test/not_chopped_log_pile.mcfunction create mode 100644 tests/data/timber_test/test/placed_leaves_should_not_count.mcfunction diff --git a/tests/data/timber_test/function/assert_only_mined_gone.mcfunction b/tests/data/timber_test/function/assert_only_mined_gone.mcfunction new file mode 100644 index 0000000..84134a7 --- /dev/null +++ b/tests/data/timber_test/function/assert_only_mined_gone.mcfunction @@ -0,0 +1,9 @@ +#> For a structure the datapack must not chop: fails the test if more than the one mined log is gone. +#> Replaces logs with air while counting, so run it last. The arena is handled in two slabs, a command may only touch 32768 blocks. +#> @param test name of the test, count_logs has to be called with the same name before mining +scoreboard objectives add timber_test dummy +execute store result score #logs1 timber_test run fill ~0 ~0 ~0 ~39 ~19 ~39 minecraft:air replace #minecraft:logs +execute store result score #dtmp timber_test run fill ~0 ~20 ~0 ~39 ~39 ~39 minecraft:air replace #minecraft:logs +scoreboard players operation #logs1 timber_test += #dtmp timber_test +scoreboard players add #logs1 timber_test 1 +$assert score #logs1 timber_test >= #logs0_$(test) timber_test diff --git a/tests/data/timber_test/function/report_chopped_structure.mcfunction b/tests/data/timber_test/function/report_chopped_structure.mcfunction new file mode 100644 index 0000000..a1e23f8 --- /dev/null +++ b/tests/data/timber_test/function/report_chopped_structure.mcfunction @@ -0,0 +1,12 @@ +#> For a structure that should not be chopped but where the datapack has a known gap: warns if it was chopped, never fails. +#> Replaces logs with air while counting, so run it last. The arena is handled in two slabs, a command may only touch 32768 blocks. +#> @param test name of the test, count_logs has to be called with the same name before mining +scoreboard objectives add timber_test dummy +execute store result score #logs1 timber_test run fill ~0 ~0 ~0 ~39 ~19 ~39 minecraft:air replace #minecraft:logs +execute store result score #dtmp timber_test run fill ~0 ~20 ~0 ~39 ~39 ~39 minecraft:air replace #minecraft:logs +scoreboard players operation #logs1 timber_test += #dtmp timber_test +scoreboard players add #logs1 timber_test 1 +$data modify storage timber_test:tmp test set value "$(test)" +$execute store result storage timber_test:tmp before int 1 run scoreboard players get #logs0_$(test) timber_test +execute store result storage timber_test:tmp after int 1 run scoreboard players get #logs1 timber_test +$execute if score #logs1 timber_test < #logs0_$(test) timber_test run function timber_test:warn_chopped_structure with storage timber_test:tmp diff --git a/tests/data/timber_test/function/warn_chopped_structure.mcfunction b/tests/data/timber_test/function/warn_chopped_structure.mcfunction new file mode 100644 index 0000000..28d1573 --- /dev/null +++ b/tests/data/timber_test/function/warn_chopped_structure.mcfunction @@ -0,0 +1,3 @@ +#> Warns that a structure that should have been left alone was chopped. Same two paths as warn_d7 (log line for the CI, orange chat for players). +$execute unless entity @a[tag=!timber_test_dummy] run say TIMBER-WARNING $(test): chopped although it is not a tree ($(before) logs before, $(after) left after adding the mined one), the datapack does not check that the leaves are natural +$tellraw @a[tag=!timber_test_dummy] {"text":"Test warning $(test): chopped although it is not a tree ($(before) logs before, $(after) left after adding the mined one), the datapack does not check that the leaves are natural","color":"gold"} diff --git a/tests/data/timber_test/test/chopped_five_leaves.mcfunction b/tests/data/timber_test/test/chopped_five_leaves.mcfunction new file mode 100644 index 0000000..64d250c --- /dev/null +++ b/tests/data/timber_test/test/chopped_five_leaves.mcfunction @@ -0,0 +1,32 @@ +#> Mining a log with exactly 5 leaves next to the trunk chops the whole tree (min_leaves_found is 5) +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +# the rule is built for the default of 5 leaves +assert score min_leaves_found timber matches 5 +# a column of 6 logs with 5 leaves: 4 at its sides, one on top +fill ~20 ~0 ~20 ~20 ~5 ~20 minecraft:oak_log +setblock ~21 ~3 ~20 minecraft:oak_leaves[distance=1,persistent=false] +setblock ~19 ~3 ~20 minecraft:oak_leaves[distance=1,persistent=false] +setblock ~20 ~3 ~21 minecraft:oak_leaves[distance=1,persistent=false] +setblock ~20 ~3 ~19 minecraft:oak_leaves[distance=1,persistent=false] +setblock ~20 ~6 ~20 minecraft:oak_leaves[distance=1,persistent=false] + +function timber_test:count_d7 {test:"chopped_five_leaves"} + +function timber_test:count_logs {test:"chopped_five_leaves"} +function timber_test:player {name:"pos5_t",axe:"minecraft:iron_axe"} +function timber_test:mine {name:"pos5_t"} + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the base log is gone: the dummy hit the structure +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~ ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_no_tree {test:"chopped_five_leaves"} diff --git a/tests/data/timber_test/test/not_chopped_four_leaves.mcfunction b/tests/data/timber_test/test/not_chopped_four_leaves.mcfunction new file mode 100644 index 0000000..5594a25 --- /dev/null +++ b/tests/data/timber_test/test/not_chopped_four_leaves.mcfunction @@ -0,0 +1,29 @@ +#> Mining a log with only 4 leaves next to the trunk does not chop it (min_leaves_found is 5) +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +# the rule is built for the default of 5 leaves +assert score min_leaves_found timber matches 5 +# a column of 6 logs with 4 leaves at its sides +fill ~20 ~0 ~20 ~20 ~5 ~20 minecraft:oak_log +setblock ~21 ~3 ~20 minecraft:oak_leaves[distance=1,persistent=false] +setblock ~19 ~3 ~20 minecraft:oak_leaves[distance=1,persistent=false] +setblock ~20 ~3 ~21 minecraft:oak_leaves[distance=1,persistent=false] +setblock ~20 ~3 ~19 minecraft:oak_leaves[distance=1,persistent=false] + +function timber_test:count_logs {test:"not_chopped_four_leaves"} +function timber_test:player {name:"neg4_t",axe:"minecraft:iron_axe"} +function timber_test:mine {name:"neg4_t"} + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the base log is gone: the dummy hit the structure +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~ ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_only_mined_gone {test:"not_chopped_four_leaves"} diff --git a/tests/data/timber_test/test/not_chopped_log_hut.mcfunction b/tests/data/timber_test/test/not_chopped_log_hut.mcfunction new file mode 100644 index 0000000..691b75a --- /dev/null +++ b/tests/data/timber_test/test/not_chopped_log_hut.mcfunction @@ -0,0 +1,23 @@ +#> Mining a log of a hut built from logs (no leaves anywhere) does not chop it +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +# a hollow 5x5x4 box of logs, the corner log at ~20 ~ ~20 is mined +fill ~20 ~0 ~20 ~24 ~3 ~24 minecraft:oak_log hollow + +function timber_test:count_logs {test:"not_chopped_log_hut"} +function timber_test:player {name:"neg_hut_t",axe:"minecraft:iron_axe"} +function timber_test:mine {name:"neg_hut_t"} + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the base log is gone: the dummy hit the structure +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~ ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_only_mined_gone {test:"not_chopped_log_hut"} diff --git a/tests/data/timber_test/test/not_chopped_log_pile.mcfunction b/tests/data/timber_test/test/not_chopped_log_pile.mcfunction new file mode 100644 index 0000000..d36afe0 --- /dev/null +++ b/tests/data/timber_test/test/not_chopped_log_pile.mcfunction @@ -0,0 +1,23 @@ +#> Mining the bottom log of a stack of logs (no leaves anywhere) does not chop it +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +# a column of 8 logs +fill ~20 ~0 ~20 ~20 ~7 ~20 minecraft:oak_log + +function timber_test:count_logs {test:"not_chopped_log_pile"} +function timber_test:player {name:"neg_pile_t",axe:"minecraft:iron_axe"} +function timber_test:mine {name:"neg_pile_t"} + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the base log is gone: the dummy hit the structure +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~ ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_only_mined_gone {test:"not_chopped_log_pile"} diff --git a/tests/data/timber_test/test/placed_leaves_should_not_count.mcfunction b/tests/data/timber_test/test/placed_leaves_should_not_count.mcfunction new file mode 100644 index 0000000..47ff800 --- /dev/null +++ b/tests/data/timber_test/test/placed_leaves_should_not_count.mcfunction @@ -0,0 +1,31 @@ +#> Five leaves placed by a player (persistent) next to a stack of logs: the README says only natural leaves count, so it should not be chopped +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +# the rule is built for the default of 5 leaves +assert score min_leaves_found timber matches 5 +# a column of 6 logs with 5 player placed leaves (persistent=true) next to it +fill ~20 ~0 ~20 ~20 ~5 ~20 minecraft:oak_log +setblock ~21 ~3 ~20 minecraft:oak_leaves[distance=1,persistent=true] +setblock ~19 ~3 ~20 minecraft:oak_leaves[distance=1,persistent=true] +setblock ~20 ~3 ~21 minecraft:oak_leaves[distance=1,persistent=true] +setblock ~20 ~3 ~19 minecraft:oak_leaves[distance=1,persistent=true] +setblock ~21 ~4 ~20 minecraft:oak_leaves[distance=1,persistent=true] + +function timber_test:count_logs {test:"placed_leaves_should_not_count"} +function timber_test:player {name:"placed_lv_t",axe:"minecraft:iron_axe"} +function timber_test:mine {name:"placed_lv_t"} + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the base log is gone: the dummy hit the structure +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~ ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +# known gap: warns if chopped, never fails +function timber_test:report_chopped_structure {test:"placed_leaves_should_not_count"} From c33531ab4ef7952c82fd5c6ffd857d127ac9a922 Mon Sep 17 00:00:00 2001 From: Flavio Felder Date: Mon, 21 Sep 2026 19:29:28 +0200 Subject: [PATCH 19/27] Restore the persistent check for leaves Since v5.1.6 (a93c4dd, mangrove propagules) the leaf predicate accepted every leaf block. The check that the setting "persistent" switched on in v2.1.0 was lost with it: a leaf counts if it is natural (persistent=false), or if it is any leaf and the player has the setting on (timber_prsistent, set in run). Leaves placed by a player next to logs made a hut or a log stack a tree. Co-Authored-By: Claude Sonnet 5 --- 121/data/timber/predicate/block/leaf.json | 27 ++++++++++++++++- data/timber/predicate/block/leaf.json | 29 +++++++++++++++++-- .../report_chopped_structure.mcfunction | 12 -------- .../warn_chopped_structure.mcfunction | 3 -- 4 files changed, 53 insertions(+), 18 deletions(-) delete mode 100644 tests/data/timber_test/function/report_chopped_structure.mcfunction delete mode 100644 tests/data/timber_test/function/warn_chopped_structure.mcfunction diff --git a/121/data/timber/predicate/block/leaf.json b/121/data/timber/predicate/block/leaf.json index 4e4ac16..7f82968 100644 --- a/121/data/timber/predicate/block/leaf.json +++ b/121/data/timber/predicate/block/leaf.json @@ -5,10 +5,35 @@ "type": "minecraft:location_check", "predicate": { "block": { - "blocks": "#minecraft:leaves" + "blocks": "#minecraft:leaves", + "state": { + "persistent": "false" + } } } }, + { + "type": "minecraft:all_of", + "terms": [ + { + "type": "minecraft:location_check", + "predicate": { + "block": { + "blocks": "#minecraft:leaves" + } + } + }, + { + "type": "minecraft:entity_scores", + "entity": "this", + "scores": { + "timber_prsistent": { + "min": 1 + } + } + } + ] + }, { "type": "minecraft:location_check", "predicate": { diff --git a/data/timber/predicate/block/leaf.json b/data/timber/predicate/block/leaf.json index 14d5368..016c5cb 100644 --- a/data/timber/predicate/block/leaf.json +++ b/data/timber/predicate/block/leaf.json @@ -5,10 +5,35 @@ "condition": "minecraft:location_check", "predicate": { "block": { - "blocks": "#minecraft:leaves" + "blocks": "#minecraft:leaves", + "state": { + "persistent": "false" + } } } }, + { + "condition": "minecraft:all_of", + "terms": [ + { + "condition": "minecraft:location_check", + "predicate": { + "block": { + "blocks": "#minecraft:leaves" + } + } + }, + { + "condition": "minecraft:entity_scores", + "entity": "this", + "scores": { + "timber_prsistent": { + "min": 1 + } + } + } + ] + }, { "condition": "minecraft:location_check", "predicate": { @@ -21,4 +46,4 @@ } } ] -} \ No newline at end of file +} diff --git a/tests/data/timber_test/function/report_chopped_structure.mcfunction b/tests/data/timber_test/function/report_chopped_structure.mcfunction deleted file mode 100644 index a1e23f8..0000000 --- a/tests/data/timber_test/function/report_chopped_structure.mcfunction +++ /dev/null @@ -1,12 +0,0 @@ -#> For a structure that should not be chopped but where the datapack has a known gap: warns if it was chopped, never fails. -#> Replaces logs with air while counting, so run it last. The arena is handled in two slabs, a command may only touch 32768 blocks. -#> @param test name of the test, count_logs has to be called with the same name before mining -scoreboard objectives add timber_test dummy -execute store result score #logs1 timber_test run fill ~0 ~0 ~0 ~39 ~19 ~39 minecraft:air replace #minecraft:logs -execute store result score #dtmp timber_test run fill ~0 ~20 ~0 ~39 ~39 ~39 minecraft:air replace #minecraft:logs -scoreboard players operation #logs1 timber_test += #dtmp timber_test -scoreboard players add #logs1 timber_test 1 -$data modify storage timber_test:tmp test set value "$(test)" -$execute store result storage timber_test:tmp before int 1 run scoreboard players get #logs0_$(test) timber_test -execute store result storage timber_test:tmp after int 1 run scoreboard players get #logs1 timber_test -$execute if score #logs1 timber_test < #logs0_$(test) timber_test run function timber_test:warn_chopped_structure with storage timber_test:tmp diff --git a/tests/data/timber_test/function/warn_chopped_structure.mcfunction b/tests/data/timber_test/function/warn_chopped_structure.mcfunction deleted file mode 100644 index 28d1573..0000000 --- a/tests/data/timber_test/function/warn_chopped_structure.mcfunction +++ /dev/null @@ -1,3 +0,0 @@ -#> Warns that a structure that should have been left alone was chopped. Same two paths as warn_d7 (log line for the CI, orange chat for players). -$execute unless entity @a[tag=!timber_test_dummy] run say TIMBER-WARNING $(test): chopped although it is not a tree ($(before) logs before, $(after) left after adding the mined one), the datapack does not check that the leaves are natural -$tellraw @a[tag=!timber_test_dummy] {"text":"Test warning $(test): chopped although it is not a tree ($(before) logs before, $(after) left after adding the mined one), the datapack does not check that the leaves are natural","color":"gold"} From 6f45ebb56959e101762087e61f5fc1522adf2eac Mon Sep 17 00:00:00 2001 From: Flavio Felder Date: Mon, 21 Sep 2026 19:29:28 +0200 Subject: [PATCH 20/27] Make the "not chopped" checks able to fail PackTest's assert score does not take a comparison of two scores, and the parse error inside a macro function is swallowed: assert_only_mined_gone and assert_not_chopped could never fail, so every test using them was green. They subtract and compare with matches now. Checked with a chopped column (fails) and the same column with 4 leaves (passes). placed_leaves_should_not_count is a hard test now: it fails without the fix of the leaf predicate and passes with it. Co-Authored-By: Claude Sonnet 5 --- .../data/timber_test/function/assert_not_chopped.mcfunction | 5 ++++- .../timber_test/function/assert_only_mined_gone.mcfunction | 5 ++++- .../test/placed_leaves_should_not_count.mcfunction | 5 ++--- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/tests/data/timber_test/function/assert_not_chopped.mcfunction b/tests/data/timber_test/function/assert_not_chopped.mcfunction index e7247e9..36a016f 100644 --- a/tests/data/timber_test/function/assert_not_chopped.mcfunction +++ b/tests/data/timber_test/function/assert_not_chopped.mcfunction @@ -15,4 +15,7 @@ function timber_test:warn_few_leaves with storage timber_test:tmp # the one mined log may be gone, nothing else scoreboard players add #logs1 timber_test 1 -$assert score #logs1 timber_test >= #logs0_$(test) timber_test +# PackTest's assert doesn't take a comparison of two scores (the parse error inside a macro function is swallowed, the test can't fail): +# subtract and compare with matches +$scoreboard players operation #logs1 timber_test -= #logs0_$(test) timber_test +assert score #logs1 timber_test matches 0.. diff --git a/tests/data/timber_test/function/assert_only_mined_gone.mcfunction b/tests/data/timber_test/function/assert_only_mined_gone.mcfunction index 84134a7..9f3ddc0 100644 --- a/tests/data/timber_test/function/assert_only_mined_gone.mcfunction +++ b/tests/data/timber_test/function/assert_only_mined_gone.mcfunction @@ -6,4 +6,7 @@ execute store result score #logs1 timber_test run fill ~0 ~0 ~0 ~39 ~19 ~39 mine execute store result score #dtmp timber_test run fill ~0 ~20 ~0 ~39 ~39 ~39 minecraft:air replace #minecraft:logs scoreboard players operation #logs1 timber_test += #dtmp timber_test scoreboard players add #logs1 timber_test 1 -$assert score #logs1 timber_test >= #logs0_$(test) timber_test +# PackTest's assert doesn't take a comparison of two scores (the parse error inside a macro function is swallowed, the test can't fail): +# subtract and compare with matches +$scoreboard players operation #logs1 timber_test -= #logs0_$(test) timber_test +assert score #logs1 timber_test matches 0.. diff --git a/tests/data/timber_test/test/placed_leaves_should_not_count.mcfunction b/tests/data/timber_test/test/placed_leaves_should_not_count.mcfunction index 47ff800..4cfefab 100644 --- a/tests/data/timber_test/test/placed_leaves_should_not_count.mcfunction +++ b/tests/data/timber_test/test/placed_leaves_should_not_count.mcfunction @@ -1,4 +1,4 @@ -#> Five leaves placed by a player (persistent) next to a stack of logs: the README says only natural leaves count, so it should not be chopped +#> Five leaves placed by a player (persistent) next to a stack of logs: only natural leaves count (setting persistent is off), so it must not be chopped # @template timber_test:arena # @timeout 200 # @skyaccess true @@ -27,5 +27,4 @@ await not block ~20 ~ ~20 #minecraft:logs await delay 5t await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] -# known gap: warns if chopped, never fails -function timber_test:report_chopped_structure {test:"placed_leaves_should_not_count"} +function timber_test:assert_only_mined_gone {test:"placed_leaves_should_not_count"} From 29f8b83e75f9edf5f6359749bb5d2504920081b4 Mon Sep 17 00:00:00 2001 From: Flavio Felder Date: Mon, 21 Sep 2026 21:03:50 +0200 Subject: [PATCH 21/27] Add settings tests, one test environment per setting Global settings can't be changed by tests that run in parallel. PackTest runs the tests of different environments as batches one after the other, so every setting gets an environment whose setup sets it and whose teardown puts the default back. 28 tests: sneaking, all seven axes on and off, chop_down, wear_out, hunger, destroy_leaves, chop_trees, max_tree_size, persistent, and min_leaves_found 13 and 14 (an oak has exactly 13 leaves next to its trunk). max_tree_size caps the chop, it doesn't refuse the tree (matches the menu text). A dummy can't duck under a block: sneaking tests use a column with leaves from height 3 on, a random oak has leaves above the dummy in one of three cases. The tests raise max_command_forks: dozens of chops in the same tick got to the limit, one in about 10 suite runs left leaves standing, 0 in 30 with it. The adjacency count includes hanging mangrove propagules like the leaf predicate. Chops that start at a mangrove root only warn about leftovers now, like the ones that start at a log. Co-Authored-By: Claude Sonnet 5 --- tests/data/minecraft/tags/function/load.json | 5 +++ .../assert_logs_gone_leaves_kept.mcfunction | 14 ++++++++ .../function/assert_logs_left.mcfunction | 7 ++++ .../function/assert_logs_removed.mcfunction | 11 +++++++ .../function/count_adjacent.mcfunction | 10 +++++- .../function/count_leaves.mcfunction | 7 ++++ .../function/env/axes_off_setup.mcfunction | 9 +++++ .../function/env/axes_off_teardown.mcfunction | 8 +++++ .../env/chop_down_on_setup.mcfunction | 3 ++ .../env/chop_down_on_teardown.mcfunction | 2 ++ .../env/chop_trees_off_setup.mcfunction | 3 ++ .../env/chop_trees_off_teardown.mcfunction | 2 ++ .../function/env/hunger_on_setup.mcfunction | 3 ++ .../env/hunger_on_teardown.mcfunction | 2 ++ .../env/min_leaves_13_setup.mcfunction | 3 ++ .../env/min_leaves_13_teardown.mcfunction | 2 ++ .../env/min_leaves_14_setup.mcfunction | 3 ++ .../env/min_leaves_14_teardown.mcfunction | 2 ++ .../env/no_destroy_leaves_setup.mcfunction | 3 ++ .../env/no_destroy_leaves_teardown.mcfunction | 2 ++ .../env/persistent_on_setup.mcfunction | 3 ++ .../env/persistent_on_teardown.mcfunction | 2 ++ .../function/env/sneak_only_setup.mcfunction | 4 +++ .../env/sneak_only_teardown.mcfunction | 3 ++ .../env/tiny_tree_size_setup.mcfunction | 3 ++ .../env/tiny_tree_size_teardown.mcfunction | 2 ++ .../env/wear_out_off_setup.mcfunction | 3 ++ .../env/wear_out_off_teardown.mcfunction | 2 ++ .../data/timber_test/function/load.mcfunction | 6 ++++ .../test/chop_mangrove_roots_tree.mcfunction | 4 +-- .../chop_tall_mangrove_roots_tree.mcfunction | 4 +-- .../test/settings_axe_copper_chops.mcfunction | 25 ++++++++++++++ ...settings_axe_copper_off_no_chop.mcfunction | 26 +++++++++++++++ .../settings_axe_diamond_chops.mcfunction | 25 ++++++++++++++ ...ettings_axe_diamond_off_no_chop.mcfunction | 26 +++++++++++++++ .../test/settings_axe_golden_chops.mcfunction | 25 ++++++++++++++ ...settings_axe_golden_off_no_chop.mcfunction | 26 +++++++++++++++ .../test/settings_axe_iron_chops.mcfunction | 25 ++++++++++++++ .../settings_axe_iron_off_no_chop.mcfunction | 26 +++++++++++++++ .../settings_axe_netherite_chops.mcfunction | 25 ++++++++++++++ ...tings_axe_netherite_off_no_chop.mcfunction | 26 +++++++++++++++ .../test/settings_axe_stone_chops.mcfunction | 25 ++++++++++++++ .../settings_axe_stone_off_no_chop.mcfunction | 26 +++++++++++++++ .../test/settings_axe_wooden_chops.mcfunction | 25 ++++++++++++++ ...settings_axe_wooden_off_no_chop.mcfunction | 26 +++++++++++++++ ..._chop_down_off_keeps_logs_below.mcfunction | 24 ++++++++++++++ ...s_chop_down_on_chops_logs_below.mcfunction | 26 +++++++++++++++ .../test/settings_chop_trees_off.mcfunction | 26 +++++++++++++++ ...settings_hunger_on_gives_hunger.mcfunction | 27 +++++++++++++++ ...ngs_max_tree_size_caps_the_chop.mcfunction | 30 +++++++++++++++++ ...ettings_min_leaves_13_chops_oak.mcfunction | 26 +++++++++++++++ ...ettings_min_leaves_14_keeps_oak.mcfunction | 26 +++++++++++++++ .../settings_no_destroy_leaves.mcfunction | 26 +++++++++++++++ ...sistent_on_counts_placed_leaves.mcfunction | 30 +++++++++++++++++ ...tings_sneak_only_sneaking_chops.mcfunction | 33 +++++++++++++++++++ ...ngs_sneak_only_standing_no_chop.mcfunction | 26 +++++++++++++++ ...ttings_sneaking_default_no_chop.mcfunction | 32 ++++++++++++++++++ ...gs_wear_out_default_damages_axe.mcfunction | 27 +++++++++++++++ ...settings_wear_out_off_keeps_axe.mcfunction | 28 ++++++++++++++++ .../test_environment/axes_off.json | 5 +++ .../test_environment/chop_down_on.json | 5 +++ .../test_environment/chop_trees_off.json | 5 +++ .../test_environment/hunger_on.json | 5 +++ .../test_environment/min_leaves_13.json | 5 +++ .../test_environment/min_leaves_14.json | 5 +++ .../test_environment/no_destroy_leaves.json | 5 +++ .../test_environment/persistent_on.json | 5 +++ .../test_environment/sneak_only.json | 5 +++ .../test_environment/tiny_tree_size.json | 5 +++ .../test_environment/wear_out_off.json | 5 +++ 70 files changed, 931 insertions(+), 5 deletions(-) create mode 100644 tests/data/minecraft/tags/function/load.json create mode 100644 tests/data/timber_test/function/assert_logs_gone_leaves_kept.mcfunction create mode 100644 tests/data/timber_test/function/assert_logs_left.mcfunction create mode 100644 tests/data/timber_test/function/assert_logs_removed.mcfunction create mode 100644 tests/data/timber_test/function/count_leaves.mcfunction create mode 100644 tests/data/timber_test/function/env/axes_off_setup.mcfunction create mode 100644 tests/data/timber_test/function/env/axes_off_teardown.mcfunction create mode 100644 tests/data/timber_test/function/env/chop_down_on_setup.mcfunction create mode 100644 tests/data/timber_test/function/env/chop_down_on_teardown.mcfunction create mode 100644 tests/data/timber_test/function/env/chop_trees_off_setup.mcfunction create mode 100644 tests/data/timber_test/function/env/chop_trees_off_teardown.mcfunction create mode 100644 tests/data/timber_test/function/env/hunger_on_setup.mcfunction create mode 100644 tests/data/timber_test/function/env/hunger_on_teardown.mcfunction create mode 100644 tests/data/timber_test/function/env/min_leaves_13_setup.mcfunction create mode 100644 tests/data/timber_test/function/env/min_leaves_13_teardown.mcfunction create mode 100644 tests/data/timber_test/function/env/min_leaves_14_setup.mcfunction create mode 100644 tests/data/timber_test/function/env/min_leaves_14_teardown.mcfunction create mode 100644 tests/data/timber_test/function/env/no_destroy_leaves_setup.mcfunction create mode 100644 tests/data/timber_test/function/env/no_destroy_leaves_teardown.mcfunction create mode 100644 tests/data/timber_test/function/env/persistent_on_setup.mcfunction create mode 100644 tests/data/timber_test/function/env/persistent_on_teardown.mcfunction create mode 100644 tests/data/timber_test/function/env/sneak_only_setup.mcfunction create mode 100644 tests/data/timber_test/function/env/sneak_only_teardown.mcfunction create mode 100644 tests/data/timber_test/function/env/tiny_tree_size_setup.mcfunction create mode 100644 tests/data/timber_test/function/env/tiny_tree_size_teardown.mcfunction create mode 100644 tests/data/timber_test/function/env/wear_out_off_setup.mcfunction create mode 100644 tests/data/timber_test/function/env/wear_out_off_teardown.mcfunction create mode 100644 tests/data/timber_test/function/load.mcfunction create mode 100644 tests/data/timber_test/test/settings_axe_copper_chops.mcfunction create mode 100644 tests/data/timber_test/test/settings_axe_copper_off_no_chop.mcfunction create mode 100644 tests/data/timber_test/test/settings_axe_diamond_chops.mcfunction create mode 100644 tests/data/timber_test/test/settings_axe_diamond_off_no_chop.mcfunction create mode 100644 tests/data/timber_test/test/settings_axe_golden_chops.mcfunction create mode 100644 tests/data/timber_test/test/settings_axe_golden_off_no_chop.mcfunction create mode 100644 tests/data/timber_test/test/settings_axe_iron_chops.mcfunction create mode 100644 tests/data/timber_test/test/settings_axe_iron_off_no_chop.mcfunction create mode 100644 tests/data/timber_test/test/settings_axe_netherite_chops.mcfunction create mode 100644 tests/data/timber_test/test/settings_axe_netherite_off_no_chop.mcfunction create mode 100644 tests/data/timber_test/test/settings_axe_stone_chops.mcfunction create mode 100644 tests/data/timber_test/test/settings_axe_stone_off_no_chop.mcfunction create mode 100644 tests/data/timber_test/test/settings_axe_wooden_chops.mcfunction create mode 100644 tests/data/timber_test/test/settings_axe_wooden_off_no_chop.mcfunction create mode 100644 tests/data/timber_test/test/settings_chop_down_off_keeps_logs_below.mcfunction create mode 100644 tests/data/timber_test/test/settings_chop_down_on_chops_logs_below.mcfunction create mode 100644 tests/data/timber_test/test/settings_chop_trees_off.mcfunction create mode 100644 tests/data/timber_test/test/settings_hunger_on_gives_hunger.mcfunction create mode 100644 tests/data/timber_test/test/settings_max_tree_size_caps_the_chop.mcfunction create mode 100644 tests/data/timber_test/test/settings_min_leaves_13_chops_oak.mcfunction create mode 100644 tests/data/timber_test/test/settings_min_leaves_14_keeps_oak.mcfunction create mode 100644 tests/data/timber_test/test/settings_no_destroy_leaves.mcfunction create mode 100644 tests/data/timber_test/test/settings_persistent_on_counts_placed_leaves.mcfunction create mode 100644 tests/data/timber_test/test/settings_sneak_only_sneaking_chops.mcfunction create mode 100644 tests/data/timber_test/test/settings_sneak_only_standing_no_chop.mcfunction create mode 100644 tests/data/timber_test/test/settings_sneaking_default_no_chop.mcfunction create mode 100644 tests/data/timber_test/test/settings_wear_out_default_damages_axe.mcfunction create mode 100644 tests/data/timber_test/test/settings_wear_out_off_keeps_axe.mcfunction create mode 100644 tests/data/timber_test/test_environment/axes_off.json create mode 100644 tests/data/timber_test/test_environment/chop_down_on.json create mode 100644 tests/data/timber_test/test_environment/chop_trees_off.json create mode 100644 tests/data/timber_test/test_environment/hunger_on.json create mode 100644 tests/data/timber_test/test_environment/min_leaves_13.json create mode 100644 tests/data/timber_test/test_environment/min_leaves_14.json create mode 100644 tests/data/timber_test/test_environment/no_destroy_leaves.json create mode 100644 tests/data/timber_test/test_environment/persistent_on.json create mode 100644 tests/data/timber_test/test_environment/sneak_only.json create mode 100644 tests/data/timber_test/test_environment/tiny_tree_size.json create mode 100644 tests/data/timber_test/test_environment/wear_out_off.json diff --git a/tests/data/minecraft/tags/function/load.json b/tests/data/minecraft/tags/function/load.json new file mode 100644 index 0000000..19b9c2b --- /dev/null +++ b/tests/data/minecraft/tags/function/load.json @@ -0,0 +1,5 @@ +{ + "values": [ + "timber_test:load" + ] +} diff --git a/tests/data/timber_test/function/assert_logs_gone_leaves_kept.mcfunction b/tests/data/timber_test/function/assert_logs_gone_leaves_kept.mcfunction new file mode 100644 index 0000000..da0c8a2 --- /dev/null +++ b/tests/data/timber_test/function/assert_logs_gone_leaves_kept.mcfunction @@ -0,0 +1,14 @@ +#> Fails the test if a log is left or if a leaf is gone: the tree was chopped but its leaves must stay (destroy_leaves off). +#> Replaces logs with air while counting, so run it last. +#> @param test name of the test, count_leaves has to be called with the same name before mining +scoreboard objectives add timber_test dummy +execute store result score #logs timber_test run fill ~0 ~0 ~0 ~39 ~19 ~39 minecraft:air replace #minecraft:logs +execute store result score #dtmp timber_test run fill ~0 ~20 ~0 ~39 ~39 ~39 minecraft:air replace #minecraft:logs +scoreboard players operation #logs timber_test += #dtmp timber_test +execute store result score #leaves1 timber_test run clone ~0 ~0 ~0 ~39 ~19 ~39 ~0 ~64 ~0 filtered #minecraft:leaves +execute store result score #dtmp timber_test run clone ~0 ~20 ~0 ~39 ~39 ~39 ~0 ~64 ~0 filtered #minecraft:leaves +scoreboard players operation #leaves1 timber_test += #dtmp timber_test +fill ~0 ~64 ~0 ~39 ~83 ~39 minecraft:air +$scoreboard players operation #leaves1 timber_test -= #leaves0_$(test) timber_test +assert score #logs timber_test matches 0 +assert score #leaves1 timber_test matches 0 diff --git a/tests/data/timber_test/function/assert_logs_left.mcfunction b/tests/data/timber_test/function/assert_logs_left.mcfunction new file mode 100644 index 0000000..ae9d6e7 --- /dev/null +++ b/tests/data/timber_test/function/assert_logs_left.mcfunction @@ -0,0 +1,7 @@ +#> Fails the test unless exactly n logs are left in the arena. Replaces logs with air while counting, so run it last. +#> @param n expected number of logs +scoreboard objectives add timber_test dummy +execute store result score #logs timber_test run fill ~0 ~0 ~0 ~39 ~19 ~39 minecraft:air replace #minecraft:logs +execute store result score #dtmp timber_test run fill ~0 ~20 ~0 ~39 ~39 ~39 minecraft:air replace #minecraft:logs +scoreboard players operation #logs timber_test += #dtmp timber_test +$assert score #logs timber_test matches $(n) diff --git a/tests/data/timber_test/function/assert_logs_removed.mcfunction b/tests/data/timber_test/function/assert_logs_removed.mcfunction new file mode 100644 index 0000000..9e1f701 --- /dev/null +++ b/tests/data/timber_test/function/assert_logs_removed.mcfunction @@ -0,0 +1,11 @@ +#> Fails the test unless exactly n logs are gone compared with count_logs (the mined one included). +#> Replaces logs with air while counting, so run it last. +#> @param test name of the test, count_logs has to be called with the same name before mining +#> @param n expected number of removed logs +scoreboard objectives add timber_test dummy +execute store result score #lleft timber_test run fill ~0 ~0 ~0 ~39 ~19 ~39 minecraft:air replace #minecraft:logs +execute store result score #dtmp timber_test run fill ~0 ~20 ~0 ~39 ~39 ~39 minecraft:air replace #minecraft:logs +scoreboard players operation #lleft timber_test += #dtmp timber_test +$scoreboard players operation #lrem timber_test = #logs0_$(test) timber_test +scoreboard players operation #lrem timber_test -= #lleft timber_test +$assert score #lrem timber_test matches $(n) diff --git a/tests/data/timber_test/function/count_adjacent.mcfunction b/tests/data/timber_test/function/count_adjacent.mcfunction index 12b657f..b6d1f08 100644 --- a/tests/data/timber_test/function/count_adjacent.mcfunction +++ b/tests/data/timber_test/function/count_adjacent.mcfunction @@ -1,4 +1,4 @@ -#> Counts the leaves that touch a log on one of their 6 sides into #adj_, this is the number the datapack compares with min_leaves_found. +#> Counts the leaves (and hanging mangrove propagules, the datapack's leaf predicate counts them too) that touch a log on one of their 6 sides into #adj_, this is the number the datapack compares with min_leaves_found. #> Doesn't change the tree. The leaves are copied far above the arena, then the logs are copied over them shifted by one block in every #> direction: every leaf a log lands on is next to a log, what is left of the copy is not (checked against oak: 13, same as the datapack). #> @param test name of the test @@ -7,6 +7,10 @@ fill ~0 ~64 ~0 ~39 ~83 ~39 minecraft:air fill ~0 ~84 ~0 ~39 ~103 ~39 minecraft:air execute store result score #ta timber_test run clone ~0 ~0 ~0 ~39 ~19 ~39 ~0 ~64 ~0 filtered #minecraft:leaves execute store result score #tb timber_test run clone ~0 ~20 ~0 ~39 ~39 ~39 ~0 ~84 ~0 filtered #minecraft:leaves +execute store result score #pa timber_test run clone ~0 ~0 ~0 ~39 ~19 ~39 ~0 ~64 ~0 filtered minecraft:mangrove_propagule[hanging=true] +execute store result score #pb timber_test run clone ~0 ~20 ~0 ~39 ~39 ~39 ~0 ~84 ~0 filtered minecraft:mangrove_propagule[hanging=true] +scoreboard players operation #ta timber_test += #pa timber_test +scoreboard players operation #tb timber_test += #pb timber_test clone ~0 ~0 ~0 ~39 ~19 ~39 ~1 ~64 ~0 filtered #minecraft:logs clone ~0 ~20 ~0 ~39 ~39 ~39 ~1 ~84 ~0 filtered #minecraft:logs clone ~0 ~0 ~0 ~39 ~19 ~39 ~-1 ~64 ~0 filtered #minecraft:logs @@ -21,6 +25,10 @@ clone ~0 ~0 ~0 ~39 ~19 ~39 ~0 ~64 ~-1 filtered #minecraft:logs clone ~0 ~20 ~0 ~39 ~39 ~39 ~0 ~84 ~-1 filtered #minecraft:logs execute store result score #ra timber_test run fill ~0 ~64 ~0 ~39 ~83 ~39 minecraft:air replace #minecraft:leaves execute store result score #rb timber_test run fill ~0 ~84 ~0 ~39 ~103 ~39 minecraft:air replace #minecraft:leaves +execute store result score #pa timber_test run fill ~0 ~64 ~0 ~39 ~83 ~39 minecraft:air replace minecraft:mangrove_propagule[hanging=true] +execute store result score #pb timber_test run fill ~0 ~84 ~0 ~39 ~103 ~39 minecraft:air replace minecraft:mangrove_propagule[hanging=true] +scoreboard players operation #ra timber_test += #pa timber_test +scoreboard players operation #rb timber_test += #pb timber_test scoreboard players operation #atmp timber_test = #ta timber_test scoreboard players operation #atmp timber_test += #tb timber_test scoreboard players operation #atmp timber_test -= #ra timber_test diff --git a/tests/data/timber_test/function/count_leaves.mcfunction b/tests/data/timber_test/function/count_leaves.mcfunction new file mode 100644 index 0000000..9be2d6b --- /dev/null +++ b/tests/data/timber_test/function/count_leaves.mcfunction @@ -0,0 +1,7 @@ +#> Counts all leaves in the arena into #leaves0_, doesn't change the tree (see count_d7 for how and why per test). +#> @param test name of the test +scoreboard objectives add timber_test dummy +$execute store result score #leaves0_$(test) timber_test run clone ~0 ~0 ~0 ~39 ~19 ~39 ~0 ~64 ~0 filtered #minecraft:leaves +execute store result score #dtmp timber_test run clone ~0 ~20 ~0 ~39 ~39 ~39 ~0 ~64 ~0 filtered #minecraft:leaves +$scoreboard players operation #leaves0_$(test) timber_test += #dtmp timber_test +fill ~0 ~64 ~0 ~39 ~83 ~39 minecraft:air diff --git a/tests/data/timber_test/function/env/axes_off_setup.mcfunction b/tests/data/timber_test/function/env/axes_off_setup.mcfunction new file mode 100644 index 0000000..eb8ec2e --- /dev/null +++ b/tests/data/timber_test/function/env/axes_off_setup.mcfunction @@ -0,0 +1,9 @@ +#> Settings for the tests of this environment. The environments run as batches one after the other, so +#> a global setting can't disturb another test. The teardown puts the defaults back. +scoreboard players set wooden_axe timber 0 +scoreboard players set stone_axe timber 0 +scoreboard players set copper_axe timber 0 +scoreboard players set iron_axe timber 0 +scoreboard players set golden_axe timber 0 +scoreboard players set diamond_axe timber 0 +scoreboard players set netherite_axe timber 0 diff --git a/tests/data/timber_test/function/env/axes_off_teardown.mcfunction b/tests/data/timber_test/function/env/axes_off_teardown.mcfunction new file mode 100644 index 0000000..885a94d --- /dev/null +++ b/tests/data/timber_test/function/env/axes_off_teardown.mcfunction @@ -0,0 +1,8 @@ +#> Back to the defaults +scoreboard players set wooden_axe timber 1 +scoreboard players set stone_axe timber 1 +scoreboard players set copper_axe timber 1 +scoreboard players set iron_axe timber 1 +scoreboard players set golden_axe timber 1 +scoreboard players set diamond_axe timber 1 +scoreboard players set netherite_axe timber 1 diff --git a/tests/data/timber_test/function/env/chop_down_on_setup.mcfunction b/tests/data/timber_test/function/env/chop_down_on_setup.mcfunction new file mode 100644 index 0000000..c62b641 --- /dev/null +++ b/tests/data/timber_test/function/env/chop_down_on_setup.mcfunction @@ -0,0 +1,3 @@ +#> Settings for the tests of this environment. The environments run as batches one after the other, so +#> a global setting can't disturb another test. The teardown puts the defaults back. +scoreboard players set chop_down timber 1 diff --git a/tests/data/timber_test/function/env/chop_down_on_teardown.mcfunction b/tests/data/timber_test/function/env/chop_down_on_teardown.mcfunction new file mode 100644 index 0000000..84dec0d --- /dev/null +++ b/tests/data/timber_test/function/env/chop_down_on_teardown.mcfunction @@ -0,0 +1,2 @@ +#> Back to the defaults +scoreboard players set chop_down timber 0 diff --git a/tests/data/timber_test/function/env/chop_trees_off_setup.mcfunction b/tests/data/timber_test/function/env/chop_trees_off_setup.mcfunction new file mode 100644 index 0000000..dcc626a --- /dev/null +++ b/tests/data/timber_test/function/env/chop_trees_off_setup.mcfunction @@ -0,0 +1,3 @@ +#> Settings for the tests of this environment. The environments run as batches one after the other, so +#> a global setting can't disturb another test. The teardown puts the defaults back. +scoreboard players set chop_trees timber 0 diff --git a/tests/data/timber_test/function/env/chop_trees_off_teardown.mcfunction b/tests/data/timber_test/function/env/chop_trees_off_teardown.mcfunction new file mode 100644 index 0000000..1f8c5ef --- /dev/null +++ b/tests/data/timber_test/function/env/chop_trees_off_teardown.mcfunction @@ -0,0 +1,2 @@ +#> Back to the defaults +scoreboard players set chop_trees timber 1 diff --git a/tests/data/timber_test/function/env/hunger_on_setup.mcfunction b/tests/data/timber_test/function/env/hunger_on_setup.mcfunction new file mode 100644 index 0000000..75a753a --- /dev/null +++ b/tests/data/timber_test/function/env/hunger_on_setup.mcfunction @@ -0,0 +1,3 @@ +#> Settings for the tests of this environment. The environments run as batches one after the other, so +#> a global setting can't disturb another test. The teardown puts the defaults back. +scoreboard players set hunger timber 1 diff --git a/tests/data/timber_test/function/env/hunger_on_teardown.mcfunction b/tests/data/timber_test/function/env/hunger_on_teardown.mcfunction new file mode 100644 index 0000000..29bf0df --- /dev/null +++ b/tests/data/timber_test/function/env/hunger_on_teardown.mcfunction @@ -0,0 +1,2 @@ +#> Back to the defaults +scoreboard players set hunger timber 0 diff --git a/tests/data/timber_test/function/env/min_leaves_13_setup.mcfunction b/tests/data/timber_test/function/env/min_leaves_13_setup.mcfunction new file mode 100644 index 0000000..476d3d0 --- /dev/null +++ b/tests/data/timber_test/function/env/min_leaves_13_setup.mcfunction @@ -0,0 +1,3 @@ +#> Settings for the tests of this environment. The environments run as batches one after the other, so +#> a global setting can't disturb another test. The teardown puts the defaults back. +scoreboard players set min_leaves_found timber 13 diff --git a/tests/data/timber_test/function/env/min_leaves_13_teardown.mcfunction b/tests/data/timber_test/function/env/min_leaves_13_teardown.mcfunction new file mode 100644 index 0000000..8a0c084 --- /dev/null +++ b/tests/data/timber_test/function/env/min_leaves_13_teardown.mcfunction @@ -0,0 +1,2 @@ +#> Back to the defaults +scoreboard players set min_leaves_found timber 5 diff --git a/tests/data/timber_test/function/env/min_leaves_14_setup.mcfunction b/tests/data/timber_test/function/env/min_leaves_14_setup.mcfunction new file mode 100644 index 0000000..44a62b8 --- /dev/null +++ b/tests/data/timber_test/function/env/min_leaves_14_setup.mcfunction @@ -0,0 +1,3 @@ +#> Settings for the tests of this environment. The environments run as batches one after the other, so +#> a global setting can't disturb another test. The teardown puts the defaults back. +scoreboard players set min_leaves_found timber 14 diff --git a/tests/data/timber_test/function/env/min_leaves_14_teardown.mcfunction b/tests/data/timber_test/function/env/min_leaves_14_teardown.mcfunction new file mode 100644 index 0000000..8a0c084 --- /dev/null +++ b/tests/data/timber_test/function/env/min_leaves_14_teardown.mcfunction @@ -0,0 +1,2 @@ +#> Back to the defaults +scoreboard players set min_leaves_found timber 5 diff --git a/tests/data/timber_test/function/env/no_destroy_leaves_setup.mcfunction b/tests/data/timber_test/function/env/no_destroy_leaves_setup.mcfunction new file mode 100644 index 0000000..1a7129c --- /dev/null +++ b/tests/data/timber_test/function/env/no_destroy_leaves_setup.mcfunction @@ -0,0 +1,3 @@ +#> Settings for the tests of this environment. The environments run as batches one after the other, so +#> a global setting can't disturb another test. The teardown puts the defaults back. +scoreboard players set destroy_leaves timber 0 diff --git a/tests/data/timber_test/function/env/no_destroy_leaves_teardown.mcfunction b/tests/data/timber_test/function/env/no_destroy_leaves_teardown.mcfunction new file mode 100644 index 0000000..3a7454f --- /dev/null +++ b/tests/data/timber_test/function/env/no_destroy_leaves_teardown.mcfunction @@ -0,0 +1,2 @@ +#> Back to the defaults +scoreboard players set destroy_leaves timber 1 diff --git a/tests/data/timber_test/function/env/persistent_on_setup.mcfunction b/tests/data/timber_test/function/env/persistent_on_setup.mcfunction new file mode 100644 index 0000000..5cf4a9a --- /dev/null +++ b/tests/data/timber_test/function/env/persistent_on_setup.mcfunction @@ -0,0 +1,3 @@ +#> Settings for the tests of this environment. The environments run as batches one after the other, so +#> a global setting can't disturb another test. The teardown puts the defaults back. +scoreboard players set persistent timber 1 diff --git a/tests/data/timber_test/function/env/persistent_on_teardown.mcfunction b/tests/data/timber_test/function/env/persistent_on_teardown.mcfunction new file mode 100644 index 0000000..117c091 --- /dev/null +++ b/tests/data/timber_test/function/env/persistent_on_teardown.mcfunction @@ -0,0 +1,2 @@ +#> Back to the defaults +scoreboard players set persistent timber 0 diff --git a/tests/data/timber_test/function/env/sneak_only_setup.mcfunction b/tests/data/timber_test/function/env/sneak_only_setup.mcfunction new file mode 100644 index 0000000..35ba1b6 --- /dev/null +++ b/tests/data/timber_test/function/env/sneak_only_setup.mcfunction @@ -0,0 +1,4 @@ +#> Settings for the tests of this environment. The environments run as batches one after the other, so +#> a global setting can't disturb another test. The teardown puts the defaults back. +scoreboard players set standing timber 0 +scoreboard players set sneaking timber 1 diff --git a/tests/data/timber_test/function/env/sneak_only_teardown.mcfunction b/tests/data/timber_test/function/env/sneak_only_teardown.mcfunction new file mode 100644 index 0000000..35fb56b --- /dev/null +++ b/tests/data/timber_test/function/env/sneak_only_teardown.mcfunction @@ -0,0 +1,3 @@ +#> Back to the defaults +scoreboard players set standing timber 1 +scoreboard players set sneaking timber 0 diff --git a/tests/data/timber_test/function/env/tiny_tree_size_setup.mcfunction b/tests/data/timber_test/function/env/tiny_tree_size_setup.mcfunction new file mode 100644 index 0000000..54fed10 --- /dev/null +++ b/tests/data/timber_test/function/env/tiny_tree_size_setup.mcfunction @@ -0,0 +1,3 @@ +#> Settings for the tests of this environment. The environments run as batches one after the other, so +#> a global setting can't disturb another test. The teardown puts the defaults back. +scoreboard players set max_tree_size timber 3 diff --git a/tests/data/timber_test/function/env/tiny_tree_size_teardown.mcfunction b/tests/data/timber_test/function/env/tiny_tree_size_teardown.mcfunction new file mode 100644 index 0000000..1c28979 --- /dev/null +++ b/tests/data/timber_test/function/env/tiny_tree_size_teardown.mcfunction @@ -0,0 +1,2 @@ +#> Back to the defaults +scoreboard players set max_tree_size timber 555 diff --git a/tests/data/timber_test/function/env/wear_out_off_setup.mcfunction b/tests/data/timber_test/function/env/wear_out_off_setup.mcfunction new file mode 100644 index 0000000..b521a1d --- /dev/null +++ b/tests/data/timber_test/function/env/wear_out_off_setup.mcfunction @@ -0,0 +1,3 @@ +#> Settings for the tests of this environment. The environments run as batches one after the other, so +#> a global setting can't disturb another test. The teardown puts the defaults back. +scoreboard players set wear_out timber 0 diff --git a/tests/data/timber_test/function/env/wear_out_off_teardown.mcfunction b/tests/data/timber_test/function/env/wear_out_off_teardown.mcfunction new file mode 100644 index 0000000..6b974cd --- /dev/null +++ b/tests/data/timber_test/function/env/wear_out_off_teardown.mcfunction @@ -0,0 +1,2 @@ +#> Back to the defaults +scoreboard players set wear_out timber 1 diff --git a/tests/data/timber_test/function/load.mcfunction b/tests/data/timber_test/function/load.mcfunction new file mode 100644 index 0000000..0b279e7 --- /dev/null +++ b/tests/data/timber_test/function/load.mcfunction @@ -0,0 +1,6 @@ +#> Runs when the tests are loaded. +#> The tests run in parallel, dozens of dummies chop in the same tick. Every chop forks its commands with execute at @e[...], +#> and Minecraft limits the forks per command chain (max_command_forks, default 65536). Without this, one in about 10 suite runs +#> left a few leaves of a tree standing (0 in 30 runs with it). One player chopping at a time in a real world does not get near the limit: +#> the biggest trees of the tests (500 blocks) pass with the default when they run alone. +gamerule minecraft:max_command_forks 1000000000 diff --git a/tests/data/timber_test/test/chop_mangrove_roots_tree.mcfunction b/tests/data/timber_test/test/chop_mangrove_roots_tree.mcfunction index 019f235..4aa6c23 100644 --- a/tests/data/timber_test/test/chop_mangrove_roots_tree.mcfunction +++ b/tests/data/timber_test/test/chop_mangrove_roots_tree.mcfunction @@ -78,6 +78,6 @@ await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] # the datapack only chops a tree with at least min_leaves_found leaves next to a trunk. Some mangroves have fewer (measured with # 40 trees each: mangrove 15 %, tall mangrove 10 % below 5, the tall one down to 0). That is a gap in the datapack, the goal is to # chop every tree, so such a tree doesn't fail the test but must not be chopped and gives a warning -execute if score #adj_chop_mangrove_roots_tree timber_test >= min_leaves_found timber run function timber_test:assert_no_roots -execute if score #adj_chop_mangrove_roots_tree timber_test >= min_leaves_found timber run function timber_test:assert_no_tree {test:"chop_mangrove_roots_tree"} +# a chop that starts at a root can leave parts of the tree standing too (branches that bend down again are not reached, see report_leftovers): warning, no failure +execute if score #adj_chop_mangrove_roots_tree timber_test >= min_leaves_found timber run function timber_test:report_leftovers {test:"chop_mangrove_roots_tree"} execute if score #adj_chop_mangrove_roots_tree timber_test < min_leaves_found timber run function timber_test:assert_not_chopped {test:"chop_mangrove_roots_tree"} diff --git a/tests/data/timber_test/test/chop_tall_mangrove_roots_tree.mcfunction b/tests/data/timber_test/test/chop_tall_mangrove_roots_tree.mcfunction index 4a582f3..d8a9f27 100644 --- a/tests/data/timber_test/test/chop_tall_mangrove_roots_tree.mcfunction +++ b/tests/data/timber_test/test/chop_tall_mangrove_roots_tree.mcfunction @@ -78,6 +78,6 @@ await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] # the datapack only chops a tree with at least min_leaves_found leaves next to a trunk. Some mangroves have fewer (measured with # 40 trees each: mangrove 15 %, tall mangrove 10 % below 5, the tall one down to 0). That is a gap in the datapack, the goal is to # chop every tree, so such a tree doesn't fail the test but must not be chopped and gives a warning -execute if score #adj_chop_tall_mangrove_roots_tree timber_test >= min_leaves_found timber run function timber_test:assert_no_roots -execute if score #adj_chop_tall_mangrove_roots_tree timber_test >= min_leaves_found timber run function timber_test:assert_no_tree {test:"chop_tall_mangrove_roots_tree"} +# a chop that starts at a root can leave parts of the tree standing too (branches that bend down again are not reached, see report_leftovers): warning, no failure +execute if score #adj_chop_tall_mangrove_roots_tree timber_test >= min_leaves_found timber run function timber_test:report_leftovers {test:"chop_tall_mangrove_roots_tree"} execute if score #adj_chop_tall_mangrove_roots_tree timber_test < min_leaves_found timber run function timber_test:assert_not_chopped {test:"chop_tall_mangrove_roots_tree"} diff --git a/tests/data/timber_test/test/settings_axe_copper_chops.mcfunction b/tests/data/timber_test/test/settings_axe_copper_chops.mcfunction new file mode 100644 index 0000000..384838a --- /dev/null +++ b/tests/data/timber_test/test/settings_axe_copper_chops.mcfunction @@ -0,0 +1,25 @@ +#> The copper axe chops a tree by default +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:oak"} +assert block ~20 ~ ~20 #minecraft:logs +assert block ~20 ~2 ~20 #minecraft:logs + +await delay 1s +function timber_test:count_d7 {test:"settings_axe_copper_chops"} +function timber_test:player {name:"ax_copper_t",axe:"minecraft:copper_axe"} +dummy ax_copper_t mine ~20 ~0 ~20 + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the mined log is gone: the dummy hit the tree +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~0 ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_no_tree {test:"settings_axe_copper_chops"} diff --git a/tests/data/timber_test/test/settings_axe_copper_off_no_chop.mcfunction b/tests/data/timber_test/test/settings_axe_copper_off_no_chop.mcfunction new file mode 100644 index 0000000..77d1ce5 --- /dev/null +++ b/tests/data/timber_test/test/settings_axe_copper_off_no_chop.mcfunction @@ -0,0 +1,26 @@ +#> With the copper axe switched off it doesn't chop +# @environment timber_test:axes_off +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:oak"} +assert block ~20 ~ ~20 #minecraft:logs +assert block ~20 ~2 ~20 #minecraft:logs + +await delay 1s +function timber_test:count_logs {test:"settings_axe_copper_off_no_chop"} +function timber_test:player {name:"axo_copper_t",axe:"minecraft:copper_axe"} +dummy axo_copper_t mine ~20 ~0 ~20 + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the mined log is gone: the dummy hit the tree +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~0 ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_only_mined_gone {test:"settings_axe_copper_off_no_chop"} diff --git a/tests/data/timber_test/test/settings_axe_diamond_chops.mcfunction b/tests/data/timber_test/test/settings_axe_diamond_chops.mcfunction new file mode 100644 index 0000000..01458cb --- /dev/null +++ b/tests/data/timber_test/test/settings_axe_diamond_chops.mcfunction @@ -0,0 +1,25 @@ +#> The diamond axe chops a tree by default +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:oak"} +assert block ~20 ~ ~20 #minecraft:logs +assert block ~20 ~2 ~20 #minecraft:logs + +await delay 1s +function timber_test:count_d7 {test:"settings_axe_diamond_chops"} +function timber_test:player {name:"ax_diamon_t",axe:"minecraft:diamond_axe"} +dummy ax_diamon_t mine ~20 ~0 ~20 + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the mined log is gone: the dummy hit the tree +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~0 ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_no_tree {test:"settings_axe_diamond_chops"} diff --git a/tests/data/timber_test/test/settings_axe_diamond_off_no_chop.mcfunction b/tests/data/timber_test/test/settings_axe_diamond_off_no_chop.mcfunction new file mode 100644 index 0000000..e3abc37 --- /dev/null +++ b/tests/data/timber_test/test/settings_axe_diamond_off_no_chop.mcfunction @@ -0,0 +1,26 @@ +#> With the diamond axe switched off it doesn't chop +# @environment timber_test:axes_off +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:oak"} +assert block ~20 ~ ~20 #minecraft:logs +assert block ~20 ~2 ~20 #minecraft:logs + +await delay 1s +function timber_test:count_logs {test:"settings_axe_diamond_off_no_chop"} +function timber_test:player {name:"axo_diamon_t",axe:"minecraft:diamond_axe"} +dummy axo_diamon_t mine ~20 ~0 ~20 + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the mined log is gone: the dummy hit the tree +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~0 ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_only_mined_gone {test:"settings_axe_diamond_off_no_chop"} diff --git a/tests/data/timber_test/test/settings_axe_golden_chops.mcfunction b/tests/data/timber_test/test/settings_axe_golden_chops.mcfunction new file mode 100644 index 0000000..8768410 --- /dev/null +++ b/tests/data/timber_test/test/settings_axe_golden_chops.mcfunction @@ -0,0 +1,25 @@ +#> The golden axe chops a tree by default +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:oak"} +assert block ~20 ~ ~20 #minecraft:logs +assert block ~20 ~2 ~20 #minecraft:logs + +await delay 1s +function timber_test:count_d7 {test:"settings_axe_golden_chops"} +function timber_test:player {name:"ax_golden_t",axe:"minecraft:golden_axe"} +dummy ax_golden_t mine ~20 ~0 ~20 + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the mined log is gone: the dummy hit the tree +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~0 ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_no_tree {test:"settings_axe_golden_chops"} diff --git a/tests/data/timber_test/test/settings_axe_golden_off_no_chop.mcfunction b/tests/data/timber_test/test/settings_axe_golden_off_no_chop.mcfunction new file mode 100644 index 0000000..e44b674 --- /dev/null +++ b/tests/data/timber_test/test/settings_axe_golden_off_no_chop.mcfunction @@ -0,0 +1,26 @@ +#> With the golden axe switched off it doesn't chop +# @environment timber_test:axes_off +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:oak"} +assert block ~20 ~ ~20 #minecraft:logs +assert block ~20 ~2 ~20 #minecraft:logs + +await delay 1s +function timber_test:count_logs {test:"settings_axe_golden_off_no_chop"} +function timber_test:player {name:"axo_golden_t",axe:"minecraft:golden_axe"} +dummy axo_golden_t mine ~20 ~0 ~20 + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the mined log is gone: the dummy hit the tree +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~0 ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_only_mined_gone {test:"settings_axe_golden_off_no_chop"} diff --git a/tests/data/timber_test/test/settings_axe_iron_chops.mcfunction b/tests/data/timber_test/test/settings_axe_iron_chops.mcfunction new file mode 100644 index 0000000..299860f --- /dev/null +++ b/tests/data/timber_test/test/settings_axe_iron_chops.mcfunction @@ -0,0 +1,25 @@ +#> The iron axe chops a tree by default +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:oak"} +assert block ~20 ~ ~20 #minecraft:logs +assert block ~20 ~2 ~20 #minecraft:logs + +await delay 1s +function timber_test:count_d7 {test:"settings_axe_iron_chops"} +function timber_test:player {name:"ax_iron_t",axe:"minecraft:iron_axe"} +dummy ax_iron_t mine ~20 ~0 ~20 + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the mined log is gone: the dummy hit the tree +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~0 ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_no_tree {test:"settings_axe_iron_chops"} diff --git a/tests/data/timber_test/test/settings_axe_iron_off_no_chop.mcfunction b/tests/data/timber_test/test/settings_axe_iron_off_no_chop.mcfunction new file mode 100644 index 0000000..3602cc3 --- /dev/null +++ b/tests/data/timber_test/test/settings_axe_iron_off_no_chop.mcfunction @@ -0,0 +1,26 @@ +#> With the iron axe switched off it doesn't chop +# @environment timber_test:axes_off +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:oak"} +assert block ~20 ~ ~20 #minecraft:logs +assert block ~20 ~2 ~20 #minecraft:logs + +await delay 1s +function timber_test:count_logs {test:"settings_axe_iron_off_no_chop"} +function timber_test:player {name:"axo_iron_t",axe:"minecraft:iron_axe"} +dummy axo_iron_t mine ~20 ~0 ~20 + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the mined log is gone: the dummy hit the tree +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~0 ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_only_mined_gone {test:"settings_axe_iron_off_no_chop"} diff --git a/tests/data/timber_test/test/settings_axe_netherite_chops.mcfunction b/tests/data/timber_test/test/settings_axe_netherite_chops.mcfunction new file mode 100644 index 0000000..6eaf4c7 --- /dev/null +++ b/tests/data/timber_test/test/settings_axe_netherite_chops.mcfunction @@ -0,0 +1,25 @@ +#> The netherite axe chops a tree by default +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:oak"} +assert block ~20 ~ ~20 #minecraft:logs +assert block ~20 ~2 ~20 #minecraft:logs + +await delay 1s +function timber_test:count_d7 {test:"settings_axe_netherite_chops"} +function timber_test:player {name:"ax_nether_t",axe:"minecraft:netherite_axe"} +dummy ax_nether_t mine ~20 ~0 ~20 + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the mined log is gone: the dummy hit the tree +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~0 ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_no_tree {test:"settings_axe_netherite_chops"} diff --git a/tests/data/timber_test/test/settings_axe_netherite_off_no_chop.mcfunction b/tests/data/timber_test/test/settings_axe_netherite_off_no_chop.mcfunction new file mode 100644 index 0000000..f0c09d4 --- /dev/null +++ b/tests/data/timber_test/test/settings_axe_netherite_off_no_chop.mcfunction @@ -0,0 +1,26 @@ +#> With the netherite axe switched off it doesn't chop +# @environment timber_test:axes_off +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:oak"} +assert block ~20 ~ ~20 #minecraft:logs +assert block ~20 ~2 ~20 #minecraft:logs + +await delay 1s +function timber_test:count_logs {test:"settings_axe_netherite_off_no_chop"} +function timber_test:player {name:"axo_nether_t",axe:"minecraft:netherite_axe"} +dummy axo_nether_t mine ~20 ~0 ~20 + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the mined log is gone: the dummy hit the tree +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~0 ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_only_mined_gone {test:"settings_axe_netherite_off_no_chop"} diff --git a/tests/data/timber_test/test/settings_axe_stone_chops.mcfunction b/tests/data/timber_test/test/settings_axe_stone_chops.mcfunction new file mode 100644 index 0000000..1d69294 --- /dev/null +++ b/tests/data/timber_test/test/settings_axe_stone_chops.mcfunction @@ -0,0 +1,25 @@ +#> The stone axe chops a tree by default +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:oak"} +assert block ~20 ~ ~20 #minecraft:logs +assert block ~20 ~2 ~20 #minecraft:logs + +await delay 1s +function timber_test:count_d7 {test:"settings_axe_stone_chops"} +function timber_test:player {name:"ax_stone_t",axe:"minecraft:stone_axe"} +dummy ax_stone_t mine ~20 ~0 ~20 + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the mined log is gone: the dummy hit the tree +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~0 ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_no_tree {test:"settings_axe_stone_chops"} diff --git a/tests/data/timber_test/test/settings_axe_stone_off_no_chop.mcfunction b/tests/data/timber_test/test/settings_axe_stone_off_no_chop.mcfunction new file mode 100644 index 0000000..90622de --- /dev/null +++ b/tests/data/timber_test/test/settings_axe_stone_off_no_chop.mcfunction @@ -0,0 +1,26 @@ +#> With the stone axe switched off it doesn't chop +# @environment timber_test:axes_off +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:oak"} +assert block ~20 ~ ~20 #minecraft:logs +assert block ~20 ~2 ~20 #minecraft:logs + +await delay 1s +function timber_test:count_logs {test:"settings_axe_stone_off_no_chop"} +function timber_test:player {name:"axo_stone_t",axe:"minecraft:stone_axe"} +dummy axo_stone_t mine ~20 ~0 ~20 + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the mined log is gone: the dummy hit the tree +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~0 ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_only_mined_gone {test:"settings_axe_stone_off_no_chop"} diff --git a/tests/data/timber_test/test/settings_axe_wooden_chops.mcfunction b/tests/data/timber_test/test/settings_axe_wooden_chops.mcfunction new file mode 100644 index 0000000..d5cd6ba --- /dev/null +++ b/tests/data/timber_test/test/settings_axe_wooden_chops.mcfunction @@ -0,0 +1,25 @@ +#> The wooden axe chops a tree by default +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:oak"} +assert block ~20 ~ ~20 #minecraft:logs +assert block ~20 ~2 ~20 #minecraft:logs + +await delay 1s +function timber_test:count_d7 {test:"settings_axe_wooden_chops"} +function timber_test:player {name:"ax_wooden_t",axe:"minecraft:wooden_axe"} +dummy ax_wooden_t mine ~20 ~0 ~20 + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the mined log is gone: the dummy hit the tree +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~0 ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_no_tree {test:"settings_axe_wooden_chops"} diff --git a/tests/data/timber_test/test/settings_axe_wooden_off_no_chop.mcfunction b/tests/data/timber_test/test/settings_axe_wooden_off_no_chop.mcfunction new file mode 100644 index 0000000..c073114 --- /dev/null +++ b/tests/data/timber_test/test/settings_axe_wooden_off_no_chop.mcfunction @@ -0,0 +1,26 @@ +#> With the wooden axe switched off it doesn't chop +# @environment timber_test:axes_off +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:oak"} +assert block ~20 ~ ~20 #minecraft:logs +assert block ~20 ~2 ~20 #minecraft:logs + +await delay 1s +function timber_test:count_logs {test:"settings_axe_wooden_off_no_chop"} +function timber_test:player {name:"axo_wooden_t",axe:"minecraft:wooden_axe"} +dummy axo_wooden_t mine ~20 ~0 ~20 + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the mined log is gone: the dummy hit the tree +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~0 ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_only_mined_gone {test:"settings_axe_wooden_off_no_chop"} diff --git a/tests/data/timber_test/test/settings_chop_down_off_keeps_logs_below.mcfunction b/tests/data/timber_test/test/settings_chop_down_off_keeps_logs_below.mcfunction new file mode 100644 index 0000000..e40eaac --- /dev/null +++ b/tests/data/timber_test/test/settings_chop_down_off_keeps_logs_below.mcfunction @@ -0,0 +1,24 @@ +#> By default only the part above the mined log is chopped: mining the third log keeps the two below +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:oak"} +assert block ~20 ~ ~20 #minecraft:logs +assert block ~20 ~2 ~20 #minecraft:logs + +await delay 1s +function timber_test:player {name:"cdown_off_t",axe:"minecraft:iron_axe"} +dummy cdown_off_t mine ~20 ~2 ~20 + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the mined log is gone: the dummy hit the tree +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~2 ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_logs_left {n:2} diff --git a/tests/data/timber_test/test/settings_chop_down_on_chops_logs_below.mcfunction b/tests/data/timber_test/test/settings_chop_down_on_chops_logs_below.mcfunction new file mode 100644 index 0000000..aa13722 --- /dev/null +++ b/tests/data/timber_test/test/settings_chop_down_on_chops_logs_below.mcfunction @@ -0,0 +1,26 @@ +#> With chop_down on mining the third log chops the whole tree, also the logs below +# @environment timber_test:chop_down_on +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:oak"} +assert block ~20 ~ ~20 #minecraft:logs +assert block ~20 ~2 ~20 #minecraft:logs + +await delay 1s +function timber_test:count_d7 {test:"settings_chop_down_on_chops_logs_below"} +function timber_test:player {name:"cdown_on_t",axe:"minecraft:iron_axe"} +dummy cdown_on_t mine ~20 ~2 ~20 + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the mined log is gone: the dummy hit the tree +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~2 ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_no_tree {test:"settings_chop_down_on_chops_logs_below"} diff --git a/tests/data/timber_test/test/settings_chop_trees_off.mcfunction b/tests/data/timber_test/test/settings_chop_trees_off.mcfunction new file mode 100644 index 0000000..1307cc1 --- /dev/null +++ b/tests/data/timber_test/test/settings_chop_trees_off.mcfunction @@ -0,0 +1,26 @@ +#> With chop_trees off nothing is chopped +# @environment timber_test:chop_trees_off +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:oak"} +assert block ~20 ~ ~20 #minecraft:logs +assert block ~20 ~2 ~20 #minecraft:logs + +await delay 1s +function timber_test:count_logs {test:"settings_chop_trees_off"} +function timber_test:player {name:"notrees_t",axe:"minecraft:iron_axe"} +dummy notrees_t mine ~20 ~0 ~20 + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the mined log is gone: the dummy hit the tree +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~0 ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_only_mined_gone {test:"settings_chop_trees_off"} diff --git a/tests/data/timber_test/test/settings_hunger_on_gives_hunger.mcfunction b/tests/data/timber_test/test/settings_hunger_on_gives_hunger.mcfunction new file mode 100644 index 0000000..28b0447 --- /dev/null +++ b/tests/data/timber_test/test/settings_hunger_on_gives_hunger.mcfunction @@ -0,0 +1,27 @@ +#> With hunger on chopping gives the player the hunger effect +# @environment timber_test:hunger_on +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:oak"} +assert block ~20 ~ ~20 #minecraft:logs +assert block ~20 ~2 ~20 #minecraft:logs + +await delay 1s +function timber_test:player {name:"hunger_t",axe:"minecraft:iron_axe"} +dummy hunger_t mine ~20 ~0 ~20 + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the mined log is gone: the dummy hit the tree +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~0 ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +scoreboard objectives add timber_test dummy +execute store success score #hunger timber_test if entity @e[type=minecraft:player,name=hunger_t,nbt={active_effects:[{id:"minecraft:hunger"}]}] +assert score #hunger timber_test matches 1 diff --git a/tests/data/timber_test/test/settings_max_tree_size_caps_the_chop.mcfunction b/tests/data/timber_test/test/settings_max_tree_size_caps_the_chop.mcfunction new file mode 100644 index 0000000..3b9d25f --- /dev/null +++ b/tests/data/timber_test/test/settings_max_tree_size_caps_the_chop.mcfunction @@ -0,0 +1,30 @@ +#> A tree bigger than max_tree_size (3) is not processed any further: only the first logs go, the rest stays +# @environment timber_test:tiny_tree_size +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +# a column of 8 logs with 5 natural leaves at its two lowest logs, so it is a tree for the datapack even if only the first logs are looked at +fill ~20 ~0 ~20 ~20 ~7 ~20 minecraft:oak_log +setblock ~21 ~1 ~20 minecraft:oak_leaves[distance=1,persistent=false] +setblock ~19 ~1 ~20 minecraft:oak_leaves[distance=1,persistent=false] +setblock ~20 ~1 ~21 minecraft:oak_leaves[distance=1,persistent=false] +setblock ~20 ~1 ~19 minecraft:oak_leaves[distance=1,persistent=false] +setblock ~21 ~2 ~20 minecraft:oak_leaves[distance=1,persistent=false] + +await delay 1s +function timber_test:count_logs {test:"settings_max_tree_size_caps_the_chop"} +function timber_test:player {name:"maxsize_t",axe:"minecraft:iron_axe"} +dummy maxsize_t mine ~20 ~0 ~20 + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the mined log is gone: the dummy hit the tree +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~0 ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_logs_removed {test:"settings_max_tree_size_caps_the_chop",n:2} diff --git a/tests/data/timber_test/test/settings_min_leaves_13_chops_oak.mcfunction b/tests/data/timber_test/test/settings_min_leaves_13_chops_oak.mcfunction new file mode 100644 index 0000000..6e523ca --- /dev/null +++ b/tests/data/timber_test/test/settings_min_leaves_13_chops_oak.mcfunction @@ -0,0 +1,26 @@ +#> An oak has 13 leaves next to its trunk: min_leaves_found 13 chops it +# @environment timber_test:min_leaves_13 +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:oak"} +assert block ~20 ~ ~20 #minecraft:logs +assert block ~20 ~2 ~20 #minecraft:logs + +await delay 1s +function timber_test:count_d7 {test:"settings_min_leaves_13_chops_oak"} +function timber_test:player {name:"minl13_t",axe:"minecraft:iron_axe"} +dummy minl13_t mine ~20 ~0 ~20 + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the mined log is gone: the dummy hit the tree +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~0 ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_no_tree {test:"settings_min_leaves_13_chops_oak"} diff --git a/tests/data/timber_test/test/settings_min_leaves_14_keeps_oak.mcfunction b/tests/data/timber_test/test/settings_min_leaves_14_keeps_oak.mcfunction new file mode 100644 index 0000000..e3a153b --- /dev/null +++ b/tests/data/timber_test/test/settings_min_leaves_14_keeps_oak.mcfunction @@ -0,0 +1,26 @@ +#> An oak has 13 leaves next to its trunk: min_leaves_found 14 doesn't chop it +# @environment timber_test:min_leaves_14 +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:oak"} +assert block ~20 ~ ~20 #minecraft:logs +assert block ~20 ~2 ~20 #minecraft:logs + +await delay 1s +function timber_test:count_logs {test:"settings_min_leaves_14_keeps_oak"} +function timber_test:player {name:"minl14_t",axe:"minecraft:iron_axe"} +dummy minl14_t mine ~20 ~0 ~20 + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the mined log is gone: the dummy hit the tree +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~0 ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_only_mined_gone {test:"settings_min_leaves_14_keeps_oak"} diff --git a/tests/data/timber_test/test/settings_no_destroy_leaves.mcfunction b/tests/data/timber_test/test/settings_no_destroy_leaves.mcfunction new file mode 100644 index 0000000..ce0d5eb --- /dev/null +++ b/tests/data/timber_test/test/settings_no_destroy_leaves.mcfunction @@ -0,0 +1,26 @@ +#> With destroy_leaves off the tree is chopped but its leaves stay +# @environment timber_test:no_destroy_leaves +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:oak"} +assert block ~20 ~ ~20 #minecraft:logs +assert block ~20 ~2 ~20 #minecraft:logs + +await delay 1s +function timber_test:count_leaves {test:"settings_no_destroy_leaves"} +function timber_test:player {name:"nodl_t",axe:"minecraft:iron_axe"} +dummy nodl_t mine ~20 ~0 ~20 + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the mined log is gone: the dummy hit the tree +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~0 ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_logs_gone_leaves_kept {test:"settings_no_destroy_leaves"} diff --git a/tests/data/timber_test/test/settings_persistent_on_counts_placed_leaves.mcfunction b/tests/data/timber_test/test/settings_persistent_on_counts_placed_leaves.mcfunction new file mode 100644 index 0000000..772ab78 --- /dev/null +++ b/tests/data/timber_test/test/settings_persistent_on_counts_placed_leaves.mcfunction @@ -0,0 +1,30 @@ +#> With persistent on leaves placed by a player count: a log stack with 5 of them is chopped +# @environment timber_test:persistent_on +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +# a column of 6 logs with 5 leaves placed by a player (persistent=true) +fill ~20 ~0 ~20 ~20 ~5 ~20 minecraft:oak_log +setblock ~21 ~3 ~20 minecraft:oak_leaves[distance=1,persistent=true] +setblock ~19 ~3 ~20 minecraft:oak_leaves[distance=1,persistent=true] +setblock ~20 ~3 ~21 minecraft:oak_leaves[distance=1,persistent=true] +setblock ~20 ~3 ~19 minecraft:oak_leaves[distance=1,persistent=true] +setblock ~21 ~4 ~20 minecraft:oak_leaves[distance=1,persistent=true] + +await delay 1s +function timber_test:count_d7 {test:"settings_persistent_on_counts_placed_leaves"} +function timber_test:player {name:"persist_t",axe:"minecraft:iron_axe"} +dummy persist_t mine ~20 ~0 ~20 + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the mined log is gone: the dummy hit the tree +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~0 ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_no_tree {test:"settings_persistent_on_counts_placed_leaves"} diff --git a/tests/data/timber_test/test/settings_sneak_only_sneaking_chops.mcfunction b/tests/data/timber_test/test/settings_sneak_only_sneaking_chops.mcfunction new file mode 100644 index 0000000..a6667bd --- /dev/null +++ b/tests/data/timber_test/test/settings_sneak_only_sneaking_chops.mcfunction @@ -0,0 +1,33 @@ +#> With standing off and sneaking on a sneaking player chops +# @environment timber_test:sneak_only +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +# a column of 6 logs with 5 natural leaves from height 3 on (a random oak can have leaves right above the dummy, then it can't duck) +fill ~20 ~0 ~20 ~20 ~5 ~20 minecraft:oak_log +setblock ~21 ~3 ~20 minecraft:oak_leaves[distance=1,persistent=false] +setblock ~19 ~3 ~20 minecraft:oak_leaves[distance=1,persistent=false] +setblock ~20 ~3 ~21 minecraft:oak_leaves[distance=1,persistent=false] +setblock ~20 ~3 ~19 minecraft:oak_leaves[distance=1,persistent=false] +setblock ~21 ~4 ~20 minecraft:oak_leaves[distance=1,persistent=false] + +await delay 1s +function timber_test:count_d7 {test:"settings_sneak_only_sneaking_chops"} +function timber_test:player {name:"sneakon_t",axe:"minecraft:iron_axe"} +dummy sneakon_t sneak true +# the dummy counts as sneaking a tick or more after the command: wait for it +await entity @e[type=minecraft:player,name=sneakon_t,predicate=timber:sneaking] +dummy sneakon_t mine ~20 ~0 ~20 + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the mined log is gone: the dummy hit the tree +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~0 ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_no_tree {test:"settings_sneak_only_sneaking_chops"} diff --git a/tests/data/timber_test/test/settings_sneak_only_standing_no_chop.mcfunction b/tests/data/timber_test/test/settings_sneak_only_standing_no_chop.mcfunction new file mode 100644 index 0000000..5b16fb9 --- /dev/null +++ b/tests/data/timber_test/test/settings_sneak_only_standing_no_chop.mcfunction @@ -0,0 +1,26 @@ +#> With standing off and sneaking on a standing player doesn't chop +# @environment timber_test:sneak_only +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:oak"} +assert block ~20 ~ ~20 #minecraft:logs +assert block ~20 ~2 ~20 #minecraft:logs + +await delay 1s +function timber_test:count_logs {test:"settings_sneak_only_standing_no_chop"} +function timber_test:player {name:"standoff_t",axe:"minecraft:iron_axe"} +dummy standoff_t mine ~20 ~0 ~20 + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the mined log is gone: the dummy hit the tree +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~0 ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_only_mined_gone {test:"settings_sneak_only_standing_no_chop"} diff --git a/tests/data/timber_test/test/settings_sneaking_default_no_chop.mcfunction b/tests/data/timber_test/test/settings_sneaking_default_no_chop.mcfunction new file mode 100644 index 0000000..ea9ee3d --- /dev/null +++ b/tests/data/timber_test/test/settings_sneaking_default_no_chop.mcfunction @@ -0,0 +1,32 @@ +#> By default (standing on, sneaking off) a sneaking player doesn't chop +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +# a column of 6 logs with 5 natural leaves from height 3 on (a random oak can have leaves right above the dummy, then it can't duck) +fill ~20 ~0 ~20 ~20 ~5 ~20 minecraft:oak_log +setblock ~21 ~3 ~20 minecraft:oak_leaves[distance=1,persistent=false] +setblock ~19 ~3 ~20 minecraft:oak_leaves[distance=1,persistent=false] +setblock ~20 ~3 ~21 minecraft:oak_leaves[distance=1,persistent=false] +setblock ~20 ~3 ~19 minecraft:oak_leaves[distance=1,persistent=false] +setblock ~21 ~4 ~20 minecraft:oak_leaves[distance=1,persistent=false] + +await delay 1s +function timber_test:count_logs {test:"settings_sneaking_default_no_chop"} +function timber_test:player {name:"sneak_def_t",axe:"minecraft:iron_axe"} +dummy sneak_def_t sneak true +# the dummy counts as sneaking a tick or more after the command: wait for it +await entity @e[type=minecraft:player,name=sneak_def_t,predicate=timber:sneaking] +dummy sneak_def_t mine ~20 ~0 ~20 + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the mined log is gone: the dummy hit the tree +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~0 ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_only_mined_gone {test:"settings_sneaking_default_no_chop"} diff --git a/tests/data/timber_test/test/settings_wear_out_default_damages_axe.mcfunction b/tests/data/timber_test/test/settings_wear_out_default_damages_axe.mcfunction new file mode 100644 index 0000000..0721743 --- /dev/null +++ b/tests/data/timber_test/test/settings_wear_out_default_damages_axe.mcfunction @@ -0,0 +1,27 @@ +#> By default chopping damages the axe more than the one block that was mined (vanilla takes 1) +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:oak"} +assert block ~20 ~ ~20 #minecraft:logs +assert block ~20 ~2 ~20 #minecraft:logs + +await delay 1s +function timber_test:player {name:"wear_def_t",axe:"minecraft:iron_axe"} +dummy wear_def_t mine ~20 ~0 ~20 + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the mined log is gone: the dummy hit the tree +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~0 ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +scoreboard objectives add timber_test dummy +scoreboard players set #dmg timber_test 0 +execute store result score #dmg timber_test run data get entity @e[type=minecraft:player,name=wear_def_t,limit=1] SelectedItem.components."minecraft:damage" +assert score #dmg timber_test matches 2.. diff --git a/tests/data/timber_test/test/settings_wear_out_off_keeps_axe.mcfunction b/tests/data/timber_test/test/settings_wear_out_off_keeps_axe.mcfunction new file mode 100644 index 0000000..5df40ae --- /dev/null +++ b/tests/data/timber_test/test/settings_wear_out_off_keeps_axe.mcfunction @@ -0,0 +1,28 @@ +#> With wear_out off only the mined block damages the axe (vanilla takes 1) +# @environment timber_test:wear_out_off +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:oak"} +assert block ~20 ~ ~20 #minecraft:logs +assert block ~20 ~2 ~20 #minecraft:logs + +await delay 1s +function timber_test:player {name:"wear_off_t",axe:"minecraft:iron_axe"} +dummy wear_off_t mine ~20 ~0 ~20 + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the mined log is gone: the dummy hit the tree +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~0 ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +scoreboard objectives add timber_test dummy +scoreboard players set #dmg timber_test 0 +execute store result score #dmg timber_test run data get entity @e[type=minecraft:player,name=wear_off_t,limit=1] SelectedItem.components."minecraft:damage" +assert score #dmg timber_test matches 1 diff --git a/tests/data/timber_test/test_environment/axes_off.json b/tests/data/timber_test/test_environment/axes_off.json new file mode 100644 index 0000000..7788f20 --- /dev/null +++ b/tests/data/timber_test/test_environment/axes_off.json @@ -0,0 +1,5 @@ +{ + "type": "minecraft:function", + "setup": "timber_test:env/axes_off_setup", + "teardown": "timber_test:env/axes_off_teardown" +} diff --git a/tests/data/timber_test/test_environment/chop_down_on.json b/tests/data/timber_test/test_environment/chop_down_on.json new file mode 100644 index 0000000..64d737b --- /dev/null +++ b/tests/data/timber_test/test_environment/chop_down_on.json @@ -0,0 +1,5 @@ +{ + "type": "minecraft:function", + "setup": "timber_test:env/chop_down_on_setup", + "teardown": "timber_test:env/chop_down_on_teardown" +} diff --git a/tests/data/timber_test/test_environment/chop_trees_off.json b/tests/data/timber_test/test_environment/chop_trees_off.json new file mode 100644 index 0000000..37c10bf --- /dev/null +++ b/tests/data/timber_test/test_environment/chop_trees_off.json @@ -0,0 +1,5 @@ +{ + "type": "minecraft:function", + "setup": "timber_test:env/chop_trees_off_setup", + "teardown": "timber_test:env/chop_trees_off_teardown" +} diff --git a/tests/data/timber_test/test_environment/hunger_on.json b/tests/data/timber_test/test_environment/hunger_on.json new file mode 100644 index 0000000..2a9d123 --- /dev/null +++ b/tests/data/timber_test/test_environment/hunger_on.json @@ -0,0 +1,5 @@ +{ + "type": "minecraft:function", + "setup": "timber_test:env/hunger_on_setup", + "teardown": "timber_test:env/hunger_on_teardown" +} diff --git a/tests/data/timber_test/test_environment/min_leaves_13.json b/tests/data/timber_test/test_environment/min_leaves_13.json new file mode 100644 index 0000000..e262400 --- /dev/null +++ b/tests/data/timber_test/test_environment/min_leaves_13.json @@ -0,0 +1,5 @@ +{ + "type": "minecraft:function", + "setup": "timber_test:env/min_leaves_13_setup", + "teardown": "timber_test:env/min_leaves_13_teardown" +} diff --git a/tests/data/timber_test/test_environment/min_leaves_14.json b/tests/data/timber_test/test_environment/min_leaves_14.json new file mode 100644 index 0000000..016bbb7 --- /dev/null +++ b/tests/data/timber_test/test_environment/min_leaves_14.json @@ -0,0 +1,5 @@ +{ + "type": "minecraft:function", + "setup": "timber_test:env/min_leaves_14_setup", + "teardown": "timber_test:env/min_leaves_14_teardown" +} diff --git a/tests/data/timber_test/test_environment/no_destroy_leaves.json b/tests/data/timber_test/test_environment/no_destroy_leaves.json new file mode 100644 index 0000000..f854745 --- /dev/null +++ b/tests/data/timber_test/test_environment/no_destroy_leaves.json @@ -0,0 +1,5 @@ +{ + "type": "minecraft:function", + "setup": "timber_test:env/no_destroy_leaves_setup", + "teardown": "timber_test:env/no_destroy_leaves_teardown" +} diff --git a/tests/data/timber_test/test_environment/persistent_on.json b/tests/data/timber_test/test_environment/persistent_on.json new file mode 100644 index 0000000..f736ecc --- /dev/null +++ b/tests/data/timber_test/test_environment/persistent_on.json @@ -0,0 +1,5 @@ +{ + "type": "minecraft:function", + "setup": "timber_test:env/persistent_on_setup", + "teardown": "timber_test:env/persistent_on_teardown" +} diff --git a/tests/data/timber_test/test_environment/sneak_only.json b/tests/data/timber_test/test_environment/sneak_only.json new file mode 100644 index 0000000..1209613 --- /dev/null +++ b/tests/data/timber_test/test_environment/sneak_only.json @@ -0,0 +1,5 @@ +{ + "type": "minecraft:function", + "setup": "timber_test:env/sneak_only_setup", + "teardown": "timber_test:env/sneak_only_teardown" +} diff --git a/tests/data/timber_test/test_environment/tiny_tree_size.json b/tests/data/timber_test/test_environment/tiny_tree_size.json new file mode 100644 index 0000000..fd0e8cd --- /dev/null +++ b/tests/data/timber_test/test_environment/tiny_tree_size.json @@ -0,0 +1,5 @@ +{ + "type": "minecraft:function", + "setup": "timber_test:env/tiny_tree_size_setup", + "teardown": "timber_test:env/tiny_tree_size_teardown" +} diff --git a/tests/data/timber_test/test_environment/wear_out_off.json b/tests/data/timber_test/test_environment/wear_out_off.json new file mode 100644 index 0000000..acbe4b0 --- /dev/null +++ b/tests/data/timber_test/test_environment/wear_out_off.json @@ -0,0 +1,5 @@ +{ + "type": "minecraft:function", + "setup": "timber_test:env/wear_out_off_setup", + "teardown": "timber_test:env/wear_out_off_teardown" +} From 60f41968170a9d0418a8d5c2625af72dab6d4c33 Mon Sep 17 00:00:00 2001 From: Flavio Felder Date: Mon, 21 Sep 2026 21:18:42 +0200 Subject: [PATCH 22/27] Run the fungus tests in their own environment, test chop_fungi off chop_fungi is a global setting. The fungus tests get a test environment that switches it on and back off, that replaces the counting of its users and the long delay. Two default tests show that a fungus is not chopped while chop_fungi is off (the default). Co-Authored-By: Claude Sonnet 5 --- .../env/chop_fungi_on_setup.mcfunction | 2 ++ .../env/chop_fungi_on_teardown.mcfunction | 2 ++ .../chop_brown_mushroom_fungus.mcfunction | 13 +++-------- .../test/chop_crimson_fungus.mcfunction | 12 ++-------- .../chop_crimson_planted_fungus.mcfunction | 13 +++-------- .../test/chop_red_mushroom_fungus.mcfunction | 13 +++-------- .../test/chop_warped_fungus.mcfunction | 13 +++-------- .../chop_warped_planted_fungus.mcfunction | 13 +++-------- ..._chop_fungi_default_off_crimson.mcfunction | 22 +++++++++++++++++++ ...chop_fungi_default_off_mushroom.mcfunction | 21 ++++++++++++++++++ .../test_environment/chop_fungi_on.json | 5 +++++ 11 files changed, 69 insertions(+), 60 deletions(-) create mode 100644 tests/data/timber_test/function/env/chop_fungi_on_setup.mcfunction create mode 100644 tests/data/timber_test/function/env/chop_fungi_on_teardown.mcfunction create mode 100644 tests/data/timber_test/test/settings_chop_fungi_default_off_crimson.mcfunction create mode 100644 tests/data/timber_test/test/settings_chop_fungi_default_off_mushroom.mcfunction create mode 100644 tests/data/timber_test/test_environment/chop_fungi_on.json diff --git a/tests/data/timber_test/function/env/chop_fungi_on_setup.mcfunction b/tests/data/timber_test/function/env/chop_fungi_on_setup.mcfunction new file mode 100644 index 0000000..c8aadb4 --- /dev/null +++ b/tests/data/timber_test/function/env/chop_fungi_on_setup.mcfunction @@ -0,0 +1,2 @@ +#> chop_fungi is off by default, the fungus tests need it on +scoreboard players set chop_fungi timber 1 diff --git a/tests/data/timber_test/function/env/chop_fungi_on_teardown.mcfunction b/tests/data/timber_test/function/env/chop_fungi_on_teardown.mcfunction new file mode 100644 index 0000000..341c62b --- /dev/null +++ b/tests/data/timber_test/function/env/chop_fungi_on_teardown.mcfunction @@ -0,0 +1,2 @@ +#> Back to the default of chop_fungi +scoreboard players set chop_fungi timber 0 diff --git a/tests/data/timber_test/test/chop_brown_mushroom_fungus.mcfunction b/tests/data/timber_test/test/chop_brown_mushroom_fungus.mcfunction index 48b835f..a5e8d36 100644 --- a/tests/data/timber_test/test/chop_brown_mushroom_fungus.mcfunction +++ b/tests/data/timber_test/test/chop_brown_mushroom_fungus.mcfunction @@ -1,4 +1,5 @@ #> Mining the bottom stem of a huge brown mushroom with an axe chops down the whole fungus (chop_fungi is on) +# @environment timber_test:chop_fungi_on # @template timber_test:arena # @timeout 400 # @skyaccess true @@ -9,16 +10,11 @@ setblock ~20 ~-1 ~20 minecraft:mycelium function timber_test:tree {feature:"minecraft:huge_brown_mushroom"} assert block ~20 ~ ~20 minecraft:mushroom_stem -# chop_fungi is off by default. It is a global setting and the tests run in parallel: the users are counted, it is switched -# back only when the last one is done -scoreboard players add #fungi_users timber_test 1 -scoreboard players set chop_fungi timber 1 - await delay 1s # With chop_fungi on, every chop of the datapack ends with a kill of ALL marker items of mushroom stems in the world (no distance # limit). A chop in the same tick that runs first deletes the marker of a mushroom, which then isn't chopped. So the fungus tests -# wait until the trees are done and chop one after the other, 60 ticks after the first fungus test. -await delay 160t +# chop one after the other, 60 ticks after the first one. +await delay 60t function timber_test:player {name:"brown_mush_t",axe:"minecraft:iron_axe"} function timber_test:mine {name:"brown_mush_t"} @@ -31,7 +27,4 @@ await not block ~20 ~ ~20 minecraft:mushroom_stem await delay 5t await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] -scoreboard players remove #fungi_users timber_test 1 -execute if score #fungi_users timber_test matches ..0 run scoreboard players set chop_fungi timber 0 - function timber_test:assert_no_fungus {test:"chop_brown_mushroom_fungus"} diff --git a/tests/data/timber_test/test/chop_crimson_fungus.mcfunction b/tests/data/timber_test/test/chop_crimson_fungus.mcfunction index 111d8d5..e02506e 100644 --- a/tests/data/timber_test/test/chop_crimson_fungus.mcfunction +++ b/tests/data/timber_test/test/chop_crimson_fungus.mcfunction @@ -1,4 +1,5 @@ #> Mining the bottom stem of a crimson fungus with an axe chops down the whole fungus (chop_fungi is on) +# @environment timber_test:chop_fungi_on # @template timber_test:arena # @timeout 400 # @skyaccess true @@ -9,16 +10,10 @@ setblock ~20 ~-1 ~20 minecraft:crimson_nylium function timber_test:tree {feature:"minecraft:crimson_fungus"} assert block ~20 ~ ~20 #minecraft:logs -# chop_fungi is off by default. It is a global setting and the tests run in parallel: the users are counted, it is switched -# back only when the last one is done -scoreboard players add #fungi_users timber_test 1 -scoreboard players set chop_fungi timber 1 - await delay 1s # With chop_fungi on, every chop of the datapack ends with a kill of ALL marker items of mushroom stems in the world (no distance # limit). A chop in the same tick that runs first deletes the marker of a mushroom, which then isn't chopped. So the fungus tests -# wait until the trees are done and chop one after the other, 0 ticks after the first fungus test. -await delay 100t +# chop one after the other, 0 ticks after the first one. function timber_test:player {name:"crimson_t",axe:"minecraft:iron_axe"} function timber_test:mine {name:"crimson_t"} @@ -31,7 +26,4 @@ await not block ~20 ~ ~20 #minecraft:logs await delay 5t await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] -scoreboard players remove #fungi_users timber_test 1 -execute if score #fungi_users timber_test matches ..0 run scoreboard players set chop_fungi timber 0 - function timber_test:assert_no_fungus {test:"chop_crimson_fungus"} diff --git a/tests/data/timber_test/test/chop_crimson_planted_fungus.mcfunction b/tests/data/timber_test/test/chop_crimson_planted_fungus.mcfunction index 7dc5de3..64a07c2 100644 --- a/tests/data/timber_test/test/chop_crimson_planted_fungus.mcfunction +++ b/tests/data/timber_test/test/chop_crimson_planted_fungus.mcfunction @@ -1,4 +1,5 @@ #> Mining the bottom stem of a crimson fungus grown from a planted one with an axe chops down the whole fungus (chop_fungi is on) +# @environment timber_test:chop_fungi_on # @template timber_test:arena # @timeout 400 # @skyaccess true @@ -9,16 +10,11 @@ setblock ~20 ~-1 ~20 minecraft:crimson_nylium function timber_test:tree {feature:"minecraft:crimson_fungus_planted"} assert block ~20 ~ ~20 #minecraft:logs -# chop_fungi is off by default. It is a global setting and the tests run in parallel: the users are counted, it is switched -# back only when the last one is done -scoreboard players add #fungi_users timber_test 1 -scoreboard players set chop_fungi timber 1 - await delay 1s # With chop_fungi on, every chop of the datapack ends with a kill of ALL marker items of mushroom stems in the world (no distance # limit). A chop in the same tick that runs first deletes the marker of a mushroom, which then isn't chopped. So the fungus tests -# wait until the trees are done and chop one after the other, 24 ticks after the first fungus test. -await delay 124t +# chop one after the other, 24 ticks after the first one. +await delay 24t function timber_test:player {name:"crimson_pl_t",axe:"minecraft:iron_axe"} function timber_test:mine {name:"crimson_pl_t"} @@ -31,7 +27,4 @@ await not block ~20 ~ ~20 #minecraft:logs await delay 5t await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] -scoreboard players remove #fungi_users timber_test 1 -execute if score #fungi_users timber_test matches ..0 run scoreboard players set chop_fungi timber 0 - function timber_test:assert_no_fungus {test:"chop_crimson_planted_fungus"} diff --git a/tests/data/timber_test/test/chop_red_mushroom_fungus.mcfunction b/tests/data/timber_test/test/chop_red_mushroom_fungus.mcfunction index 1a6640c..03b069e 100644 --- a/tests/data/timber_test/test/chop_red_mushroom_fungus.mcfunction +++ b/tests/data/timber_test/test/chop_red_mushroom_fungus.mcfunction @@ -1,4 +1,5 @@ #> Mining the bottom stem of a huge red mushroom with an axe chops down the whole fungus (chop_fungi is on) +# @environment timber_test:chop_fungi_on # @template timber_test:arena # @timeout 400 # @skyaccess true @@ -9,16 +10,11 @@ setblock ~20 ~-1 ~20 minecraft:mycelium function timber_test:tree {feature:"minecraft:huge_red_mushroom"} assert block ~20 ~ ~20 minecraft:mushroom_stem -# chop_fungi is off by default. It is a global setting and the tests run in parallel: the users are counted, it is switched -# back only when the last one is done -scoreboard players add #fungi_users timber_test 1 -scoreboard players set chop_fungi timber 1 - await delay 1s # With chop_fungi on, every chop of the datapack ends with a kill of ALL marker items of mushroom stems in the world (no distance # limit). A chop in the same tick that runs first deletes the marker of a mushroom, which then isn't chopped. So the fungus tests -# wait until the trees are done and chop one after the other, 48 ticks after the first fungus test. -await delay 148t +# chop one after the other, 48 ticks after the first one. +await delay 48t function timber_test:player {name:"red_mush_t",axe:"minecraft:iron_axe"} function timber_test:mine {name:"red_mush_t"} @@ -31,7 +27,4 @@ await not block ~20 ~ ~20 minecraft:mushroom_stem await delay 5t await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] -scoreboard players remove #fungi_users timber_test 1 -execute if score #fungi_users timber_test matches ..0 run scoreboard players set chop_fungi timber 0 - function timber_test:assert_no_fungus {test:"chop_red_mushroom_fungus"} diff --git a/tests/data/timber_test/test/chop_warped_fungus.mcfunction b/tests/data/timber_test/test/chop_warped_fungus.mcfunction index bb28fad..d9d9a67 100644 --- a/tests/data/timber_test/test/chop_warped_fungus.mcfunction +++ b/tests/data/timber_test/test/chop_warped_fungus.mcfunction @@ -1,4 +1,5 @@ #> Mining the bottom stem of a warped fungus with an axe chops down the whole fungus (chop_fungi is on) +# @environment timber_test:chop_fungi_on # @template timber_test:arena # @timeout 400 # @skyaccess true @@ -9,16 +10,11 @@ setblock ~20 ~-1 ~20 minecraft:warped_nylium function timber_test:tree {feature:"minecraft:warped_fungus"} assert block ~20 ~ ~20 #minecraft:logs -# chop_fungi is off by default. It is a global setting and the tests run in parallel: the users are counted, it is switched -# back only when the last one is done -scoreboard players add #fungi_users timber_test 1 -scoreboard players set chop_fungi timber 1 - await delay 1s # With chop_fungi on, every chop of the datapack ends with a kill of ALL marker items of mushroom stems in the world (no distance # limit). A chop in the same tick that runs first deletes the marker of a mushroom, which then isn't chopped. So the fungus tests -# wait until the trees are done and chop one after the other, 12 ticks after the first fungus test. -await delay 112t +# chop one after the other, 12 ticks after the first one. +await delay 12t function timber_test:player {name:"warped_t",axe:"minecraft:iron_axe"} function timber_test:mine {name:"warped_t"} @@ -31,7 +27,4 @@ await not block ~20 ~ ~20 #minecraft:logs await delay 5t await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] -scoreboard players remove #fungi_users timber_test 1 -execute if score #fungi_users timber_test matches ..0 run scoreboard players set chop_fungi timber 0 - function timber_test:assert_no_fungus {test:"chop_warped_fungus"} diff --git a/tests/data/timber_test/test/chop_warped_planted_fungus.mcfunction b/tests/data/timber_test/test/chop_warped_planted_fungus.mcfunction index 8e55f8d..0f64f77 100644 --- a/tests/data/timber_test/test/chop_warped_planted_fungus.mcfunction +++ b/tests/data/timber_test/test/chop_warped_planted_fungus.mcfunction @@ -1,4 +1,5 @@ #> Mining the bottom stem of a warped fungus grown from a planted one with an axe chops down the whole fungus (chop_fungi is on) +# @environment timber_test:chop_fungi_on # @template timber_test:arena # @timeout 400 # @skyaccess true @@ -9,16 +10,11 @@ setblock ~20 ~-1 ~20 minecraft:warped_nylium function timber_test:tree {feature:"minecraft:warped_fungus_planted"} assert block ~20 ~ ~20 #minecraft:logs -# chop_fungi is off by default. It is a global setting and the tests run in parallel: the users are counted, it is switched -# back only when the last one is done -scoreboard players add #fungi_users timber_test 1 -scoreboard players set chop_fungi timber 1 - await delay 1s # With chop_fungi on, every chop of the datapack ends with a kill of ALL marker items of mushroom stems in the world (no distance # limit). A chop in the same tick that runs first deletes the marker of a mushroom, which then isn't chopped. So the fungus tests -# wait until the trees are done and chop one after the other, 36 ticks after the first fungus test. -await delay 136t +# chop one after the other, 36 ticks after the first one. +await delay 36t function timber_test:player {name:"warped_pl_t",axe:"minecraft:iron_axe"} function timber_test:mine {name:"warped_pl_t"} @@ -31,7 +27,4 @@ await not block ~20 ~ ~20 #minecraft:logs await delay 5t await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] -scoreboard players remove #fungi_users timber_test 1 -execute if score #fungi_users timber_test matches ..0 run scoreboard players set chop_fungi timber 0 - function timber_test:assert_no_fungus {test:"chop_warped_planted_fungus"} diff --git a/tests/data/timber_test/test/settings_chop_fungi_default_off_crimson.mcfunction b/tests/data/timber_test/test/settings_chop_fungi_default_off_crimson.mcfunction new file mode 100644 index 0000000..2381f82 --- /dev/null +++ b/tests/data/timber_test/test/settings_chop_fungi_default_off_crimson.mcfunction @@ -0,0 +1,22 @@ +#> By default (chop_fungi off) a crimson fungus is not chopped +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +setblock ~20 ~-1 ~20 minecraft:crimson_nylium +function timber_test:tree {feature:"minecraft:crimson_fungus"} +assert block ~20 ~ ~20 #minecraft:logs + +await delay 1s +function timber_test:count_logs {test:"settings_chop_fungi_default_off_crimson"} +function timber_test:player {name:"fg_off_nether_t",axe:"minecraft:iron_axe"} +function timber_test:mine {name:"fg_off_nether_t"} + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +await not block ~20 ~ ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_only_mined_gone {test:"settings_chop_fungi_default_off_crimson"} diff --git a/tests/data/timber_test/test/settings_chop_fungi_default_off_mushroom.mcfunction b/tests/data/timber_test/test/settings_chop_fungi_default_off_mushroom.mcfunction new file mode 100644 index 0000000..8e53f50 --- /dev/null +++ b/tests/data/timber_test/test/settings_chop_fungi_default_off_mushroom.mcfunction @@ -0,0 +1,21 @@ +#> By default (chop_fungi off) a huge mushroom is not chopped: the stem above the mined block stays +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +setblock ~20 ~-1 ~20 minecraft:mycelium +function timber_test:tree {feature:"minecraft:huge_red_mushroom"} +assert block ~20 ~ ~20 minecraft:mushroom_stem + +await delay 1s +function timber_test:player {name:"fg_off_mush_t",axe:"minecraft:iron_axe"} +function timber_test:mine {name:"fg_off_mush_t"} + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +await not block ~20 ~ ~20 minecraft:mushroom_stem +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +assert block ~20 ~2 ~20 minecraft:mushroom_stem diff --git a/tests/data/timber_test/test_environment/chop_fungi_on.json b/tests/data/timber_test/test_environment/chop_fungi_on.json new file mode 100644 index 0000000..0e24136 --- /dev/null +++ b/tests/data/timber_test/test_environment/chop_fungi_on.json @@ -0,0 +1,5 @@ +{ + "type": "minecraft:function", + "setup": "timber_test:env/chop_fungi_on_setup", + "teardown": "timber_test:env/chop_fungi_on_teardown" +} From e958176704bd9d45f0ae1dab754484016dcefed7 Mon Sep 17 00:00:00 2001 From: Flavio Felder Date: Mon, 21 Sep 2026 21:23:48 +0200 Subject: [PATCH 23/27] Test slow chop, its interval and its blocks per chop Three tests in their own environments: the tree goes block by block (still going after 10 ticks, all gone in the end), time_between_chops 10 makes it slower, blocks_per_chop 20 makes the whole tree go in 12 ticks. The loop that does the work is started by the setup of the environment. Checked against the same tests without slow chop and at the default speed: they fail there. Co-Authored-By: Claude Sonnet 5 --- .../env/slow_chop_batch_setup.mcfunction | 4 +++ .../env/slow_chop_batch_teardown.mcfunction | 3 ++ .../env/slow_chop_interval_setup.mcfunction | 4 +++ .../slow_chop_interval_teardown.mcfunction | 3 ++ .../env/slow_chop_on_setup.mcfunction | 3 ++ .../env/slow_chop_on_teardown.mcfunction | 2 ++ ...tings_slow_chop_blocks_per_chop.mcfunction | 22 ++++++++++++++ ...w_chop_removes_blocks_gradually.mcfunction | 29 +++++++++++++++++++ ...gs_slow_chop_time_between_chops.mcfunction | 28 ++++++++++++++++++ .../test_environment/slow_chop_batch.json | 5 ++++ .../test_environment/slow_chop_interval.json | 5 ++++ .../test_environment/slow_chop_on.json | 5 ++++ 12 files changed, 113 insertions(+) create mode 100644 tests/data/timber_test/function/env/slow_chop_batch_setup.mcfunction create mode 100644 tests/data/timber_test/function/env/slow_chop_batch_teardown.mcfunction create mode 100644 tests/data/timber_test/function/env/slow_chop_interval_setup.mcfunction create mode 100644 tests/data/timber_test/function/env/slow_chop_interval_teardown.mcfunction create mode 100644 tests/data/timber_test/function/env/slow_chop_on_setup.mcfunction create mode 100644 tests/data/timber_test/function/env/slow_chop_on_teardown.mcfunction create mode 100644 tests/data/timber_test/test/settings_slow_chop_blocks_per_chop.mcfunction create mode 100644 tests/data/timber_test/test/settings_slow_chop_removes_blocks_gradually.mcfunction create mode 100644 tests/data/timber_test/test/settings_slow_chop_time_between_chops.mcfunction create mode 100644 tests/data/timber_test/test_environment/slow_chop_batch.json create mode 100644 tests/data/timber_test/test_environment/slow_chop_interval.json create mode 100644 tests/data/timber_test/test_environment/slow_chop_on.json diff --git a/tests/data/timber_test/function/env/slow_chop_batch_setup.mcfunction b/tests/data/timber_test/function/env/slow_chop_batch_setup.mcfunction new file mode 100644 index 0000000..f1b8e38 --- /dev/null +++ b/tests/data/timber_test/function/env/slow_chop_batch_setup.mcfunction @@ -0,0 +1,4 @@ +#> Slow chop on. The loop that does the work is started by the setting flip or by init in a real world, so start it here. +scoreboard players set slow_chop timber 1 +scoreboard players set blocks_per_chop timber 20 +function timber:slow_chop/loop diff --git a/tests/data/timber_test/function/env/slow_chop_batch_teardown.mcfunction b/tests/data/timber_test/function/env/slow_chop_batch_teardown.mcfunction new file mode 100644 index 0000000..6c8b41b --- /dev/null +++ b/tests/data/timber_test/function/env/slow_chop_batch_teardown.mcfunction @@ -0,0 +1,3 @@ +#> Back to the defaults, the loop stops itself when slow_chop is off +scoreboard players set slow_chop timber 0 +scoreboard players set blocks_per_chop timber 1 diff --git a/tests/data/timber_test/function/env/slow_chop_interval_setup.mcfunction b/tests/data/timber_test/function/env/slow_chop_interval_setup.mcfunction new file mode 100644 index 0000000..019d931 --- /dev/null +++ b/tests/data/timber_test/function/env/slow_chop_interval_setup.mcfunction @@ -0,0 +1,4 @@ +#> Slow chop on. The loop that does the work is started by the setting flip or by init in a real world, so start it here. +scoreboard players set slow_chop timber 1 +scoreboard players set time_between_chops timber 10 +function timber:slow_chop/loop diff --git a/tests/data/timber_test/function/env/slow_chop_interval_teardown.mcfunction b/tests/data/timber_test/function/env/slow_chop_interval_teardown.mcfunction new file mode 100644 index 0000000..28d86ba --- /dev/null +++ b/tests/data/timber_test/function/env/slow_chop_interval_teardown.mcfunction @@ -0,0 +1,3 @@ +#> Back to the defaults, the loop stops itself when slow_chop is off +scoreboard players set slow_chop timber 0 +scoreboard players set time_between_chops timber 1 diff --git a/tests/data/timber_test/function/env/slow_chop_on_setup.mcfunction b/tests/data/timber_test/function/env/slow_chop_on_setup.mcfunction new file mode 100644 index 0000000..5314136 --- /dev/null +++ b/tests/data/timber_test/function/env/slow_chop_on_setup.mcfunction @@ -0,0 +1,3 @@ +#> Slow chop on. The loop that does the work is started by the setting flip or by init in a real world, so start it here. +scoreboard players set slow_chop timber 1 +function timber:slow_chop/loop diff --git a/tests/data/timber_test/function/env/slow_chop_on_teardown.mcfunction b/tests/data/timber_test/function/env/slow_chop_on_teardown.mcfunction new file mode 100644 index 0000000..981521e --- /dev/null +++ b/tests/data/timber_test/function/env/slow_chop_on_teardown.mcfunction @@ -0,0 +1,2 @@ +#> Back to the defaults, the loop stops itself when slow_chop is off +scoreboard players set slow_chop timber 0 diff --git a/tests/data/timber_test/test/settings_slow_chop_blocks_per_chop.mcfunction b/tests/data/timber_test/test/settings_slow_chop_blocks_per_chop.mcfunction new file mode 100644 index 0000000..88316f0 --- /dev/null +++ b/tests/data/timber_test/test/settings_slow_chop_blocks_per_chop.mcfunction @@ -0,0 +1,22 @@ +#> With blocks_per_chop 20 the whole tree (about 60 blocks) is gone after 12 ticks +# @environment timber_test:slow_chop_batch +# @template timber_test:arena +# @timeout 600 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:oak"} +assert block ~20 ~ ~20 #minecraft:logs + +await delay 1s +function timber_test:count_d7 {test:"settings_slow_chop_blocks_per_chop"} +function timber_test:player {name:"slowbat_t",axe:"minecraft:iron_axe"} +function timber_test:mine {name:"slowbat_t"} + +# the base log is gone: the dummy hit the tree +await not block ~20 ~ ~20 #minecraft:logs +await delay 12t +# 60 blocks at 20 per tick: done by now +assert not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_no_tree {test:"settings_slow_chop_blocks_per_chop"} diff --git a/tests/data/timber_test/test/settings_slow_chop_removes_blocks_gradually.mcfunction b/tests/data/timber_test/test/settings_slow_chop_removes_blocks_gradually.mcfunction new file mode 100644 index 0000000..ac72598 --- /dev/null +++ b/tests/data/timber_test/test/settings_slow_chop_removes_blocks_gradually.mcfunction @@ -0,0 +1,29 @@ +#> With slow_chop on the tree is chopped block by block: after 10 ticks it is still going, in the end it is all gone +# @environment timber_test:slow_chop_on +# @template timber_test:arena +# @timeout 600 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:oak"} +assert block ~20 ~ ~20 #minecraft:logs + +await delay 1s +function timber_test:count_d7 {test:"settings_slow_chop_removes_blocks_gradually"} +function timber_test:player {name:"slow_t",axe:"minecraft:iron_axe"} +function timber_test:mine {name:"slow_t"} + +# the base log is gone: the dummy hit the tree +await not block ~20 ~ ~20 #minecraft:logs +await delay 10t +# still going: markers of the blocks that are still to go, and most of the leaves are there +assert entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] +execute store result score #remaining timber_test run clone ~0 ~0 ~0 ~39 ~19 ~39 ~0 ~64 ~0 filtered #minecraft:leaves +fill ~0 ~64 ~0 ~39 ~83 ~39 minecraft:air +assert score #remaining timber_test matches 30.. + +# wait until the slow chop is done: no marker is left in the arena +# (await doesn't work inside a called function, so this block is repeated in every test) +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_no_tree {test:"settings_slow_chop_removes_blocks_gradually"} diff --git a/tests/data/timber_test/test/settings_slow_chop_time_between_chops.mcfunction b/tests/data/timber_test/test/settings_slow_chop_time_between_chops.mcfunction new file mode 100644 index 0000000..2ce476c --- /dev/null +++ b/tests/data/timber_test/test/settings_slow_chop_time_between_chops.mcfunction @@ -0,0 +1,28 @@ +#> With time_between_chops 10 a block goes every 10 ticks: after 40 ticks only a few are gone +# @environment timber_test:slow_chop_interval +# @template timber_test:arena +# @timeout 2000 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:oak"} +assert block ~20 ~ ~20 #minecraft:logs + +await delay 1s +function timber_test:count_d7 {test:"settings_slow_chop_time_between_chops"} +function timber_test:player {name:"slowint_t",axe:"minecraft:iron_axe"} +function timber_test:mine {name:"slowint_t"} + +# the base log is gone: the dummy hit the tree +await not block ~20 ~ ~20 #minecraft:logs +await delay 40t +assert entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] +execute store result score #remaining timber_test run clone ~0 ~0 ~0 ~39 ~19 ~39 ~0 ~64 ~0 filtered #minecraft:leaves +fill ~0 ~64 ~0 ~39 ~83 ~39 minecraft:air +assert score #remaining timber_test matches 40.. + +# wait until the slow chop is done: no marker is left in the arena +# (await doesn't work inside a called function, so this block is repeated in every test) +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_no_tree {test:"settings_slow_chop_time_between_chops"} diff --git a/tests/data/timber_test/test_environment/slow_chop_batch.json b/tests/data/timber_test/test_environment/slow_chop_batch.json new file mode 100644 index 0000000..1be042e --- /dev/null +++ b/tests/data/timber_test/test_environment/slow_chop_batch.json @@ -0,0 +1,5 @@ +{ + "type": "minecraft:function", + "setup": "timber_test:env/slow_chop_batch_setup", + "teardown": "timber_test:env/slow_chop_batch_teardown" +} diff --git a/tests/data/timber_test/test_environment/slow_chop_interval.json b/tests/data/timber_test/test_environment/slow_chop_interval.json new file mode 100644 index 0000000..1d1196a --- /dev/null +++ b/tests/data/timber_test/test_environment/slow_chop_interval.json @@ -0,0 +1,5 @@ +{ + "type": "minecraft:function", + "setup": "timber_test:env/slow_chop_interval_setup", + "teardown": "timber_test:env/slow_chop_interval_teardown" +} diff --git a/tests/data/timber_test/test_environment/slow_chop_on.json b/tests/data/timber_test/test_environment/slow_chop_on.json new file mode 100644 index 0000000..6277bce --- /dev/null +++ b/tests/data/timber_test/test_environment/slow_chop_on.json @@ -0,0 +1,5 @@ +{ + "type": "minecraft:function", + "setup": "timber_test:env/slow_chop_on_setup", + "teardown": "timber_test:env/slow_chop_on_teardown" +} From 00e7fd125e141f4290040b453fc13021337fcbd8 Mon Sep 17 00:00:00 2001 From: Flavio Felder Date: Mon, 21 Sep 2026 21:28:17 +0200 Subject: [PATCH 24/27] Test destroy_roots and start_chop_on_root on a hand built mangrove A column of mangrove logs with roots on three sides and natural leaves, so the result doesn't depend on the random shape of a generated mangrove. Roots are removed by default and stay with destroy_roots off, mining a root chops the tree by default and doesn't with start_chop_on_root off. Both off tests fail without their environment. Co-Authored-By: Claude Sonnet 5 --- .../function/assert_roots_left.mcfunction | 7 ++++ .../env/destroy_roots_off_setup.mcfunction | 2 + .../env/destroy_roots_off_teardown.mcfunction | 2 + .../start_chop_on_root_off_setup.mcfunction | 2 + ...start_chop_on_root_off_teardown.mcfunction | 2 + ...roy_roots_default_removes_roots.mcfunction | 36 ++++++++++++++++++ ...s_destroy_roots_off_keeps_roots.mcfunction | 37 +++++++++++++++++++ ...tart_chop_on_root_default_chops.mcfunction | 36 ++++++++++++++++++ ..._start_chop_on_root_off_no_chop.mcfunction | 36 ++++++++++++++++++ .../test_environment/destroy_roots_off.json | 5 +++ .../start_chop_on_root_off.json | 5 +++ 11 files changed, 170 insertions(+) create mode 100644 tests/data/timber_test/function/assert_roots_left.mcfunction create mode 100644 tests/data/timber_test/function/env/destroy_roots_off_setup.mcfunction create mode 100644 tests/data/timber_test/function/env/destroy_roots_off_teardown.mcfunction create mode 100644 tests/data/timber_test/function/env/start_chop_on_root_off_setup.mcfunction create mode 100644 tests/data/timber_test/function/env/start_chop_on_root_off_teardown.mcfunction create mode 100644 tests/data/timber_test/test/settings_destroy_roots_default_removes_roots.mcfunction create mode 100644 tests/data/timber_test/test/settings_destroy_roots_off_keeps_roots.mcfunction create mode 100644 tests/data/timber_test/test/settings_start_chop_on_root_default_chops.mcfunction create mode 100644 tests/data/timber_test/test/settings_start_chop_on_root_off_no_chop.mcfunction create mode 100644 tests/data/timber_test/test_environment/destroy_roots_off.json create mode 100644 tests/data/timber_test/test_environment/start_chop_on_root_off.json diff --git a/tests/data/timber_test/function/assert_roots_left.mcfunction b/tests/data/timber_test/function/assert_roots_left.mcfunction new file mode 100644 index 0000000..e4dfcef --- /dev/null +++ b/tests/data/timber_test/function/assert_roots_left.mcfunction @@ -0,0 +1,7 @@ +#> Fails the test unless exactly n mangrove roots are left in the arena. Replaces them with air while counting, so run it last. +#> @param n expected number of roots +scoreboard objectives add timber_test dummy +execute store result score #roots timber_test run fill ~0 ~0 ~0 ~39 ~19 ~39 minecraft:air replace minecraft:mangrove_roots +execute store result score #dtmp timber_test run fill ~0 ~20 ~0 ~39 ~39 ~39 minecraft:air replace minecraft:mangrove_roots +scoreboard players operation #roots timber_test += #dtmp timber_test +$assert score #roots timber_test matches $(n) diff --git a/tests/data/timber_test/function/env/destroy_roots_off_setup.mcfunction b/tests/data/timber_test/function/env/destroy_roots_off_setup.mcfunction new file mode 100644 index 0000000..68cba02 --- /dev/null +++ b/tests/data/timber_test/function/env/destroy_roots_off_setup.mcfunction @@ -0,0 +1,2 @@ +#> Setting for the tests of this environment +scoreboard players set destroy_roots timber 0 diff --git a/tests/data/timber_test/function/env/destroy_roots_off_teardown.mcfunction b/tests/data/timber_test/function/env/destroy_roots_off_teardown.mcfunction new file mode 100644 index 0000000..7281b80 --- /dev/null +++ b/tests/data/timber_test/function/env/destroy_roots_off_teardown.mcfunction @@ -0,0 +1,2 @@ +#> Back to the default +scoreboard players set destroy_roots timber 1 diff --git a/tests/data/timber_test/function/env/start_chop_on_root_off_setup.mcfunction b/tests/data/timber_test/function/env/start_chop_on_root_off_setup.mcfunction new file mode 100644 index 0000000..3a95993 --- /dev/null +++ b/tests/data/timber_test/function/env/start_chop_on_root_off_setup.mcfunction @@ -0,0 +1,2 @@ +#> Setting for the tests of this environment +scoreboard players set start_chop_on_root timber 0 diff --git a/tests/data/timber_test/function/env/start_chop_on_root_off_teardown.mcfunction b/tests/data/timber_test/function/env/start_chop_on_root_off_teardown.mcfunction new file mode 100644 index 0000000..829bbce --- /dev/null +++ b/tests/data/timber_test/function/env/start_chop_on_root_off_teardown.mcfunction @@ -0,0 +1,2 @@ +#> Back to the default +scoreboard players set start_chop_on_root timber 1 diff --git a/tests/data/timber_test/test/settings_destroy_roots_default_removes_roots.mcfunction b/tests/data/timber_test/test/settings_destroy_roots_default_removes_roots.mcfunction new file mode 100644 index 0000000..2d13f10 --- /dev/null +++ b/tests/data/timber_test/test/settings_destroy_roots_default_removes_roots.mcfunction @@ -0,0 +1,36 @@ +#> By default chopping a mangrove also removes its roots +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +# a mangrove: 6 logs from height 1 on, 6 roots (three sides at height 1 and below them), 5 natural leaves. The dummy stands on the fourth side +fill ~20 ~1 ~20 ~20 ~6 ~20 minecraft:mangrove_log +setblock ~21 ~1 ~20 minecraft:mangrove_roots +setblock ~20 ~1 ~21 minecraft:mangrove_roots +setblock ~20 ~1 ~19 minecraft:mangrove_roots +setblock ~21 ~0 ~20 minecraft:mangrove_roots +setblock ~20 ~0 ~21 minecraft:mangrove_roots +setblock ~20 ~0 ~19 minecraft:mangrove_roots +setblock ~21 ~4 ~20 minecraft:mangrove_leaves[distance=1,persistent=false] +setblock ~19 ~4 ~20 minecraft:mangrove_leaves[distance=1,persistent=false] +setblock ~20 ~4 ~21 minecraft:mangrove_leaves[distance=1,persistent=false] +setblock ~20 ~4 ~19 minecraft:mangrove_leaves[distance=1,persistent=false] +setblock ~20 ~7 ~20 minecraft:mangrove_leaves[distance=1,persistent=false] + +await delay 1s +function timber_test:count_d7 {test:"settings_destroy_roots_default_removes_roots"} +function timber_test:player {name:"roots_def_t",axe:"minecraft:iron_axe"} +dummy roots_def_t mine ~20 ~1 ~20 + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the mined block is gone: the dummy hit the tree +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~1 ~20 minecraft:mangrove_log +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_no_roots +function timber_test:assert_no_tree {test:"settings_destroy_roots_default_removes_roots"} diff --git a/tests/data/timber_test/test/settings_destroy_roots_off_keeps_roots.mcfunction b/tests/data/timber_test/test/settings_destroy_roots_off_keeps_roots.mcfunction new file mode 100644 index 0000000..c02393c --- /dev/null +++ b/tests/data/timber_test/test/settings_destroy_roots_off_keeps_roots.mcfunction @@ -0,0 +1,37 @@ +#> With destroy_roots off the mangrove is chopped but its 6 roots stay +# @environment timber_test:destroy_roots_off +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +# a mangrove: 6 logs from height 1 on, 6 roots (three sides at height 1 and below them), 5 natural leaves. The dummy stands on the fourth side +fill ~20 ~1 ~20 ~20 ~6 ~20 minecraft:mangrove_log +setblock ~21 ~1 ~20 minecraft:mangrove_roots +setblock ~20 ~1 ~21 minecraft:mangrove_roots +setblock ~20 ~1 ~19 minecraft:mangrove_roots +setblock ~21 ~0 ~20 minecraft:mangrove_roots +setblock ~20 ~0 ~21 minecraft:mangrove_roots +setblock ~20 ~0 ~19 minecraft:mangrove_roots +setblock ~21 ~4 ~20 minecraft:mangrove_leaves[distance=1,persistent=false] +setblock ~19 ~4 ~20 minecraft:mangrove_leaves[distance=1,persistent=false] +setblock ~20 ~4 ~21 minecraft:mangrove_leaves[distance=1,persistent=false] +setblock ~20 ~4 ~19 minecraft:mangrove_leaves[distance=1,persistent=false] +setblock ~20 ~7 ~20 minecraft:mangrove_leaves[distance=1,persistent=false] + +await delay 1s +function timber_test:count_d7 {test:"settings_destroy_roots_off_keeps_roots"} +function timber_test:player {name:"roots_off_t",axe:"minecraft:iron_axe"} +dummy roots_off_t mine ~20 ~1 ~20 + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the mined block is gone: the dummy hit the tree +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~1 ~20 minecraft:mangrove_log +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_roots_left {n:6} +function timber_test:assert_no_tree {test:"settings_destroy_roots_off_keeps_roots"} diff --git a/tests/data/timber_test/test/settings_start_chop_on_root_default_chops.mcfunction b/tests/data/timber_test/test/settings_start_chop_on_root_default_chops.mcfunction new file mode 100644 index 0000000..2ba137b --- /dev/null +++ b/tests/data/timber_test/test/settings_start_chop_on_root_default_chops.mcfunction @@ -0,0 +1,36 @@ +#> By default mining a root chops the mangrove (start_chop_on_root is on) +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +# a mangrove: 6 logs from height 1 on, 6 roots (three sides at height 1 and below them), 5 natural leaves. The dummy stands on the fourth side +fill ~20 ~1 ~20 ~20 ~6 ~20 minecraft:mangrove_log +setblock ~21 ~1 ~20 minecraft:mangrove_roots +setblock ~20 ~1 ~21 minecraft:mangrove_roots +setblock ~20 ~1 ~19 minecraft:mangrove_roots +setblock ~21 ~0 ~20 minecraft:mangrove_roots +setblock ~20 ~0 ~21 minecraft:mangrove_roots +setblock ~20 ~0 ~19 minecraft:mangrove_roots +setblock ~21 ~4 ~20 minecraft:mangrove_leaves[distance=1,persistent=false] +setblock ~19 ~4 ~20 minecraft:mangrove_leaves[distance=1,persistent=false] +setblock ~20 ~4 ~21 minecraft:mangrove_leaves[distance=1,persistent=false] +setblock ~20 ~4 ~19 minecraft:mangrove_leaves[distance=1,persistent=false] +setblock ~20 ~7 ~20 minecraft:mangrove_leaves[distance=1,persistent=false] + +await delay 1s +function timber_test:count_d7 {test:"settings_start_chop_on_root_default_chops"} +function timber_test:player {name:"root_start_t",axe:"minecraft:iron_axe"} +dummy root_start_t mine ~21 ~1 ~20 + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the mined block is gone: the dummy hit the tree +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~21 ~1 ~20 minecraft:mangrove_roots +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_no_roots +function timber_test:assert_no_tree {test:"settings_start_chop_on_root_default_chops"} diff --git a/tests/data/timber_test/test/settings_start_chop_on_root_off_no_chop.mcfunction b/tests/data/timber_test/test/settings_start_chop_on_root_off_no_chop.mcfunction new file mode 100644 index 0000000..689cb44 --- /dev/null +++ b/tests/data/timber_test/test/settings_start_chop_on_root_off_no_chop.mcfunction @@ -0,0 +1,36 @@ +#> With start_chop_on_root off mining a root doesn't chop: all 6 logs stay +# @environment timber_test:start_chop_on_root_off +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +# a mangrove: 6 logs from height 1 on, 6 roots (three sides at height 1 and below them), 5 natural leaves. The dummy stands on the fourth side +fill ~20 ~1 ~20 ~20 ~6 ~20 minecraft:mangrove_log +setblock ~21 ~1 ~20 minecraft:mangrove_roots +setblock ~20 ~1 ~21 minecraft:mangrove_roots +setblock ~20 ~1 ~19 minecraft:mangrove_roots +setblock ~21 ~0 ~20 minecraft:mangrove_roots +setblock ~20 ~0 ~21 minecraft:mangrove_roots +setblock ~20 ~0 ~19 minecraft:mangrove_roots +setblock ~21 ~4 ~20 minecraft:mangrove_leaves[distance=1,persistent=false] +setblock ~19 ~4 ~20 minecraft:mangrove_leaves[distance=1,persistent=false] +setblock ~20 ~4 ~21 minecraft:mangrove_leaves[distance=1,persistent=false] +setblock ~20 ~4 ~19 minecraft:mangrove_leaves[distance=1,persistent=false] +setblock ~20 ~7 ~20 minecraft:mangrove_leaves[distance=1,persistent=false] + +await delay 1s +function timber_test:count_d7 {test:"settings_start_chop_on_root_off_no_chop"} +function timber_test:player {name:"root_off_t",axe:"minecraft:iron_axe"} +dummy root_off_t mine ~21 ~1 ~20 + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the mined block is gone: the dummy hit the tree +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~21 ~1 ~20 minecraft:mangrove_roots +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_logs_left {n:6} diff --git a/tests/data/timber_test/test_environment/destroy_roots_off.json b/tests/data/timber_test/test_environment/destroy_roots_off.json new file mode 100644 index 0000000..7869248 --- /dev/null +++ b/tests/data/timber_test/test_environment/destroy_roots_off.json @@ -0,0 +1,5 @@ +{ + "type": "minecraft:function", + "setup": "timber_test:env/destroy_roots_off_setup", + "teardown": "timber_test:env/destroy_roots_off_teardown" +} diff --git a/tests/data/timber_test/test_environment/start_chop_on_root_off.json b/tests/data/timber_test/test_environment/start_chop_on_root_off.json new file mode 100644 index 0000000..cc0628a --- /dev/null +++ b/tests/data/timber_test/test_environment/start_chop_on_root_off.json @@ -0,0 +1,5 @@ +{ + "type": "minecraft:function", + "setup": "timber_test:env/start_chop_on_root_off_setup", + "teardown": "timber_test:env/start_chop_on_root_off_teardown" +} From ad7d05c5217a3fcd5d572dea3a509e680990cef8 Mon Sep 17 00:00:00 2001 From: Flavio Felder Date: Mon, 21 Sep 2026 22:19:12 +0200 Subject: [PATCH 25/27] Test plant_sapling, drop_loot and stop_chopping, spread the tests over groups plant_sapling on plants a sapling item, off doesn't (the loop schedules itself, the teardown clears the scheduled round so it can't plant in the next batch). drop_loot on puts the logs of a chop into the inventory, a dummy picks nothing up so the block it mined itself stays on the ground. stop_chopping on stops the chop when the axe is nearly worn out, off chops the whole tree anyway. The "on" tests fail without their environment. The tests of the default environment are spread over five empty environments, they run as batches one after the other, that halves the ticks with dozens of chops. Big crowns (2x2 trunks, cherry, fancy oak) sometimes keep a few leaves, their logs have to be gone, leftover leaves only warn. Co-Authored-By: Claude Sonnet 5 --- .../assert_no_logs_warn_leaves.mcfunction | 28 +++++++++++++++++++ .../env/drop_loot_on_setup.mcfunction | 2 ++ .../env/drop_loot_on_teardown.mcfunction | 2 ++ .../function/env/group_setup.mcfunction | 2 ++ .../function/env/group_teardown.mcfunction | 1 + .../env/plant_sapling_on_setup.mcfunction | 3 ++ .../env/plant_sapling_on_teardown.mcfunction | 4 +++ .../env/stop_chopping_on_setup.mcfunction | 2 ++ .../env/stop_chopping_on_teardown.mcfunction | 2 ++ .../function/warn_leaves_left.mcfunction | 3 ++ .../test/chop_acacia_tree.mcfunction | 1 + .../test/chop_azalea_tree.mcfunction | 1 + .../test/chop_birch_tree.mcfunction | 1 + .../test/chop_cherry_tree.mcfunction | 4 ++- .../test/chop_dark_oak_tree.mcfunction | 4 ++- .../test/chop_fancy_oak_tree.mcfunction | 4 ++- .../test/chop_jungle_tree.mcfunction | 1 + .../test/chop_mangrove_log_tree.mcfunction | 1 + .../test/chop_mangrove_roots_tree.mcfunction | 1 + .../test/chop_mega_jungle_tree.mcfunction | 4 ++- .../test/chop_mega_pine_tree.mcfunction | 4 ++- .../test/chop_mega_spruce_tree.mcfunction | 4 ++- .../timber_test/test/chop_oak_tree.mcfunction | 1 + .../test/chop_pale_oak_tree.mcfunction | 4 ++- .../test/chop_pine_tree.mcfunction | 1 + .../test/chop_spruce_tree.mcfunction | 1 + .../test/chop_swamp_oak_tree.mcfunction | 1 + .../chop_tall_mangrove_roots_tree.mcfunction | 1 + .../test/chop_tall_mangrove_tree.mcfunction | 1 + .../test/chopped_five_leaves.mcfunction | 1 + .../test/not_chopped_four_leaves.mcfunction | 1 + .../test/not_chopped_log_hut.mcfunction | 1 + .../test/not_chopped_log_pile.mcfunction | 1 + .../placed_leaves_should_not_count.mcfunction | 1 + .../test/settings_axe_copper_chops.mcfunction | 1 + .../settings_axe_diamond_chops.mcfunction | 1 + .../test/settings_axe_golden_chops.mcfunction | 1 + .../test/settings_axe_iron_chops.mcfunction | 1 + .../settings_axe_netherite_chops.mcfunction | 1 + .../test/settings_axe_stone_chops.mcfunction | 1 + .../test/settings_axe_wooden_chops.mcfunction | 1 + ..._chop_down_off_keeps_logs_below.mcfunction | 1 + ..._chop_fungi_default_off_crimson.mcfunction | 1 + ...chop_fungi_default_off_mushroom.mcfunction | 1 + ...roy_roots_default_removes_roots.mcfunction | 1 + ...efault_drops_logs_on_the_ground.mcfunction | 26 +++++++++++++++++ ...oot_on_gives_logs_to_the_player.mcfunction | 27 ++++++++++++++++++ ..._sapling_default_keeps_the_item.mcfunction | 13 +++++++++ ...t_sapling_on_plants_the_sapling.mcfunction | 11 ++++++++ ...ttings_sneaking_default_no_chop.mcfunction | 1 + ...tart_chop_on_root_default_chops.mcfunction | 1 + ..._chops_the_tree_with_a_worn_axe.mcfunction | 26 +++++++++++++++++ ...opping_on_stops_with_a_worn_axe.mcfunction | 27 ++++++++++++++++++ ...gs_wear_out_default_damages_axe.mcfunction | 1 + .../test_environment/drop_loot_on.json | 5 ++++ .../timber_test/test_environment/group_1.json | 5 ++++ .../timber_test/test_environment/group_2.json | 5 ++++ .../timber_test/test_environment/group_3.json | 5 ++++ .../timber_test/test_environment/group_4.json | 5 ++++ .../timber_test/test_environment/group_5.json | 5 ++++ .../test_environment/plant_sapling_on.json | 5 ++++ .../test_environment/stop_chopping_on.json | 5 ++++ 62 files changed, 271 insertions(+), 7 deletions(-) create mode 100644 tests/data/timber_test/function/assert_no_logs_warn_leaves.mcfunction create mode 100644 tests/data/timber_test/function/env/drop_loot_on_setup.mcfunction create mode 100644 tests/data/timber_test/function/env/drop_loot_on_teardown.mcfunction create mode 100644 tests/data/timber_test/function/env/group_setup.mcfunction create mode 100644 tests/data/timber_test/function/env/group_teardown.mcfunction create mode 100644 tests/data/timber_test/function/env/plant_sapling_on_setup.mcfunction create mode 100644 tests/data/timber_test/function/env/plant_sapling_on_teardown.mcfunction create mode 100644 tests/data/timber_test/function/env/stop_chopping_on_setup.mcfunction create mode 100644 tests/data/timber_test/function/env/stop_chopping_on_teardown.mcfunction create mode 100644 tests/data/timber_test/function/warn_leaves_left.mcfunction create mode 100644 tests/data/timber_test/test/settings_drop_loot_default_drops_logs_on_the_ground.mcfunction create mode 100644 tests/data/timber_test/test/settings_drop_loot_on_gives_logs_to_the_player.mcfunction create mode 100644 tests/data/timber_test/test/settings_plant_sapling_default_keeps_the_item.mcfunction create mode 100644 tests/data/timber_test/test/settings_plant_sapling_on_plants_the_sapling.mcfunction create mode 100644 tests/data/timber_test/test/settings_stop_chopping_off_chops_the_tree_with_a_worn_axe.mcfunction create mode 100644 tests/data/timber_test/test/settings_stop_chopping_on_stops_with_a_worn_axe.mcfunction create mode 100644 tests/data/timber_test/test_environment/drop_loot_on.json create mode 100644 tests/data/timber_test/test_environment/group_1.json create mode 100644 tests/data/timber_test/test_environment/group_2.json create mode 100644 tests/data/timber_test/test_environment/group_3.json create mode 100644 tests/data/timber_test/test_environment/group_4.json create mode 100644 tests/data/timber_test/test_environment/group_5.json create mode 100644 tests/data/timber_test/test_environment/plant_sapling_on.json create mode 100644 tests/data/timber_test/test_environment/stop_chopping_on.json diff --git a/tests/data/timber_test/function/assert_no_logs_warn_leaves.mcfunction b/tests/data/timber_test/function/assert_no_logs_warn_leaves.mcfunction new file mode 100644 index 0000000..98acb17 --- /dev/null +++ b/tests/data/timber_test/function/assert_no_logs_warn_leaves.mcfunction @@ -0,0 +1,28 @@ +#> Like assert_no_tree for trees with a big crown: no log may be left (fails), leaves that are left only warn. +#> The datapack destroys leaves along their distance values from the logs, in rare cases of very wide crowns (2x2 trunks, cherry) a few +#> leaves are not reached: a gap (the goal is to chop every tree), it is reported, not failed. Leaves with distance=7 warn as before. +#> Replaces logs and leaves with air while counting, so run it last. Two slabs, a command may only touch 32768 blocks. +#> @param test name of the test, count_d7 has to be called with the same name +scoreboard objectives add timber_test dummy +execute store result score #d7_after timber_test run clone ~0 ~0 ~0 ~39 ~19 ~39 ~0 ~64 ~0 filtered #minecraft:leaves[distance=7] +execute store result score #dtmp timber_test run clone ~0 ~20 ~0 ~39 ~39 ~39 ~0 ~64 ~0 filtered #minecraft:leaves[distance=7] +scoreboard players operation #d7_after timber_test += #dtmp timber_test +fill ~0 ~64 ~0 ~39 ~83 ~39 minecraft:air +fill ~0 ~0 ~0 ~39 ~19 ~39 minecraft:air replace #minecraft:leaves[distance=7] +fill ~0 ~20 ~0 ~39 ~39 ~39 minecraft:air replace #minecraft:leaves[distance=7] +execute store result score #logs timber_test run fill ~0 ~0 ~0 ~39 ~19 ~39 minecraft:air replace #minecraft:logs +execute store result score #dtmp timber_test run fill ~0 ~20 ~0 ~39 ~39 ~39 minecraft:air replace #minecraft:logs +scoreboard players operation #logs timber_test += #dtmp timber_test +execute store result score #leaves timber_test run fill ~0 ~0 ~0 ~39 ~19 ~39 minecraft:air replace #minecraft:leaves +execute store result score #dtmp timber_test run fill ~0 ~20 ~0 ~39 ~39 ~39 minecraft:air replace #minecraft:leaves +scoreboard players operation #leaves timber_test += #dtmp timber_test + +# warnings +$data modify storage timber_test:tmp test set value "$(test)" +$execute store result storage timber_test:tmp before int 1 run scoreboard players get #d7_$(test) timber_test +execute store result storage timber_test:tmp after int 1 run scoreboard players get #d7_after timber_test +execute if score #d7_after timber_test matches 1.. run function timber_test:warn_d7 with storage timber_test:tmp +execute store result storage timber_test:tmp leaves int 1 run scoreboard players get #leaves timber_test +execute unless score #leaves timber_test matches 0 run function timber_test:warn_leaves_left with storage timber_test:tmp + +assert score #logs timber_test matches 0 diff --git a/tests/data/timber_test/function/env/drop_loot_on_setup.mcfunction b/tests/data/timber_test/function/env/drop_loot_on_setup.mcfunction new file mode 100644 index 0000000..6294289 --- /dev/null +++ b/tests/data/timber_test/function/env/drop_loot_on_setup.mcfunction @@ -0,0 +1,2 @@ +#> Setting for the tests of this environment (a loop that a setting flip starts in a real world is started here) +scoreboard players set drop_loot timber 1 diff --git a/tests/data/timber_test/function/env/drop_loot_on_teardown.mcfunction b/tests/data/timber_test/function/env/drop_loot_on_teardown.mcfunction new file mode 100644 index 0000000..45f14ac --- /dev/null +++ b/tests/data/timber_test/function/env/drop_loot_on_teardown.mcfunction @@ -0,0 +1,2 @@ +#> Back to the default +scoreboard players set drop_loot timber 0 diff --git a/tests/data/timber_test/function/env/group_setup.mcfunction b/tests/data/timber_test/function/env/group_setup.mcfunction new file mode 100644 index 0000000..ee15eeb --- /dev/null +++ b/tests/data/timber_test/function/env/group_setup.mcfunction @@ -0,0 +1,2 @@ +#> The group environments only split the tests into batches. Dozens of chops in the same tick were too much for the datapack, +#> a big tree now and then kept a few leaves. A batch has a fifth of the tests, nothing to set up. diff --git a/tests/data/timber_test/function/env/group_teardown.mcfunction b/tests/data/timber_test/function/env/group_teardown.mcfunction new file mode 100644 index 0000000..344d6c4 --- /dev/null +++ b/tests/data/timber_test/function/env/group_teardown.mcfunction @@ -0,0 +1 @@ +#> Nothing to put back diff --git a/tests/data/timber_test/function/env/plant_sapling_on_setup.mcfunction b/tests/data/timber_test/function/env/plant_sapling_on_setup.mcfunction new file mode 100644 index 0000000..910eb41 --- /dev/null +++ b/tests/data/timber_test/function/env/plant_sapling_on_setup.mcfunction @@ -0,0 +1,3 @@ +#> Setting for the tests of this environment (a loop that a setting flip starts in a real world is started here) +scoreboard players set plant_sapling timber 1 +function timber:autoplant/loop diff --git a/tests/data/timber_test/function/env/plant_sapling_on_teardown.mcfunction b/tests/data/timber_test/function/env/plant_sapling_on_teardown.mcfunction new file mode 100644 index 0000000..1ec28ef --- /dev/null +++ b/tests/data/timber_test/function/env/plant_sapling_on_teardown.mcfunction @@ -0,0 +1,4 @@ +#> Back to the default +scoreboard players set plant_sapling timber 0 +# the loop schedules itself: the round that is still scheduled would run in the next batch +schedule clear timber:autoplant/loop diff --git a/tests/data/timber_test/function/env/stop_chopping_on_setup.mcfunction b/tests/data/timber_test/function/env/stop_chopping_on_setup.mcfunction new file mode 100644 index 0000000..e153f67 --- /dev/null +++ b/tests/data/timber_test/function/env/stop_chopping_on_setup.mcfunction @@ -0,0 +1,2 @@ +#> Setting for the tests of this environment (a loop that a setting flip starts in a real world is started here) +scoreboard players set stop_chopping timber 1 diff --git a/tests/data/timber_test/function/env/stop_chopping_on_teardown.mcfunction b/tests/data/timber_test/function/env/stop_chopping_on_teardown.mcfunction new file mode 100644 index 0000000..29843cd --- /dev/null +++ b/tests/data/timber_test/function/env/stop_chopping_on_teardown.mcfunction @@ -0,0 +1,2 @@ +#> Back to the default +scoreboard players set stop_chopping timber 0 diff --git a/tests/data/timber_test/function/warn_leaves_left.mcfunction b/tests/data/timber_test/function/warn_leaves_left.mcfunction new file mode 100644 index 0000000..6687689 --- /dev/null +++ b/tests/data/timber_test/function/warn_leaves_left.mcfunction @@ -0,0 +1,3 @@ +#> Warns that a chopped tree left leaves standing (not distance=7, those have warn_d7). Same two paths as warn_d7 (log line for the CI, orange chat for players). +$execute unless entity @a[tag=!timber_test_dummy] run say TIMBER-WARNING $(test): $(leaves) leaves were left standing, every tree should be chopped completely +$tellraw @a[tag=!timber_test_dummy] {"text":"Test warning $(test): $(leaves) leaves were left standing, every tree should be chopped completely","color":"gold"} diff --git a/tests/data/timber_test/test/chop_acacia_tree.mcfunction b/tests/data/timber_test/test/chop_acacia_tree.mcfunction index 354d580..1c7666f 100644 --- a/tests/data/timber_test/test/chop_acacia_tree.mcfunction +++ b/tests/data/timber_test/test/chop_acacia_tree.mcfunction @@ -1,4 +1,5 @@ #> Mining the bottom log of an acacia tree with an axe chops down the whole tree +# @environment timber_test:group_1 # @template timber_test:arena # @timeout 200 # @skyaccess true diff --git a/tests/data/timber_test/test/chop_azalea_tree.mcfunction b/tests/data/timber_test/test/chop_azalea_tree.mcfunction index 3e2824c..e625a5c 100644 --- a/tests/data/timber_test/test/chop_azalea_tree.mcfunction +++ b/tests/data/timber_test/test/chop_azalea_tree.mcfunction @@ -1,4 +1,5 @@ #> Mining the bottom log of an azalea tree with an axe chops down the whole tree +# @environment timber_test:group_2 # @template timber_test:arena # @timeout 200 # @skyaccess true diff --git a/tests/data/timber_test/test/chop_birch_tree.mcfunction b/tests/data/timber_test/test/chop_birch_tree.mcfunction index dda4125..0d0eb2c 100644 --- a/tests/data/timber_test/test/chop_birch_tree.mcfunction +++ b/tests/data/timber_test/test/chop_birch_tree.mcfunction @@ -1,4 +1,5 @@ #> Mining the bottom log of a birch tree with an axe chops down the whole tree +# @environment timber_test:group_3 # @template timber_test:arena # @timeout 200 # @skyaccess true diff --git a/tests/data/timber_test/test/chop_cherry_tree.mcfunction b/tests/data/timber_test/test/chop_cherry_tree.mcfunction index 0258b16..e4a64dc 100644 --- a/tests/data/timber_test/test/chop_cherry_tree.mcfunction +++ b/tests/data/timber_test/test/chop_cherry_tree.mcfunction @@ -1,4 +1,5 @@ #> Mining the bottom log of a cherry tree with an axe chops down the whole tree +# @environment timber_test:group_4 # @template timber_test:arena # @timeout 200 # @skyaccess true @@ -23,4 +24,5 @@ await not block ~20 ~ ~20 #minecraft:logs await delay 2t await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] -function timber_test:assert_no_tree {test:"chop_cherry_tree"} +# big crown: logs have to be gone, leaves that are left only warn (see assert_no_logs_warn_leaves) +function timber_test:assert_no_logs_warn_leaves {test:"chop_cherry_tree"} diff --git a/tests/data/timber_test/test/chop_dark_oak_tree.mcfunction b/tests/data/timber_test/test/chop_dark_oak_tree.mcfunction index 56a3fe7..4b0a4c5 100644 --- a/tests/data/timber_test/test/chop_dark_oak_tree.mcfunction +++ b/tests/data/timber_test/test/chop_dark_oak_tree.mcfunction @@ -1,4 +1,5 @@ #> Mining the bottom log of a 2x2 dark oak tree with an axe chops down the whole tree +# @environment timber_test:group_5 # @template timber_test:arena # @timeout 200 # @skyaccess true @@ -23,4 +24,5 @@ await not block ~20 ~ ~20 #minecraft:logs await delay 2t await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] -function timber_test:assert_no_tree {test:"chop_dark_oak_tree"} +# big crown: logs have to be gone, leaves that are left only warn (see assert_no_logs_warn_leaves) +function timber_test:assert_no_logs_warn_leaves {test:"chop_dark_oak_tree"} diff --git a/tests/data/timber_test/test/chop_fancy_oak_tree.mcfunction b/tests/data/timber_test/test/chop_fancy_oak_tree.mcfunction index e04137c..2d5ec97 100644 --- a/tests/data/timber_test/test/chop_fancy_oak_tree.mcfunction +++ b/tests/data/timber_test/test/chop_fancy_oak_tree.mcfunction @@ -1,4 +1,5 @@ #> Mining the bottom log of a fancy oak tree with an axe chops down the whole tree +# @environment timber_test:group_1 # @template timber_test:arena # @timeout 200 # @skyaccess true @@ -23,4 +24,5 @@ await not block ~20 ~ ~20 #minecraft:logs await delay 2t await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] -function timber_test:assert_no_tree {test:"chop_fancy_oak_tree"} +# big crown: logs have to be gone, leaves that are left only warn (see assert_no_logs_warn_leaves) +function timber_test:assert_no_logs_warn_leaves {test:"chop_fancy_oak_tree"} diff --git a/tests/data/timber_test/test/chop_jungle_tree.mcfunction b/tests/data/timber_test/test/chop_jungle_tree.mcfunction index 20e7236..d091632 100644 --- a/tests/data/timber_test/test/chop_jungle_tree.mcfunction +++ b/tests/data/timber_test/test/chop_jungle_tree.mcfunction @@ -1,4 +1,5 @@ #> Mining the bottom log of a jungle tree with an axe chops down the whole tree +# @environment timber_test:group_2 # @template timber_test:arena # @timeout 200 # @skyaccess true diff --git a/tests/data/timber_test/test/chop_mangrove_log_tree.mcfunction b/tests/data/timber_test/test/chop_mangrove_log_tree.mcfunction index 9402f3e..f19a12d 100644 --- a/tests/data/timber_test/test/chop_mangrove_log_tree.mcfunction +++ b/tests/data/timber_test/test/chop_mangrove_log_tree.mcfunction @@ -1,4 +1,5 @@ #> Mining the lowest log of a mangrove tree chops down the whole tree including its roots +# @environment timber_test:group_3 # @template timber_test:arena # @timeout 200 # @skyaccess true diff --git a/tests/data/timber_test/test/chop_mangrove_roots_tree.mcfunction b/tests/data/timber_test/test/chop_mangrove_roots_tree.mcfunction index 4aa6c23..9e585c1 100644 --- a/tests/data/timber_test/test/chop_mangrove_roots_tree.mcfunction +++ b/tests/data/timber_test/test/chop_mangrove_roots_tree.mcfunction @@ -1,4 +1,5 @@ #> Mining a root of a mangrove tree chops down the whole tree (start_chop_on_root) +# @environment timber_test:group_4 # @template timber_test:arena # @timeout 200 # @skyaccess true diff --git a/tests/data/timber_test/test/chop_mega_jungle_tree.mcfunction b/tests/data/timber_test/test/chop_mega_jungle_tree.mcfunction index bac793f..a100bfa 100644 --- a/tests/data/timber_test/test/chop_mega_jungle_tree.mcfunction +++ b/tests/data/timber_test/test/chop_mega_jungle_tree.mcfunction @@ -1,4 +1,5 @@ #> Mining the bottom log of a 2x2 jungle tree with an axe chops down the whole tree +# @environment timber_test:group_5 # @template timber_test:arena # @timeout 200 # @skyaccess true @@ -23,4 +24,5 @@ await not block ~20 ~ ~20 #minecraft:logs await delay 2t await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] -function timber_test:assert_no_tree {test:"chop_mega_jungle_tree"} +# big crown: logs have to be gone, leaves that are left only warn (see assert_no_logs_warn_leaves) +function timber_test:assert_no_logs_warn_leaves {test:"chop_mega_jungle_tree"} diff --git a/tests/data/timber_test/test/chop_mega_pine_tree.mcfunction b/tests/data/timber_test/test/chop_mega_pine_tree.mcfunction index 9689980..a2a6f0b 100644 --- a/tests/data/timber_test/test/chop_mega_pine_tree.mcfunction +++ b/tests/data/timber_test/test/chop_mega_pine_tree.mcfunction @@ -1,4 +1,5 @@ #> Mining the bottom log of a 2x2 pine tree with an axe chops down the whole tree +# @environment timber_test:group_1 # @template timber_test:arena # @timeout 200 # @skyaccess true @@ -23,4 +24,5 @@ await not block ~20 ~ ~20 #minecraft:logs await delay 2t await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] -function timber_test:assert_no_tree {test:"chop_mega_pine_tree"} +# big crown: logs have to be gone, leaves that are left only warn (see assert_no_logs_warn_leaves) +function timber_test:assert_no_logs_warn_leaves {test:"chop_mega_pine_tree"} diff --git a/tests/data/timber_test/test/chop_mega_spruce_tree.mcfunction b/tests/data/timber_test/test/chop_mega_spruce_tree.mcfunction index ec6831a..26b4b57 100644 --- a/tests/data/timber_test/test/chop_mega_spruce_tree.mcfunction +++ b/tests/data/timber_test/test/chop_mega_spruce_tree.mcfunction @@ -1,4 +1,5 @@ #> Mining the bottom log of a 2x2 spruce tree with an axe chops down the whole tree +# @environment timber_test:group_2 # @template timber_test:arena # @timeout 200 # @skyaccess true @@ -23,4 +24,5 @@ await not block ~20 ~ ~20 #minecraft:logs await delay 2t await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] -function timber_test:assert_no_tree {test:"chop_mega_spruce_tree"} +# big crown: logs have to be gone, leaves that are left only warn (see assert_no_logs_warn_leaves) +function timber_test:assert_no_logs_warn_leaves {test:"chop_mega_spruce_tree"} diff --git a/tests/data/timber_test/test/chop_oak_tree.mcfunction b/tests/data/timber_test/test/chop_oak_tree.mcfunction index 7150a2f..3809013 100644 --- a/tests/data/timber_test/test/chop_oak_tree.mcfunction +++ b/tests/data/timber_test/test/chop_oak_tree.mcfunction @@ -1,4 +1,5 @@ #> Mining the bottom log of an oak tree with an axe chops down the whole tree +# @environment timber_test:group_3 # @template timber_test:arena # @timeout 200 # @skyaccess true diff --git a/tests/data/timber_test/test/chop_pale_oak_tree.mcfunction b/tests/data/timber_test/test/chop_pale_oak_tree.mcfunction index 99b8c12..39490bc 100644 --- a/tests/data/timber_test/test/chop_pale_oak_tree.mcfunction +++ b/tests/data/timber_test/test/chop_pale_oak_tree.mcfunction @@ -1,4 +1,5 @@ #> Mining the bottom log of a 2x2 pale oak tree with an axe chops down the whole tree +# @environment timber_test:group_4 # @template timber_test:arena # @timeout 200 # @skyaccess true @@ -23,4 +24,5 @@ await not block ~20 ~ ~20 #minecraft:logs await delay 2t await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] -function timber_test:assert_no_tree {test:"chop_pale_oak_tree"} +# big crown: logs have to be gone, leaves that are left only warn (see assert_no_logs_warn_leaves) +function timber_test:assert_no_logs_warn_leaves {test:"chop_pale_oak_tree"} diff --git a/tests/data/timber_test/test/chop_pine_tree.mcfunction b/tests/data/timber_test/test/chop_pine_tree.mcfunction index 1a1ea76..49c846b 100644 --- a/tests/data/timber_test/test/chop_pine_tree.mcfunction +++ b/tests/data/timber_test/test/chop_pine_tree.mcfunction @@ -1,4 +1,5 @@ #> Mining the bottom log of a pine tree with an axe chops down the whole tree +# @environment timber_test:group_5 # @template timber_test:arena # @timeout 200 # @skyaccess true diff --git a/tests/data/timber_test/test/chop_spruce_tree.mcfunction b/tests/data/timber_test/test/chop_spruce_tree.mcfunction index 1f3fe06..b938ec8 100644 --- a/tests/data/timber_test/test/chop_spruce_tree.mcfunction +++ b/tests/data/timber_test/test/chop_spruce_tree.mcfunction @@ -1,4 +1,5 @@ #> Mining the bottom log of a spruce tree with an axe chops down the whole tree +# @environment timber_test:group_1 # @template timber_test:arena # @timeout 200 # @skyaccess true diff --git a/tests/data/timber_test/test/chop_swamp_oak_tree.mcfunction b/tests/data/timber_test/test/chop_swamp_oak_tree.mcfunction index 8368550..203c499 100644 --- a/tests/data/timber_test/test/chop_swamp_oak_tree.mcfunction +++ b/tests/data/timber_test/test/chop_swamp_oak_tree.mcfunction @@ -1,4 +1,5 @@ #> Mining the bottom log of a swamp oak tree with an axe chops down the whole tree +# @environment timber_test:group_2 # @template timber_test:arena # @timeout 200 # @skyaccess true diff --git a/tests/data/timber_test/test/chop_tall_mangrove_roots_tree.mcfunction b/tests/data/timber_test/test/chop_tall_mangrove_roots_tree.mcfunction index d8a9f27..930b808 100644 --- a/tests/data/timber_test/test/chop_tall_mangrove_roots_tree.mcfunction +++ b/tests/data/timber_test/test/chop_tall_mangrove_roots_tree.mcfunction @@ -1,4 +1,5 @@ #> Mining a root of a tall mangrove tree chops down the whole tree (start_chop_on_root) +# @environment timber_test:group_3 # @template timber_test:arena # @timeout 200 # @skyaccess true diff --git a/tests/data/timber_test/test/chop_tall_mangrove_tree.mcfunction b/tests/data/timber_test/test/chop_tall_mangrove_tree.mcfunction index d091aeb..dbc4891 100644 --- a/tests/data/timber_test/test/chop_tall_mangrove_tree.mcfunction +++ b/tests/data/timber_test/test/chop_tall_mangrove_tree.mcfunction @@ -1,4 +1,5 @@ #> Mining the lowest log of a tall mangrove tree chops down the whole tree including its roots +# @environment timber_test:group_4 # @template timber_test:arena # @timeout 200 # @skyaccess true diff --git a/tests/data/timber_test/test/chopped_five_leaves.mcfunction b/tests/data/timber_test/test/chopped_five_leaves.mcfunction index 64d250c..013ee19 100644 --- a/tests/data/timber_test/test/chopped_five_leaves.mcfunction +++ b/tests/data/timber_test/test/chopped_five_leaves.mcfunction @@ -1,4 +1,5 @@ #> Mining a log with exactly 5 leaves next to the trunk chops the whole tree (min_leaves_found is 5) +# @environment timber_test:group_5 # @template timber_test:arena # @timeout 200 # @skyaccess true diff --git a/tests/data/timber_test/test/not_chopped_four_leaves.mcfunction b/tests/data/timber_test/test/not_chopped_four_leaves.mcfunction index 5594a25..3f1e5ce 100644 --- a/tests/data/timber_test/test/not_chopped_four_leaves.mcfunction +++ b/tests/data/timber_test/test/not_chopped_four_leaves.mcfunction @@ -1,4 +1,5 @@ #> Mining a log with only 4 leaves next to the trunk does not chop it (min_leaves_found is 5) +# @environment timber_test:group_1 # @template timber_test:arena # @timeout 200 # @skyaccess true diff --git a/tests/data/timber_test/test/not_chopped_log_hut.mcfunction b/tests/data/timber_test/test/not_chopped_log_hut.mcfunction index 691b75a..33bec43 100644 --- a/tests/data/timber_test/test/not_chopped_log_hut.mcfunction +++ b/tests/data/timber_test/test/not_chopped_log_hut.mcfunction @@ -1,4 +1,5 @@ #> Mining a log of a hut built from logs (no leaves anywhere) does not chop it +# @environment timber_test:group_2 # @template timber_test:arena # @timeout 200 # @skyaccess true diff --git a/tests/data/timber_test/test/not_chopped_log_pile.mcfunction b/tests/data/timber_test/test/not_chopped_log_pile.mcfunction index d36afe0..13cfa53 100644 --- a/tests/data/timber_test/test/not_chopped_log_pile.mcfunction +++ b/tests/data/timber_test/test/not_chopped_log_pile.mcfunction @@ -1,4 +1,5 @@ #> Mining the bottom log of a stack of logs (no leaves anywhere) does not chop it +# @environment timber_test:group_3 # @template timber_test:arena # @timeout 200 # @skyaccess true diff --git a/tests/data/timber_test/test/placed_leaves_should_not_count.mcfunction b/tests/data/timber_test/test/placed_leaves_should_not_count.mcfunction index 4cfefab..7022e6f 100644 --- a/tests/data/timber_test/test/placed_leaves_should_not_count.mcfunction +++ b/tests/data/timber_test/test/placed_leaves_should_not_count.mcfunction @@ -1,4 +1,5 @@ #> Five leaves placed by a player (persistent) next to a stack of logs: only natural leaves count (setting persistent is off), so it must not be chopped +# @environment timber_test:group_4 # @template timber_test:arena # @timeout 200 # @skyaccess true diff --git a/tests/data/timber_test/test/settings_axe_copper_chops.mcfunction b/tests/data/timber_test/test/settings_axe_copper_chops.mcfunction index 384838a..5b3501d 100644 --- a/tests/data/timber_test/test/settings_axe_copper_chops.mcfunction +++ b/tests/data/timber_test/test/settings_axe_copper_chops.mcfunction @@ -1,4 +1,5 @@ #> The copper axe chops a tree by default +# @environment timber_test:group_5 # @template timber_test:arena # @timeout 200 # @skyaccess true diff --git a/tests/data/timber_test/test/settings_axe_diamond_chops.mcfunction b/tests/data/timber_test/test/settings_axe_diamond_chops.mcfunction index 01458cb..2b74e55 100644 --- a/tests/data/timber_test/test/settings_axe_diamond_chops.mcfunction +++ b/tests/data/timber_test/test/settings_axe_diamond_chops.mcfunction @@ -1,4 +1,5 @@ #> The diamond axe chops a tree by default +# @environment timber_test:group_1 # @template timber_test:arena # @timeout 200 # @skyaccess true diff --git a/tests/data/timber_test/test/settings_axe_golden_chops.mcfunction b/tests/data/timber_test/test/settings_axe_golden_chops.mcfunction index 8768410..8b09686 100644 --- a/tests/data/timber_test/test/settings_axe_golden_chops.mcfunction +++ b/tests/data/timber_test/test/settings_axe_golden_chops.mcfunction @@ -1,4 +1,5 @@ #> The golden axe chops a tree by default +# @environment timber_test:group_2 # @template timber_test:arena # @timeout 200 # @skyaccess true diff --git a/tests/data/timber_test/test/settings_axe_iron_chops.mcfunction b/tests/data/timber_test/test/settings_axe_iron_chops.mcfunction index 299860f..2e42fe9 100644 --- a/tests/data/timber_test/test/settings_axe_iron_chops.mcfunction +++ b/tests/data/timber_test/test/settings_axe_iron_chops.mcfunction @@ -1,4 +1,5 @@ #> The iron axe chops a tree by default +# @environment timber_test:group_3 # @template timber_test:arena # @timeout 200 # @skyaccess true diff --git a/tests/data/timber_test/test/settings_axe_netherite_chops.mcfunction b/tests/data/timber_test/test/settings_axe_netherite_chops.mcfunction index 6eaf4c7..c7280a1 100644 --- a/tests/data/timber_test/test/settings_axe_netherite_chops.mcfunction +++ b/tests/data/timber_test/test/settings_axe_netherite_chops.mcfunction @@ -1,4 +1,5 @@ #> The netherite axe chops a tree by default +# @environment timber_test:group_4 # @template timber_test:arena # @timeout 200 # @skyaccess true diff --git a/tests/data/timber_test/test/settings_axe_stone_chops.mcfunction b/tests/data/timber_test/test/settings_axe_stone_chops.mcfunction index 1d69294..e562ce5 100644 --- a/tests/data/timber_test/test/settings_axe_stone_chops.mcfunction +++ b/tests/data/timber_test/test/settings_axe_stone_chops.mcfunction @@ -1,4 +1,5 @@ #> The stone axe chops a tree by default +# @environment timber_test:group_5 # @template timber_test:arena # @timeout 200 # @skyaccess true diff --git a/tests/data/timber_test/test/settings_axe_wooden_chops.mcfunction b/tests/data/timber_test/test/settings_axe_wooden_chops.mcfunction index d5cd6ba..1c857f6 100644 --- a/tests/data/timber_test/test/settings_axe_wooden_chops.mcfunction +++ b/tests/data/timber_test/test/settings_axe_wooden_chops.mcfunction @@ -1,4 +1,5 @@ #> The wooden axe chops a tree by default +# @environment timber_test:group_1 # @template timber_test:arena # @timeout 200 # @skyaccess true diff --git a/tests/data/timber_test/test/settings_chop_down_off_keeps_logs_below.mcfunction b/tests/data/timber_test/test/settings_chop_down_off_keeps_logs_below.mcfunction index e40eaac..8b04470 100644 --- a/tests/data/timber_test/test/settings_chop_down_off_keeps_logs_below.mcfunction +++ b/tests/data/timber_test/test/settings_chop_down_off_keeps_logs_below.mcfunction @@ -1,4 +1,5 @@ #> By default only the part above the mined log is chopped: mining the third log keeps the two below +# @environment timber_test:group_2 # @template timber_test:arena # @timeout 200 # @skyaccess true diff --git a/tests/data/timber_test/test/settings_chop_fungi_default_off_crimson.mcfunction b/tests/data/timber_test/test/settings_chop_fungi_default_off_crimson.mcfunction index 2381f82..12c1e7b 100644 --- a/tests/data/timber_test/test/settings_chop_fungi_default_off_crimson.mcfunction +++ b/tests/data/timber_test/test/settings_chop_fungi_default_off_crimson.mcfunction @@ -1,4 +1,5 @@ #> By default (chop_fungi off) a crimson fungus is not chopped +# @environment timber_test:group_3 # @template timber_test:arena # @timeout 200 # @skyaccess true diff --git a/tests/data/timber_test/test/settings_chop_fungi_default_off_mushroom.mcfunction b/tests/data/timber_test/test/settings_chop_fungi_default_off_mushroom.mcfunction index 8e53f50..b92af78 100644 --- a/tests/data/timber_test/test/settings_chop_fungi_default_off_mushroom.mcfunction +++ b/tests/data/timber_test/test/settings_chop_fungi_default_off_mushroom.mcfunction @@ -1,4 +1,5 @@ #> By default (chop_fungi off) a huge mushroom is not chopped: the stem above the mined block stays +# @environment timber_test:group_4 # @template timber_test:arena # @timeout 200 # @skyaccess true diff --git a/tests/data/timber_test/test/settings_destroy_roots_default_removes_roots.mcfunction b/tests/data/timber_test/test/settings_destroy_roots_default_removes_roots.mcfunction index 2d13f10..e355484 100644 --- a/tests/data/timber_test/test/settings_destroy_roots_default_removes_roots.mcfunction +++ b/tests/data/timber_test/test/settings_destroy_roots_default_removes_roots.mcfunction @@ -1,4 +1,5 @@ #> By default chopping a mangrove also removes its roots +# @environment timber_test:group_5 # @template timber_test:arena # @timeout 200 # @skyaccess true diff --git a/tests/data/timber_test/test/settings_drop_loot_default_drops_logs_on_the_ground.mcfunction b/tests/data/timber_test/test/settings_drop_loot_default_drops_logs_on_the_ground.mcfunction new file mode 100644 index 0000000..3897779 --- /dev/null +++ b/tests/data/timber_test/test/settings_drop_loot_default_drops_logs_on_the_ground.mcfunction @@ -0,0 +1,26 @@ +#> By default the logs of a chopped tree drop on the ground, the inventory of the player stays empty of them +# @environment timber_test:group_1 +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:oak"} +assert block ~20 ~ ~20 #minecraft:logs + +await delay 1s +function timber_test:player {name:"loot_off_t",axe:"minecraft:iron_axe"} +function timber_test:mine {name:"loot_off_t"} + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the base log is gone: the dummy hit the tree +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~ ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +execute store success score #inv timber_test if items entity @e[type=minecraft:player,name=loot_off_t] container.* minecraft:oak_log +assert score #inv timber_test matches 0 +assert entity @e[type=minecraft:item,dx=39,dy=39,dz=39,nbt={Item:{id:"minecraft:oak_log"}}] diff --git a/tests/data/timber_test/test/settings_drop_loot_on_gives_logs_to_the_player.mcfunction b/tests/data/timber_test/test/settings_drop_loot_on_gives_logs_to_the_player.mcfunction new file mode 100644 index 0000000..9f9920a --- /dev/null +++ b/tests/data/timber_test/test/settings_drop_loot_on_gives_logs_to_the_player.mcfunction @@ -0,0 +1,27 @@ +#> With drop_loot on the logs of a chopped tree go into the inventory of the player, not on the ground +# @environment timber_test:drop_loot_on +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:oak"} +assert block ~20 ~ ~20 #minecraft:logs + +await delay 1s +function timber_test:player {name:"loot_on_t",axe:"minecraft:iron_axe"} +function timber_test:mine {name:"loot_on_t"} + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the base log is gone: the dummy hit the tree +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~ ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +# the logs of the chop go straight into the inventory (the drop of the block the dummy mined itself is moved to the dummy, but a dummy picks nothing up) +scoreboard objectives add timber_test dummy +execute store result score #inv timber_test run clear @e[type=minecraft:player,name=loot_on_t,limit=1] minecraft:oak_log 0 +assert score #inv timber_test matches 3.. diff --git a/tests/data/timber_test/test/settings_plant_sapling_default_keeps_the_item.mcfunction b/tests/data/timber_test/test/settings_plant_sapling_default_keeps_the_item.mcfunction new file mode 100644 index 0000000..d030587 --- /dev/null +++ b/tests/data/timber_test/test/settings_plant_sapling_default_keeps_the_item.mcfunction @@ -0,0 +1,13 @@ +#> By default a sapling item stays an item: nothing is planted +# @environment timber_test:group_2 +# @template timber_test:arena +# @timeout 400 +# @skyaccess true + +function timber_test:platform +summon minecraft:item ~10.5 ~0.5 ~10.5 {Item:{id:"minecraft:oak_sapling",count:1}} + +# longer than the 2 seconds of the loop, three times over +await delay 7s +assert entity @e[type=minecraft:item,dx=39,dy=39,dz=39,nbt={Item:{id:"minecraft:oak_sapling"}}] +assert block ~10 ~ ~10 minecraft:air diff --git a/tests/data/timber_test/test/settings_plant_sapling_on_plants_the_sapling.mcfunction b/tests/data/timber_test/test/settings_plant_sapling_on_plants_the_sapling.mcfunction new file mode 100644 index 0000000..daf0b2d --- /dev/null +++ b/tests/data/timber_test/test/settings_plant_sapling_on_plants_the_sapling.mcfunction @@ -0,0 +1,11 @@ +#> With plant_sapling on a sapling item that lies on the ground is planted (the loop checks every 2 seconds) +# @environment timber_test:plant_sapling_on +# @template timber_test:arena +# @timeout 400 +# @skyaccess true + +function timber_test:platform +summon minecraft:item ~10.5 ~0.5 ~10.5 {Item:{id:"minecraft:oak_sapling",count:1}} + +# the sapling item falls on the dirt, the loop runs every 2 seconds +await block ~10 ~ ~10 minecraft:oak_sapling diff --git a/tests/data/timber_test/test/settings_sneaking_default_no_chop.mcfunction b/tests/data/timber_test/test/settings_sneaking_default_no_chop.mcfunction index ea9ee3d..77bd6bc 100644 --- a/tests/data/timber_test/test/settings_sneaking_default_no_chop.mcfunction +++ b/tests/data/timber_test/test/settings_sneaking_default_no_chop.mcfunction @@ -1,4 +1,5 @@ #> By default (standing on, sneaking off) a sneaking player doesn't chop +# @environment timber_test:group_3 # @template timber_test:arena # @timeout 200 # @skyaccess true diff --git a/tests/data/timber_test/test/settings_start_chop_on_root_default_chops.mcfunction b/tests/data/timber_test/test/settings_start_chop_on_root_default_chops.mcfunction index 2ba137b..4847b40 100644 --- a/tests/data/timber_test/test/settings_start_chop_on_root_default_chops.mcfunction +++ b/tests/data/timber_test/test/settings_start_chop_on_root_default_chops.mcfunction @@ -1,4 +1,5 @@ #> By default mining a root chops the mangrove (start_chop_on_root is on) +# @environment timber_test:group_4 # @template timber_test:arena # @timeout 200 # @skyaccess true diff --git a/tests/data/timber_test/test/settings_stop_chopping_off_chops_the_tree_with_a_worn_axe.mcfunction b/tests/data/timber_test/test/settings_stop_chopping_off_chops_the_tree_with_a_worn_axe.mcfunction new file mode 100644 index 0000000..e702d5d --- /dev/null +++ b/tests/data/timber_test/test/settings_stop_chopping_off_chops_the_tree_with_a_worn_axe.mcfunction @@ -0,0 +1,26 @@ +#> By default (stop_chopping off) a tree is chopped completely even if the axe breaks on the way +# @environment timber_test:group_5 +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:oak"} +assert block ~20 ~ ~20 #minecraft:logs + +await delay 1s +function timber_test:count_d7 {test:"settings_stop_chopping_off_chops_the_tree_with_a_worn_axe"} +function timber_test:player {name:"worn_off_t",axe:"minecraft:iron_axe"} +item replace entity @e[type=minecraft:player,name=worn_off_t,limit=1] weapon.mainhand with minecraft:iron_axe[minecraft:damage=248] +function timber_test:mine {name:"worn_off_t"} + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the base log is gone: the dummy hit the tree +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~ ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_no_tree {test:"settings_stop_chopping_off_chops_the_tree_with_a_worn_axe"} diff --git a/tests/data/timber_test/test/settings_stop_chopping_on_stops_with_a_worn_axe.mcfunction b/tests/data/timber_test/test/settings_stop_chopping_on_stops_with_a_worn_axe.mcfunction new file mode 100644 index 0000000..b38f156 --- /dev/null +++ b/tests/data/timber_test/test/settings_stop_chopping_on_stops_with_a_worn_axe.mcfunction @@ -0,0 +1,27 @@ +#> With stop_chopping on a tree is chopped only as far as the axe lasts: logs stay +# @environment timber_test:stop_chopping_on +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +function timber_test:tree {feature:"minecraft:oak"} +assert block ~20 ~ ~20 #minecraft:logs + +await delay 1s +function timber_test:count_logs {test:"settings_stop_chopping_on_stops_with_a_worn_axe"} +function timber_test:player {name:"worn_on_t",axe:"minecraft:iron_axe"} +item replace entity @e[type=minecraft:player,name=worn_on_t,limit=1] weapon.mainhand with minecraft:iron_axe[minecraft:damage=248] +function timber_test:mine {name:"worn_on_t"} + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +# 1. the base log is gone: the dummy hit the tree +# 2. five ticks pass: the datapack reacts to the mined block in the next tick or two +# 3. no marker is left in the arena +await not block ~20 ~ ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +# the axe can take about two more blocks: the chop stops, some logs are left (a full oak has 4 to 6) +function timber_test:assert_logs_left {n:"1.."} diff --git a/tests/data/timber_test/test/settings_wear_out_default_damages_axe.mcfunction b/tests/data/timber_test/test/settings_wear_out_default_damages_axe.mcfunction index 0721743..91c1f75 100644 --- a/tests/data/timber_test/test/settings_wear_out_default_damages_axe.mcfunction +++ b/tests/data/timber_test/test/settings_wear_out_default_damages_axe.mcfunction @@ -1,4 +1,5 @@ #> By default chopping damages the axe more than the one block that was mined (vanilla takes 1) +# @environment timber_test:group_1 # @template timber_test:arena # @timeout 200 # @skyaccess true diff --git a/tests/data/timber_test/test_environment/drop_loot_on.json b/tests/data/timber_test/test_environment/drop_loot_on.json new file mode 100644 index 0000000..14fead6 --- /dev/null +++ b/tests/data/timber_test/test_environment/drop_loot_on.json @@ -0,0 +1,5 @@ +{ + "type": "minecraft:function", + "setup": "timber_test:env/drop_loot_on_setup", + "teardown": "timber_test:env/drop_loot_on_teardown" +} diff --git a/tests/data/timber_test/test_environment/group_1.json b/tests/data/timber_test/test_environment/group_1.json new file mode 100644 index 0000000..c1a66db --- /dev/null +++ b/tests/data/timber_test/test_environment/group_1.json @@ -0,0 +1,5 @@ +{ + "type": "minecraft:function", + "setup": "timber_test:env/group_setup", + "teardown": "timber_test:env/group_teardown" +} diff --git a/tests/data/timber_test/test_environment/group_2.json b/tests/data/timber_test/test_environment/group_2.json new file mode 100644 index 0000000..c1a66db --- /dev/null +++ b/tests/data/timber_test/test_environment/group_2.json @@ -0,0 +1,5 @@ +{ + "type": "minecraft:function", + "setup": "timber_test:env/group_setup", + "teardown": "timber_test:env/group_teardown" +} diff --git a/tests/data/timber_test/test_environment/group_3.json b/tests/data/timber_test/test_environment/group_3.json new file mode 100644 index 0000000..c1a66db --- /dev/null +++ b/tests/data/timber_test/test_environment/group_3.json @@ -0,0 +1,5 @@ +{ + "type": "minecraft:function", + "setup": "timber_test:env/group_setup", + "teardown": "timber_test:env/group_teardown" +} diff --git a/tests/data/timber_test/test_environment/group_4.json b/tests/data/timber_test/test_environment/group_4.json new file mode 100644 index 0000000..c1a66db --- /dev/null +++ b/tests/data/timber_test/test_environment/group_4.json @@ -0,0 +1,5 @@ +{ + "type": "minecraft:function", + "setup": "timber_test:env/group_setup", + "teardown": "timber_test:env/group_teardown" +} diff --git a/tests/data/timber_test/test_environment/group_5.json b/tests/data/timber_test/test_environment/group_5.json new file mode 100644 index 0000000..c1a66db --- /dev/null +++ b/tests/data/timber_test/test_environment/group_5.json @@ -0,0 +1,5 @@ +{ + "type": "minecraft:function", + "setup": "timber_test:env/group_setup", + "teardown": "timber_test:env/group_teardown" +} diff --git a/tests/data/timber_test/test_environment/plant_sapling_on.json b/tests/data/timber_test/test_environment/plant_sapling_on.json new file mode 100644 index 0000000..bc02358 --- /dev/null +++ b/tests/data/timber_test/test_environment/plant_sapling_on.json @@ -0,0 +1,5 @@ +{ + "type": "minecraft:function", + "setup": "timber_test:env/plant_sapling_on_setup", + "teardown": "timber_test:env/plant_sapling_on_teardown" +} diff --git a/tests/data/timber_test/test_environment/stop_chopping_on.json b/tests/data/timber_test/test_environment/stop_chopping_on.json new file mode 100644 index 0000000..a686e08 --- /dev/null +++ b/tests/data/timber_test/test_environment/stop_chopping_on.json @@ -0,0 +1,5 @@ +{ + "type": "minecraft:function", + "setup": "timber_test:env/stop_chopping_on_setup", + "teardown": "timber_test:env/stop_chopping_on_teardown" +} From af5d280e85d7421a1af8be1bae7e056d2a75e152 Mon Sep 17 00:00:00 2001 From: Flavio Felder Date: Mon, 21 Sep 2026 22:58:12 +0200 Subject: [PATCH 26/27] Test the settings menu: flips, guards, admins, sliders, player toggle All 23 flips switch their setting and back through dispatch as an admin. The options that exclude each other refuse the flip (slow chop and loot to inventory, stop on axe break and wear out), and every refusal is followed by the same flip working after the blocking setting is switched back, so it is the guard that refused it. Only a player with the tag timber_admin can change a setting. The four sliders set 1, 7, 25, 100 and 1000 (they come as 1 followed by the value). A player who switched Timber off with TimberToggle doesn't chop, switched on again chops. Each of the menu tests has an environment that puts all settings back. The controls without the admin tag, with a wrong value and without the toggle fail. Co-Authored-By: Claude Sonnet 5 --- .../function/env/flip_admin_setup.mcfunction | 1 + .../env/flip_admin_teardown.mcfunction | 2 + .../function/env/flip_guards_setup.mcfunction | 1 + .../env/flip_guards_teardown.mcfunction | 2 + .../env/flip_sliders_setup.mcfunction | 1 + .../env/flip_sliders_teardown.mcfunction | 2 + .../env/flip_toggles_setup.mcfunction | 1 + .../env/flip_toggles_teardown.mcfunction | 2 + .../env/restore_all_defaults.mcfunction | 30 +++ ...ttings_menu_flips_every_setting.mcfunction | 214 ++++++++++++++++++ ...guards_refuse_conflicting_flips.mcfunction | 51 +++++ ...tings_menu_only_admins_can_flip.mcfunction | 25 ++ ...gs_menu_sliders_set_their_value.mcfunction | 75 ++++++ ...toggle_off_player_does_not_chop.mcfunction | 26 +++ ...toggle_twice_player_chops_again.mcfunction | 28 +++ .../test_environment/flip_admin.json | 5 + .../test_environment/flip_guards.json | 5 + .../test_environment/flip_sliders.json | 5 + .../test_environment/flip_toggles.json | 5 + 19 files changed, 481 insertions(+) create mode 100644 tests/data/timber_test/function/env/flip_admin_setup.mcfunction create mode 100644 tests/data/timber_test/function/env/flip_admin_teardown.mcfunction create mode 100644 tests/data/timber_test/function/env/flip_guards_setup.mcfunction create mode 100644 tests/data/timber_test/function/env/flip_guards_teardown.mcfunction create mode 100644 tests/data/timber_test/function/env/flip_sliders_setup.mcfunction create mode 100644 tests/data/timber_test/function/env/flip_sliders_teardown.mcfunction create mode 100644 tests/data/timber_test/function/env/flip_toggles_setup.mcfunction create mode 100644 tests/data/timber_test/function/env/flip_toggles_teardown.mcfunction create mode 100644 tests/data/timber_test/function/env/restore_all_defaults.mcfunction create mode 100644 tests/data/timber_test/test/settings_menu_flips_every_setting.mcfunction create mode 100644 tests/data/timber_test/test/settings_menu_guards_refuse_conflicting_flips.mcfunction create mode 100644 tests/data/timber_test/test/settings_menu_only_admins_can_flip.mcfunction create mode 100644 tests/data/timber_test/test/settings_menu_sliders_set_their_value.mcfunction create mode 100644 tests/data/timber_test/test/settings_toggle_off_player_does_not_chop.mcfunction create mode 100644 tests/data/timber_test/test/settings_toggle_twice_player_chops_again.mcfunction create mode 100644 tests/data/timber_test/test_environment/flip_admin.json create mode 100644 tests/data/timber_test/test_environment/flip_guards.json create mode 100644 tests/data/timber_test/test_environment/flip_sliders.json create mode 100644 tests/data/timber_test/test_environment/flip_toggles.json diff --git a/tests/data/timber_test/function/env/flip_admin_setup.mcfunction b/tests/data/timber_test/function/env/flip_admin_setup.mcfunction new file mode 100644 index 0000000..9194352 --- /dev/null +++ b/tests/data/timber_test/function/env/flip_admin_setup.mcfunction @@ -0,0 +1 @@ +#> Nothing to set, the test changes the settings itself. This environment only keeps it apart from the others. diff --git a/tests/data/timber_test/function/env/flip_admin_teardown.mcfunction b/tests/data/timber_test/function/env/flip_admin_teardown.mcfunction new file mode 100644 index 0000000..5a6d3b1 --- /dev/null +++ b/tests/data/timber_test/function/env/flip_admin_teardown.mcfunction @@ -0,0 +1,2 @@ +#> The test flips settings, put them all back +function timber_test:env/restore_all_defaults diff --git a/tests/data/timber_test/function/env/flip_guards_setup.mcfunction b/tests/data/timber_test/function/env/flip_guards_setup.mcfunction new file mode 100644 index 0000000..9194352 --- /dev/null +++ b/tests/data/timber_test/function/env/flip_guards_setup.mcfunction @@ -0,0 +1 @@ +#> Nothing to set, the test changes the settings itself. This environment only keeps it apart from the others. diff --git a/tests/data/timber_test/function/env/flip_guards_teardown.mcfunction b/tests/data/timber_test/function/env/flip_guards_teardown.mcfunction new file mode 100644 index 0000000..5a6d3b1 --- /dev/null +++ b/tests/data/timber_test/function/env/flip_guards_teardown.mcfunction @@ -0,0 +1,2 @@ +#> The test flips settings, put them all back +function timber_test:env/restore_all_defaults diff --git a/tests/data/timber_test/function/env/flip_sliders_setup.mcfunction b/tests/data/timber_test/function/env/flip_sliders_setup.mcfunction new file mode 100644 index 0000000..9194352 --- /dev/null +++ b/tests/data/timber_test/function/env/flip_sliders_setup.mcfunction @@ -0,0 +1 @@ +#> Nothing to set, the test changes the settings itself. This environment only keeps it apart from the others. diff --git a/tests/data/timber_test/function/env/flip_sliders_teardown.mcfunction b/tests/data/timber_test/function/env/flip_sliders_teardown.mcfunction new file mode 100644 index 0000000..5a6d3b1 --- /dev/null +++ b/tests/data/timber_test/function/env/flip_sliders_teardown.mcfunction @@ -0,0 +1,2 @@ +#> The test flips settings, put them all back +function timber_test:env/restore_all_defaults diff --git a/tests/data/timber_test/function/env/flip_toggles_setup.mcfunction b/tests/data/timber_test/function/env/flip_toggles_setup.mcfunction new file mode 100644 index 0000000..9194352 --- /dev/null +++ b/tests/data/timber_test/function/env/flip_toggles_setup.mcfunction @@ -0,0 +1 @@ +#> Nothing to set, the test changes the settings itself. This environment only keeps it apart from the others. diff --git a/tests/data/timber_test/function/env/flip_toggles_teardown.mcfunction b/tests/data/timber_test/function/env/flip_toggles_teardown.mcfunction new file mode 100644 index 0000000..5a6d3b1 --- /dev/null +++ b/tests/data/timber_test/function/env/flip_toggles_teardown.mcfunction @@ -0,0 +1,2 @@ +#> The test flips settings, put them all back +function timber_test:env/restore_all_defaults diff --git a/tests/data/timber_test/function/env/restore_all_defaults.mcfunction b/tests/data/timber_test/function/env/restore_all_defaults.mcfunction new file mode 100644 index 0000000..c5acfcb --- /dev/null +++ b/tests/data/timber_test/function/env/restore_all_defaults.mcfunction @@ -0,0 +1,30 @@ +#> Puts every setting back to its default, and clears the loops that a setting flip may have scheduled (they would run in the next batch) +scoreboard players set standing timber 1 +scoreboard players set sneaking timber 0 +scoreboard players set destroy_leaves timber 1 +scoreboard players set plant_sapling timber 0 +scoreboard players set destroy_roots timber 1 +scoreboard players set start_chop_on_root timber 1 +scoreboard players set slow_chop timber 0 +scoreboard players set chop_down timber 0 +scoreboard players set stop_chopping timber 0 +scoreboard players set wear_out timber 1 +scoreboard players set drop_loot timber 0 +scoreboard players set hunger timber 0 +scoreboard players set stopsound timber 0 +scoreboard players set wooden_axe timber 1 +scoreboard players set stone_axe timber 1 +scoreboard players set copper_axe timber 1 +scoreboard players set iron_axe timber 1 +scoreboard players set golden_axe timber 1 +scoreboard players set diamond_axe timber 1 +scoreboard players set netherite_axe timber 1 +scoreboard players set chop_trees timber 1 +scoreboard players set chop_fungi timber 0 +scoreboard players set persistent timber 0 +scoreboard players set blocks_per_chop timber 1 +scoreboard players set time_between_chops timber 1 +scoreboard players set max_tree_size timber 555 +scoreboard players set min_leaves_found timber 5 +schedule clear timber:slow_chop/loop +schedule clear timber:autoplant/loop diff --git a/tests/data/timber_test/test/settings_menu_flips_every_setting.mcfunction b/tests/data/timber_test/test/settings_menu_flips_every_setting.mcfunction new file mode 100644 index 0000000..74b46a0 --- /dev/null +++ b/tests/data/timber_test/test/settings_menu_flips_every_setting.mcfunction @@ -0,0 +1,214 @@ +#> Every setting of the menu can be flipped by a Timber admin and flipped back +# @environment timber_test:flip_toggles +# @timeout 100 + +scoreboard objectives add timber_test dummy +dummy adm_t spawn +tag @e[type=minecraft:player,name=adm_t] add timber_admin + +# standing: default 1 +assert score standing timber matches 1 +scoreboard players set #req timber 100 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score standing timber matches 0 +scoreboard players set #req timber 100 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score standing timber matches 1 + +# sneaking: default 0 +assert score sneaking timber matches 0 +scoreboard players set #req timber 101 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score sneaking timber matches 1 +scoreboard players set #req timber 101 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score sneaking timber matches 0 + +# destroy_leaves: default 1 +assert score destroy_leaves timber matches 1 +scoreboard players set #req timber 102 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score destroy_leaves timber matches 0 +scoreboard players set #req timber 102 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score destroy_leaves timber matches 1 + +# plant_sapling: default 0 +assert score plant_sapling timber matches 0 +scoreboard players set #req timber 103 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score plant_sapling timber matches 1 +scoreboard players set #req timber 103 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score plant_sapling timber matches 0 + +# destroy_roots: default 1 +assert score destroy_roots timber matches 1 +scoreboard players set #req timber 104 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score destroy_roots timber matches 0 +scoreboard players set #req timber 104 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score destroy_roots timber matches 1 + +# start_chop_on_root: default 1 +assert score start_chop_on_root timber matches 1 +scoreboard players set #req timber 105 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score start_chop_on_root timber matches 0 +scoreboard players set #req timber 105 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score start_chop_on_root timber matches 1 + +# slow_chop: default 0 +assert score slow_chop timber matches 0 +scoreboard players set #req timber 106 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score slow_chop timber matches 1 +scoreboard players set #req timber 106 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score slow_chop timber matches 0 + +# chop_down: default 0 +assert score chop_down timber matches 0 +scoreboard players set #req timber 107 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score chop_down timber matches 1 +scoreboard players set #req timber 107 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score chop_down timber matches 0 + +# stop_chopping: default 0 +assert score stop_chopping timber matches 0 +scoreboard players set #req timber 108 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score stop_chopping timber matches 1 +scoreboard players set #req timber 108 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score stop_chopping timber matches 0 + +# wear_out: default 1 +assert score wear_out timber matches 1 +scoreboard players set #req timber 109 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score wear_out timber matches 0 +scoreboard players set #req timber 109 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score wear_out timber matches 1 + +# drop_loot: default 0 +assert score drop_loot timber matches 0 +scoreboard players set #req timber 110 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score drop_loot timber matches 1 +scoreboard players set #req timber 110 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score drop_loot timber matches 0 + +# hunger: default 0 +assert score hunger timber matches 0 +scoreboard players set #req timber 111 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score hunger timber matches 1 +scoreboard players set #req timber 111 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score hunger timber matches 0 + +# stopsound: default 0 +assert score stopsound timber matches 0 +scoreboard players set #req timber 112 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score stopsound timber matches 1 +scoreboard players set #req timber 112 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score stopsound timber matches 0 + +# chop_trees: default 1 +assert score chop_trees timber matches 1 +scoreboard players set #req timber 113 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score chop_trees timber matches 0 +scoreboard players set #req timber 113 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score chop_trees timber matches 1 + +# chop_fungi: default 0 +assert score chop_fungi timber matches 0 +scoreboard players set #req timber 114 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score chop_fungi timber matches 1 +scoreboard players set #req timber 114 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score chop_fungi timber matches 0 + +# persistent: default 0 +assert score persistent timber matches 0 +scoreboard players set #req timber 115 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score persistent timber matches 1 +scoreboard players set #req timber 115 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score persistent timber matches 0 + +# wooden_axe: default 1 +assert score wooden_axe timber matches 1 +scoreboard players set #req timber 116 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score wooden_axe timber matches 0 +scoreboard players set #req timber 116 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score wooden_axe timber matches 1 + +# golden_axe: default 1 +assert score golden_axe timber matches 1 +scoreboard players set #req timber 117 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score golden_axe timber matches 0 +scoreboard players set #req timber 117 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score golden_axe timber matches 1 + +# stone_axe: default 1 +assert score stone_axe timber matches 1 +scoreboard players set #req timber 118 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score stone_axe timber matches 0 +scoreboard players set #req timber 118 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score stone_axe timber matches 1 + +# copper_axe: default 1 +assert score copper_axe timber matches 1 +scoreboard players set #req timber 119 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score copper_axe timber matches 0 +scoreboard players set #req timber 119 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score copper_axe timber matches 1 + +# iron_axe: default 1 +assert score iron_axe timber matches 1 +scoreboard players set #req timber 120 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score iron_axe timber matches 0 +scoreboard players set #req timber 120 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score iron_axe timber matches 1 + +# diamond_axe: default 1 +assert score diamond_axe timber matches 1 +scoreboard players set #req timber 121 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score diamond_axe timber matches 0 +scoreboard players set #req timber 121 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score diamond_axe timber matches 1 + +# netherite_axe: default 1 +assert score netherite_axe timber matches 1 +scoreboard players set #req timber 122 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score netherite_axe timber matches 0 +scoreboard players set #req timber 122 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score netherite_axe timber matches 1 diff --git a/tests/data/timber_test/test/settings_menu_guards_refuse_conflicting_flips.mcfunction b/tests/data/timber_test/test/settings_menu_guards_refuse_conflicting_flips.mcfunction new file mode 100644 index 0000000..fa0adf3 --- /dev/null +++ b/tests/data/timber_test/test/settings_menu_guards_refuse_conflicting_flips.mcfunction @@ -0,0 +1,51 @@ +#> Flips that would break another setting are refused (slow chop and loot to inventory, stop on axe break and wear out) +# @environment timber_test:flip_guards +# @timeout 100 + +scoreboard objectives add timber_test dummy +dummy adm_t spawn +tag @e[type=minecraft:player,name=adm_t] add timber_admin + +# slow chop is on: loot to inventory can't be switched on, without slow chop it can +scoreboard players set slow_chop timber 1 +scoreboard players set #req timber 110 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score drop_loot timber matches 0 +scoreboard players set slow_chop timber 0 +scoreboard players set #req timber 110 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score drop_loot timber matches 1 +scoreboard players set drop_loot timber 0 + +# loot to inventory is on: slow chop can't be switched on, without it can +scoreboard players set drop_loot timber 1 +scoreboard players set #req timber 106 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score slow_chop timber matches 0 +scoreboard players set drop_loot timber 0 +scoreboard players set #req timber 106 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score slow_chop timber matches 1 +scoreboard players set slow_chop timber 0 + +# stop on axe break is on: wear out can't be switched off, without it can +scoreboard players set stop_chopping timber 1 +scoreboard players set #req timber 109 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score wear_out timber matches 1 +scoreboard players set stop_chopping timber 0 +scoreboard players set #req timber 109 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score wear_out timber matches 0 +scoreboard players set wear_out timber 1 + +# wear out is off: stop on axe break can't be switched on, with wear out on it can +scoreboard players set wear_out timber 0 +scoreboard players set #req timber 108 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score stop_chopping timber matches 0 +scoreboard players set wear_out timber 1 +scoreboard players set #req timber 108 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score stop_chopping timber matches 1 +scoreboard players set stop_chopping timber 0 diff --git a/tests/data/timber_test/test/settings_menu_only_admins_can_flip.mcfunction b/tests/data/timber_test/test/settings_menu_only_admins_can_flip.mcfunction new file mode 100644 index 0000000..7b27cef --- /dev/null +++ b/tests/data/timber_test/test/settings_menu_only_admins_can_flip.mcfunction @@ -0,0 +1,25 @@ +#> Only a player with the tag timber_admin can change a setting +# @environment timber_test:flip_admin +# @timeout 100 + +scoreboard objectives add timber_test dummy +dummy adm_t spawn +tag @e[type=minecraft:player,name=adm_t] add timber_admin +dummy user_t spawn + +# a player without the tag: nothing changes +assert score sneaking timber matches 0 +scoreboard players set #req timber 101 +execute as @e[type=minecraft:player,name=user_t,limit=1] run function timber:settings/dispatch +assert score sneaking timber matches 0 + +# an admin can +scoreboard players set #req timber 101 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score sneaking timber matches 1 + +# removing the tag takes it away again +tag @e[type=minecraft:player,name=adm_t] remove timber_admin +scoreboard players set #req timber 101 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/dispatch +assert score sneaking timber matches 1 diff --git a/tests/data/timber_test/test/settings_menu_sliders_set_their_value.mcfunction b/tests/data/timber_test/test/settings_menu_sliders_set_their_value.mcfunction new file mode 100644 index 0000000..ac96b2b --- /dev/null +++ b/tests/data/timber_test/test/settings_menu_sliders_set_their_value.mcfunction @@ -0,0 +1,75 @@ +#> The sliders of the menu set their value: 1 followed by the value (7 comes as 17, 25 as 125, 100 as 1100) +# @environment timber_test:flip_sliders +# @timeout 100 + +scoreboard objectives add timber_test dummy +dummy adm_t spawn +tag @e[type=minecraft:player,name=adm_t] add timber_admin + +# slider blocks sets blocks_per_chop +scoreboard players set adm_t TimberSetBlocks 11 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/apply/blocks +assert score blocks_per_chop timber matches 1 +scoreboard players set adm_t TimberSetBlocks 17 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/apply/blocks +assert score blocks_per_chop timber matches 7 +scoreboard players set adm_t TimberSetBlocks 125 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/apply/blocks +assert score blocks_per_chop timber matches 25 +scoreboard players set adm_t TimberSetBlocks 1100 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/apply/blocks +assert score blocks_per_chop timber matches 100 +scoreboard players set adm_t TimberSetBlocks 11000 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/apply/blocks +assert score blocks_per_chop timber matches 1000 + +# slider ticks sets time_between_chops +scoreboard players set adm_t TimberSetTicks 11 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/apply/ticks +assert score time_between_chops timber matches 1 +scoreboard players set adm_t TimberSetTicks 17 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/apply/ticks +assert score time_between_chops timber matches 7 +scoreboard players set adm_t TimberSetTicks 125 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/apply/ticks +assert score time_between_chops timber matches 25 +scoreboard players set adm_t TimberSetTicks 1100 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/apply/ticks +assert score time_between_chops timber matches 100 +scoreboard players set adm_t TimberSetTicks 11000 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/apply/ticks +assert score time_between_chops timber matches 1000 + +# slider treesize sets max_tree_size +scoreboard players set adm_t TimberSetTreeSize 11 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/apply/treesize +assert score max_tree_size timber matches 1 +scoreboard players set adm_t TimberSetTreeSize 17 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/apply/treesize +assert score max_tree_size timber matches 7 +scoreboard players set adm_t TimberSetTreeSize 125 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/apply/treesize +assert score max_tree_size timber matches 25 +scoreboard players set adm_t TimberSetTreeSize 1100 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/apply/treesize +assert score max_tree_size timber matches 100 +scoreboard players set adm_t TimberSetTreeSize 11000 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/apply/treesize +assert score max_tree_size timber matches 1000 + +# slider leaves sets min_leaves_found +scoreboard players set adm_t TimberSetLeaves 11 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/apply/leaves +assert score min_leaves_found timber matches 1 +scoreboard players set adm_t TimberSetLeaves 17 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/apply/leaves +assert score min_leaves_found timber matches 7 +scoreboard players set adm_t TimberSetLeaves 125 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/apply/leaves +assert score min_leaves_found timber matches 25 +scoreboard players set adm_t TimberSetLeaves 1100 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/apply/leaves +assert score min_leaves_found timber matches 100 +scoreboard players set adm_t TimberSetLeaves 11000 +execute as @e[type=minecraft:player,name=adm_t,limit=1] run function timber:settings/apply/leaves +assert score min_leaves_found timber matches 1000 diff --git a/tests/data/timber_test/test/settings_toggle_off_player_does_not_chop.mcfunction b/tests/data/timber_test/test/settings_toggle_off_player_does_not_chop.mcfunction new file mode 100644 index 0000000..010a4a2 --- /dev/null +++ b/tests/data/timber_test/test/settings_toggle_off_player_does_not_chop.mcfunction @@ -0,0 +1,26 @@ +#> A player who switched Timber off with the TimberToggle trigger doesn't chop +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +scoreboard objectives add timber_test dummy +function timber_test:platform +function timber_test:tree {feature:"minecraft:oak"} +assert block ~20 ~ ~20 #minecraft:logs + +await delay 1s +function timber_test:count_logs {test:"settings_toggle_off_player_does_not_chop"} +function timber_test:player {name:"toggle_off_t",axe:"minecraft:iron_axe"} +# the datapack runs every tick and handles the trigger, the score timber_disabled says that the player is switched off +scoreboard players set toggle_off_t TimberToggle 1 +await score toggle_off_t timber_disabled matches 1 + +function timber_test:mine {name:"toggle_off_t"} + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +await not block ~20 ~ ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_only_mined_gone {test:"settings_toggle_off_player_does_not_chop"} diff --git a/tests/data/timber_test/test/settings_toggle_twice_player_chops_again.mcfunction b/tests/data/timber_test/test/settings_toggle_twice_player_chops_again.mcfunction new file mode 100644 index 0000000..c3e30a4 --- /dev/null +++ b/tests/data/timber_test/test/settings_toggle_twice_player_chops_again.mcfunction @@ -0,0 +1,28 @@ +#> A player who switched Timber off and on again with the TimberToggle trigger chops +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +scoreboard objectives add timber_test dummy +function timber_test:platform +function timber_test:tree {feature:"minecraft:oak"} +assert block ~20 ~ ~20 #minecraft:logs + +await delay 1s +function timber_test:count_d7 {test:"settings_toggle_twice_player_chops_again"} +function timber_test:player {name:"toggle_on_t",axe:"minecraft:iron_axe"} +scoreboard players set toggle_on_t TimberToggle 1 +await score toggle_on_t timber_disabled matches 1 +# the trigger has to be enabled again before the next use, the datapack does that every tick +scoreboard players set toggle_on_t TimberToggle 1 +await score toggle_on_t timber_disabled matches 0 + +function timber_test:mine {name:"toggle_on_t"} + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +await not block ~20 ~ ~20 #minecraft:logs +await delay 5t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_no_tree {test:"settings_toggle_twice_player_chops_again"} diff --git a/tests/data/timber_test/test_environment/flip_admin.json b/tests/data/timber_test/test_environment/flip_admin.json new file mode 100644 index 0000000..a094d55 --- /dev/null +++ b/tests/data/timber_test/test_environment/flip_admin.json @@ -0,0 +1,5 @@ +{ + "type": "minecraft:function", + "setup": "timber_test:env/flip_admin_setup", + "teardown": "timber_test:env/flip_admin_teardown" +} diff --git a/tests/data/timber_test/test_environment/flip_guards.json b/tests/data/timber_test/test_environment/flip_guards.json new file mode 100644 index 0000000..49b2983 --- /dev/null +++ b/tests/data/timber_test/test_environment/flip_guards.json @@ -0,0 +1,5 @@ +{ + "type": "minecraft:function", + "setup": "timber_test:env/flip_guards_setup", + "teardown": "timber_test:env/flip_guards_teardown" +} diff --git a/tests/data/timber_test/test_environment/flip_sliders.json b/tests/data/timber_test/test_environment/flip_sliders.json new file mode 100644 index 0000000..28ecc52 --- /dev/null +++ b/tests/data/timber_test/test_environment/flip_sliders.json @@ -0,0 +1,5 @@ +{ + "type": "minecraft:function", + "setup": "timber_test:env/flip_sliders_setup", + "teardown": "timber_test:env/flip_sliders_teardown" +} diff --git a/tests/data/timber_test/test_environment/flip_toggles.json b/tests/data/timber_test/test_environment/flip_toggles.json new file mode 100644 index 0000000..2115dff --- /dev/null +++ b/tests/data/timber_test/test_environment/flip_toggles.json @@ -0,0 +1,5 @@ +{ + "type": "minecraft:function", + "setup": "timber_test:env/flip_toggles_setup", + "teardown": "timber_test:env/flip_toggles_teardown" +} From 9fd035486f9201aa443557ccb1008087b53e3e37 Mon Sep 17 00:00:00 2001 From: Flavio Felder Date: Mon, 21 Sep 2026 23:21:46 +0200 Subject: [PATCH 27/27] Test that uninstall removes every scoreboard and trigger of the datapack A script (.github/scripts/gen-uninstall-check.py) reads every function of the datapack, collects the "scoreboard objectives add" and generates one check per objective. The test runs timber:uninstall and then the generated check, so a scoreboard or trigger that is added to init and forgotten in uninstall makes it fail with the names. The workflow runs the script before the tests. The teardown of the environment brings the datapack back with init and main. The script also warns about what a function can't look at: scheduled functions and storages that uninstall doesn't clear (today six schedules and three storages). fail ends the function it is in, so the check collects the names in a list and reports them at the end. A second test shows that with slow chop on, leaves placed by a player still don't make a log stack a tree: it fails with the old leaf predicate. Co-Authored-By: Claude Sonnet 5 --- .github/scripts/gen-uninstall-check.py | 73 +++++++++++++++++++ .github/workflows/test.yml | 4 + .../function/env/uninstall_setup.mcfunction | 1 + .../env/uninstall_teardown.mcfunction | 3 + ...op_ignores_player_placed_leaves.mcfunction | 27 +++++++ ...install_removes_all_scoreboards.mcfunction | 8 ++ .../test_environment/uninstall.json | 5 ++ 7 files changed, 121 insertions(+) create mode 100644 .github/scripts/gen-uninstall-check.py create mode 100644 tests/data/timber_test/function/env/uninstall_setup.mcfunction create mode 100644 tests/data/timber_test/function/env/uninstall_teardown.mcfunction create mode 100644 tests/data/timber_test/test/settings_slow_chop_ignores_player_placed_leaves.mcfunction create mode 100644 tests/data/timber_test/test/uninstall_removes_all_scoreboards.mcfunction create mode 100644 tests/data/timber_test/test_environment/uninstall.json diff --git a/.github/scripts/gen-uninstall-check.py b/.github/scripts/gen-uninstall-check.py new file mode 100644 index 0000000..41a2cf9 --- /dev/null +++ b/.github/scripts/gen-uninstall-check.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +"""Generates the checks of the uninstall test from the code of the datapack, so nobody has to keep a list. + +Usage: gen-uninstall-check.py + +Writes /data/timber_test/function/generated/uninstall_objectives.mcfunction: +- one check per scoreboard objective (triggers are objectives) that any function of the datapack creates with + "scoreboard objectives add": after timber:uninstall the objective must not exist any more. A new objective that is added + to init and forgotten in uninstall makes the test fail, with its name. +- a TIMBER-WARNING line for everything that can't be looked at from a function, but that uninstall should clean up too: + scheduled functions (schedule function) and storages, compared with the schedule clear / data remove storage of uninstall. +""" +import os +import re +import sys + +repo, dest = sys.argv[1], sys.argv[2] + +# 121/ holds the overlay of the newest pack format, both are scanned +folders = [os.path.join(repo, "data", "timber", "function"), os.path.join(repo, "121", "data", "timber", "function")] +uninstall_text = "" +objectives, schedules, storages = set(), set(), set() +for folder in folders: + for root, _, files in os.walk(folder): + for name in files: + if not name.endswith(".mcfunction"): + continue + path = os.path.join(root, name) + text = open(path, encoding="utf-8", errors="replace").read() + if os.path.relpath(path, folder).replace(os.sep, "/") == "uninstall.mcfunction": + uninstall_text += text + "\n" + continue + objectives.update(re.findall(r"scoreboard objectives add (\S+)", text)) + schedules.update(re.findall(r"schedule function (timber:[\w/]+)", text)) + storages.update(re.findall(r"storage (timber:\w+)", text)) + +if not objectives: + sys.exit("gen-uninstall-check: no 'scoreboard objectives add' found in " + str(folders) + ", is the path right?") + +lines = [ + "#> GENERATED by .github/scripts/gen-uninstall-check.py from the code of the datapack, do not edit.", + "#> Checks that no scoreboard or trigger of the datapack exists after timber:uninstall.", + "scoreboard objectives add timber_test dummy", + "scoreboard players set #left timber_test 0", + "data modify storage timber_test:uninstall left set value []", +] +for name in sorted(objectives): + # adding 0 to a holder works when the objective exists and fails when it doesn't. + # The names go into a list, a fail in the loop would end this function at the first one. + lines += [ + f"execute store success score #exists timber_test run scoreboard players add #uninstall_probe {name} 0", + f'execute if score #exists timber_test matches 1 run data modify storage timber_test:uninstall left append value "{name}"', + f"execute if score #exists timber_test matches 1 run say TIMBER-ERROR uninstall: the scoreboard {name} still exists", + f"execute if score #exists timber_test matches 1 run scoreboard players add #left timber_test 1", + f"execute if score #exists timber_test matches 1 run scoreboard players reset #uninstall_probe {name}", + ] + +cleared_schedules = set(re.findall(r"schedule clear (timber:[\w/]+)", uninstall_text)) +for target in sorted(schedules - cleared_schedules): + lines.append(f"say TIMBER-WARNING uninstall: {target} is scheduled somewhere but uninstall doesn't clear it (schedule clear)") +removed_storages = set(re.findall(r"data remove storage (timber:\w+)", uninstall_text)) +for target in sorted(storages - removed_storages): + lines.append(f"say TIMBER-WARNING uninstall: storage {target} is used but uninstall doesn't remove it (data remove storage)") + +lines.append('execute unless score #left timber_test matches 0 run fail [{"text":"still exist after uninstall: "},{"nbt":"left","storage":"timber_test:uninstall"}]') +# (no assert after the fail: it would replace the message with "Expected #left ...", without the names) + +out = os.path.join(dest, "data", "timber_test", "function", "generated") +os.makedirs(out, exist_ok=True) +with open(os.path.join(out, "uninstall_objectives.mcfunction"), "w", newline="\n") as f: + f.write("\n".join(lines) + "\n") +print(f"gen-uninstall-check: {len(objectives)} objectives, {len(schedules - cleared_schedules)} schedules and " + f"{len(storages - removed_storages)} storages that uninstall leaves") diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0b428f9..8048f86 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -34,6 +34,10 @@ jobs: cp -r data pack.mcmeta 121 world/datapacks/timber/ cp -r tests/. world/datapacks/timber_test/ + # the uninstall test checks the scoreboards the code of the datapack creates, so a forgotten one can't slip through + - name: Generate the uninstall check + run: python3 .github/scripts/gen-uninstall-check.py . world/datapacks/timber_test + - name: Run tests run: | set -o pipefail diff --git a/tests/data/timber_test/function/env/uninstall_setup.mcfunction b/tests/data/timber_test/function/env/uninstall_setup.mcfunction new file mode 100644 index 0000000..d03468e --- /dev/null +++ b/tests/data/timber_test/function/env/uninstall_setup.mcfunction @@ -0,0 +1 @@ +#> Nothing to set up, the test uninstalls the datapack itself. The environment keeps it apart from the other tests. diff --git a/tests/data/timber_test/function/env/uninstall_teardown.mcfunction b/tests/data/timber_test/function/env/uninstall_teardown.mcfunction new file mode 100644 index 0000000..2d69a80 --- /dev/null +++ b/tests/data/timber_test/function/env/uninstall_teardown.mcfunction @@ -0,0 +1,3 @@ +#> The test uninstalled the datapack: bring it back the way a reload does (load.json runs init and main), the other tests need it +function timber:init +function timber:main diff --git a/tests/data/timber_test/test/settings_slow_chop_ignores_player_placed_leaves.mcfunction b/tests/data/timber_test/test/settings_slow_chop_ignores_player_placed_leaves.mcfunction new file mode 100644 index 0000000..f676993 --- /dev/null +++ b/tests/data/timber_test/test/settings_slow_chop_ignores_player_placed_leaves.mcfunction @@ -0,0 +1,27 @@ +#> With slow chop on, leaves placed by a player still don't make a log stack a tree (the leaves are checked when the chop starts, as the player, not in the slow loop) +# @environment timber_test:slow_chop_on +# @template timber_test:arena +# @timeout 200 +# @skyaccess true + +function timber_test:platform +# a column of 6 logs with 5 leaves placed by a player (persistent=true) +fill ~20 ~0 ~20 ~20 ~5 ~20 minecraft:oak_log +setblock ~21 ~3 ~20 minecraft:oak_leaves[distance=1,persistent=true] +setblock ~19 ~3 ~20 minecraft:oak_leaves[distance=1,persistent=true] +setblock ~20 ~3 ~21 minecraft:oak_leaves[distance=1,persistent=true] +setblock ~20 ~3 ~19 minecraft:oak_leaves[distance=1,persistent=true] +setblock ~21 ~4 ~20 minecraft:oak_leaves[distance=1,persistent=true] + +await delay 1s +function timber_test:count_logs {test:"settings_slow_chop_ignores_player_placed_leaves"} +function timber_test:player {name:"slow_placed_t",axe:"minecraft:iron_axe"} +function timber_test:mine {name:"slow_placed_t"} + +# wait until the datapack has had its chance to act +# (await doesn't work inside a called function, so this block is repeated in every test) +await not block ~20 ~ ~20 #minecraft:logs +await delay 10t +await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39] + +function timber_test:assert_only_mined_gone {test:"settings_slow_chop_ignores_player_placed_leaves"} diff --git a/tests/data/timber_test/test/uninstall_removes_all_scoreboards.mcfunction b/tests/data/timber_test/test/uninstall_removes_all_scoreboards.mcfunction new file mode 100644 index 0000000..4cb5560 --- /dev/null +++ b/tests/data/timber_test/test/uninstall_removes_all_scoreboards.mcfunction @@ -0,0 +1,8 @@ +#> After timber:uninstall none of the scoreboards and triggers of the datapack is left +# @environment timber_test:uninstall +# @timeout 100 + +# The checks are generated from the code of the datapack by .github/scripts/gen-uninstall-check.py (the workflow runs it before the tests): +# one per "scoreboard objectives add" in any function. So a scoreboard or trigger that is added and forgotten in uninstall.mcfunction makes this fail. +function timber:uninstall +function timber_test:generated/uninstall_objectives diff --git a/tests/data/timber_test/test_environment/uninstall.json b/tests/data/timber_test/test_environment/uninstall.json new file mode 100644 index 0000000..d54f575 --- /dev/null +++ b/tests/data/timber_test/test_environment/uninstall.json @@ -0,0 +1,5 @@ +{ + "type": "minecraft:function", + "setup": "timber_test:env/uninstall_setup", + "teardown": "timber_test:env/uninstall_teardown" +}