From aa74a55a1938f4dfffb2aa85cce36e4b3ed1d1f6 Mon Sep 17 00:00:00 2001 From: p2r3 Date: Sat, 23 Aug 2025 23:04:45 +0300 Subject: [PATCH] implement growing saplings with bone meal --- include/structures.h | 6 +++++ src/procedures.c | 52 +++++++++++++++++++++++++++++------------ src/structures.c | 55 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 15 deletions(-) create mode 100644 include/structures.h create mode 100644 src/structures.c diff --git a/include/structures.h b/include/structures.h new file mode 100644 index 0000000..8eca4eb --- /dev/null +++ b/include/structures.h @@ -0,0 +1,6 @@ +#ifndef H_STRUCTURES +#define H_STRUCTURES + +void placeTreeStructure (short x, uint8_t y, short z); + +#endif diff --git a/src/procedures.c b/src/procedures.c index 5cc87fd..008cb04 100644 --- a/src/procedures.c +++ b/src/procedures.c @@ -7,6 +7,8 @@ #include "packets.h" #include "registries.h" #include "worldgen.h" +#include "structures.h" +#include "procedures.h" int client_states[MAX_PLAYERS * 2]; @@ -573,6 +575,35 @@ void handlePlayerUseItem (PlayerData *player, short x, short y, short z, uint8_t } } + // If the selected slot doesn't hold any items, exit + uint8_t *count = &player->inventory_count[player->hotbar]; + if (*count == 0) return; + + // Check special item handling + uint16_t *item = &player->inventory_items[player->hotbar]; + if (*item == I_bone_meal) { + uint8_t target = getBlockAt(x, y, z); + uint8_t target_below = getBlockAt(x, y - 1, z); + if (target == B_oak_sapling) { + // Consume the bone meal (yes, even before checks) + // Wasting bone meal on misplanted saplings is vanilla behavior + if ((*count -= 1) == 0) item = 0; + sc_setContainerSlot(player->client_fd, 0, serverSlotToClientSlot(0, player->hotbar), *count, *item); + if ( // Saplings can only grow when placed on these blocks + target_below == B_dirt || + target_below == B_grass_block || + target_below == B_snowy_grass_block + ) { + // Bone meal has a 25% chance of growing a tree from a sapling + if ((fast_rand() & 3) == 0) placeTreeStructure(x, y, z); + } + } + } + + // If the selected item doesn't correspond to a block, exit + uint8_t block = I_to_B(*item); + if (block == 0) return; + switch (face) { case 0: y -= 1; break; case 1: y += 1; break; @@ -583,18 +614,9 @@ void handlePlayerUseItem (PlayerData *player, short x, short y, short z, uint8_t default: break; } - uint16_t *item = &player->inventory_items[player->hotbar]; - uint8_t *count = &player->inventory_count[player->hotbar]; - uint8_t block = I_to_B(*item); - - // if the selected item doesn't correspond to a block, exit - if (block == 0) return; - // if the selected slot doesn't hold any items, exit - if (*count == 0) return; - - // check if the block's placement conditions are met + // Check if the block's placement conditions are met if ( - !( // player is not in the way + !( // Is player in the way? !isPassableBlock(block) && x == player->x && (y == player->y || y == player->y + 1) && @@ -603,15 +625,15 @@ void handlePlayerUseItem (PlayerData *player, short x, short y, short z, uint8_t isReplaceableBlock(getBlockAt(x, y, z)) && (!isColumnBlock(block) || getBlockAt(x, y - 1, z) != B_air) ) { - // decrease item amount in selected slot + // Decrease item amount in selected slot *count -= 1; - // clear item id in slot if amount is zero + // Clear item id in slot if amount is zero if (*count == 0) *item = 0; - // apply server-side block change + // Apply server-side block change makeBlockChange(x, y, z, block); } - // sync hotbar contents to player + // Sync hotbar contents to player sc_setContainerSlot(player->client_fd, 0, serverSlotToClientSlot(0, player->hotbar), *count, *item); } diff --git a/src/structures.c b/src/structures.c new file mode 100644 index 0000000..348a7cb --- /dev/null +++ b/src/structures.c @@ -0,0 +1,55 @@ +#include + +#include "globals.h" +#include "tools.h" +#include "registries.h" +#include "worldgen.h" +#include "procedures.h" +#include "structures.h" + +void setBlockIfReplaceable (short x, uint8_t y, short z, uint8_t block) { + if (!isReplaceableBlock(getBlockAt(x, y, z))) return; + makeBlockChange(x, y, z, block); +} + +// Places a tree centered on the input coordinates +void placeTreeStructure (short x, uint8_t y, short z) { + + // Get a random number for tree height and leaf edges + uint32_t r = fast_rand(); + uint8_t height = 4 + (r % 3); + + // Set tree base - replace sapling with log and put dirt below + makeBlockChange(x, y - 1, z, B_dirt); + makeBlockChange(x, y, z, B_oak_log); + // Create tree stump + for (int i = 1; i < height; i ++) { + setBlockIfReplaceable(x, y + i, z, B_oak_log); + } + // Keep track of leaf corners, determines random number bit shift + uint8_t t = 2; + // First (bottom) leaf layer + for (int i = -2; i <= 2; i ++) { + for (int j = -2; j <= 2; j ++) { + setBlockIfReplaceable(x + i, y + height - 3, z + j, B_oak_leaves); + // Randomly skip some corners, emulating vanilla tree shape + if ((i == 2 || i == -2) && (j == 2 || j == -2)) { + t ++; + if ((r >> t) & 1) continue; + } + setBlockIfReplaceable(x + i, y + height - 2, z + j, B_oak_leaves); + } + } + // Second (top) leaf layer + for (int i = -1; i <= 1; i ++) { + for (int j = -1; j <= 1; j ++) { + setBlockIfReplaceable(x + i, y + height - 1, z + j, B_oak_leaves); + if ((i == 1 || i == -1) && (j == 1 || j == -1)) { + t ++; + if ((r >> t) & 1) continue; + } + setBlockIfReplaceable(x + i, y + height, z + j, B_oak_leaves); + } + } + +}