#pragma once #include "ItemHandler.h" #include "../World.h" #include "../Entities/Player.h" #include "../Blocks/BlockCocoaPod.h" class cItemDyeHandler : public cItemHandler { using super = cItemHandler; public: cItemDyeHandler(int a_ItemType): super(a_ItemType) { } virtual bool OnItemUse( cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace ) override { if ((a_Item.m_ItemDamage == E_META_DYE_WHITE) && (a_BlockFace != BLOCK_FACE_NONE)) { // Bonemeal (white dye) is used to fertilize plants: if (fertilizePlant(*a_World, {a_BlockX, a_BlockY, a_BlockZ})) { if (a_Player->IsGameModeSurvival()) { a_Player->GetInventory().RemoveOneEquippedItem(); return true; } } } else if ((a_Item.m_ItemDamage == E_META_DYE_BROWN) && (a_BlockFace >= BLOCK_FACE_ZM) && (a_BlockFace <= BLOCK_FACE_XP)) { // Cocoa (brown dye) can be planted on jungle logs: BLOCKTYPE BlockType; NIBBLETYPE BlockMeta; a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); // Check if the block that the player clicked is a jungle log. if ((BlockType != E_BLOCK_LOG) || ((BlockMeta & 0x3) != E_META_LOG_JUNGLE)) { return false; } // Get the location from the new cocoa pod. AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, false); BlockMeta = cBlockCocoaPodHandler::BlockFaceToMeta(a_BlockFace); if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) != E_BLOCK_AIR) { return false; } // Place the cocoa pod: if (a_Player->PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_COCOA_POD, BlockMeta)) { if (a_Player->IsGameModeSurvival()) { a_Player->GetInventory().RemoveOneEquippedItem(); } return true; } } return false; } /** Attempts to use the bonemeal on the plant at the specified (absolute) position. The effect of fertilization depends on the plant: https://minecraft.gamepedia.com/Bone_Meal#Fertilizer - grow a few stages - grow 1 stage with a chance - drop pickups without destroying the plant - grow more plants in the vicinity If fertilized succesfully, spawn appropriate particle effects, too. Returns true if the plant was fertilized successfully, false if not / not a plant. Note that successful fertilization doesn't mean successful growth - for blocks that have only a chance to grow, fertilization success is reported even in the case when the chance fails (bonemeal still needs to be consumed). */ bool fertilizePlant(cWorld & a_World, Vector3i a_BlockPos) { BLOCKTYPE blockType; NIBBLETYPE blockMeta; if (!a_World.GetBlockTypeMeta(a_BlockPos, blockType, blockMeta)) { return false; } switch (blockType) { case E_BLOCK_WHEAT: case E_BLOCK_CARROTS: case E_BLOCK_POTATOES: case E_BLOCK_MELON_STEM: case E_BLOCK_PUMPKIN_STEM: { // Grow by 2 - 5 stages: auto numStages = GetRandomProvider().RandInt(2, 5); if (a_World.GrowPlantAt(a_BlockPos, numStages) <= 0) { return false; } a_World.BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockPos, 0); return true; } // case wheat, carrots, potatoes, melon stem, pumpkin stem case E_BLOCK_BEETROOTS: { // 75% chance of 1-stage growth: if (GetRandomProvider().RandBool(0.75)) { if (a_World.GrowPlantAt(a_BlockPos, 1) > 0) { a_World.BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockPos, 0); } } return true; } // case beetroots case E_BLOCK_SAPLING: { // 45% chance of growing to the next stage / full tree: if (GetRandomProvider().RandBool(0.45)) { if (a_World.GrowPlantAt(a_BlockPos, 1) > 0) { a_World.BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockPos, 0); } } return true; } case E_BLOCK_BIG_FLOWER: { // Drop the corresponding flower item without destroying the block: cItems pickups; switch (blockMeta) { case E_META_BIG_FLOWER_SUNFLOWER: pickups.Add(E_BLOCK_BIG_FLOWER, 1, E_META_BIG_FLOWER_SUNFLOWER); break; case E_META_BIG_FLOWER_LILAC: pickups.Add(E_BLOCK_BIG_FLOWER, 1, E_META_BIG_FLOWER_LILAC); break; case E_META_BIG_FLOWER_ROSE_BUSH: pickups.Add(E_BLOCK_BIG_FLOWER, 1, E_META_BIG_FLOWER_ROSE_BUSH); break; case E_META_BIG_FLOWER_PEONY: pickups.Add(E_BLOCK_BIG_FLOWER, 1, E_META_BIG_FLOWER_PEONY); break; } // TODO: Should we call any hook for this? a_World.SpawnItemPickups(pickups, a_BlockPos); return true; } // big flower case E_BLOCK_TALL_GRASS: case E_BLOCK_COCOA_POD: case E_BLOCK_SUGARCANE: case E_BLOCK_CACTUS: { // Always try to grow 1 stage: if (a_World.GrowPlantAt(a_BlockPos, 1) <= 0) { return false; } a_World.BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockPos, 0); return true; } // case tall grass case E_BLOCK_RED_MUSHROOM: case E_BLOCK_BROWN_MUSHROOM: { // 40% chance of growing into a large mushroom: if (GetRandomProvider().RandBool(0.6)) { return false; } if (a_World.GrowPlantAt(a_BlockPos, 1) <= 0) { return false; } a_World.BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockPos, 0); return true; } // case red or brown mushroom case E_BLOCK_GRASS: { growPlantsAround(a_World, a_BlockPos); return true; } // TODO: case E_BLOCK_SWEET_BERRY_BUSH: // TODO: case E_BLOCK_SEA_PICKLE: // TODO: case E_BLOCK_KELP: // TODO: case E_BLOCK_BAMBOO: } // switch (blockType) return false; } /** Grows new plants around the specified block. Places up to 40 new plants, with the following probability: - 20 % big grass (2-block tall grass) - 60 % tall grass (1-block tall grass) - 20 % flowers (biome dependent variants) The new plants are spawned within 7 taxicab distance of a_BlockPos, on a grass block. Broadcasts a particle for each new spawned plant. */ void growPlantsAround(cWorld & a_World, Vector3i a_BlockPos) { auto & r1 = GetRandomProvider(); for (int i = 0; i < 40; ++i) { int ofsY = r1.RandInt(3) + r1.RandInt(3) - 3; if (!cChunkDef::IsValidHeight(a_BlockPos.y + ofsY)) { continue; } int ofsX = (r1.RandInt(3) + r1.RandInt(3) + r1.RandInt(3) + r1.RandInt(3)) / 2 - 3; int ofsZ = (r1.RandInt(3) + r1.RandInt(3) + r1.RandInt(3) + r1.RandInt(3)) / 2 - 3; Vector3i ofs(ofsX, ofsY, ofsZ); auto typeGround = a_World.GetBlock(a_BlockPos + ofs); if (typeGround != E_BLOCK_GRASS) { continue; } auto pos = a_BlockPos + ofs.addedY(1); auto typeAbove = a_World.GetBlock(pos); if (typeAbove != E_BLOCK_AIR) { continue; } BLOCKTYPE spawnType; NIBBLETYPE spawnMeta = 0; switch (r1.RandInt(10)) { case 0: spawnType = E_BLOCK_YELLOW_FLOWER; break; case 1: spawnType = E_BLOCK_RED_ROSE; break; default: { spawnType = E_BLOCK_TALL_GRASS; spawnMeta = E_META_TALL_GRASS_GRASS; break; } } // switch (random spawn block type) a_World.SetBlock(pos, spawnType, spawnMeta); a_World.BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, pos, 0); } // for i - attempts } } ;