summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Server/Plugins/APIDump/APIDesc.lua8
-rw-r--r--src/BlockEntities/DispenserEntity.cpp89
-rw-r--r--src/BlockEntities/DispenserEntity.h2
-rw-r--r--src/Chunk.cpp19
-rw-r--r--src/Chunk.h12
-rw-r--r--src/ChunkMap.cpp15
-rw-r--r--src/ChunkMap.h12
-rw-r--r--src/Defines.h13
-rw-r--r--src/World.cpp131
-rw-r--r--src/World.h16
10 files changed, 211 insertions, 106 deletions
diff --git a/Server/Plugins/APIDump/APIDesc.lua b/Server/Plugins/APIDump/APIDesc.lua
index 0f399bd07..16c39824b 100644
--- a/Server/Plugins/APIDump/APIDesc.lua
+++ b/Server/Plugins/APIDump/APIDesc.lua
@@ -2519,10 +2519,10 @@ end
GetTNTShrapnelLevel = { Params = "", Return = "{{Globals#ShrapnelLevel|ShrapnelLevel}}", Notes = "Returns the shrapnel level, representing the block types that are propelled outwards following an explosion. Based on this value and a random picker, blocks are selectively converted to physics entities (FallingSand) and flung outwards." },
GetWeather = { Params = "", Return = "eWeather", Notes = "Returns the current weather in the world (wSunny, wRain, wStorm). To check for weather, use IsWeatherXXX() functions instead." },
GetWorldAge = { Params = "", Return = "number", Notes = "Returns the total age of the world, in ticks. The age always grows, cannot be set by plugins and is unrelated to TimeOfDay." },
- GrowCactus = { Params = "BlockX, BlockY, BlockZ, NumBlocksToGrow", Return = "", Notes = "Grows a cactus block at the specified coords, by up to the specified number of blocks. Adheres to the world's maximum cactus growth (GetMaxCactusHeight())." },
- GrowMelonPumpkin = { Params = "BlockX, BlockY, BlockZ, StemType", Return = "", Notes = "Grows a melon or pumpkin, based on the stem type specified (assumed to be in the coords provided). Checks for normal melon / pumpkin growth conditions - stem not having another produce next to it and suitable ground below." },
+ GrowCactus = { Params = "BlockX, BlockY, BlockZ, NumBlocksToGrow", Return = "number", Notes = "Grows a cactus block at the specified coords, by up to the specified number of blocks. Adheres to the world's maximum cactus growth (GetMaxCactusHeight()). Returns the amount of blocks the cactus grew inside this call." },
+ GrowMelonPumpkin = { Params = "BlockX, BlockY, BlockZ, StemType", Return = "bool", Notes = "Grows a melon or pumpkin, based on the stem type specified (assumed to be in the coords provided). Checks for normal melon / pumpkin growth conditions - stem not having another produce next to it and suitable ground below. Returns true if the melon or pumpkin grew successfully." },
GrowRipePlant = { Params = "BlockX, BlockY, BlockZ, IsByBonemeal", Return = "bool", Notes = "Grows the plant at the specified coords. If IsByBonemeal is true, checks first if the specified plant type is bonemealable in the settings. Returns true if the plant was grown, false if not." },
- GrowSugarcane = { Params = "BlockX, BlockY, BlockZ, NumBlocksToGrow", Return = "", Notes = "Grows a sugarcane block at the specified coords, by up to the specified number of blocks. Adheres to the world's maximum sugarcane growth (GetMaxSugarcaneHeight())." },
+ GrowSugarcane = { Params = "BlockX, BlockY, BlockZ, NumBlocksToGrow", Return = "number", Notes = "Grows a sugarcane block at the specified coords, by up to the specified number of blocks. Adheres to the world's maximum sugarcane growth (GetMaxSugarcaneHeight()). Returns the amount of blocks the sugarcane grew inside this call." },
GrowTree = { Params = "BlockX, BlockY, BlockZ", Return = "", Notes = "Grows a tree based at the specified coords. If there is a sapling there, grows the tree based on that sapling, otherwise chooses a tree image based on the biome." },
GrowTreeByBiome = { Params = "BlockX, BlockY, BlockZ", Return = "", Notes = "Grows a tree based at the specified coords. The tree type is picked from types available for the biome at those coords." },
GrowTreeFromSapling = { Params = "BlockX, BlockY, BlockZ, SaplingMeta", Return = "", Notes = "Grows a tree based at the specified coords. The tree type is determined from the sapling meta (the sapling itself needn't be present)." },
@@ -2593,6 +2593,7 @@ end
{ Params = "{{cItems|Pickups}}, X, Y, Z, SpeedX, SpeedY, SpeedZ", Return = "", Notes = "Spawns the specified pickups at the position specified. All the pickups fly away from the spawn position using the specified speed." },
},
SpawnMinecart = { Params = "X, Y, Z, MinecartType, Item, BlockHeight", Return = "number", Notes = "Spawns a minecart at the specific coordinates. MinecartType is the item type of the minecart. If the minecart is an empty minecart then the given item is the block inside the minecart, and blockheight is the distance of the block and the minecart." },
+ SpawnBoat = { Params = "X, Y, Z", Return = "number", Notes = "Spawns a boat at the specific coordinates." },
SpawnMob = { Params = "X, Y, Z, {{cMonster|MonsterType}}, [Baby]", Return = "EntityID", Notes = "Spawns the specified type of mob at the specified coords. If the Baby parameter is true, the mob will be a baby. Returns the EntityID of the creates entity, or -1 on failure. " },
SpawnFallingBlock = { Params = "X, Y, Z, BlockType, BlockMeta", Return = "EntityID", Notes = "Spawns an {{cFallingBlock|Falling Block}} entity at the specified coords with the given block type/meta" },
SpawnExperienceOrb = { Params = "X, Y, Z, Reward", Return = "EntityID", Notes = "Spawns an {{cExpOrb|experience orb}} at the specified coords, with the given reward" },
@@ -2679,6 +2680,7 @@ World:ForEachEntity(
IsArmor = { Params = "ItemType", Return = "bool", Notes = "(STATIC) Returns true if the specified item type is any kind of an armor." },
IsAxe = { Params = "ItemType", Return = "bool", Notes = "(STATIC) Returns true if the specified item type is any kind of an axe." },
IsBoots = { Params = "ItemType", Return = "bool", Notes = "(STATIC) Returns true if the specified item type is any kind of boots." },
+ IsMinecart = { Params = "ItemType", Return = "bool", Notes = "(STATIC) Returns true if the specified item type is any kind of a minecart." },
IsChestPlate = { Params = "ItemType", Return = "bool", Notes = "(STATIC) Returns true if the specified item type is any kind of a chestplate." },
IsHelmet = { Params = "ItemType", Return = "bool", Notes = "(STATIC) Returns true if the specified item type is any kind of a helmet." },
IsHoe = { Params = "ItemType", Return = "bool", Notes = "(STATIC) Returns true if the specified item type is any kind of a hoe." },
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..c65c043da 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,10 @@ void cChunk::GrowCactus(int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks)
}
else
{
- break;
+ return i;
}
} // for i
+ return ToGrow;
}
diff --git a/src/Chunk.h b/src/Chunk.h
index c7987723b..f8bd9075d 100644
--- a/src/Chunk.h
+++ b/src/Chunk.h
@@ -580,14 +580,14 @@ 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 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..be22244ad 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,9 @@ 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;
}
diff --git a/src/ChunkMap.h b/src/ChunkMap.h
index bbde06dcf..b481e3c9b 100644
--- a/src/ChunkMap.h
+++ b/src/ChunkMap.h
@@ -360,14 +360,14 @@ 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);
/** 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..55079250c 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"
@@ -1705,25 +1706,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 +1729,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:
@@ -1783,6 +1779,7 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy
}
FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta);
BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0);
+ return true;
}
else
{
@@ -1790,32 +1787,28 @@ 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);
+ return GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, BlockType);
}
- return BlockMeta == 7;
}
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;
+ }
+ 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_PUMPKIN_STEM:
@@ -1838,6 +1831,7 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy
}
FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta);
BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0);
+ return true;
}
else
{
@@ -1845,9 +1839,8 @@ 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);
+ return GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, BlockType);
}
- return BlockMeta == 7;
}
case E_BLOCK_SAPLING:
@@ -1930,8 +1923,7 @@ 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);
- return true;
+ return m_ChunkMap->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, 1) != 0;
}
case E_BLOCK_CACTUS:
@@ -1940,8 +1932,7 @@ 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);
- return true;
+ return m_ChunkMap->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, 1) != 0;
}
} // switch (BlockType)
return false;
@@ -1951,28 +1942,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 +2172,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..2181671ad 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);