summaryrefslogtreecommitdiffstats
path: root/src/Blocks
diff options
context:
space:
mode:
Diffstat (limited to 'src/Blocks')
-rw-r--r--src/Blocks/BlockBed.cpp4
-rw-r--r--src/Blocks/BlockBed.h4
-rw-r--r--src/Blocks/BlockBigFlower.h11
-rw-r--r--src/Blocks/BlockButton.h2
-rw-r--r--src/Blocks/BlockCactus.h111
-rw-r--r--src/Blocks/BlockCake.h15
-rw-r--r--src/Blocks/BlockCocoaPod.h53
-rw-r--r--src/Blocks/BlockCrops.h25
-rw-r--r--src/Blocks/BlockDirt.h34
-rw-r--r--src/Blocks/BlockDoor.h36
-rw-r--r--src/Blocks/BlockHandler.cpp40
-rw-r--r--src/Blocks/BlockHandler.h4
-rw-r--r--src/Blocks/BlockNetherWart.h17
-rw-r--r--src/Blocks/BlockPlant.h61
-rw-r--r--src/Blocks/BlockSapling.h60
-rw-r--r--src/Blocks/BlockStems.h146
-rw-r--r--src/Blocks/BlockSugarcane.h65
-rw-r--r--src/Blocks/BlockTallGrass.h25
-rw-r--r--src/Blocks/ChunkInterface.cpp24
-rw-r--r--src/Blocks/ChunkInterface.h23
20 files changed, 567 insertions, 193 deletions
diff --git a/src/Blocks/BlockBed.cpp b/src/Blocks/BlockBed.cpp
index 3f10a2e88..e6193682a 100644
--- a/src/Blocks/BlockBed.cpp
+++ b/src/Blocks/BlockBed.cpp
@@ -99,7 +99,7 @@ bool cBlockBedHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface
PillowDirection = MetaDataToDirection(Meta & 0x3);
if (a_ChunkInterface.GetBlock(Coords + PillowDirection) == E_BLOCK_BED) // Must always use pillow location for sleeping
{
- a_WorldInterface.GetBroadcastManager().BroadcastUseBed(a_Player, Vector3i{ a_BlockX, a_BlockY, a_BlockZ } + PillowDirection);
+ a_WorldInterface.GetBroadcastManager().BroadcastUseBed(a_Player, Vector3i{a_BlockX, a_BlockY, a_BlockZ} + PillowDirection);
}
}
@@ -127,7 +127,7 @@ bool cBlockBedHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface
}
);
a_WorldInterface.SetTimeOfDay(0);
- a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta & 0x0b); // Clear the "occupied" bit of the bed's block
+ a_ChunkInterface.SetBlockMeta({a_BlockX, a_BlockY, a_BlockZ}, Meta & 0x0b); // Clear the "occupied" bit of the bed's block
}
}
}
diff --git a/src/Blocks/BlockBed.h b/src/Blocks/BlockBed.h
index 585067ce2..bedc383e1 100644
--- a/src/Blocks/BlockBed.h
+++ b/src/Blocks/BlockBed.h
@@ -75,7 +75,7 @@ public:
- static void SetBedOccupationState(cChunkInterface & a_ChunkInterface, const Vector3i & a_BedPosition, bool a_IsOccupied)
+ static void SetBedOccupationState(cChunkInterface & a_ChunkInterface, Vector3i a_BedPosition, bool a_IsOccupied)
{
auto Meta = a_ChunkInterface.GetBlockMeta(a_BedPosition);
if (a_IsOccupied)
@@ -87,7 +87,7 @@ public:
Meta &= 0x0b; // Clear the "occupied" bit of the bed's block
}
- a_ChunkInterface.SetBlockMeta(a_BedPosition.x, a_BedPosition.y, a_BedPosition.z, Meta);
+ a_ChunkInterface.SetBlockMeta(a_BedPosition, Meta);
}
diff --git a/src/Blocks/BlockBigFlower.h b/src/Blocks/BlockBigFlower.h
index ae4b2500a..f81b16bd2 100644
--- a/src/Blocks/BlockBigFlower.h
+++ b/src/Blocks/BlockBigFlower.h
@@ -11,14 +11,19 @@
class cBlockBigFlowerHandler :
public cBlockHandler
{
+ using super = cBlockHandler;
+
public:
- typedef cBlockHandler super;
- cBlockBigFlowerHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
+ cBlockBigFlowerHandler(BLOCKTYPE a_BlockType):
+ super(a_BlockType)
{
}
+
+
+
+
virtual bool DoesIgnoreBuildCollision(cChunkInterface & a_ChunkInterface, Vector3i a_Pos, cPlayer & a_Player, NIBBLETYPE a_Meta) override
{
if (IsMetaTopPart(a_Meta))
diff --git a/src/Blocks/BlockButton.h b/src/Blocks/BlockButton.h
index 86c7cb2c5..8f90f84e5 100644
--- a/src/Blocks/BlockButton.h
+++ b/src/Blocks/BlockButton.h
@@ -39,7 +39,7 @@ public:
// Set p the ON bit to on
Meta |= 0x08;
- a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta, false);
+ a_ChunkInterface.SetBlockMeta({a_BlockX, a_BlockY, a_BlockZ}, Meta, false);
a_WorldInterface.WakeUpSimulators(Pos);
a_WorldInterface.GetBroadcastManager().BroadcastSoundEffect("block.stone_button.click_on", SoundPos, 0.5f, 0.6f);
diff --git a/src/Blocks/BlockCactus.h b/src/Blocks/BlockCactus.h
index f33c17153..8c24cda9f 100644
--- a/src/Blocks/BlockCactus.h
+++ b/src/Blocks/BlockCactus.h
@@ -1,4 +1,3 @@
-
#pragma once
#include "BlockPlant.h"
@@ -8,9 +7,9 @@
class cBlockCactusHandler :
- public cBlockPlant<false>
+ public cClearMetaOnDrop<cBlockPlant<false>>
{
- using super = cBlockPlant<false>;
+ using super = cClearMetaOnDrop<cBlockPlant<false>>;
public:
@@ -23,16 +22,6 @@ public:
- virtual cItems ConvertToPickups(NIBBLETYPE a_BlockMeta, cBlockEntity * a_BlockEntity, const cEntity * a_Digger, const cItem * a_Tool) override
- {
- // Reset meta to 0
- return cItem(m_BlockType, 1, 0);
- }
-
-
-
-
-
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
{
if (a_RelY <= 0)
@@ -77,13 +66,9 @@ public:
return true;
}
- virtual void OnUpdate(cChunkInterface & cChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_PluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override
- {
- if (CanGrow(a_Chunk, a_RelX, a_RelY, a_RelZ) == paGrowth)
- {
- a_Chunk.GetWorld()->GrowCactus(a_RelX + a_Chunk.GetPosX() * cChunkDef::Width, a_RelY, a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width, 1);
- }
- }
+
+
+
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{
@@ -91,17 +76,91 @@ public:
return 7;
}
-protected:
- virtual PlantAction CanGrow(cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override
+
+
+ virtual int Grow(cChunk & a_Chunk, Vector3i a_RelPos, int a_NumStages = 1) override
{
- auto Action = paStay;
- if (((a_RelY + 1) < cChunkDef::Height) && (a_Chunk.GetBlock(a_RelX, a_RelY + 1, a_RelZ) == E_BLOCK_AIR))
+ // Check the total height of the cacti blocks here:
+ int top = a_RelPos.y + 1;
+ while (
+ (top < cChunkDef::Height) &&
+ (a_Chunk.GetBlock({a_RelPos.x, top, a_RelPos.z}) == E_BLOCK_CACTUS)
+ )
+ {
+ ++top;
+ }
+ int bottom = a_RelPos.y - 1;
+ while (
+ (bottom > 0) &&
+ (a_Chunk.GetBlock({a_RelPos.x, bottom, a_RelPos.z}) == E_BLOCK_CACTUS)
+ )
+ {
+ --bottom;
+ }
+
+ // Refuse if already too high:
+ auto numToGrow = std::min(a_NumStages, a_Chunk.GetWorld()->GetMaxCactusHeight() + 1 - (top - bottom));
+ if (numToGrow <= 0)
{
- Action = super::CanGrow(a_Chunk, a_RelX, a_RelY, a_RelZ);
+ return 0;
}
- return Action;
+ BLOCKTYPE blockType;
+ for (int i = 0; i < numToGrow; ++i)
+ {
+ Vector3i pos(a_RelPos.x, top + i, a_RelPos.z);
+ if (!a_Chunk.UnboundedRelGetBlockType(pos, blockType) || (blockType != E_BLOCK_AIR))
+ {
+ // Cannot grow there
+ return i;
+ }
+
+ a_Chunk.UnboundedRelFastSetBlock(pos, E_BLOCK_CACTUS, 0);
+
+ // Check surroundings. Cacti may ONLY be surrounded by non-solid blocks; if they aren't, drop as pickup and bail out the growing
+ static const Vector3i neighborOffsets[] =
+ {
+ {-1, 0, 0},
+ { 1, 0, 0},
+ { 0, 0, -1},
+ { 0, 0, 1},
+ } ;
+ for (const auto & ofs: neighborOffsets)
+ {
+ if (
+ a_Chunk.UnboundedRelGetBlockType(pos + ofs, blockType) &&
+ (
+ cBlockInfo::IsSolid(blockType) ||
+ (blockType == E_BLOCK_LAVA) ||
+ (blockType == E_BLOCK_STATIONARY_LAVA)
+ )
+ )
+ {
+ // Remove the cactus
+ auto absPos = a_Chunk.RelativeToAbsolute(pos);
+ a_Chunk.GetWorld()->DropBlockAsPickups(absPos);
+ return i + 1;
+ }
+ } // for neighbor
+ } // for i - numToGrow
+ return numToGrow;
+ }
+
+
+
+
+
+protected:
+
+ virtual PlantAction CanGrow(cChunk & a_Chunk, Vector3i a_RelPos) override
+ {
+ // Only allow growing if there's an air block above:
+ if (((a_RelPos.y + 1) < cChunkDef::Height) && (a_Chunk.GetBlock(a_RelPos.addedY(1)) == E_BLOCK_AIR))
+ {
+ return super::CanGrow(a_Chunk, a_RelPos);
+ }
+ return paStay;
}
} ;
diff --git a/src/Blocks/BlockCake.h b/src/Blocks/BlockCake.h
index 4767a5148..6a80e939c 100644
--- a/src/Blocks/BlockCake.h
+++ b/src/Blocks/BlockCake.h
@@ -6,15 +6,20 @@
-class cBlockCakeHandler :
+class cBlockCakeHandler:
public cBlockHandler
{
+ using super = cBlockHandler;
public:
- cBlockCakeHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
+ cBlockCakeHandler(BLOCKTYPE a_BlockType):
+ super(a_BlockType)
{
}
+
+
+
+
virtual bool OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override
{
NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta({a_BlockX, a_BlockY, a_BlockZ});
@@ -26,11 +31,11 @@ public:
if (Meta >= 5)
{
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
+ a_ChunkInterface.DigBlock(a_WorldInterface, {a_BlockX, a_BlockY, a_BlockZ});
}
else
{
- a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta + 1);
+ a_ChunkInterface.SetBlockMeta({a_BlockX, a_BlockY, a_BlockZ}, Meta + 1);
}
return true;
}
diff --git a/src/Blocks/BlockCocoaPod.h b/src/Blocks/BlockCocoaPod.h
index 2d5243995..f9b7fbd36 100644
--- a/src/Blocks/BlockCocoaPod.h
+++ b/src/Blocks/BlockCocoaPod.h
@@ -10,12 +10,18 @@
class cBlockCocoaPodHandler :
public cBlockHandler
{
+ using super = cBlockHandler;
+
public:
- cBlockCocoaPodHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
+ cBlockCocoaPodHandler(BLOCKTYPE a_BlockType):
+ super(a_BlockType)
{
}
+
+
+
+
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
{
eBlockFace BlockFace = MetaToBlockFace(a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ));
@@ -28,19 +34,15 @@ public:
return ((BlockType == E_BLOCK_LOG) && ((BlockMeta & 0x3) == E_META_LOG_JUNGLE));
}
+
+
+
+
virtual void OnUpdate(cChunkInterface & cChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_PluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override
{
if (GetRandomProvider().RandBool(0.20))
{
- NIBBLETYPE Meta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ);
- NIBBLETYPE TypeMeta = Meta & 0x03;
- int GrowState = Meta >> 2;
-
- if (GrowState < 2)
- {
- ++GrowState;
- a_Chunk.SetMeta(a_RelX, a_RelY, a_RelZ, static_cast<NIBBLETYPE>(GrowState << 2 | TypeMeta));
- }
+ Grow(a_Chunk, {a_RelX, a_RelY, a_RelZ});
}
}
@@ -59,9 +61,28 @@ public:
+ virtual int Grow(cChunk & a_Chunk, Vector3i a_RelPos, int a_NumStages = 1) override
+ {
+ auto meta = a_Chunk.GetMeta(a_RelPos);
+ auto typeMeta = meta & 0x03;
+ auto growState = meta >> 2;
+
+ if (growState >= 3)
+ {
+ return 0;
+ }
+ auto newState = std::min(growState + a_NumStages, 3);
+ a_Chunk.SetMeta(a_RelPos, static_cast<NIBBLETYPE>(newState << 2 | typeMeta));
+ return newState - growState;
+ }
+
+
+
+
+
static eBlockFace MetaToBlockFace(NIBBLETYPE a_Meta)
{
- switch (a_Meta & 0x3)
+ switch (a_Meta & 0x03)
{
case 0: return BLOCK_FACE_ZM;
case 1: return BLOCK_FACE_XP;
@@ -75,6 +96,10 @@ public:
}
}
+
+
+
+
static NIBBLETYPE BlockFaceToMeta(eBlockFace a_BlockFace)
{
switch (a_BlockFace)
@@ -94,6 +119,10 @@ public:
UNREACHABLE("Unsupported block face");
}
+
+
+
+
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{
UNUSED(a_Meta);
diff --git a/src/Blocks/BlockCrops.h b/src/Blocks/BlockCrops.h
index 0d6296a33..0dc0ebbb8 100644
--- a/src/Blocks/BlockCrops.h
+++ b/src/Blocks/BlockCrops.h
@@ -90,27 +90,24 @@ public:
- virtual void OnUpdate(cChunkInterface & cChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_PluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override
+ virtual int Grow(cChunk & a_Chunk, Vector3i a_RelPos, int a_NumStages = 1) override
{
- NIBBLETYPE Meta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ);
-
- // Check to see if the plant can grow
- auto Action = CanGrow(a_Chunk, a_RelX, a_RelY, a_RelZ);
-
- // If there is still room to grow and the plant can grow, then grow.
- // Otherwise if the plant needs to die, then dig it up
- if ((Meta < RipeMeta) && (Action == paGrowth))
- {
- a_Chunk.FastSetBlock(a_RelX, a_RelY, a_RelZ, m_BlockType, ++Meta);
- }
- else if (Action == paDeath)
+ auto oldMeta = a_Chunk.GetMeta(a_RelPos);
+ if (oldMeta >= RipeMeta)
{
- a_Chunk.GetWorld()->DigBlock(a_RelX + a_Chunk.GetPosX() * cChunkDef::Width, a_RelY, a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width);
+ // Already ripe
+ return 0;
}
+ auto newMeta = std::min<int>(oldMeta + a_NumStages, RipeMeta);
+ ASSERT(newMeta > oldMeta);
+ a_Chunk.GetWorld()->SetBlock(a_Chunk.RelativeToAbsolute(a_RelPos), m_BlockType, static_cast<NIBBLETYPE>(newMeta));
+ return newMeta - oldMeta;
}
+
+
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
{
return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) == E_BLOCK_FARMLAND));
diff --git a/src/Blocks/BlockDirt.h b/src/Blocks/BlockDirt.h
index 0d4f73212..727c5d295 100644
--- a/src/Blocks/BlockDirt.h
+++ b/src/Blocks/BlockDirt.h
@@ -10,13 +10,17 @@
-/** Handler used for all types of dirt and grass */
+/** Handler used for all types of dirt and grassblock.
+TODO: Split the Grassblock handler away from this class. */
class cBlockDirtHandler :
public cBlockHandler
{
+ using super = cBlockHandler;
+
public:
- cBlockDirtHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
+
+ cBlockDirtHandler(BLOCKTYPE a_BlockType):
+ super(a_BlockType)
{
}
@@ -91,37 +95,41 @@ public:
// Y Coord out of range
continue;
}
- int BlockX = a_RelX + OfsX;
- int BlockY = a_RelY + OfsY;
- int BlockZ = a_RelZ + OfsZ;
- cChunk * Chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(BlockX, BlockZ);
- if (Chunk == nullptr)
+ Vector3i pos(a_RelX + OfsX, a_RelY + OfsY, a_RelZ + OfsZ);
+ auto chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(pos);
+ if (chunk == nullptr)
{
// Unloaded chunk
continue;
}
- Chunk->GetBlockTypeMeta(BlockX, BlockY, BlockZ, DestBlock, DestMeta);
+ chunk->GetBlockTypeMeta(pos, DestBlock, DestMeta);
if ((DestBlock != E_BLOCK_DIRT) || (DestMeta != E_META_DIRT_NORMAL))
{
// Not a regular dirt block
continue;
}
- BLOCKTYPE above = Chunk->GetBlock(BlockX, BlockY + 1, BlockZ);
- NIBBLETYPE light = std::max(Chunk->GetBlockLight(BlockX, BlockY + 1, BlockZ), Chunk->GetTimeAlteredLight(Chunk->GetSkyLight(BlockX, BlockY + 1, BlockZ)));
+ auto abovePos = pos.addedY(1);
+ BLOCKTYPE above = chunk->GetBlock(abovePos);
+ NIBBLETYPE light = std::max(chunk->GetBlockLight(abovePos), chunk->GetTimeAlteredLight(chunk->GetSkyLight(abovePos)));
if ((light > 4) &&
cBlockInfo::IsTransparent(above) &&
(!IsBlockLava(above)) &&
(!IsBlockWaterOrIce(above))
)
{
- if (!cRoot::Get()->GetPluginManager()->CallHookBlockSpread(*Chunk->GetWorld(), Chunk->GetPosX() * cChunkDef::Width + BlockX, BlockY, Chunk->GetPosZ() * cChunkDef::Width + BlockZ, ssGrassSpread))
+ auto absPos = chunk->RelativeToAbsolute(pos);
+ if (!cRoot::Get()->GetPluginManager()->CallHookBlockSpread(*chunk->GetWorld(), absPos.x, absPos.y, absPos.z, ssGrassSpread))
{
- Chunk->FastSetBlock(BlockX, BlockY, BlockZ, E_BLOCK_GRASS, 0);
+ chunk->FastSetBlock(pos, E_BLOCK_GRASS, 0);
}
}
} // for i - repeat twice
}
+
+
+
+
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{
UNUSED(a_Meta);
diff --git a/src/Blocks/BlockDoor.h b/src/Blocks/BlockDoor.h
index b1a606f67..596e69793 100644
--- a/src/Blocks/BlockDoor.h
+++ b/src/Blocks/BlockDoor.h
@@ -141,6 +141,10 @@ public:
return false;
}
+
+
+
+
/** Converts the player's yaw to placed door's blockmeta */
inline static NIBBLETYPE PlayerYawToMetaData(double a_Yaw)
{
@@ -169,6 +173,10 @@ public:
}
}
+
+
+
+
/** Returns a vector pointing one block in the direction the door is facing (where the outside is). */
inline static Vector3i GetRelativeDirectionToOutside(NIBBLETYPE a_BlockMeta)
{
@@ -181,6 +189,10 @@ public:
}
}
+
+
+
+
/** Returns true if the specified blocktype is any kind of door */
inline static bool IsDoorBlockType(BLOCKTYPE a_Block)
{
@@ -203,6 +215,10 @@ public:
}
}
+
+
+
+
/** Returns true iff the door at the specified coords is open.
The coords may point to either the top part or the bottom part of the door. */
static NIBBLETYPE IsOpen(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ)
@@ -211,6 +227,10 @@ public:
return ((Meta & 0x04) != 0);
}
+
+
+
+
/** Returns the complete meta composed from the both parts of the door as (TopMeta << 4) | BottomMeta
The coords may point to either part of the door.
The returned value has bit 3 (0x08) set iff the coords point to the top part of the door.
@@ -243,6 +263,10 @@ public:
}
}
+
+
+
+
/** Sets the door to the specified state. If the door is already in that state, does nothing. */
static void SetOpen(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ, bool a_Open)
{
@@ -264,24 +288,32 @@ public:
if ((Meta & 0x08) == 0)
{
// The block is the bottom part of the door
- a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, NewMeta);
+ a_ChunkInterface.SetBlockMeta({a_BlockX, a_BlockY, a_BlockZ}, NewMeta);
}
else
{
// The block is the top part of the door, set the meta to the corresponding top part
if (a_BlockY > 0)
{
- a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY - 1, a_BlockZ, NewMeta);
+ a_ChunkInterface.SetBlockMeta({a_BlockX, a_BlockY - 1, a_BlockZ}, NewMeta);
}
}
}
+
+
+
+
/** Changes the door at the specified coords from open to close or vice versa */
static void ChangeDoor(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ)
{
SetOpen(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ, !IsOpen(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ));
}
+
+
+
+
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{
UNUSED(a_Meta);
diff --git a/src/Blocks/BlockHandler.cpp b/src/Blocks/BlockHandler.cpp
index 58096f038..9651f072a 100644
--- a/src/Blocks/BlockHandler.cpp
+++ b/src/Blocks/BlockHandler.cpp
@@ -189,6 +189,7 @@ static cBlockHandler * CreateBlockHandler(BLOCKTYPE a_BlockType)
case E_BLOCK_ACACIA_FENCE_GATE: return new cBlockFenceGateHandler (a_BlockType);
case E_BLOCK_ACACIA_WOOD_STAIRS: return new cBlockStairsHandler (a_BlockType);
case E_BLOCK_ACTIVATOR_RAIL: return new cBlockRailHandler (a_BlockType);
+ case E_BLOCK_ACTIVE_COMPARATOR: return new cBlockComparatorHandler (a_BlockType);
case E_BLOCK_AIR: return new cBlockWithNoDrops<> (a_BlockType);
case E_BLOCK_ANVIL: return new cBlockAnvilHandler (a_BlockType);
case E_BLOCK_BEACON: return new cBlockEntityHandler (a_BlockType);
@@ -204,20 +205,19 @@ static cBlockHandler * CreateBlockHandler(BLOCKTYPE a_BlockType)
case E_BLOCK_BROWN_MUSHROOM: return new cBlockMushroomHandler (a_BlockType);
case E_BLOCK_CACTUS: return new cBlockCactusHandler (a_BlockType);
case E_BLOCK_CAKE: return new cBlockCakeHandler (a_BlockType);
- case E_BLOCK_CARROTS: return new cBlockCropsHandler<7> (a_BlockType); // 8 stages of growth
case E_BLOCK_CARPET: return new cBlockCarpetHandler (a_BlockType);
+ case E_BLOCK_CARROTS: return new cBlockCropsHandler<7> (a_BlockType); // 8 stages of growth
case E_BLOCK_CAULDRON: return new cBlockCauldronHandler (a_BlockType);
case E_BLOCK_CHAIN_COMMAND_BLOCK: return new cBlockCommandBlockHandler (a_BlockType);
case E_BLOCK_CHEST: return new cBlockChestHandler (a_BlockType);
case E_BLOCK_CLAY: return new cBlockOreHandler (a_BlockType);
case E_BLOCK_COAL_ORE: return new cBlockOreHandler (a_BlockType);
- case E_BLOCK_COCOA_POD: return new cBlockCocoaPodHandler (a_BlockType);
- case E_BLOCK_COMMAND_BLOCK: return new cBlockCommandBlockHandler (a_BlockType);
- case E_BLOCK_ACTIVE_COMPARATOR: return new cBlockComparatorHandler (a_BlockType);
- case E_BLOCK_CONCRETE_POWDER: return new cBlockConcretePowderHandler (a_BlockType);
case E_BLOCK_COBBLESTONE: return new cBlockStoneHandler (a_BlockType);
case E_BLOCK_COBBLESTONE_STAIRS: return new cBlockStairsHandler (a_BlockType);
case E_BLOCK_COBWEB: return new cBlockCobWebHandler (a_BlockType);
+ case E_BLOCK_COCOA_POD: return new cBlockCocoaPodHandler (a_BlockType);
+ case E_BLOCK_COMMAND_BLOCK: return new cBlockCommandBlockHandler (a_BlockType);
+ case E_BLOCK_CONCRETE_POWDER: return new cBlockConcretePowderHandler (a_BlockType);
case E_BLOCK_CROPS: return new cBlockCropsHandler<7> (a_BlockType); // 8 stages of growth
case E_BLOCK_DARK_OAK_DOOR: return new cBlockDoorHandler (a_BlockType);
case E_BLOCK_DARK_OAK_FENCE: return new cBlockFenceHandler (a_BlockType);
@@ -234,18 +234,18 @@ static cBlockHandler * CreateBlockHandler(BLOCKTYPE a_BlockType)
case E_BLOCK_DROPPER: return new cBlockDropSpenserHandler (a_BlockType);
case E_BLOCK_EMERALD_ORE: return new cBlockOreHandler (a_BlockType);
case E_BLOCK_ENCHANTMENT_TABLE: return new cBlockEnchantmentTableHandler(a_BlockType);
- case E_BLOCK_END_PORTAL_FRAME: return new cBlockEndPortalFrameHandler (a_BlockType);
case E_BLOCK_ENDER_CHEST: return new cBlockEnderchestHandler (a_BlockType);
+ case E_BLOCK_END_PORTAL_FRAME: return new cBlockEndPortalFrameHandler (a_BlockType);
case E_BLOCK_FARMLAND: return new cBlockFarmlandHandler (a_BlockType);
case E_BLOCK_FENCE: return new cBlockFenceHandler (a_BlockType);
- case E_BLOCK_FROSTED_ICE: return new cBlockIceHandler (a_BlockType);
case E_BLOCK_FIRE: return new cBlockFireHandler (a_BlockType);
case E_BLOCK_FLOWER_POT: return new cBlockFlowerPotHandler (a_BlockType);
+ case E_BLOCK_FROSTED_ICE: return new cBlockIceHandler (a_BlockType);
case E_BLOCK_FURNACE: return new cBlockFurnaceHandler (a_BlockType);
- case E_BLOCK_GLOWSTONE: return new cBlockGlowstoneHandler (a_BlockType);
- case E_BLOCK_GOLD_ORE: return new cBlockOreHandler (a_BlockType);
case E_BLOCK_GLASS: return new cBlockGlassHandler (a_BlockType);
case E_BLOCK_GLASS_PANE: return new cBlockGlassHandler (a_BlockType);
+ case E_BLOCK_GLOWSTONE: return new cBlockGlowstoneHandler (a_BlockType);
+ case E_BLOCK_GOLD_ORE: return new cBlockOreHandler (a_BlockType);
case E_BLOCK_GRASS: return new cBlockDirtHandler (a_BlockType);
case E_BLOCK_GRAVEL: return new cBlockGravelHandler (a_BlockType);
case E_BLOCK_HAY_BALE: return new cBlockSidewaysHandler (a_BlockType);
@@ -264,29 +264,30 @@ static cBlockHandler * CreateBlockHandler(BLOCKTYPE a_BlockType)
case E_BLOCK_JUNGLE_FENCE_GATE: return new cBlockFenceGateHandler (a_BlockType);
case E_BLOCK_JUNGLE_WOOD_STAIRS: return new cBlockStairsHandler (a_BlockType);
case E_BLOCK_LADDER: return new cBlockLadderHandler (a_BlockType);
- case E_BLOCK_LEVER: return new cBlockLeverHandler (a_BlockType);
case E_BLOCK_LAPIS_ORE: return new cBlockOreHandler (a_BlockType);
case E_BLOCK_LAVA: return new cBlockLavaHandler (a_BlockType);
case E_BLOCK_LEAVES: return new cBlockLeavesHandler (a_BlockType);
+ case E_BLOCK_LEVER: return new cBlockLeverHandler (a_BlockType);
case E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE: return new cBlockPressurePlateHandler (a_BlockType);
case E_BLOCK_LILY_PAD: return new cBlockLilypadHandler (a_BlockType);
case E_BLOCK_LIT_FURNACE: return new cBlockFurnaceHandler (a_BlockType);
case E_BLOCK_LOG: return new cBlockSidewaysHandler (a_BlockType);
case E_BLOCK_MELON: return new cBlockMelonHandler (a_BlockType);
- case E_BLOCK_MELON_STEM: return new cBlockStemsHandler (a_BlockType);
+ case E_BLOCK_MELON_STEM: return new cBlockMelonStemHandler (a_BlockType);
case E_BLOCK_MOB_SPAWNER: return new cBlockMobSpawnerHandler (a_BlockType);
case E_BLOCK_MYCELIUM: return new cBlockMyceliumHandler (a_BlockType);
+ case E_BLOCK_NETHERRACK: return new cBlockNetherrack (a_BlockType);
case E_BLOCK_NETHER_BRICK_FENCE: return new cBlockFenceHandler (a_BlockType);
case E_BLOCK_NETHER_BRICK_STAIRS: return new cBlockStairsHandler (a_BlockType);
case E_BLOCK_NETHER_PORTAL: return new cBlockPortalHandler (a_BlockType);
- case E_BLOCK_NETHER_WART: return new cBlockNetherWartHandler (a_BlockType);
- case E_BLOCK_NETHERRACK: return new cBlockNetherrack (a_BlockType);
case E_BLOCK_NETHER_QUARTZ_ORE: return new cBlockOreHandler (a_BlockType);
+ case E_BLOCK_NETHER_WART: return new cBlockNetherWartHandler (a_BlockType);
case E_BLOCK_NEW_LEAVES: return new cBlockLeavesHandler (a_BlockType);
case E_BLOCK_NEW_LOG: return new cBlockSidewaysHandler (a_BlockType);
- case E_BLOCK_RED_SANDSTONE_SLAB: return new cBlockSlabHandler (a_BlockType);
case E_BLOCK_NOTE_BLOCK: return new cBlockEntityHandler (a_BlockType);
+ case E_BLOCK_OAK_DOOR: return new cBlockDoorHandler (a_BlockType);
case E_BLOCK_OAK_FENCE_GATE: return new cBlockFenceGateHandler (a_BlockType);
+ case E_BLOCK_OAK_WOOD_STAIRS: return new cBlockStairsHandler (a_BlockType);
case E_BLOCK_PACKED_ICE: return new cBlockIceHandler (a_BlockType);
case E_BLOCK_PISTON: return new cBlockPistonHandler (a_BlockType);
case E_BLOCK_PISTON_EXTENSION: return new cBlockPistonHeadHandler;
@@ -294,14 +295,13 @@ static cBlockHandler * CreateBlockHandler(BLOCKTYPE a_BlockType)
case E_BLOCK_POTATOES: return new cBlockCropsHandler<7> (a_BlockType); // 8 stages of growth
case E_BLOCK_POWERED_RAIL: return new cBlockRailHandler (a_BlockType);
case E_BLOCK_PUMPKIN: return new cBlockPumpkinHandler (a_BlockType);
- case E_BLOCK_PUMPKIN_STEM: return new cBlockStemsHandler (a_BlockType);
+ case E_BLOCK_PUMPKIN_STEM: return new cBlockPumpkinStemHandler (a_BlockType);
case E_BLOCK_PURPUR_DOUBLE_SLAB: return new cBlockDoubleSlabHandler (a_BlockType);
case E_BLOCK_PURPUR_SLAB: return new cBlockSlabHandler (a_BlockType);
case E_BLOCK_PURPUR_STAIRS: return new cBlockStairsHandler (a_BlockType);
case E_BLOCK_QUARTZ_BLOCK: return new cBlockQuartzHandler (a_BlockType);
case E_BLOCK_QUARTZ_STAIRS: return new cBlockStairsHandler (a_BlockType);
case E_BLOCK_RAIL: return new cBlockRailHandler (a_BlockType);
- case E_BLOCK_RED_SANDSTONE_STAIRS: return new cBlockStairsHandler (a_BlockType);
case E_BLOCK_REDSTONE_LAMP_ON: return new cBlockRedstoneLampHandler (a_BlockType);
case E_BLOCK_REDSTONE_ORE: return new cBlockOreHandler (a_BlockType);
case E_BLOCK_REDSTONE_ORE_GLOWING: return new cBlockOreHandler (a_BlockType);
@@ -312,14 +312,16 @@ static cBlockHandler * CreateBlockHandler(BLOCKTYPE a_BlockType)
case E_BLOCK_REDSTONE_WIRE: return new cBlockRedstoneHandler (a_BlockType);
case E_BLOCK_RED_MUSHROOM: return new cBlockMushroomHandler (a_BlockType);
case E_BLOCK_RED_ROSE: return new cBlockFlowerHandler (a_BlockType);
+ case E_BLOCK_RED_SANDSTONE_SLAB: return new cBlockSlabHandler (a_BlockType);
+ case E_BLOCK_RED_SANDSTONE_STAIRS: return new cBlockStairsHandler (a_BlockType);
case E_BLOCK_REPEATING_COMMAND_BLOCK: return new cBlockCommandBlockHandler (a_BlockType);
case E_BLOCK_SAND: return new cBlockSandHandler (a_BlockType);
case E_BLOCK_SANDSTONE_STAIRS: return new cBlockStairsHandler (a_BlockType);
case E_BLOCK_SAPLING: return new cBlockSaplingHandler (a_BlockType);
case E_BLOCK_SEA_LANTERN: return new cBlockSeaLanternHandler (a_BlockType);
case E_BLOCK_SIGN_POST: return new cBlockSignPostHandler (a_BlockType);
- case E_BLOCK_SNOW: return new cBlockSnowHandler (a_BlockType);
case E_BLOCK_SLIME_BLOCK: return new cBlockSlimeHandler (a_BlockType);
+ case E_BLOCK_SNOW: return new cBlockSnowHandler (a_BlockType);
case E_BLOCK_SPONGE: return new cBlockSpongeHandler (a_BlockType);
case E_BLOCK_SPRUCE_DOOR: return new cBlockDoorHandler (a_BlockType);
case E_BLOCK_SPRUCE_FENCE: return new cBlockFenceHandler (a_BlockType);
@@ -337,9 +339,9 @@ static cBlockHandler * CreateBlockHandler(BLOCKTYPE a_BlockType)
case E_BLOCK_STONE_SLAB: return new cBlockSlabHandler (a_BlockType);
case E_BLOCK_SUGARCANE: return new cBlockSugarcaneHandler (a_BlockType);
case E_BLOCK_TALL_GRASS: return new cBlockTallGrassHandler (a_BlockType);
+ case E_BLOCK_TNT: return new cBlockTNTHandler (a_BlockType);
case E_BLOCK_TORCH: return new cBlockTorchHandler (a_BlockType);
case E_BLOCK_TRAPDOOR: return new cBlockTrapdoorHandler (a_BlockType);
- case E_BLOCK_TNT: return new cBlockTNTHandler (a_BlockType);
case E_BLOCK_TRAPPED_CHEST: return new cBlockChestHandler (a_BlockType);
case E_BLOCK_TRIPWIRE: return new cBlockTripwireHandler (a_BlockType);
case E_BLOCK_TRIPWIRE_HOOK: return new cBlockTripwireHookHandler (a_BlockType);
@@ -347,10 +349,8 @@ static cBlockHandler * CreateBlockHandler(BLOCKTYPE a_BlockType)
case E_BLOCK_WALLSIGN: return new cBlockWallSignHandler (a_BlockType);
case E_BLOCK_WATER: return new cBlockFluidHandler (a_BlockType);
case E_BLOCK_WOODEN_BUTTON: return new cBlockButtonHandler (a_BlockType);
- case E_BLOCK_OAK_DOOR: return new cBlockDoorHandler (a_BlockType);
case E_BLOCK_WOODEN_PRESSURE_PLATE: return new cBlockPressurePlateHandler (a_BlockType);
case E_BLOCK_WOODEN_SLAB: return new cBlockSlabHandler (a_BlockType);
- case E_BLOCK_OAK_WOOD_STAIRS: return new cBlockStairsHandler (a_BlockType);
case E_BLOCK_WOOL: return new cBlockClothHandler (a_BlockType);
case E_BLOCK_WORKBENCH: return new cBlockWorkbenchHandler (a_BlockType);
case E_BLOCK_YELLOW_FLOWER: return new cBlockFlowerHandler (a_BlockType);
diff --git a/src/Blocks/BlockHandler.h b/src/Blocks/BlockHandler.h
index c0a11186e..789ef4dd5 100644
--- a/src/Blocks/BlockHandler.h
+++ b/src/Blocks/BlockHandler.h
@@ -200,6 +200,10 @@ public:
Returns block meta following rotation */
virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) { return a_Meta; }
+ /** Grows this block, if it supports growing, by the specified amount of stages (at most).
+ Returns the number of stages actually grown, zero if not supported (default). */
+ virtual int Grow(cChunk & a_Chunk, Vector3i a_RelPos, int a_NumStages = 1) { return 0; }
+
/** Returns true if the specified tool is valid and has a non-zero silk-touch enchantment.
Helper used in many ConvertToPickups() implementations. */
static bool ToolHasSilkTouch(const cItem * a_Tool);
diff --git a/src/Blocks/BlockNetherWart.h b/src/Blocks/BlockNetherWart.h
index 6b0b394b5..5664093e9 100644
--- a/src/Blocks/BlockNetherWart.h
+++ b/src/Blocks/BlockNetherWart.h
@@ -42,17 +42,22 @@ public:
- virtual void OnUpdate(cChunkInterface & cChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_PluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override
+ virtual int Grow(cChunk & a_Chunk, Vector3i a_RelPos, int a_NumStages = 1) override
{
- NIBBLETYPE Meta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ);
- if ((Meta < 3) && (CanGrow(a_Chunk, a_RelX, a_RelY, a_RelZ) == paGrowth))
+ auto oldMeta = a_Chunk.GetMeta(a_RelPos);
+ auto meta = std::min(oldMeta + a_NumStages, 3);
+ if ((oldMeta < 3) && (CanGrow(a_Chunk, a_RelPos) == paGrowth))
{
- a_Chunk.FastSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_NETHER_WART, ++Meta);
+ a_Chunk.SetBlock(a_RelPos, m_BlockType, static_cast<NIBBLETYPE>(meta));
+ return meta - oldMeta;
}
- else if (Meta > 3) // In older versions of cuberite, there was a bug which made wart grow too much. This check fixes previously saved buggy warts.
+
+ // In older versions of cuberite, there was a bug which made wart grow too much. This check fixes previously saved buggy warts.
+ if (oldMeta > 3)
{
- a_Chunk.FastSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_NETHER_WART, 3);
+ a_Chunk.FastSetBlock(a_RelPos, m_BlockType, 3);
}
+ return 0;
}
diff --git a/src/Blocks/BlockPlant.h b/src/Blocks/BlockPlant.h
index 02092fc38..0e249bbde 100644
--- a/src/Blocks/BlockPlant.h
+++ b/src/Blocks/BlockPlant.h
@@ -1,19 +1,20 @@
-
#pragma once
-#include "BlockHandler.h"
+#include "BlockHandler.h"
-/** Base class for plants that use light values to decide whether to grow or not. */
+/** Base class for plants that use light values to decide whether to grow or not.
+On block update, the plant decides whether to grow, die or stay as-is, based on the CanGrow() overridable method result. */
template <bool NeedsLightToGrow>
class cBlockPlant:
public cBlockHandler
{
using super = cBlockHandler;
+
public:
cBlockPlant(BLOCKTYPE a_BlockType):
@@ -22,6 +23,33 @@ public:
}
+
+
+
+ virtual void OnUpdate(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_PluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override
+ {
+ Vector3i relPos(a_RelX, a_RelY, a_RelZ);
+ auto action = CanGrow(a_Chunk, relPos);
+ switch (action)
+ {
+ case paGrowth:
+ {
+ Grow(a_Chunk, relPos);
+ break;
+ }
+ case paDeath:
+ {
+ a_ChunkInterface.DigBlock(a_WorldInterface, a_Chunk.RelativeToAbsolute(relPos));
+ break;
+ }
+ case paStay: break; // do nothing
+ }
+ }
+
+
+
+
+
protected:
/** The action the plant can take on an update. */
@@ -40,9 +68,8 @@ protected:
If the plant doesn't require light to grow, then it returns paGrowth.
If the plant requires light to grow and there is enough light, it returns paGrowth.
If the plant requires light to grow and there isn't enough light, it returns paStay.
- If the plant requires light to grow and there is too little light, it returns paDeath.
- */
- PlantAction HasEnoughLight(cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
+ If the plant requires light to grow and there is too little light, it returns paDeath. */
+ PlantAction HasEnoughLight(cChunk & a_Chunk, Vector3i a_RelPos)
{
// If the plant requires light to grow, check to see if there is enough light
// Otherwise, return true
@@ -50,8 +77,8 @@ protected:
{
return paGrowth;
}
- NIBBLETYPE Blocklight = a_Chunk.GetBlockLight(a_RelX, a_RelY, a_RelZ);
- NIBBLETYPE SkyLight = a_Chunk.GetSkyLight (a_RelX, a_RelY, a_RelZ);
+ NIBBLETYPE Blocklight = a_Chunk.GetBlockLight(a_RelPos);
+ NIBBLETYPE SkyLight = a_Chunk.GetSkyLight (a_RelPos);
NIBBLETYPE Light = a_Chunk.GetTimeAlteredLight(SkyLight);
// If the amount of light provided by blocks is greater than the sky light, use it instead
@@ -85,27 +112,27 @@ protected:
paDeath is returned when there isn't enough light for the plant to survive.
Plants that don't require light will never have a paDeath returned
*/
- virtual PlantAction CanGrow(cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
+ virtual PlantAction CanGrow(cChunk & a_Chunk, Vector3i a_RelPos)
{
// Plant can grow if it has the required amount of light, and it passes a random chance based on surrounding blocks
- PlantAction Action = HasEnoughLight(a_Chunk, a_RelX, a_RelY, a_RelZ);
- if ((Action == paGrowth) && !GetRandomProvider().RandBool(1.0 / GetGrowthChance(a_Chunk, a_RelX, a_RelY, a_RelZ)))
+ auto action = HasEnoughLight(a_Chunk, a_RelPos);
+ if ((action == paGrowth) && !GetRandomProvider().RandBool(1.0 / GetGrowthChance(a_Chunk, a_RelPos)))
{
- Action = paStay;
+ action = paStay;
}
- return Action;
+ return action;
}
- /** Generates a int value between 4 and 25 based on surrounding blocks that affect how quickly the plant grows.
+ /** Generates an int value between 4 and 25 based on surrounding blocks that affect how quickly the plant grows.
The higher the value, the less likely the plant is to grow */
- virtual int GetGrowthChance(cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
+ virtual int GetGrowthChance(cChunk & a_Chunk, Vector3i a_RelPos)
{
float Chance = 1.0f;
- a_RelY -= 1;
+ a_RelPos.y -= 1;
for (int x = -1; x < 2; ++x)
{
for (int z = -1; z < 2; ++z)
@@ -115,7 +142,7 @@ protected:
NIBBLETYPE Meta;
// If the chunk we are trying to get the block information from is loaded
- if (a_Chunk.UnboundedRelGetBlock(a_RelX + x, a_RelY, a_RelZ + z, Block, Meta))
+ if (a_Chunk.UnboundedRelGetBlock(a_RelPos + Vector3i(x, 0, z), Block, Meta))
{
cBlockHandler * Handler = BlockHandler(Block);
diff --git a/src/Blocks/BlockSapling.h b/src/Blocks/BlockSapling.h
index f3cefd722..15bd57f6a 100644
--- a/src/Blocks/BlockSapling.h
+++ b/src/Blocks/BlockSapling.h
@@ -39,6 +39,10 @@ public:
return (a_RelY > 0) && IsBlockTypeOfDirt(a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ));
}
+
+
+
+
virtual void OnUpdate(cChunkInterface & cChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_PluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override
{
NIBBLETYPE Meta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ);
@@ -63,6 +67,10 @@ public:
}
}
+
+
+
+
bool CanGrowAt(cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Meta)
{
a_Meta = a_Meta & 0x07;
@@ -149,8 +157,6 @@ public:
a_Chunk.UnboundedRelGetBlockType(a_RelX, a_RelY, a_RelZ + 1, check);
CanGrow = CanGrow && cBlockInfo::IsTransparent(check);
-
-
while (CheckHeight && CanGrow)
{
check = a_Chunk.GetBlock(a_RelX, a_RelY + CheckHeight, a_RelZ);
@@ -175,7 +181,51 @@ public:
return CanGrow;
}
+
+
+
+
+ virtual int Grow(cChunk & a_Chunk, Vector3i a_RelPos, int a_NumStages = 1) override
+ {
+ auto blockMeta = a_Chunk.GetMeta(a_RelPos);
+ auto typeMeta = blockMeta & 0x07;
+ auto growState = blockMeta >> 3;
+ int res = 0;
+
+ // Try to increase the sapling's growState:
+ if (growState < 1)
+ {
+ ++growState;
+ a_Chunk.FastSetBlock(a_RelPos, m_BlockType, static_cast<NIBBLETYPE>(growState << 3 | typeMeta));
+ if (a_NumStages == 1)
+ {
+ // Only asked to grow one stage, which we did. Bail out.
+ return 1;
+ }
+ res = 1;
+ }
+
+ // The sapling is grown, now it becomes a tree:
+ a_Chunk.GetWorld()->GrowTreeFromSapling(a_Chunk.RelativeToAbsolute(a_RelPos), blockMeta);
+ return res + 1;
+ }
+
+
+
+
+
+ virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
+ {
+ UNUSED(a_Meta);
+ return 7;
+ }
+
+
+
+
+
private:
+
bool IsLargeTree(cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Meta)
{
BLOCKTYPE type;
@@ -192,12 +242,6 @@ private:
return LargeTree;
}
-
- virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
- {
- UNUSED(a_Meta);
- return 7;
- }
} ;
diff --git a/src/Blocks/BlockStems.h b/src/Blocks/BlockStems.h
index 132526b93..0eb091b3c 100644
--- a/src/Blocks/BlockStems.h
+++ b/src/Blocks/BlockStems.h
@@ -7,7 +7,11 @@
-class cBlockStemsHandler :
+/** Handler for stems from which produce grows in an adjacent block (melon, pumpkin) after it becomes ripe (meta == 7).
+ProduceBlockType is the blocktype for the produce to be grown.
+StemPickupType is the item type for the pickup resulting from breaking the stem. */
+template <BLOCKTYPE ProduceBlockType, ENUM_ITEM_ID StemPickupType>
+class cBlockStemsHandler:
public cBlockPlant<true>
{
using super = cBlockPlant<true>;
@@ -25,51 +29,143 @@ public:
virtual cItems ConvertToPickups(NIBBLETYPE a_BlockMeta, cBlockEntity * a_BlockEntity, const cEntity * a_Digger, const cItem * a_Tool) override
{
- auto itemType = (m_BlockType == E_BLOCK_MELON_STEM) ? E_ITEM_MELON_SEEDS : E_ITEM_PUMPKIN_SEEDS;
- return cItem(itemType, 1, 0);
+ return cItem(StemPickupType, 1, 0);
}
- virtual void OnUpdate(cChunkInterface & cChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_PluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override
+ virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
+ {
+ return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) == E_BLOCK_FARMLAND));
+ }
+
+
+
+
+
+ virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
+ {
+ UNUSED(a_Meta);
+ return 7;
+ }
+
+
+
+
+
+ virtual int Grow(cChunk & a_Chunk, Vector3i a_RelPos, int a_NumStages = 1) override
{
- auto Action = CanGrow(a_Chunk, a_RelX, a_RelY, a_RelZ);
- if (Action == paGrowth)
+ auto oldMeta = a_Chunk.GetMeta(a_RelPos);
+ auto meta = oldMeta + a_NumStages;
+ a_Chunk.SetBlock(a_RelPos, m_BlockType, static_cast<NIBBLETYPE>(std::min(meta, 7))); // Update the stem
+ if (meta > 7)
{
- NIBBLETYPE Meta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ);
- if (Meta >= 7)
+ if (growProduce(a_Chunk, a_RelPos))
{
- // Grow the produce:
- int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width;
- int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width;
- a_Chunk.GetWorld()->GrowMelonPumpkin(BlockX, a_RelY, BlockZ, m_BlockType);
+ return 8 - oldMeta;
}
else
{
- // Grow the stem:
- a_Chunk.FastSetBlock(a_RelX, a_RelY, a_RelZ, m_BlockType, Meta + 1);
+ return 7 - oldMeta;
}
}
- else if (Action == paDeath)
- {
- a_Chunk.GetWorld()->DigBlock(a_RelX + a_Chunk.GetPosX() * cChunkDef::Width, a_RelY, a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width);
- }
+ return meta - oldMeta;
}
- virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
- {
- return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) == E_BLOCK_FARMLAND));
- }
- virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
+
+
+protected:
+
+ /** Grows the final produce next to the stem at the specified pos.
+ Returns true if successful, false if not. */
+ bool growProduce(cChunk & a_Chunk, Vector3i a_StemRelPos)
{
- UNUSED(a_Meta);
- return 7;
+ auto & random = GetRandomProvider();
+
+ // Check if there's another produce around the stem, if so, abort:
+ static const Vector3i neighborOfs[] =
+ {
+ { 1, 0, 0},
+ {-1, 0, 0},
+ { 0, 0, 1},
+ { 0, 0, -1},
+ };
+ bool isValid;
+ BLOCKTYPE blockType[4];
+ NIBBLETYPE blockMeta; // unused
+ isValid = a_Chunk.UnboundedRelGetBlock(a_StemRelPos + neighborOfs[0], blockType[0], blockMeta);
+ isValid = isValid && a_Chunk.UnboundedRelGetBlock(a_StemRelPos + neighborOfs[1], blockType[1], blockMeta);
+ isValid = isValid && a_Chunk.UnboundedRelGetBlock(a_StemRelPos + neighborOfs[2], blockType[2], blockMeta);
+ isValid = isValid && a_Chunk.UnboundedRelGetBlock(a_StemRelPos + neighborOfs[3], blockType[3], blockMeta);
+ if (
+ !isValid ||
+ (blockType[0] == ProduceBlockType) ||
+ (blockType[1] == ProduceBlockType) ||
+ (blockType[2] == ProduceBlockType) ||
+ (blockType[3] == ProduceBlockType)
+ )
+ {
+ // Neighbors not valid or already taken by the same produce
+ return false;
+ }
+
+ // Pick a direction in which to place the produce:
+ int x = 0, z = 0;
+ int checkType = random.RandInt(3); // The index to the neighbors array which should be checked for emptiness
+ switch (checkType)
+ {
+ case 0: x = 1; break;
+ case 1: x = -1; break;
+ case 2: z = 1; break;
+ case 3: z = -1; break;
+ }
+
+ // Check that the block in that direction is empty:
+ switch (blockType[checkType])
+ {
+ case E_BLOCK_AIR:
+ case E_BLOCK_SNOW:
+ case E_BLOCK_TALL_GRASS:
+ case E_BLOCK_DEAD_BUSH:
+ {
+ break;
+ }
+ default: return false;
+ }
+
+ // Check if there's soil under the neighbor. We already know the neighbors are valid. Place produce if ok
+ BLOCKTYPE soilType;
+ auto produceRelPos = a_StemRelPos + Vector3i(x, 0, z);
+ VERIFY(a_Chunk.UnboundedRelGetBlock(produceRelPos.addedY(-1), soilType, blockMeta));
+ switch (soilType)
+ {
+ case E_BLOCK_DIRT:
+ case E_BLOCK_GRASS:
+ case E_BLOCK_FARMLAND:
+ {
+ // Place a randomly-facing produce:
+ NIBBLETYPE meta = (ProduceBlockType == E_BLOCK_MELON) ? 0 : static_cast<NIBBLETYPE>(random.RandInt(4) % 4);
+ auto produceAbsPos = a_Chunk.RelativeToAbsolute(produceRelPos);
+ FLOGD("Growing melon / pumpkin at {0} (<{1}, {2}> from stem), overwriting {3}, growing on top of {4}, meta {5}",
+ produceAbsPos,
+ x, z,
+ ItemTypeToString(blockType[checkType]),
+ ItemTypeToString(soilType),
+ meta
+ );
+ a_Chunk.GetWorld()->SetBlock(produceAbsPos, ProduceBlockType, meta);
+ return true;
+ }
+ }
+ return false;
}
} ;
+using cBlockMelonStemHandler = cBlockStemsHandler<E_BLOCK_MELON, E_ITEM_MELON_SEEDS>;
+using cBlockPumpkinStemHandler = cBlockStemsHandler<E_BLOCK_PUMPKIN, E_ITEM_PUMPKIN_SEEDS>;
diff --git a/src/Blocks/BlockSugarcane.h b/src/Blocks/BlockSugarcane.h
index 622f82138..3c929e37d 100644
--- a/src/Blocks/BlockSugarcane.h
+++ b/src/Blocks/BlockSugarcane.h
@@ -82,13 +82,9 @@ public:
return false;
}
- virtual void OnUpdate(cChunkInterface & cChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_PluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override
- {
- if (CanGrow(a_Chunk, a_RelX, a_RelY, a_RelZ) == paGrowth)
- {
- a_Chunk.GetWorld()->GrowSugarcane(a_RelX + a_Chunk.GetPosX() * cChunkDef::Width, a_RelY, a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width, 1);
- }
- }
+
+
+
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{
@@ -97,17 +93,60 @@ public:
}
-protected:
- virtual PlantAction CanGrow(cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override
+
+
+ virtual int Grow(cChunk & a_Chunk, Vector3i a_RelPos, int a_NumStages = 1) override
{
- auto Action = paStay;
- if (((a_RelY + 1) < cChunkDef::Height) && (a_Chunk.GetBlock(a_RelX, a_RelY + 1, a_RelZ) == E_BLOCK_AIR))
+ // Check the total height of the sugarcane blocks here:
+ int top = a_RelPos.y + 1;
+ while (
+ (top < cChunkDef::Height) &&
+ (a_Chunk.GetBlock({a_RelPos.x, top, a_RelPos.z}) == E_BLOCK_SUGARCANE)
+ )
{
- Action = super::CanGrow(a_Chunk, a_RelX, a_RelY, a_RelZ);
+ ++top;
}
+ int bottom = a_RelPos.y - 1;
+ while (
+ (bottom > 0) &&
+ (a_Chunk.GetBlock({a_RelPos.x, bottom, a_RelPos.z}) == E_BLOCK_SUGARCANE)
+ )
+ {
+ --bottom;
+ }
+
+ // Grow by at most a_NumStages, but no more than max height:
+ auto toGrow = std::min(a_NumStages, a_Chunk.GetWorld()->GetMaxSugarcaneHeight() + 1 - (top - bottom));
+ Vector3i topPos(a_RelPos.x, top, a_RelPos.z);
+ for (int i = 0; i < toGrow; i++)
+ {
+ if (a_Chunk.GetBlock(topPos.addedY(i)) == E_BLOCK_AIR)
+ {
+ a_Chunk.SetBlock(topPos.addedY(i), E_BLOCK_SUGARCANE, 0);
+ }
+ else
+ {
+ return i;
+ }
+ } // for i
+ return toGrow;
+ }
+
+
- return Action;
+
+
+protected:
+
+ virtual PlantAction CanGrow(cChunk & a_Chunk, Vector3i a_RelPos) override
+ {
+ // Only allow growing if there's an air block above:
+ if (((a_RelPos.y + 1) < cChunkDef::Height) && (a_Chunk.GetBlock(a_RelPos.addedY(1)) == E_BLOCK_AIR))
+ {
+ return super::CanGrow(a_Chunk, a_RelPos);
+ }
+ return paStay;
}
} ;
diff --git a/src/Blocks/BlockTallGrass.h b/src/Blocks/BlockTallGrass.h
index c4b7194b5..f02c20523 100644
--- a/src/Blocks/BlockTallGrass.h
+++ b/src/Blocks/BlockTallGrass.h
@@ -8,6 +8,7 @@
+/** Handles the grass that is 1 block tall */
class cBlockTallGrassHandler:
public cBlockHandler
{
@@ -68,6 +69,30 @@ public:
+ /** Growing a tall grass produces a big flower (2-block high fern or double-tall grass). */
+ virtual int Grow(cChunk & a_Chunk, Vector3i a_RelPos, int a_NumStages = 1) override
+ {
+ if (a_RelPos.y > (cChunkDef::Height - 2))
+ {
+ return 0;
+ }
+ auto blockMeta = a_Chunk.GetMeta(a_RelPos);
+ NIBBLETYPE largeFlowerMeta;
+ switch (blockMeta)
+ {
+ case E_META_TALL_GRASS_GRASS: largeFlowerMeta = E_META_BIG_FLOWER_DOUBLE_TALL_GRASS; break;
+ case E_META_TALL_GRASS_FERN: largeFlowerMeta = E_META_BIG_FLOWER_LARGE_FERN; break;
+ default: return 0;
+ }
+ a_Chunk.SetBlock(a_RelPos, E_BLOCK_BIG_FLOWER, largeFlowerMeta);
+ a_Chunk.SetBlock(a_RelPos.addedY(1), E_BLOCK_BIG_FLOWER, E_META_BIG_FLOWER_TOP);
+ return 1;
+ }
+
+
+
+
+
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{
UNUSED(a_Meta);
diff --git a/src/Blocks/ChunkInterface.cpp b/src/Blocks/ChunkInterface.cpp
index 4d6301925..eff3f4da9 100644
--- a/src/Blocks/ChunkInterface.cpp
+++ b/src/Blocks/ChunkInterface.cpp
@@ -13,7 +13,7 @@
BLOCKTYPE cChunkInterface::GetBlock(Vector3i a_Pos)
{
- return m_ChunkMap->GetBlock(a_Pos.x, a_Pos.y, a_Pos.z);
+ return m_ChunkMap->GetBlock(a_Pos);
}
@@ -22,7 +22,7 @@ BLOCKTYPE cChunkInterface::GetBlock(Vector3i a_Pos)
NIBBLETYPE cChunkInterface::GetBlockMeta(Vector3i a_Pos)
{
- return m_ChunkMap->GetBlockMeta(a_Pos.x, a_Pos.y, a_Pos.z);
+ return m_ChunkMap->GetBlockMeta(a_Pos);
}
@@ -47,30 +47,18 @@ void cChunkInterface::SetBlock(Vector3i a_BlockPos, BLOCKTYPE a_BlockType, NIBBL
-void cChunkInterface::SetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_MetaData, bool a_ShouldMarkDirty, bool a_ShouldInformClient)
+void cChunkInterface::SetBlockMeta(Vector3i a_BlockPos, NIBBLETYPE a_MetaData, bool a_ShouldMarkDirty, bool a_ShouldInformClient)
{
- m_ChunkMap->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, a_MetaData, a_ShouldMarkDirty, a_ShouldInformClient);
-}
-
-
-
-
-/** Sets the block at the specified coords to the specified value.
-The replacement doesn't trigger block updates.
-The replaced blocks aren't checked for block entities (block entity is leaked if it exists at this block)
-*/
-void cChunkInterface::FastSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
-{
- m_ChunkMap->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta);
+ m_ChunkMap->SetBlockMeta(a_BlockPos, a_MetaData, a_ShouldMarkDirty, a_ShouldInformClient);
}
-void cChunkInterface::FastSetBlock(const Vector3i & a_Pos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+void cChunkInterface::FastSetBlock(Vector3i a_BlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
{
- FastSetBlock( a_Pos.x, a_Pos.y, a_Pos.z, a_BlockType, a_BlockMeta);
+ m_ChunkMap->FastSetBlock(a_BlockPos, a_BlockType, a_BlockMeta);
}
diff --git a/src/Blocks/ChunkInterface.h b/src/Blocks/ChunkInterface.h
index 6bf450748..4ff2fb3fe 100644
--- a/src/Blocks/ChunkInterface.h
+++ b/src/Blocks/ChunkInterface.h
@@ -33,15 +33,26 @@ public:
return SetBlock({a_BlockX, a_BlockY, a_BlockZ}, a_BlockType, a_BlockMeta);
}
- void SetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_MetaData, bool a_ShouldMarkDirty = true, bool a_ShouldInformClient = true);
+ /** Sets the meta for the specified block, while keeping the blocktype.
+ If a_ShouldMarkDirty is true, the chunk is marked dirty by this change (false is used eg. by water turning still).
+ If a_ShouldInformClients is true, the change is broadcast to all clients of the chunk.
+ Ignored if the chunk is invalid. */
+ void SetBlockMeta(Vector3i a_BlockPos, NIBBLETYPE a_MetaData, bool a_ShouldMarkDirty = true, bool a_ShouldInformClient = true);
+
+ /** OBSOLETE, Use the Vector3-based overload instead.
+ Sets the meta for the specified block, while keeping the blocktype.
+ If a_ShouldMarkDirty is true, the chunk is marked dirty by this change (false is used eg. by water turning still).
+ If a_ShouldInformClients is true, the change is broadcast to all clients of the chunk.
+ Ignored if the chunk is invalid. */
+ void SetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_MetaData, bool a_ShouldMarkDirty = true, bool a_ShouldInformClient = true)
+ {
+ return SetBlockMeta({a_BlockX, a_BlockY, a_BlockZ}, a_MetaData, a_ShouldMarkDirty, a_ShouldInformClient);
+ }
/** Sets the block at the specified coords to the specified value.
The replacement doesn't trigger block updates.
- The replaced blocks aren't checked for block entities (block entity is leaked if it exists at this block)
- */
- void FastSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
-
- void FastSetBlock(const Vector3i & a_Pos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
+ The replaced blocks aren't checked for block entities (block entity is leaked if it exists at this block). */
+ void FastSetBlock(Vector3i a_BlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
/** Use block entity on coordinate.
returns true if the use was successful, return false to use the block as a "normal" block */