summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/BlockEntities/DispenserEntity.cpp89
-rw-r--r--src/BlockEntities/DispenserEntity.h2
-rw-r--r--src/Chunk.cpp50
-rw-r--r--src/Chunk.h15
-rw-r--r--src/ChunkMap.cpp33
-rw-r--r--src/ChunkMap.h15
-rw-r--r--src/Defines.h13
-rw-r--r--src/World.cpp202
-rw-r--r--src/World.h18
9 files changed, 330 insertions, 107 deletions
diff --git a/src/BlockEntities/DispenserEntity.cpp b/src/BlockEntities/DispenserEntity.cpp
index 297d5273e..e03644a0f 100644
--- a/src/BlockEntities/DispenserEntity.cpp
+++ b/src/BlockEntities/DispenserEntity.cpp
@@ -3,8 +3,10 @@
#include "DispenserEntity.h"
#include "../Simulator/FluidSimulator.h"
+#include "../Entities/Boat.h"
#include "../Chunk.h"
+#include "../Defines.h"
#include "../World.h"
#include "../Entities/ProjectileEntity.h"
@@ -39,7 +41,16 @@ void cDispenserEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum)
int BlockZ = (DispZ + DispChunk->GetPosZ() * cChunkDef::Width);
// Dispense the item:
- switch (m_Contents.GetSlot(a_SlotNum).m_ItemType)
+ const cItem & SlotItem = m_Contents.GetSlot(a_SlotNum);
+ if (ItemCategory::IsMinecart(SlotItem.m_ItemType) && IsBlockRail(DispBlock)) // only actually place the minecart if there are rails!
+ {
+ if (m_World->SpawnMinecart(BlockX + 0.5, DispY + 0.5, BlockZ + 0.5, SlotItem.m_ItemType) != cEntity::INVALID_ID)
+ {
+ m_Contents.ChangeSlotCount(a_SlotNum, -1);
+ }
+ return;
+ }
+ switch (SlotItem.m_ItemType)
{
case E_ITEM_BUCKET:
{
@@ -115,7 +126,7 @@ void cDispenserEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum)
case E_BLOCK_TNT:
{
// Spawn a primed TNT entity, if space allows:
- if (DispChunk->GetBlock(DispX, DispY, DispZ) == E_BLOCK_AIR)
+ if (!cBlockInfo::IsSolid(DispBlock))
{
double TNTX = 0.5 + (DispX + DispChunk->GetPosX() * cChunkDef::Width);
double TNTZ = 0.5 + (DispZ + DispChunk->GetPosZ() * cChunkDef::Width);
@@ -128,7 +139,7 @@ void cDispenserEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum)
case E_ITEM_FLINT_AND_STEEL:
{
// Spawn fire if the block in front is air.
- if (DispChunk->GetBlock(DispX, DispY, DispZ) == E_BLOCK_AIR)
+ if (DispBlock == E_BLOCK_AIR)
{
DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_FIRE, 0);
@@ -153,7 +164,7 @@ void cDispenserEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum)
case E_ITEM_ARROW:
{
- if (SpawnProjectileFromDispenser(BlockX, DispY, BlockZ, cProjectileEntity::pkArrow, GetShootVector(Meta) * 20 + Vector3d(0, 1, 0)) != cEntity::INVALID_ID)
+ if (SpawnProjectileFromDispenser(BlockX, DispY, BlockZ, cProjectileEntity::pkArrow, GetShootVector(Meta) * 30 + Vector3d(0, 1, 0)) != cEntity::INVALID_ID)
{
m_Contents.ChangeSlotCount(a_SlotNum, -1);
}
@@ -178,6 +189,68 @@ void cDispenserEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum)
break;
}
+ case E_ITEM_BOTTLE_O_ENCHANTING:
+ {
+ if (SpawnProjectileFromDispenser(BlockX, DispY, BlockZ, cProjectileEntity::pkExpBottle, GetShootVector(Meta) * 20 + Vector3d(0, 1, 0)) != cEntity::INVALID_ID)
+ {
+ m_Contents.ChangeSlotCount(a_SlotNum, -1);
+ }
+ break;
+ }
+
+ case E_ITEM_POTION:
+ {
+ if (SpawnProjectileFromDispenser(BlockX, DispY, BlockZ, cProjectileEntity::pkSplashPotion, GetShootVector(Meta) * 20 + Vector3d(0, 1, 0), &SlotItem) != cEntity::INVALID_ID)
+ {
+ m_Contents.ChangeSlotCount(a_SlotNum, -1);
+ }
+ break;
+ }
+
+ case E_ITEM_DYE:
+ {
+ if (SlotItem.m_ItemDamage != E_META_DYE_WHITE)
+ {
+ DropFromSlot(a_Chunk, a_SlotNum);
+ break;
+ }
+ if (m_World->GrowRipePlant(BlockX, DispY, BlockZ, true))
+ {
+ m_Contents.ChangeSlotCount(a_SlotNum, -1);
+ }
+ break;
+ }
+
+ case E_ITEM_BOAT:
+ {
+ Vector3d SpawnPos;
+ if (IsBlockWater(DispBlock))
+ {
+ // Water next to the dispenser, spawn a boat above the water block
+ SpawnPos.Set(BlockX, DispY + 1, BlockZ);
+ }
+ else if (IsBlockWater(DispChunk->GetBlock(DispX, DispY - 1, DispZ)))
+ {
+ // Water one block below the dispenser, spawn a boat at the dispenser's Y level
+ SpawnPos.Set(BlockX, DispY, BlockZ);
+ }
+ else
+ {
+ // There's no eligible water block, drop the boat as a pickup
+ DropFromSlot(a_Chunk, a_SlotNum);
+ break;
+ }
+
+ SpawnPos += GetShootVector(Meta) * 0.8; // A boat is bigger than one block. Add the shoot vector to put it outside the dispenser.
+ SpawnPos += Vector3d(0.5, 0.5, 0.5);
+
+ if (m_World->SpawnBoat(SpawnPos.x, SpawnPos.y, SpawnPos.z))
+ {
+ m_Contents.ChangeSlotCount(a_SlotNum, -1);
+ }
+ break;
+ }
+
case E_ITEM_FIREWORK_ROCKET:
{
// TODO: Add the fireworks entity
@@ -189,27 +262,25 @@ void cDispenserEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum)
DropFromSlot(a_Chunk, a_SlotNum);
break;
}
- } // switch (ItemType)
+ } // switch (SlotItem.m_ItemType)
}
-UInt32 cDispenserEntity::SpawnProjectileFromDispenser(int a_BlockX, int a_BlockY, int a_BlockZ, cProjectileEntity::eKind a_Kind, const Vector3d & a_ShootVector)
+UInt32 cDispenserEntity::SpawnProjectileFromDispenser(int a_BlockX, int a_BlockY, int a_BlockZ, cProjectileEntity::eKind a_Kind, const Vector3d & a_ShootVector, const cItem * a_Item)
{
return m_World->CreateProjectile(
static_cast<double>(a_BlockX + 0.5),
static_cast<double>(a_BlockY + 0.5),
static_cast<double>(a_BlockZ + 0.5),
- a_Kind, nullptr, nullptr, &a_ShootVector
+ a_Kind, nullptr, a_Item, &a_ShootVector
);
}
-
-
Vector3d cDispenserEntity::GetShootVector(NIBBLETYPE a_Meta)
{
switch (a_Meta & 0x7)
diff --git a/src/BlockEntities/DispenserEntity.h b/src/BlockEntities/DispenserEntity.h
index c9b553017..03ce3ad69 100644
--- a/src/BlockEntities/DispenserEntity.h
+++ b/src/BlockEntities/DispenserEntity.h
@@ -26,7 +26,7 @@ public:
/** Spawns a projectile of the given kind in front of the dispenser with the specified speed.
Returns the UniqueID of the spawned projectile, or 0 on failure. */
- UInt32 SpawnProjectileFromDispenser(int a_BlockX, int a_BlockY, int a_BlockZ, cProjectileEntity::eKind a_Kind, const Vector3d & a_Speed);
+ UInt32 SpawnProjectileFromDispenser(int a_BlockX, int a_BlockY, int a_BlockZ, cProjectileEntity::eKind a_Kind, const Vector3d & a_Speed, const cItem * a_Item = nullptr);
/** Returns a unit vector in the cardinal direction of where the dispenser is facing. */
Vector3d GetShootVector(NIBBLETYPE a_Meta);
diff --git a/src/Chunk.cpp b/src/Chunk.cpp
index 363de9933..06d5eb319 100644
--- a/src/Chunk.cpp
+++ b/src/Chunk.cpp
@@ -929,7 +929,7 @@ void cChunk::ApplyWeatherToTop()
-void cChunk::GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, MTRand & a_TickRandom)
+bool cChunk::GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, MTRand & a_TickRandom)
{
// Convert the stem BlockType into produce BlockType
BLOCKTYPE ProduceType;
@@ -940,7 +940,7 @@ void cChunk::GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_Bl
default:
{
ASSERT(!"Unhandled blocktype in TickMelonPumpkin()");
- return;
+ return false;
}
}
@@ -961,7 +961,7 @@ void cChunk::GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_Bl
)
{
// Neighbors not valid or already taken by the same produce
- return;
+ return false;
}
// Pick a direction in which to place the produce:
@@ -985,7 +985,7 @@ void cChunk::GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_Bl
{
break;
}
- default: return;
+ default: return false;
}
// Check if there's soil under the neighbor. We already know the neighbors are valid. Place produce if ok
@@ -1013,13 +1013,14 @@ void cChunk::GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_Bl
break;
}
}
+ return true;
}
-void cChunk::GrowSugarcane(int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks)
+int cChunk::GrowSugarcane(int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks)
{
// Check the total height of the sugarcane blocks here:
int Top = a_RelY + 1;
@@ -1051,16 +1052,17 @@ void cChunk::GrowSugarcane(int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks)
}
else
{
- break;
+ return i;
}
} // for i
+ return ToGrow;
}
-void cChunk::GrowCactus(int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks)
+int cChunk::GrowCactus(int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks)
{
// Check the total height of the sugarcane blocks here:
int Top = a_RelY + 1;
@@ -1093,9 +1095,41 @@ void cChunk::GrowCactus(int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks)
}
else
{
- break;
+ return i;
}
} // for i
+ return ToGrow;
+}
+
+
+
+
+
+bool cChunk::GrowTallGrass(int a_RelX, int a_RelY, int a_RelZ)
+{
+ if (a_RelY > (cChunkDef::Height - 2))
+ {
+ return false;
+ }
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ if (!UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ, BlockType, BlockMeta))
+ {
+ return false;
+ }
+ if (BlockType != E_BLOCK_TALL_GRASS)
+ {
+ return false;
+ }
+ 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 false;
+ }
+ return UnboundedRelFastSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_BIG_FLOWER, LargeFlowerMeta) &&
+ UnboundedRelFastSetBlock(a_RelX, a_RelY + 1, a_RelZ, E_BLOCK_BIG_FLOWER, 8);
}
diff --git a/src/Chunk.h b/src/Chunk.h
index c7987723b..162c8de96 100644
--- a/src/Chunk.h
+++ b/src/Chunk.h
@@ -580,14 +580,17 @@ private:
/** Adds snow to the top of snowy biomes and hydrates farmland / fills cauldrons in rainy biomes */
void ApplyWeatherToTop(void);
- /** Grows sugarcane by the specified number of blocks, but no more than 3 blocks high (used by both bonemeal and ticking) */
- void GrowSugarcane (int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks);
+ /** Grows sugarcane by the specified number of blocks, but no more than 3 blocks high (used by both bonemeal and ticking); returns the amount of blocks the sugarcane grew inside this call */
+ int GrowSugarcane (int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks);
- /** Grows cactus by the specified number of blocks, but no more than 3 blocks high (used by both bonemeal and ticking) */
- void GrowCactus (int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks);
+ /** Grows cactus by the specified number of blocks, but no more than 3 blocks high (used by both bonemeal and ticking); returns the amount of blocks the cactus grew inside this call */
+ int GrowCactus (int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks);
- /** Grows a melon or a pumpkin next to the block specified (assumed to be the stem) */
- void GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, MTRand & a_Random);
+ /** Grows a tall grass present at the block specified to a two tall grass; returns true if the grass grew */
+ bool GrowTallGrass (int a_RelX, int a_RelY, int a_RelZ);
+
+ /** Grows a melon or a pumpkin next to the block specified (assumed to be the stem); returns true if the pumpkin or melon sucessfully grew */
+ bool GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, MTRand & a_Random);
/** Called by Tick() when an entity moves out of this chunk into a neighbor; moves the entity and sends spawn / despawn packet to clients */
void MoveEntityToNewChunk(cEntity * a_Entity);
diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp
index 83c1c12dd..55cabaa67 100644
--- a/src/ChunkMap.cpp
+++ b/src/ChunkMap.cpp
@@ -2597,7 +2597,7 @@ void cChunkMap::GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty)
-void cChunkMap::GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, MTRand & a_Rand)
+bool cChunkMap::GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, MTRand & a_Rand)
{
int ChunkX, ChunkZ;
cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
@@ -2606,15 +2606,16 @@ void cChunkMap::GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCK
cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ);
if (Chunk != nullptr)
{
- Chunk->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_Rand);
+ return Chunk->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_Rand);
}
+ return false;
}
-void cChunkMap::GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow)
+int cChunkMap::GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow)
{
int ChunkX, ChunkZ;
cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
@@ -2623,15 +2624,16 @@ void cChunkMap::GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_Nu
cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ);
if (Chunk != nullptr)
{
- Chunk->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow);
+ return Chunk->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow);
}
+ return 0;
}
-void cChunkMap::GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow)
+int cChunkMap::GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow)
{
int ChunkX, ChunkZ;
cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
@@ -2640,8 +2642,27 @@ void cChunkMap::GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBl
cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ);
if (Chunk != nullptr)
{
- Chunk->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow);
+ return Chunk->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow);
}
+ return 0;
+}
+
+
+
+
+
+bool cChunkMap::GrowTallGrass(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ int ChunkX, ChunkZ;
+ cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
+
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ);
+ if (Chunk != nullptr)
+ {
+ return Chunk->GrowTallGrass(a_BlockX, a_BlockY, a_BlockZ);
+ }
+ return 0;
}
diff --git a/src/ChunkMap.h b/src/ChunkMap.h
index bbde06dcf..8a604ac09 100644
--- a/src/ChunkMap.h
+++ b/src/ChunkMap.h
@@ -360,14 +360,17 @@ public:
/** Returns the number of valid chunks and the number of dirty chunks */
void GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty);
- /** Grows a melon or a pumpkin next to the block specified (assumed to be the stem) */
- void GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, MTRand & a_Rand);
+ /** Grows a melon or a pumpkin next to the block specified (assumed to be the stem); returns true if the pumpkin or melon sucessfully grew */
+ bool GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, MTRand & a_Rand);
- /** Grows a sugarcane present at the block specified by the amount of blocks specified, up to the max height specified in the config */
- void GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow);
+ /** Grows a sugarcane present at the block specified by the amount of blocks specified, up to the max height specified in the config; returns the amount of blocks the sugarcane grew inside this call */
+ int GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow);
- /** Grows a cactus present at the block specified by the amount of blocks specified, up to the max height specified in the config */
- void GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow);
+ /** Grows a cactus present at the block specified by the amount of blocks specified, up to the max height specified in the config; returns the amount of blocks the cactus grew inside this call */
+ int GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow);
+
+ /** Grows a tall grass present at the block specified to a two tall grass; returns true if the grass grew */
+ bool GrowTallGrass(int a_BlockX, int a_BlockY, int a_BlockZ);
/** Sets the blockticking to start at the specified block. Only one blocktick per chunk may be set, second call overwrites the first call */
void SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ);
diff --git a/src/Defines.h b/src/Defines.h
index 37b80cca3..13049ce4f 100644
--- a/src/Defines.h
+++ b/src/Defines.h
@@ -788,6 +788,19 @@ namespace ItemCategory
+ inline bool IsMinecart(short a_ItemType)
+ {
+ return (
+ (a_ItemType == E_ITEM_MINECART) ||
+ (a_ItemType == E_ITEM_CHEST_MINECART) ||
+ (a_ItemType == E_ITEM_FURNACE_MINECART) ||
+ (a_ItemType == E_ITEM_MINECART_WITH_TNT) ||
+ (a_ItemType == E_ITEM_MINECART_WITH_HOPPER)
+ );
+ }
+
+
+
inline bool IsArmor(short a_ItemType)
{
return (
diff --git a/src/World.cpp b/src/World.cpp
index d05f90365..a8225693f 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -17,6 +17,7 @@
#include "WorldStorage/ScoreboardSerializer.h"
// Entities (except mobs):
+#include "Entities/Boat.h"
#include "Entities/ExpOrb.h"
#include "Entities/FallingBlock.h"
#include "Entities/Minecart.h"
@@ -186,6 +187,8 @@ cWorld::cWorld(const AString & a_WorldName, eDimension a_Dimension, const AStrin
m_IsPumpkinBonemealable(true),
m_IsSaplingBonemealable(true),
m_IsSugarcaneBonemealable(false),
+ m_IsBigFlowerBonemealable(true),
+ m_IsTallGrassBonemealable(true),
m_bCommandBlocksEnabled(true),
m_bUseChatPrefixes(false),
m_TNTShrapnelLevel(slNone),
@@ -470,6 +473,8 @@ void cWorld::Start(void)
m_IsPumpkinBonemealable = IniFile.GetValueSetB("Plants", "IsPumpkinBonemealable", false);
m_IsSaplingBonemealable = IniFile.GetValueSetB("Plants", "IsSaplingBonemealable", true);
m_IsSugarcaneBonemealable = IniFile.GetValueSetB("Plants", "IsSugarcaneBonemealable", false);
+ m_IsBigFlowerBonemealable = IniFile.GetValueSetB("Plants", "IsBigFlowerBonemealable", true);
+ m_IsTallGrassBonemealable = IniFile.GetValueSetB("Plants", "IsTallGrassBonemealable", true);
m_IsDeepSnowEnabled = IniFile.GetValueSetB("Physics", "DeepSnow", true);
m_ShouldLavaSpawnFire = IniFile.GetValueSetB("Physics", "ShouldLavaSpawnFire", true);
int TNTShrapnelLevel = IniFile.GetValueSetI("Physics", "TNTShrapnelLevel", static_cast<int>(slAll));
@@ -1705,25 +1710,22 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy
{
case E_BLOCK_CARROTS:
{
- if (a_IsByBonemeal && !m_IsCarrotsBonemealable)
+ if ((a_IsByBonemeal && !m_IsCarrotsBonemealable) || (BlockMeta >= 7))
{
return false;
}
- if (BlockMeta < 7)
+ if (!a_IsByBonemeal)
{
- if (!a_IsByBonemeal)
- {
- ++BlockMeta;
- }
- else
- {
- BlockMeta += random.NextInt(4) + 2;
- BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7));
- }
- FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta);
- BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0);
+ ++BlockMeta;
+ }
+ else
+ {
+ BlockMeta += random.NextInt(4) + 2;
+ BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7));
}
- return BlockMeta == 7;
+ FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta);
+ BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0);
+ return true;
}
case E_BLOCK_COCOA_POD:
@@ -1731,36 +1733,34 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy
NIBBLETYPE TypeMeta = BlockMeta & 0x03;
int GrowState = BlockMeta >> 2;
- if (GrowState < 2)
+ if (GrowState >= 2)
{
- GrowState++;
- FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, static_cast<NIBBLETYPE>(GrowState << 2 | TypeMeta));
- BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0);
+ return false;
}
- return GrowState == 2;
+ ++GrowState;
+ FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, static_cast<NIBBLETYPE>(GrowState << 2 | TypeMeta));
+ BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0);
+ return true;
}
case E_BLOCK_CROPS:
{
- if (a_IsByBonemeal && !m_IsCropsBonemealable)
+ if ((a_IsByBonemeal && !m_IsCropsBonemealable) || (BlockMeta >= 7))
{
return false;
}
- if (BlockMeta < 7)
+ if (!a_IsByBonemeal)
{
- if (!a_IsByBonemeal)
- {
- ++BlockMeta;
- }
- else
- {
- BlockMeta += random.NextInt(4) + 2;
- BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7));
- }
- FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta);
- BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0);
+ ++BlockMeta;
+ }
+ else
+ {
+ BlockMeta += random.NextInt(4) + 2;
+ BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7));
}
- return BlockMeta == 7;
+ FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta);
+ BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0);
+ return true;
}
case E_BLOCK_MELON_STEM:
@@ -1782,7 +1782,6 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy
BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7));
}
FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta);
- BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0);
}
else
{
@@ -1790,32 +1789,33 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy
{
return false;
}
- GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, BlockType);
+ if (!GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, BlockType))
+ {
+ return false;
+ }
}
- return BlockMeta == 7;
+ BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0);
+ return true;
}
case E_BLOCK_POTATOES:
{
- if (a_IsByBonemeal && !m_IsPotatoesBonemealable)
+ if ((a_IsByBonemeal && !m_IsPotatoesBonemealable) || (BlockMeta >= 7))
{
return false;
}
- if (BlockMeta < 7)
+ if (!a_IsByBonemeal)
{
- if (!a_IsByBonemeal)
- {
- ++BlockMeta;
- }
- else
- {
- BlockMeta += random.NextInt(4) + 2;
- BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7));
- }
- FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta);
- BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0);
+ ++BlockMeta;
}
- return BlockMeta == 7;
+ else
+ {
+ BlockMeta += random.NextInt(4) + 2;
+ BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7));
+ }
+ FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta);
+ BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0);
+ return true;
}
case E_BLOCK_PUMPKIN_STEM:
@@ -1837,7 +1837,6 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy
BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7));
}
FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta);
- BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0);
}
else
{
@@ -1845,9 +1844,13 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy
{
return false;
}
- GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, BlockType);
+ if (!GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, BlockType))
+ {
+ return false;
+ }
}
- return BlockMeta == 7;
+ BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0);
+ return true;
}
case E_BLOCK_SAPLING:
@@ -1872,15 +1875,13 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy
}
FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, static_cast<NIBBLETYPE>(GrowState << 3 | TypeMeta));
- BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0);
}
else if (random.NextInt(99) < 45)
{
-
GrowTreeFromSapling(a_BlockX, a_BlockY, a_BlockZ, BlockMeta);
- return true;
}
- return false;
+ BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0);
+ return true;
}
case E_BLOCK_GRASS:
@@ -1930,7 +1931,11 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy
{
return false;
}
- m_ChunkMap->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, m_MaxSugarcaneHeight);
+ if (m_ChunkMap->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, 1) == 0)
+ {
+ return false;
+ }
+ BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0);
return true;
}
@@ -1940,9 +1945,58 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy
{
return false;
}
- m_ChunkMap->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, m_MaxCactusHeight);
+ if (m_ChunkMap->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, 1) == 0)
+ {
+ return false;
+ }
+ BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0);
+ return true;
+ }
+
+ case E_BLOCK_TALL_GRASS:
+ {
+ if (a_IsByBonemeal && !m_IsTallGrassBonemealable)
+ {
+ return false;
+ }
+ if (!m_ChunkMap->GrowTallGrass(a_BlockX, a_BlockY, a_BlockZ))
+ {
+ return false;
+ }
+ BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0);
+ return true;
+ }
+
+ case E_BLOCK_BIG_FLOWER:
+ {
+ if (a_IsByBonemeal && !m_IsBigFlowerBonemealable)
+ {
+ return false;
+ }
+ if (BlockMeta & 8) // the upper flower block does not save the type of the flower
+ {
+ GetBlockTypeMeta(a_BlockX, a_BlockY - 1, a_BlockZ, BlockType, BlockMeta);
+ if (BlockType != E_BLOCK_BIG_FLOWER)
+ {
+ return false;
+ }
+ }
+ if (
+ (BlockMeta == E_META_BIG_FLOWER_DOUBLE_TALL_GRASS) ||
+ (BlockMeta == E_META_BIG_FLOWER_LARGE_FERN)
+ ) // tall grass and fern do not work
+ {
+ return false;
+ }
+
+ // spawn flower item
+ BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0);
+ cItems FlowerItem;
+ FlowerItem.Add(E_BLOCK_BIG_FLOWER, 1, BlockMeta);
+ SpawnItemPickups(FlowerItem, a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5);
return true;
}
+
} // switch (BlockType)
return false;
}
@@ -1951,28 +2005,28 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy
-void cWorld::GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow)
+int cWorld::GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow)
{
- m_ChunkMap->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow);
+ return m_ChunkMap->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow);
}
-void cWorld::GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType)
+bool cWorld::GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType)
{
MTRand Rand;
- m_ChunkMap->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, Rand);
+ return m_ChunkMap->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, Rand);
}
-void cWorld::GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow)
+int cWorld::GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow)
{
- m_ChunkMap->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow);
+ return m_ChunkMap->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow);
}
@@ -2181,6 +2235,24 @@ UInt32 cWorld::SpawnMinecart(double a_X, double a_Y, double a_Z, int a_MinecartT
+UInt32 cWorld::SpawnBoat(double a_X, double a_Y, double a_Z)
+{
+ cBoat * Boat = new cBoat(a_X, a_Y, a_Z);
+ if (Boat == nullptr)
+ {
+ return cEntity::INVALID_ID;
+ }
+ if (!Boat->Initialize(*this))
+ {
+ delete Boat;
+ return cEntity::INVALID_ID;
+ }
+ return Boat->GetUniqueID();
+}
+
+
+
+
UInt32 cWorld::SpawnPrimedTNT(double a_X, double a_Y, double a_Z, int a_FuseTicks, double a_InitialVelocityCoeff)
{
cTNTEntity * TNT = new cTNTEntity(a_X, a_Y, a_Z, a_FuseTicks);
diff --git a/src/World.h b/src/World.h
index eafe7b9b6..faddc6f59 100644
--- a/src/World.h
+++ b/src/World.h
@@ -445,6 +445,10 @@ public:
Returns the UniqueID of the spawned minecart, or cEntity::INVALID_ID on failure. */
UInt32 SpawnMinecart(double a_X, double a_Y, double a_Z, int a_MinecartType, const cItem & a_Content = cItem(), int a_BlockHeight = 1);
+ /** Spawns a boat at the given coordinates.
+ Returns the UniqueID of the spawned boat, or cEntity::INVALID_ID on failure. */
+ UInt32 SpawnBoat(double a_X, double a_Y, double a_Z);
+
/** Spawns an experience orb at the given location with the given reward.
Returns the UniqueID of the spawned experience orb, or cEntity::INVALID_ID on failure. */
virtual UInt32 SpawnExperienceOrb(double a_X, double a_Y, double a_Z, int a_Reward) override;
@@ -582,14 +586,14 @@ public:
/** Grows the plant at the specified block to its ripe stage (bonemeal used); returns false if the block is not growable. If a_IsBonemeal is true, block is not grown if not allowed in world.ini */
bool GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsByBonemeal = false);
- /** Grows a cactus present at the block specified by the amount of blocks specified, up to the max height specified in the config */
- void GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow);
+ /** Grows a cactus present at the block specified by the amount of blocks specified, up to the max height specified in the config; returns the amount of blocks the cactus grew inside this call */
+ int GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow);
- /** Grows a melon or a pumpkin next to the block specified (assumed to be the stem) */
- void GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType);
+ /** Grows a melon or a pumpkin next to the block specified (assumed to be the stem); returns true if the pumpkin or melon sucessfully grew. */
+ bool GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType);
- /** Grows a sugarcane present at the block specified by the amount of blocks specified, up to the max height specified in the config */
- void GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow);
+ /** Grows a sugarcane present at the block specified by the amount of blocks specified, up to the max height specified in the config; returns the amount of blocks the sugarcane grew inside this call */
+ int GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow);
/** Returns the biome at the specified coords. Reads the biome from the chunk, if loaded, otherwise uses the world generator to provide the biome value */
EMCSBiome GetBiomeAt(int a_BlockX, int a_BlockZ);
@@ -933,6 +937,8 @@ private:
bool m_IsPumpkinBonemealable;
bool m_IsSaplingBonemealable;
bool m_IsSugarcaneBonemealable;
+ bool m_IsBigFlowerBonemealable;
+ bool m_IsTallGrassBonemealable;
/** Whether command blocks are enabled or not */
bool m_bCommandBlocksEnabled;