diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/BlockEntities/DispenserEntity.cpp | 89 | ||||
-rw-r--r-- | src/BlockEntities/DispenserEntity.h | 2 | ||||
-rw-r--r-- | src/Chunk.cpp | 50 | ||||
-rw-r--r-- | src/Chunk.h | 15 | ||||
-rw-r--r-- | src/ChunkMap.cpp | 33 | ||||
-rw-r--r-- | src/ChunkMap.h | 15 | ||||
-rw-r--r-- | src/Defines.h | 13 | ||||
-rw-r--r-- | src/World.cpp | 202 | ||||
-rw-r--r-- | src/World.h | 18 |
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; |