diff options
author | Mattes D <github@xoft.cz> | 2019-10-11 11:02:53 +0200 |
---|---|---|
committer | Mattes D <github@xoft.cz> | 2019-10-28 10:45:43 +0100 |
commit | 61904af626b036b6e4e045ca219b2a361aa45a6e (patch) | |
tree | 60b99ab37c9ec87ca96d403b3254a4da023cf6ac /src | |
parent | Update README.md (#4423) (diff) | |
download | cuberite-61904af626b036b6e4e045ca219b2a361aa45a6e.tar cuberite-61904af626b036b6e4e045ca219b2a361aa45a6e.tar.gz cuberite-61904af626b036b6e4e045ca219b2a361aa45a6e.tar.bz2 cuberite-61904af626b036b6e4e045ca219b2a361aa45a6e.tar.lz cuberite-61904af626b036b6e4e045ca219b2a361aa45a6e.tar.xz cuberite-61904af626b036b6e4e045ca219b2a361aa45a6e.tar.zst cuberite-61904af626b036b6e4e045ca219b2a361aa45a6e.zip |
Diffstat (limited to 'src')
40 files changed, 1464 insertions, 1287 deletions
diff --git a/src/BlockEntities/DispenserEntity.cpp b/src/BlockEntities/DispenserEntity.cpp index 9d8267c38..6cb63b495 100644 --- a/src/BlockEntities/DispenserEntity.cpp +++ b/src/BlockEntities/DispenserEntity.cpp @@ -28,7 +28,7 @@ void cDispenserEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) Vector3i dispRelCoord(GetRelPos()); auto meta = a_Chunk.GetMeta(dispRelCoord); AddDropSpenserDir(dispRelCoord, meta); - auto dispChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(dispRelCoord.x, dispRelCoord.z); + auto dispChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(dispRelCoord); if (dispChunk == nullptr) { // Would dispense into / interact with a non-loaded chunk, ignore the tick diff --git a/src/BlockEntities/HopperEntity.cpp b/src/BlockEntities/HopperEntity.cpp index a0705e996..e85cfb952 100644 --- a/src/BlockEntities/HopperEntity.cpp +++ b/src/BlockEntities/HopperEntity.cpp @@ -299,7 +299,7 @@ bool cHopperEntity::MoveItemsOut(cChunk & a_Chunk, Int64 a_CurrentTick) // Convert coords to relative: auto relCoord = a_Chunk.AbsoluteToRelative(out.second); - auto destChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(relCoord.x, relCoord.z); + auto destChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(relCoord); if (destChunk == nullptr) { // The destination chunk has been unloaded, don't tick @@ -379,7 +379,7 @@ bool cHopperEntity::MoveItemsFromChest(cChunk & a_Chunk) for (const auto & ofs: neighborOfs) { auto neighborRelCoord = ofs.addedXZ(m_RelX, m_RelZ); - auto neighbor = a_Chunk.GetRelNeighborChunkAdjustCoords(neighborRelCoord.x, neighborRelCoord.z); + auto neighbor = a_Chunk.GetRelNeighborChunkAdjustCoords(neighborRelCoord); if (neighbor == nullptr) { continue; @@ -543,7 +543,7 @@ bool cHopperEntity::MoveItemsToChest(cChunk & a_Chunk, Vector3i a_Coords) for (const auto & ofs: neighborOfs) { auto otherHalfRelCoord = relCoord + ofs; - auto neighbor = a_Chunk.GetRelNeighborChunkAdjustCoords(otherHalfRelCoord.x, otherHalfRelCoord.z); + auto neighbor = a_Chunk.GetRelNeighborChunkAdjustCoords(otherHalfRelCoord); if (neighbor == nullptr) { continue; diff --git a/src/BlockEntities/MobSpawnerEntity.cpp b/src/BlockEntities/MobSpawnerEntity.cpp index 85bdd7ecf..76b86f73a 100644 --- a/src/BlockEntities/MobSpawnerEntity.cpp +++ b/src/BlockEntities/MobSpawnerEntity.cpp @@ -151,36 +151,36 @@ void cMobSpawnerEntity::SpawnEntity(void) break; } - int RelX = m_RelX + static_cast<int>((Random.RandReal<double>() - Random.RandReal<double>()) * 4.0); - int RelY = m_Pos.y + Random.RandInt(-1, 1); - int RelZ = m_RelZ + static_cast<int>((Random.RandReal<double>() - Random.RandReal<double>()) * 4.0); - - cChunk * Chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelX, RelZ); - if ((Chunk == nullptr) || !Chunk->IsValid()) + Vector3i spawnRelPos(GetRelPos()); + spawnRelPos += Vector3i( + static_cast<int>((Random.RandReal<double>() - Random.RandReal<double>()) * 4.0), + Random.RandInt(-1, 1), + static_cast<int>((Random.RandReal<double>() - Random.RandReal<double>()) * 4.0) + ); + + auto chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(spawnRelPos); + if ((chunk == nullptr) || !chunk->IsValid()) { continue; } - EMCSBiome Biome = Chunk->GetBiomeAt(RelX, RelZ); + EMCSBiome Biome = chunk->GetBiomeAt(spawnRelPos.x, spawnRelPos.z); - if (cMobSpawner::CanSpawnHere(Chunk, RelX, RelY, RelZ, MobType, Biome)) + if (cMobSpawner::CanSpawnHere(chunk, spawnRelPos, MobType, Biome)) { - double PosX = Chunk->GetPosX() * cChunkDef::Width + RelX; - double PosZ = Chunk->GetPosZ() * cChunkDef::Width + RelZ; - - auto Monster = cMonster::NewMonsterFromType(MobType); - if (Monster == nullptr) + auto absPos = chunk->RelativeToAbsolute(spawnRelPos); + auto monster = cMonster::NewMonsterFromType(MobType); + if (monster == nullptr) { continue; } - - Monster->SetPosition(PosX, RelY, PosZ); - Monster->SetYaw(Random.RandReal(360.0f)); - if (Chunk->GetWorld()->SpawnMobFinalize(std::move(Monster)) != cEntity::INVALID_ID) + monster->SetPosition(absPos); + monster->SetYaw(Random.RandReal(360.0f)); + if (chunk->GetWorld()->SpawnMobFinalize(std::move(monster)) != cEntity::INVALID_ID) { HaveSpawnedEntity = true; m_World->BroadcastSoundParticleEffect( EffectID::PARTICLE_MOBSPAWN, - Vector3d(PosX, RelY, PosZ).Floor(), + absPos, 0 ); NearbyEntities++; diff --git a/src/BlockID.h b/src/BlockID.h index 226178699..07ef043c0 100644 --- a/src/BlockID.h +++ b/src/BlockID.h @@ -66,7 +66,8 @@ enum ENUM_BLOCK_ID : BLOCKTYPE E_BLOCK_DIAMOND_BLOCK = 57, E_BLOCK_CRAFTING_TABLE = 58, E_BLOCK_WORKBENCH = 58, - E_BLOCK_CROPS = 59, + E_BLOCK_WHEAT = 59, + E_BLOCK_CROPS = E_BLOCK_WHEAT, E_BLOCK_FARMLAND = 60, E_BLOCK_FURNACE = 61, E_BLOCK_LIT_FURNACE = 62, 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 */ diff --git a/src/Chunk.cpp b/src/Chunk.cpp index a84bbd9b7..75773c8b9 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -57,7 +57,7 @@ cChunk::cChunk( cChunkMap * a_ChunkMap, cWorld * a_World, cChunk * a_NeighborXM, cChunk * a_NeighborXP, cChunk * a_NeighborZM, cChunk * a_NeighborZP, cAllocationPool<cChunkData::sChunkSection> & a_Pool -) : +): m_Presence(cpInvalid), m_ShouldGenerateIfLoadFailed(false), m_IsLightValid(false), @@ -611,7 +611,7 @@ void cChunk::SpawnMobs(cMobSpawner & a_MobSpawner) continue; } - auto newMob = a_MobSpawner.TryToSpawnHere(this, TryX, TryY, TryZ, Biome, MaxNbOfSuccess); + auto newMob = a_MobSpawner.TryToSpawnHere(this, {TryX, TryY, TryZ}, Biome, MaxNbOfSuccess); if (newMob == nullptr) { continue; @@ -984,255 +984,30 @@ cItems cChunk::PickupsFromBlock(Vector3i a_RelPos, const cEntity * a_Digger, con -bool cChunk::GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType) +int cChunk::GrowPlantAt(Vector3i a_RelPos, int a_NumStages) { - auto & Random = GetRandomProvider(); - - // Convert the stem BlockType into produce BlockType - BLOCKTYPE ProduceType; - switch (a_BlockType) - { - case E_BLOCK_MELON_STEM: ProduceType = E_BLOCK_MELON; break; - case E_BLOCK_PUMPKIN_STEM: ProduceType = E_BLOCK_PUMPKIN; break; - default: - { - ASSERT(!"Unhandled blocktype in TickMelonPumpkin()"); - return false; - } - } - - // Check if there's another melon / pumpkin around that stem, if so, abort: - bool IsValid; - BLOCKTYPE BlockType[4]; - NIBBLETYPE BlockMeta; // unused - IsValid = UnboundedRelGetBlock(a_RelX + 1, a_RelY, a_RelZ, BlockType[0], BlockMeta); - IsValid = IsValid && UnboundedRelGetBlock(a_RelX - 1, a_RelY, a_RelZ, BlockType[1], BlockMeta); - IsValid = IsValid && UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ + 1, BlockType[2], BlockMeta); - IsValid = IsValid && UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ - 1, BlockType[3], BlockMeta); - if ( - !IsValid || - (BlockType[0] == ProduceType) || - (BlockType[1] == ProduceType) || - (BlockType[2] == ProduceType) || - (BlockType[3] == ProduceType) - ) - { - // 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 Soil; - VERIFY(UnboundedRelGetBlock(a_RelX + x, a_RelY - 1, a_RelZ + z, Soil, BlockMeta)); - switch (Soil) - { - case E_BLOCK_DIRT: - case E_BLOCK_GRASS: - case E_BLOCK_FARMLAND: - { - // Place a randomly-facing produce: - NIBBLETYPE Meta = (ProduceType == E_BLOCK_MELON) ? 0 : static_cast<NIBBLETYPE>(Random.RandInt(4) % 4); - FLOGD("Growing melon / pumpkin at {0} (<{1}, {2}> from stem), overwriting {3}, growing on top of {4}, meta {5}", - PositionToWorldPosition(a_RelX + x, a_RelY, a_RelZ + z), - x, z, - ItemTypeToString(BlockType[CheckType]), - ItemTypeToString(Soil), - Meta - ); - VERIFY(UnboundedRelFastSetBlock(a_RelX + x, a_RelY, a_RelZ + z, ProduceType, Meta)); - auto absolute = RelativeToAbsolute(Vector3i{a_RelX + x, a_RelY, a_RelZ + z}); - cChunkInterface ChunkInterface(this->GetWorld()->GetChunkMap()); - cBlockHandler::NeighborChanged(ChunkInterface, absolute.addedY(-1), BLOCK_FACE_YP); - break; - } - } - return true; -} - - - - - -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; - while ( - (Top < cChunkDef::Height) && - (GetBlock(a_RelX, Top, a_RelZ) == E_BLOCK_SUGARCANE) - ) - { - ++Top; - } - int Bottom = a_RelY - 1; - while ( - (Bottom > 0) && - (GetBlock(a_RelX, Bottom, a_RelZ) == E_BLOCK_SUGARCANE) - ) - { - --Bottom; - } - - // Grow by at most a_NumBlocks, but no more than max height: - int ToGrow = std::min(a_NumBlocks, m_World->GetMaxSugarcaneHeight() + 1 - (Top - Bottom)); - for (int i = 0; i < ToGrow; i++) - { - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - if (UnboundedRelGetBlock(a_RelX, Top + i, a_RelZ, BlockType, BlockMeta) && (BlockType == E_BLOCK_AIR)) - { - UnboundedRelFastSetBlock(a_RelX, Top + i, a_RelZ, E_BLOCK_SUGARCANE, 0); - } - else - { - return i; - } - } // for i - return ToGrow; + auto blockHandler = BlockHandler(GetBlock(a_RelPos)); + return blockHandler->Grow(*this, a_RelPos, a_NumStages); } -int cChunk::GrowCactus(int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks) +bool cChunk::UnboundedRelGetBlock(Vector3i a_RelPos, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const { - // Check the total height of the cacti blocks here: - int Top = a_RelY + 1; - while ( - (Top < cChunkDef::Height) && - (GetBlock(a_RelX, Top, a_RelZ) == E_BLOCK_CACTUS) - ) - { - ++Top; - } - int Bottom = a_RelY - 1; - while ( - (Bottom > 0) && - (GetBlock(a_RelX, Bottom, a_RelZ) == E_BLOCK_CACTUS) - ) - { - --Bottom; - } - - // Grow by at most a_NumBlocks, but no more than max height: - int ToGrow = std::min(a_NumBlocks, m_World->GetMaxCactusHeight() + 1 - (Top - Bottom)); - for (int i = 0; i < ToGrow; i++) - { - BLOCKTYPE BlockType; - if (UnboundedRelGetBlockType(a_RelX, Top + i, a_RelZ, BlockType) && (BlockType == E_BLOCK_AIR)) - { - UnboundedRelFastSetBlock(a_RelX, Top + i, a_RelZ, E_BLOCK_CACTUS, 0); - - // Check surroundings. Cacti may ONLY be surrounded by non-solid blocks - static const struct - { - int x, z; - } Coords[] = - { - {-1, 0}, - { 1, 0}, - { 0, -1}, - { 0, 1}, - } ; - for (auto & Coord : Coords) - { - if ( - UnboundedRelGetBlockType(a_RelX + Coord.x, Top + i, a_RelZ + Coord.z, BlockType) && - ( - cBlockInfo::IsSolid(BlockType) || - (BlockType == E_BLOCK_LAVA) || - (BlockType == E_BLOCK_STATIONARY_LAVA) - ) - ) - { - // Remove the cactus - GetWorld()->DropBlockAsPickups(RelativeToAbsolute({a_RelX, a_RelY, a_RelZ}), nullptr, nullptr); - return false; - } - } // for i - Coords[] - } - else - { - 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, E_META_BIG_FLOWER_TOP); -} - - - - - -bool cChunk::UnboundedRelGetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const -{ - if (!cChunkDef::IsValidHeight(a_RelY)) + if (!cChunkDef::IsValidHeight(a_RelPos.y)) { - LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY); + LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelPos.y); return false; } - cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); - if ((Chunk == nullptr) || !Chunk->IsValid()) + auto chunk = GetRelNeighborChunkAdjustCoords(a_RelPos); + if ((chunk == nullptr) || !chunk->IsValid()) { // The chunk is not available, bail out return false; } - Chunk->GetBlockTypeMeta(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); + chunk->GetBlockTypeMeta(a_RelPos, a_BlockType, a_BlockMeta); return true; } @@ -1240,20 +1015,20 @@ bool cChunk::UnboundedRelGetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE -bool cChunk::UnboundedRelGetBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType) const +bool cChunk::UnboundedRelGetBlockType(Vector3i a_RelPos, BLOCKTYPE & a_BlockType) const { - if (!cChunkDef::IsValidHeight(a_RelY)) + if (!cChunkDef::IsValidHeight(a_RelPos.y)) { - LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY); + LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelPos.y); return false; } - cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); - if ((Chunk == nullptr) || !Chunk->IsValid()) + auto chunk = GetRelNeighborChunkAdjustCoords(a_RelPos); + if ((chunk == nullptr) || !chunk->IsValid()) { // The chunk is not available, bail out return false; } - a_BlockType = Chunk->GetBlock(a_RelX, a_RelY, a_RelZ); + a_BlockType = chunk->GetBlock(a_RelPos); return true; } @@ -1261,20 +1036,20 @@ bool cChunk::UnboundedRelGetBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKT -bool cChunk::UnboundedRelGetBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockMeta) const +bool cChunk::UnboundedRelGetBlockMeta(Vector3i a_RelPos, NIBBLETYPE & a_BlockMeta) const { - if (!cChunkDef::IsValidHeight(a_RelY)) + if (!cChunkDef::IsValidHeight(a_RelPos.y)) { - LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY); + LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelPos.y); return false; } - cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); - if ((Chunk == nullptr) || !Chunk->IsValid()) + auto chunk = GetRelNeighborChunkAdjustCoords(a_RelPos); + if ((chunk == nullptr) || !chunk->IsValid()) { // The chunk is not available, bail out return false; } - a_BlockMeta = Chunk->GetMeta(a_RelX, a_RelY, a_RelZ); + a_BlockMeta = chunk->GetMeta(a_RelPos); return true; } @@ -1282,20 +1057,20 @@ bool cChunk::UnboundedRelGetBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLE -bool cChunk::UnboundedRelGetBlockBlockLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockBlockLight) const +bool cChunk::UnboundedRelGetBlockBlockLight(Vector3i a_RelPos, NIBBLETYPE & a_BlockBlockLight) const { - if (!cChunkDef::IsValidHeight(a_RelY)) + if (!cChunkDef::IsValidHeight(a_RelPos.y)) { - LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY); + LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelPos.y); return false; } - cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); - if ((Chunk == nullptr) || !Chunk->IsValid()) + auto chunk = GetRelNeighborChunkAdjustCoords(a_RelPos); + if ((chunk == nullptr) || !chunk->IsValid()) { // The chunk is not available, bail out return false; } - a_BlockBlockLight = Chunk->GetBlockLight(a_RelX, a_RelY, a_RelZ); + a_BlockBlockLight = chunk->GetBlockLight(a_RelPos); return true; } @@ -1303,20 +1078,20 @@ bool cChunk::UnboundedRelGetBlockBlockLight(int a_RelX, int a_RelY, int a_RelZ, -bool cChunk::UnboundedRelGetBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockSkyLight) const +bool cChunk::UnboundedRelGetBlockSkyLight(Vector3i a_RelPos, NIBBLETYPE & a_BlockSkyLight) const { - if (!cChunkDef::IsValidHeight(a_RelY)) + if (!cChunkDef::IsValidHeight(a_RelPos.y)) { - LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY); + LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelPos.y); return false; } - cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); - if ((Chunk == nullptr) || !Chunk->IsValid()) + auto chunk = GetRelNeighborChunkAdjustCoords(a_RelPos); + if ((chunk == nullptr) || !chunk->IsValid()) { // The chunk is not available, bail out return false; } - a_BlockSkyLight = Chunk->GetSkyLight(a_RelX, a_RelY, a_RelZ); + a_BlockSkyLight = chunk->GetSkyLight(a_RelPos); return true; } @@ -1324,21 +1099,21 @@ bool cChunk::UnboundedRelGetBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ, NI -bool cChunk::UnboundedRelGetBlockLights(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockLight, NIBBLETYPE & a_SkyLight) const +bool cChunk::UnboundedRelGetBlockLights(Vector3i a_RelPos, NIBBLETYPE & a_BlockLight, NIBBLETYPE & a_SkyLight) const { - if (!cChunkDef::IsValidHeight(a_RelY)) + if (!cChunkDef::IsValidHeight(a_RelPos.y)) { - LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY); + LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelPos.y); return false; } - cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); - if ((Chunk == nullptr) || !Chunk->IsValid()) + auto chunk = GetRelNeighborChunkAdjustCoords(a_RelPos); + if ((chunk == nullptr) || !chunk->IsValid()) { // The chunk is not available, bail out return false; } - a_BlockLight = Chunk->GetBlockLight(a_RelX, a_RelY, a_RelZ); - a_SkyLight = Chunk->GetSkyLight(a_RelX, a_RelY, a_RelZ); + a_BlockLight = chunk->GetBlockLight(a_RelPos); + a_SkyLight = chunk->GetSkyLight (a_RelPos); return true; } @@ -1346,20 +1121,20 @@ bool cChunk::UnboundedRelGetBlockLights(int a_RelX, int a_RelY, int a_RelZ, NIBB -bool cChunk::UnboundedRelSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +bool cChunk::UnboundedRelSetBlock(Vector3i a_RelPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) { - if (!cChunkDef::IsValidHeight(a_RelY)) + if (!cChunkDef::IsValidHeight(a_RelPos.y)) { - LOGWARNING("UnboundedRelSetBlock(): requesting a block with a_RelY out of range: %d", a_RelY); + LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelPos.y); return false; } - cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); - if ((Chunk == nullptr) || !Chunk->IsValid()) + auto chunk = GetRelNeighborChunkAdjustCoords(a_RelPos); + if ((chunk == nullptr) || !chunk->IsValid()) { // The chunk is not available, bail out return false; } - Chunk->SetBlock({a_RelX, a_RelY, a_RelZ}, a_BlockType, a_BlockMeta); + chunk->SetBlock(a_RelPos, a_BlockType, a_BlockMeta); return true; } @@ -1367,20 +1142,20 @@ bool cChunk::UnboundedRelSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE -bool cChunk::UnboundedRelFastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +bool cChunk::UnboundedRelFastSetBlock(Vector3i a_RelPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) { - if (!cChunkDef::IsValidHeight(a_RelY)) + if (!cChunkDef::IsValidHeight(a_RelPos.y)) { - LOGWARNING("UnboundedRelFastSetBlock(): requesting a block with a_RelY out of range: %d", a_RelY); + LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelPos.y); return false; } - cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); - if ((Chunk == nullptr) || !Chunk->IsValid()) + auto chunk = GetRelNeighborChunkAdjustCoords(a_RelPos); + if ((chunk == nullptr) || !chunk->IsValid()) { // The chunk is not available, bail out return false; } - Chunk->FastSetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); + chunk->FastSetBlock(a_RelPos, a_BlockType, a_BlockMeta); return true; } @@ -1392,10 +1167,9 @@ void cChunk::UnboundedQueueTickBlock(Vector3i a_RelPos) { if (!cChunkDef::IsValidHeight(a_RelPos.y)) { - // Outside of chunkmap return; } - auto chunk = GetRelNeighborChunkAdjustCoords(a_RelPos.x, a_RelPos.z); + auto chunk = GetRelNeighborChunkAdjustCoords(a_RelPos); if ((chunk != nullptr) && chunk->IsValid()) { chunk->QueueTickBlock(a_RelPos); @@ -1436,14 +1210,14 @@ void cChunk::CreateBlockEntities(void) auto BlockType = Section->m_BlockTypes[BlockIdx]; if (cBlockEntity::IsBlockEntityBlockType(BlockType)) { - auto RelPos = IndexToCoordinate(BlockIdx); - RelPos.y += static_cast<int>(SectionIdx * cChunkData::SectionHeight); - auto WorldPos = RelativeToAbsolute(RelPos); + auto relPos = IndexToCoordinate(BlockIdx); + relPos.y += static_cast<int>(SectionIdx * cChunkData::SectionHeight); + auto absPos = RelativeToAbsolute(relPos); - if (!HasBlockEntityAt(WorldPos)) + if (!HasBlockEntityAt(absPos)) { AddBlockEntityClean(cBlockEntity::CreateByBlockType( - BlockType, GetMeta(RelPos), WorldPos, m_World + BlockType, GetMeta(relPos), absPos, m_World )); } } @@ -1590,7 +1364,7 @@ void cChunk::SetBlock(Vector3i a_RelPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_Blo void cChunk::QueueTickBlock(Vector3i a_RelPos) { - ASSERT (IsValidRelPos(a_RelPos)); + ASSERT(IsValidRelPos(a_RelPos)); if (!IsValid()) { @@ -1606,7 +1380,7 @@ void cChunk::QueueTickBlock(Vector3i a_RelPos) void cChunk::QueueTickBlockNeighbors(Vector3i a_RelPos) { - static const Vector3i neighborCoords[] = + static const Vector3i neighborOfs[] = { { 1, 0, 0}, {-1, 0, 0}, @@ -1615,9 +1389,9 @@ void cChunk::QueueTickBlockNeighbors(Vector3i a_RelPos) { 0, 0, 1}, { 0, 0, -1}, } ; - for (const auto & neighbor: neighborCoords) + for (const auto & ofs: neighborOfs) { - UnboundedQueueTickBlock(a_RelPos + neighbor); + UnboundedQueueTickBlock(a_RelPos + ofs); } // for i - Coords[] } @@ -2581,22 +2355,22 @@ cChunk * cChunk::GetRelNeighborChunk(int a_RelX, int a_RelZ) -cChunk * cChunk::GetRelNeighborChunkAdjustCoords(int & a_RelX, int & a_RelZ) const +cChunk * cChunk::GetRelNeighborChunkAdjustCoords(Vector3i & a_RelPos) const { cChunk * ToReturn = const_cast<cChunk *>(this); // The most common case: inside this chunk: if ( - (a_RelX >= 0) && (a_RelX < Width) && - (a_RelZ >= 0) && (a_RelZ < Width) + (a_RelPos.x >= 0) && (a_RelPos.x < Width) && + (a_RelPos.z >= 0) && (a_RelPos.z < Width) ) { return ToReturn; } // Request for a different chunk, calculate chunk offset: - int RelX = a_RelX; // Make a local copy of the coords (faster access) - int RelZ = a_RelZ; + int RelX = a_RelPos.x; // Make a local copy of the coords (faster access) + int RelZ = a_RelPos.z; while ((RelX >= Width) && (ToReturn != nullptr)) { RelX -= Width; @@ -2619,19 +2393,19 @@ cChunk * cChunk::GetRelNeighborChunkAdjustCoords(int & a_RelX, int & a_RelZ) con } if (ToReturn != nullptr) { - a_RelX = RelX; - a_RelZ = RelZ; + a_RelPos.x = RelX; + a_RelPos.z = RelZ; return ToReturn; } // The chunk cannot be walked through neighbors, find it through the chunkmap: - int AbsX = a_RelX + m_PosX * Width; - int AbsZ = a_RelZ + m_PosZ * Width; + int AbsX = a_RelPos.x + m_PosX * Width; + int AbsZ = a_RelPos.z + m_PosZ * Width; int DstChunkX, DstChunkZ; BlockToChunk(AbsX, AbsZ, DstChunkX, DstChunkZ); ToReturn = m_ChunkMap->FindChunk(DstChunkX, DstChunkZ); - a_RelX = AbsX - DstChunkX * Width; - a_RelZ = AbsZ - DstChunkZ * Width; + a_RelPos.x = AbsX - DstChunkX * Width; + a_RelPos.z = AbsZ - DstChunkZ * Width; return ToReturn; } diff --git a/src/Chunk.h b/src/Chunk.h index d153a44ea..f6e38dc81 100644 --- a/src/Chunk.h +++ b/src/Chunk.h @@ -210,23 +210,18 @@ public: bool GetChunkAndRelByAbsolute(const Vector3i & a_Position, cChunk ** a_Chunk, Vector3i & a_Rel); /** Returns the chunk into which the specified block belongs, by walking the neighbors. - Will return self if appropriate. Returns nullptr if not reachable through neighbors. - */ + Will return self if appropriate. Returns nullptr if not reachable through neighbors. */ cChunk * GetNeighborChunk(int a_BlockX, int a_BlockZ); - /** - Returns the chunk into which the relatively-specified block belongs, by walking the neighbors. - Will return self if appropriate. Returns nullptr if not reachable through neighbors. - */ + /** Returns the chunk into which the relatively-specified block belongs, by walking the neighbors. + Will return self if appropriate. Returns nullptr if not reachable through neighbors. */ cChunk * GetRelNeighborChunk(int a_RelX, int a_RelZ); - /** - Returns the chunk into which the relatively-specified block belongs. + /** Returns the chunk into which the relatively-specified block belongs. Also modifies the relative coords from this-relative to return-relative. Will return self if appropriate. - Will try walking the neighbors first; if that fails, will query the chunkmap - */ - cChunk * GetRelNeighborChunkAdjustCoords(int & a_RelX, int & a_RelZ) const; + Will try walking the neighbors first; if that fails, will query the chunkmap. */ + cChunk * GetRelNeighborChunkAdjustCoords(Vector3i & a_RelPos) const; EMCSBiome GetBiomeAt(int a_RelX, int a_RelZ) const {return cChunkDef::GetBiome(m_BiomeMap, a_RelX, a_RelZ); } @@ -428,31 +423,104 @@ public: inline NIBBLETYPE GetSkyLightAltered(Vector3i a_RelPos) const { return GetTimeAlteredLight(m_ChunkData.GetSkyLight(a_RelPos)); } inline NIBBLETYPE GetSkyLightAltered(int a_RelX, int a_RelY, int a_RelZ) const { return GetSkyLightAltered({ a_RelX, a_RelY, a_RelZ }); } - /** Same as GetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success */ - bool UnboundedRelGetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const; + /** Same as GetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case) + Returns true on success, false if queried chunk not loaded. */ + bool UnboundedRelGetBlock(Vector3i a_RelCoords, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const; + + /** OBSOLETE, use the Vector3i-based overload. + Same as GetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case) + Returns true on success, false if queried chunk not loaded. */ + bool UnboundedRelGetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const + { + return UnboundedRelGetBlock({a_RelX, a_RelY, a_RelZ}, a_BlockType, a_BlockMeta); + } + + /** Same as GetBlockType(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case) + Returns true on success, false if queried chunk not loaded. */ + bool UnboundedRelGetBlockType(Vector3i a_RelCoords, BLOCKTYPE & a_BlockType) const; + + /** OBSOLETE, use the Vector3i-based overload. + Same as GetBlockType(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case) + Returns true on success, false if queried chunk not loaded. */ + bool UnboundedRelGetBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType) const + { + return UnboundedRelGetBlockType({a_RelX, a_RelY, a_RelZ}, a_BlockType); + } + + /** Same as GetBlockMeta(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case) + Returns true on success, false if queried chunk not loaded. */ + bool UnboundedRelGetBlockMeta(Vector3i a_RelPos, NIBBLETYPE & a_BlockMeta) const; - /** Same as GetBlockType(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success */ - bool UnboundedRelGetBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType) const; + /** OBSOLETE, use the Vector3i-based overload. + Same as GetBlockMeta(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case) + Returns true on success, false if queried chunk not loaded. */ + bool UnboundedRelGetBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockMeta) const + { + return UnboundedRelGetBlockMeta({a_RelX, a_RelY, a_RelZ}, a_BlockMeta); + } - /** Same as GetBlockMeta(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success */ - bool UnboundedRelGetBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockMeta) const; + /** Same as GetBlockBlockLight(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case) + Returns true on success, false if queried chunk not loaded. */ + bool UnboundedRelGetBlockBlockLight(Vector3i a_RelPos, NIBBLETYPE & a_BlockLight) const; + + /** OBSOLETE, use the Vector3i-based overload. + Same as GetBlockBlockLight(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case) + Returns true on success, false if queried chunk not loaded. */ + bool UnboundedRelGetBlockBlockLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockLight) const + { + return UnboundedRelGetBlockBlockLight({a_RelX, a_RelY, a_RelZ}, a_BlockLight); + } - /** Same as GetBlockBlockLight(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success */ - bool UnboundedRelGetBlockBlockLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockLight) const; + /** Same as GetBlockSkyLight(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case) + Returns true on success, false if queried chunk not loaded. */ + bool UnboundedRelGetBlockSkyLight(Vector3i a_RelPos, NIBBLETYPE & a_SkyLight) const; - /** Same as GetBlockSkyLight(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success */ - bool UnboundedRelGetBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_SkyLight) const; + /** OBSOLETE, use the Vector3i-based overload. + Same as GetBlockSkyLight(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case) + Returns true on success, false if queried chunk not loaded. */ + bool UnboundedRelGetBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_SkyLight) const + { + return UnboundedRelGetBlockSkyLight({a_RelX, a_RelY, a_RelZ}, a_SkyLight); + } - /** Queries both BlockLight and SkyLight, relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success */ - bool UnboundedRelGetBlockLights(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockLight, NIBBLETYPE & a_SkyLight) const; + /** Queries both BlockLight and SkyLight, relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case) + Returns true on success, false if queried chunk not loaded. */ + bool UnboundedRelGetBlockLights(Vector3i a_RelPos, NIBBLETYPE & a_BlockLight, NIBBLETYPE & a_SkyLight) const; - /** Same as SetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success */ - bool UnboundedRelSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + /** OBSOLETE, use the Vector3i-based overload. + Queries both BlockLight and SkyLight, relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case) + Returns true on success, false if queried chunk not loaded. */ + bool UnboundedRelGetBlockLights(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockLight, NIBBLETYPE & a_SkyLight) const + { + return UnboundedRelGetBlockLights({a_RelX, a_RelY, a_RelZ}, a_BlockLight, a_SkyLight); + } - /** Same as FastSetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success */ - bool UnboundedRelFastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + /** Same as SetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case) + Returns true on success, false if queried chunk not loaded. */ + bool UnboundedRelSetBlock(Vector3i a_RelPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - /** Same as QueueTickBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s in such a case), ignores unsuccessful attempts */ + /** OBSOLETE, use the Vector3i-based overload. + Same as SetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case) + Returns true on success, false if queried chunk not loaded. */ + bool UnboundedRelSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) + { + return UnboundedRelSetBlock({a_RelX, a_RelY, a_RelZ}, a_BlockType, a_BlockMeta); + } + + /** Same as FastSetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case) + Returns true on success, false if queried chunk not loaded. */ + bool UnboundedRelFastSetBlock(Vector3i a_RelPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + /** OBSOLETE, use the Vector3i-based overload. + Same as FastSetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case) + Returns true on success, false if queried chunk not loaded. */ + bool UnboundedRelFastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) + { + return UnboundedRelFastSetBlock({a_RelX, a_RelY, a_RelZ}, a_BlockType, a_BlockMeta); + } + + /** Same as QueueTickBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s in such a case) + Ignores unsuccessful attempts */ void UnboundedQueueTickBlock(Vector3i a_RelPos); @@ -466,15 +534,15 @@ public: cRedstoneSimulatorChunkData * GetRedstoneSimulatorData(void) { return m_RedstoneSimulatorData; } void SetRedstoneSimulatorData(cRedstoneSimulatorChunkData * a_Data) { m_RedstoneSimulatorData = a_Data; } - /** OBSOLETE, use the Vector3i-based overload isntead. - Returns the block entity at the specified (absolute) coords. - Returns nullptr if no such BE or outside this chunk. */ - cBlockEntity * GetBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ) { return GetBlockEntity({a_BlockX, a_BlockY, a_BlockZ}); } - /** Returns the block entity at the specified (absolute) coords. Returns nullptr if no such BE or outside this chunk. */ cBlockEntity * GetBlockEntity(Vector3i a_AbsPos); + /** OBSOLETE, use the Vector3i-based overload instead. + Returns the block entity at the specified (absolute) coords. + Returns nullptr if no such BE or outside this chunk. */ + cBlockEntity * GetBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ) { return GetBlockEntity({a_BlockX, a_BlockY, a_BlockZ}); } + /** Returns the block entity at the specified (relative) coords. Returns nullptr if no such BE. Asserts that the position is a valid relative position. */ @@ -500,7 +568,7 @@ public: Doesn't check relative coord validity. */ Vector3i RelativeToAbsolute(Vector3i a_RelBlockPosition) { - return cChunkDef::RelativeToAbsolute(a_RelBlockPosition, m_PosX, m_PosZ); + return cChunkDef::RelativeToAbsolute(a_RelBlockPosition, {m_PosX, m_PosZ}); } @@ -608,17 +676,10 @@ private: Doesn't dig the block, only queries the block handlers and then plugins for the pickups. */ cItems PickupsFromBlock(Vector3i a_RelPos, const cEntity * a_Digger, const cItem * a_Tool); - /** 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); 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 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); + /** Grows the plant at the specified position by at most a_NumStages. + The block's Grow handler is invoked. + Returns the number of stages the plant has grown, 0 if not a plant. */ + int GrowPlantAt(Vector3i a_RelPos, int a_NumStages = 1); /** 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(OwnedEntity a_Entity); diff --git a/src/ChunkDef.h b/src/ChunkDef.h index f3621c787..57e4adcfa 100644 --- a/src/ChunkDef.h +++ b/src/ChunkDef.h @@ -61,16 +61,33 @@ public: cChunkCoords(int a_ChunkX, int a_ChunkZ) : m_ChunkX(a_ChunkX), m_ChunkZ(a_ChunkZ) {} + bool operator == (const cChunkCoords & a_Other) const { return ((m_ChunkX == a_Other.m_ChunkX) && (m_ChunkZ == a_Other.m_ChunkZ)); } + bool operator != (const cChunkCoords & a_Other) const { return !(operator == (a_Other)); } + + /** Simple comparison, to support ordering. */ + bool operator < (const cChunkCoords & a_Other) const + { + if (a_Other.m_ChunkX == m_ChunkX) + { + return (m_ChunkZ < a_Other.m_ChunkZ); + } + else + { + return (m_ChunkX < a_Other.m_ChunkX); + } + } + + /** Returns a string that describes the chunk coords, suitable for logging. */ AString ToString() const { @@ -152,24 +169,45 @@ public: a_Z = a_Z - a_ChunkZ * Width; } + + + + + /** Converts the specified absolute position into a relative position within its chunk. + Use BlockToChunk to query the chunk coords. */ inline static Vector3i AbsoluteToRelative(Vector3i a_BlockPosition) { - cChunkCoords ChunckPos = BlockToChunk(a_BlockPosition); - - return {a_BlockPosition.x - ChunckPos.m_ChunkX * Width, a_BlockPosition.y, a_BlockPosition.z - ChunckPos.m_ChunkZ * Width}; + cChunkCoords chunkPos = BlockToChunk(a_BlockPosition); + return AbsoluteToRelative(a_BlockPosition, chunkPos); } + + + + + /** Converts the absolute coords into coords relative to the specified chunk. */ inline static Vector3i AbsoluteToRelative(Vector3i a_BlockPosition, cChunkCoords a_ChunkPos) { return {a_BlockPosition.x - a_ChunkPos.m_ChunkX * Width, a_BlockPosition.y, a_BlockPosition.z - a_ChunkPos.m_ChunkZ * Width}; } + + + /** Converts relative block coordinates into absolute coordinates with a known chunk location */ - inline static Vector3i RelativeToAbsolute(Vector3i a_RelBlockPosition, int a_ChunkX, int a_ChunkZ) + inline static Vector3i RelativeToAbsolute(Vector3i a_RelBlockPosition, cChunkCoords a_ChunkCoords) { - return {a_RelBlockPosition.x + a_ChunkX * Width, a_RelBlockPosition.y, a_RelBlockPosition.z + a_ChunkZ * Width}; + return Vector3i( + a_RelBlockPosition.x + a_ChunkCoords.m_ChunkX * Width, + a_RelBlockPosition.y, + a_RelBlockPosition.z + a_ChunkCoords.m_ChunkZ * Width + ); } + + + + /** Validates a height-coordinate. Returns false if height-coordiante is out of height bounds */ inline static bool IsValidHeight(int a_Height) { diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp index ec6b74192..32c7d548f 100644 --- a/src/ChunkMap.cpp +++ b/src/ChunkMap.cpp @@ -135,10 +135,10 @@ cChunkPtr cChunkMap::GetChunkNoGen(cChunkCoords a_Chunk) -cChunkPtr cChunkMap::GetChunkNoLoad(int a_ChunkX, int a_ChunkZ) +cChunkPtr cChunkMap::GetChunkNoLoad(cChunkCoords a_Coords) { ASSERT(m_CSChunks.IsLockedByCurrentThread()); // m_CSChunks should already be locked by the operation that called us - return ConstructChunk(a_ChunkX, a_ChunkZ); + return ConstructChunk(a_Coords.m_ChunkX, a_Coords.m_ChunkZ); } @@ -460,6 +460,23 @@ bool cChunkMap::TryGetHeight(int a_BlockX, int a_BlockZ, int & a_Height) +void cChunkMap::FastSetBlock(Vector3i a_BlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + auto chunkPos = cChunkDef::BlockToChunk(a_BlockPos); + auto relPos = cChunkDef::AbsoluteToRelative(a_BlockPos, chunkPos); + + cCSLock Lock(m_CSChunks); + auto chunk = GetChunk(chunkPos.m_ChunkX, chunkPos.m_ChunkZ); + if ((chunk != nullptr) && chunk->IsValid()) + { + chunk->FastSetBlock(relPos, a_BlockType, a_BlockMeta); + } +} + + + + + void cChunkMap::SetBlocks(const sSetBlockVector & a_Blocks) { cCSLock lock(m_CSChunks); @@ -515,18 +532,17 @@ void cChunkMap::CollectPickupsByPlayer(cPlayer & a_Player) -BLOCKTYPE cChunkMap::GetBlock(int a_BlockX, int a_BlockY, int a_BlockZ) +BLOCKTYPE cChunkMap::GetBlock(Vector3i a_BlockPos) { - int X = a_BlockX, Y = a_BlockY, Z = a_BlockZ; - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(X, Y, Z, ChunkX, ChunkZ); + auto chunkPos = cChunkDef::BlockToChunk(a_BlockPos); + auto relPos = cChunkDef::AbsoluteToRelative(a_BlockPos, chunkPos); // Query the chunk, if loaded: cCSLock Lock(m_CSChunks); - cChunkPtr Chunk = GetChunk(ChunkX, ChunkZ); - if ((Chunk != nullptr) && Chunk->IsValid()) + auto chunk = GetChunk(chunkPos.m_ChunkX, chunkPos.m_ChunkZ); + if ((chunk != nullptr) && chunk->IsValid()) { - return Chunk->GetBlock(X, Y, Z); + return chunk->GetBlock(relPos); } return 0; } @@ -535,18 +551,17 @@ BLOCKTYPE cChunkMap::GetBlock(int a_BlockX, int a_BlockY, int a_BlockZ) -NIBBLETYPE cChunkMap::GetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ) +NIBBLETYPE cChunkMap::GetBlockMeta(Vector3i a_BlockPos) { - int X = a_BlockX, Y = a_BlockY, Z = a_BlockZ; - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(X, Y, Z, ChunkX, ChunkZ); + auto chunkPos = cChunkDef::BlockToChunk(a_BlockPos); + auto relPos = cChunkDef::AbsoluteToRelative(a_BlockPos, chunkPos); // Query the chunk, if loaded: cCSLock Lock(m_CSChunks); - cChunkPtr Chunk = GetChunk(ChunkX, ChunkZ); - if ((Chunk != nullptr) && Chunk->IsValid()) + auto chunk = GetChunk(chunkPos.m_ChunkX, chunkPos.m_ChunkZ); + if ((chunk != nullptr) && chunk->IsValid()) { - return Chunk->GetMeta(X, Y, Z); + return chunk->GetMeta(relPos); } return 0; } @@ -555,16 +570,17 @@ NIBBLETYPE cChunkMap::GetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ) -NIBBLETYPE cChunkMap::GetBlockSkyLight(int a_BlockX, int a_BlockY, int a_BlockZ) +NIBBLETYPE cChunkMap::GetBlockSkyLight(Vector3i a_BlockPos) { - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + auto chunkPos = cChunkDef::BlockToChunk(a_BlockPos); + auto relPos = cChunkDef::AbsoluteToRelative(a_BlockPos, chunkPos); + // Query the chunk, if loaded: cCSLock Lock(m_CSChunks); - cChunkPtr Chunk = GetChunk( ChunkX, ChunkZ); - if ((Chunk != nullptr) && Chunk->IsValid()) + auto chunk = GetChunk(chunkPos.m_ChunkX, chunkPos.m_ChunkZ); + if ((chunk != nullptr) && chunk->IsValid()) { - return Chunk->GetSkyLight(a_BlockX, a_BlockY, a_BlockZ); + return chunk->GetSkyLight(relPos); } return 0; } @@ -573,16 +589,17 @@ NIBBLETYPE cChunkMap::GetBlockSkyLight(int a_BlockX, int a_BlockY, int a_BlockZ) -NIBBLETYPE cChunkMap::GetBlockBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ) +NIBBLETYPE cChunkMap::GetBlockBlockLight(Vector3i a_BlockPos) { - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + auto chunkPos = cChunkDef::BlockToChunk(a_BlockPos); + auto relPos = cChunkDef::AbsoluteToRelative(a_BlockPos, chunkPos); + // Query the chunk, if loaded: cCSLock Lock(m_CSChunks); - cChunkPtr Chunk = GetChunk( ChunkX, ChunkZ); - if ((Chunk != nullptr) && Chunk->IsValid()) + auto chunk = GetChunk(chunkPos.m_ChunkX, chunkPos.m_ChunkZ); + if ((chunk != nullptr) && chunk->IsValid()) { - return Chunk->GetBlockLight(a_BlockX, a_BlockY, a_BlockZ); + return chunk->GetBlockLight(relPos); } return 0; } @@ -591,17 +608,17 @@ NIBBLETYPE cChunkMap::GetBlockBlockLight(int a_BlockX, int a_BlockY, int a_Block -void cChunkMap::SetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockMeta, bool a_ShouldMarkDirty, bool a_ShouldInformClients) +void cChunkMap::SetBlockMeta(Vector3i a_BlockPos, NIBBLETYPE a_BlockMeta, bool a_ShouldMarkDirty, bool a_ShouldInformClients) { - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); - // a_BlockXYZ now contains relative coords! + auto chunkPos = cChunkDef::BlockToChunk(a_BlockPos); + auto relPos = cChunkDef::AbsoluteToRelative(a_BlockPos, chunkPos); + // Query the chunk, if loaded: cCSLock Lock(m_CSChunks); - cChunkPtr Chunk = GetChunk(ChunkX, ChunkZ); - if ((Chunk != nullptr) && Chunk->IsValid()) + auto chunk = GetChunk(chunkPos.m_ChunkX, chunkPos.m_ChunkZ); + if ((chunk != nullptr) && chunk->IsValid()) { - Chunk->SetMeta(a_BlockX, a_BlockY, a_BlockZ, a_BlockMeta, a_ShouldMarkDirty, a_ShouldInformClients); + chunk->SetMeta(relPos, a_BlockMeta, a_ShouldMarkDirty, a_ShouldInformClients); } } @@ -611,11 +628,11 @@ void cChunkMap::SetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYP void cChunkMap::SetBlock(Vector3i a_BlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) { - auto chunkCoord = cChunkDef::BlockToChunk(a_BlockPos); - auto relPos = cChunkDef::AbsoluteToRelative(a_BlockPos, chunkCoord); + auto chunkPos = cChunkDef::BlockToChunk(a_BlockPos); + auto relPos = cChunkDef::AbsoluteToRelative(a_BlockPos, chunkPos); cCSLock Lock(m_CSChunks); - auto chunk = GetChunk(chunkCoord.m_ChunkX, chunkCoord.m_ChunkZ); + auto chunk = GetChunk(chunkPos.m_ChunkX, chunkPos.m_ChunkZ); if ((chunk != nullptr) && chunk->IsValid()) { BLOCKTYPE blockType; @@ -652,16 +669,17 @@ bool cChunkMap::GetBlockTypeMeta(Vector3i a_BlockPos, BLOCKTYPE & a_BlockType, N -bool cChunkMap::GetBlockInfo(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight) +bool cChunkMap::GetBlockInfo(Vector3i a_BlockPos, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight) { - int ChunkX, ChunkZ, X = a_BlockX, Y = a_BlockY, Z = a_BlockZ; - cChunkDef::AbsoluteToRelative( X, Y, Z, ChunkX, ChunkZ); + auto chunkPos = cChunkDef::BlockToChunk(a_BlockPos); + auto relPos = cChunkDef::AbsoluteToRelative(a_BlockPos, chunkPos); + // Query the chunk, if loaded: cCSLock Lock(m_CSChunks); - cChunkPtr Chunk = GetChunk( ChunkX, ChunkZ); - if ((Chunk != nullptr) && Chunk->IsValid()) + auto chunk = GetChunk(chunkPos.m_ChunkX, chunkPos.m_ChunkZ); + if ((chunk != nullptr) && chunk->IsValid()) { - Chunk->GetBlockInfo(X, Y, Z, a_BlockType, a_Meta, a_SkyLight, a_BlockLight); + chunk->GetBlockInfo(relPos, a_BlockType, a_Meta, a_SkyLight, a_BlockLight); return true; } return false; @@ -1973,72 +1991,17 @@ void cChunkMap::GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty) -bool cChunkMap::GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType) +int cChunkMap::GrowPlantAt(Vector3i a_BlockPos, int a_NumStages) { - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); - - cCSLock Lock(m_CSChunks); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ); - if (Chunk != nullptr) - { - return Chunk->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, a_BlockType); - } - return false; -} - - - - - -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); - - cCSLock Lock(m_CSChunks); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ); - if (Chunk != nullptr) - { - return Chunk->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow); - } - return 0; -} - - - - - -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); - - cCSLock Lock(m_CSChunks); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ); - if (Chunk != nullptr) - { - 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_CSChunks); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ); - if (Chunk != nullptr) + auto chunkPos = cChunkDef::BlockToChunk(a_BlockPos); + auto relPos = cChunkDef::AbsoluteToRelative(a_BlockPos, chunkPos); + cCSLock lock(m_CSChunks); + auto chunk = GetChunkNoLoad(chunkPos); + if (chunk == nullptr) { - return Chunk->GrowTallGrass(a_BlockX, a_BlockY, a_BlockZ); + return 0; } - return 0; + return chunk->GrowPlantAt(relPos, a_NumStages); } @@ -2208,17 +2171,16 @@ void cChunkMap::ChunkValidated(void) -void cChunkMap::QueueTickBlock(int a_BlockX, int a_BlockY, int a_BlockZ) +void cChunkMap::QueueTickBlock(Vector3i a_AbsPos) { - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); - // a_BlockXYZ now contains relative coords! + auto chunkCoords = cChunkDef::BlockToChunk(a_AbsPos); + auto relPos = cChunkDef::AbsoluteToRelative(a_AbsPos, chunkCoords); cCSLock Lock(m_CSChunks); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(chunkCoords); if (Chunk != nullptr) { - Chunk->QueueTickBlock(a_BlockX, a_BlockY, a_BlockZ); + Chunk->QueueTickBlock(relPos); } } @@ -2258,23 +2220,6 @@ void cChunkMap::UntrackInDeadlockDetect(cDeadlockDetect & a_DeadlockDetect) -void cChunkMap::FastSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - int ChunkX, ChunkZ, X = a_BlockX, Y = a_BlockY, Z = a_BlockZ; - cChunkDef::AbsoluteToRelative(X, Y, Z, ChunkX, ChunkZ); - - cCSLock Lock(m_CSChunks); - cChunkPtr Chunk = GetChunk(ChunkX, ChunkZ); - if ((Chunk != nullptr) && Chunk->IsValid()) - { - Chunk->FastSetBlock(X, Y, Z, a_BlockType, a_BlockMeta); - } -} - - - - - void cChunkMap::AddChunkStay(cChunkStay & a_ChunkStay) { cCSLock Lock(m_CSChunks); diff --git a/src/ChunkMap.h b/src/ChunkMap.h index 771eed2e7..3623400c8 100644 --- a/src/ChunkMap.h +++ b/src/ChunkMap.h @@ -123,23 +123,35 @@ public: bool HasChunkAnyClients (int a_ChunkX, int a_ChunkZ); int GetHeight (int a_BlockX, int a_BlockZ); // Waits for the chunk to get loaded / generated bool TryGetHeight (int a_BlockX, int a_BlockZ, int & a_Height); // Returns false if chunk not loaded / generated - void FastSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + /** Sets the block at the specified coords to the specified value. + The replacement doesn't trigger block updates, nor wake up simulators. + The replaced blocks aren't checked for block entities (block entity is leaked if it exists at this block). + If the chunk is invalid, the operation is ignored silently. */ + void FastSetBlock(Vector3i a_BlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); /** Performs the specified single-block set operations simultaneously, as if SetBlock() was called for each item. Is more efficient than calling SetBlock() multiple times. If the chunk for any of the blocks is not loaded, the set operation is ignored silently. */ void SetBlocks(const sSetBlockVector & a_Blocks); - void CollectPickupsByPlayer(cPlayer & a_Player); + /** Makes the specified player collect all the pickups around them. */ + void CollectPickupsByPlayer(cPlayer & a_Player); + + BLOCKTYPE GetBlock (Vector3i a_BlockPos); + NIBBLETYPE GetBlockMeta (Vector3i a_BlockPos); + NIBBLETYPE GetBlockSkyLight (Vector3i a_BlockPos); + NIBBLETYPE GetBlockBlockLight(Vector3i a_BlockPos); + + /** 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_BlockMeta, bool a_ShouldMarkDirty, bool a_ShouldInformClients); - BLOCKTYPE GetBlock (int a_BlockX, int a_BlockY, int a_BlockZ); - NIBBLETYPE GetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ); - NIBBLETYPE GetBlockSkyLight (int a_BlockX, int a_BlockY, int a_BlockZ); - NIBBLETYPE GetBlockBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ); - void SetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockMeta, bool a_ShouldMarkDirty, bool a_ShouldInformClients); void SetBlock (Vector3i a_BlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); bool GetBlockTypeMeta (Vector3i a_BlockPos, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); - bool GetBlockInfo (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight); + bool GetBlockInfo (Vector3i, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight); /** Replaces world blocks with a_Blocks, if they are of type a_FilterBlockType */ void ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType); @@ -350,17 +362,10 @@ 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); 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; 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; 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); + /** Grows the plant at the specified position by at most a_NumStages. + The block's Grow handler is invoked. + Returns the number of stages the plant has grown, 0 if not a plant. */ + int GrowPlantAt(Vector3i a_BlockPos, int a_NumStages = 1); /** 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); @@ -389,7 +394,7 @@ public: void ChunkValidated(void); // Called by chunks that have become valid /** Queues the specified block for ticking (block update) */ - void QueueTickBlock(int a_BlockX, int a_BlockY, int a_BlockZ); + void QueueTickBlock(Vector3i a_AbsPos); /** Returns the CS for locking the chunkmap; only cWorld::cLock may use this function! */ cCriticalSection & GetCS(void) { return m_CSChunks; } @@ -482,7 +487,14 @@ private: } /** Constructs a chunk, returning it. Doesn't load, doesn't generate */ - cChunkPtr GetChunkNoLoad(int a_ChunkX, int a_ChunkZ); + cChunkPtr GetChunkNoLoad(cChunkCoords a_Coords); + + /** OBSOLETE, use the cChunkCoords-based overload instead. + Constructs a chunk, returning it. Doesn't load, doesn't generate */ + cChunkPtr GetChunkNoLoad(int a_ChunkX, int a_ChunkZ) + { + return GetChunkNoLoad({a_ChunkX, a_ChunkZ}); + } /** Locates a chunk ptr in the chunkmap; doesn't create it when not found; assumes m_CSChunks is locked. To be called only from cChunkMap. */ cChunk * FindChunk(int a_ChunkX, int a_ChunkZ); diff --git a/src/Entities/ArrowEntity.cpp b/src/Entities/ArrowEntity.cpp index adbee3cf8..ce0adcdcc 100644 --- a/src/Entities/ArrowEntity.cpp +++ b/src/Entities/ArrowEntity.cpp @@ -198,17 +198,16 @@ void cArrowEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) } } - int RelPosX = m_HitBlockPos.x - a_Chunk.GetPosX() * cChunkDef::Width; - int RelPosZ = m_HitBlockPos.z - a_Chunk.GetPosZ() * cChunkDef::Width; - cChunk * Chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelPosX, RelPosZ); + auto relPos = a_Chunk.RelativeToAbsolute(m_HitBlockPos); + auto chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(relPos); - if (Chunk == nullptr) + if (chunk == nullptr) { // Inside an unloaded chunk, abort return; } - if (Chunk->GetBlock(RelPosX, m_HitBlockPos.y, RelPosZ) == E_BLOCK_AIR) // Block attached to was destroyed? + if (chunk->GetBlock(relPos) == E_BLOCK_AIR) // Block attached to was destroyed? { m_IsInGround = false; // Yes, begin simulating physics again } diff --git a/src/Entities/Minecart.cpp b/src/Entities/Minecart.cpp index 9ff8c04b6..01d89ceb4 100644 --- a/src/Entities/Minecart.cpp +++ b/src/Entities/Minecart.cpp @@ -136,10 +136,9 @@ void cMinecart::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) return; } - int RelPosX = POSX_TOINT - a_Chunk.GetPosX() * cChunkDef::Width; - int RelPosZ = POSZ_TOINT - a_Chunk.GetPosZ() * cChunkDef::Width; - cChunk * Chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelPosX, RelPosZ); - if (Chunk == nullptr) + auto relPos = a_Chunk.AbsoluteToRelative(GetPosition()); + auto chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(relPos); + if (chunk == nullptr) { // Inside an unloaded chunk, bail out all processing return; @@ -147,12 +146,12 @@ void cMinecart::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) BLOCKTYPE InsideType; NIBBLETYPE InsideMeta; - Chunk->GetBlockTypeMeta(RelPosX, PosY, RelPosZ, InsideType, InsideMeta); + chunk->GetBlockTypeMeta(relPos, InsideType, InsideMeta); if (!IsBlockRail(InsideType)) { // When a descending minecart hits a flat rail, it goes through the ground; check for this - Chunk->GetBlockTypeMeta(RelPosX, PosY + 1, RelPosZ, InsideType, InsideMeta); + chunk->GetBlockTypeMeta(relPos.addedY(1), InsideType, InsideMeta); if (IsBlockRail(InsideType)) { // Push cart upwards @@ -192,12 +191,12 @@ void cMinecart::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { // Not on rail, default physics SetPosY(floor(GetPosY()) + 0.35); // HandlePhysics overrides this if minecart can fall, else, it is to stop ground clipping minecart bottom when off-rail - super::HandlePhysics(a_Dt, *Chunk); + super::HandlePhysics(a_Dt, *chunk); } if (m_bIsOnDetectorRail && !Vector3i(POSX_TOINT, POSY_TOINT, POSZ_TOINT).Equals(m_DetectorRailPosition)) { - m_World->SetBlock(m_DetectorRailPosition.x, m_DetectorRailPosition.y, m_DetectorRailPosition.z, E_BLOCK_DETECTOR_RAIL, m_World->GetBlockMeta(m_DetectorRailPosition) & 0x07); + m_World->SetBlock(m_DetectorRailPosition, E_BLOCK_DETECTOR_RAIL, m_World->GetBlockMeta(m_DetectorRailPosition) & 0x07); m_bIsOnDetectorRail = false; } else if (WasDetectorRail) diff --git a/src/Items/ItemDye.h b/src/Items/ItemDye.h index c776d5244..94ee9ed6f 100644 --- a/src/Items/ItemDye.h +++ b/src/Items/ItemDye.h @@ -13,26 +13,29 @@ class cItemDyeHandler : public cItemHandler { + using super = cItemHandler; + public: - cItemDyeHandler(int a_ItemType) - : cItemHandler(a_ItemType) + cItemDyeHandler(int a_ItemType): + super(a_ItemType) { } + + virtual bool OnItemUse( cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace ) override { - // Handle growing the plants: if ((a_Item.m_ItemDamage == E_META_DYE_WHITE) && (a_BlockFace != BLOCK_FACE_NONE)) { - if (a_World->GrowRipePlant(a_BlockX, a_BlockY, a_BlockZ, true)) + // Bonemeal (white dye) is used to fertilize plants: + if (fertilizePlant(*a_World, {a_BlockX, a_BlockY, a_BlockZ})) { - // Particle effects are in GrowRipePlant - if (!a_Player->IsGameModeCreative()) + if (a_Player->IsGameModeSurvival()) { a_Player->GetInventory().RemoveOneEquippedItem(); return true; @@ -41,6 +44,7 @@ public: } else if ((a_Item.m_ItemDamage == E_META_DYE_BROWN) && (a_BlockFace >= BLOCK_FACE_ZM) && (a_BlockFace <= BLOCK_FACE_XP)) { + // Cocoa (brown dye) can be planted on jungle logs: BLOCKTYPE BlockType; NIBBLETYPE BlockMeta; a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); @@ -73,6 +77,184 @@ public: return false; } + + + + + /** Attempts to use the bonemeal on the plant at the specified (absolute) position. + The effect of fertilization depends on the plant: https://minecraft.gamepedia.com/Bone_Meal#Fertilizer + - grow a few stages + - grow 1 stage with a chance + - drop pickups without destroying the plant + - grow more plants in the vicinity + If fertilized succesfully, spawn appropriate particle effects, too. + Returns true if the plant was fertilized successfully, false if not / not a plant. + Note that successful fertilization doesn't mean successful growth - for blocks that have only a chance to grow, + fertilization success is reported even in the case when the chance fails (bonemeal still needs to be consumed). */ + bool fertilizePlant(cWorld & a_World, Vector3i a_BlockPos) + { + BLOCKTYPE blockType; + NIBBLETYPE blockMeta; + if (!a_World.GetBlockTypeMeta(a_BlockPos, blockType, blockMeta)) + { + return false; + } + switch (blockType) + { + case E_BLOCK_WHEAT: + case E_BLOCK_CARROTS: + case E_BLOCK_POTATOES: + case E_BLOCK_MELON_STEM: + case E_BLOCK_PUMPKIN_STEM: + { + // Grow by 2 - 5 stages: + auto numStages = GetRandomProvider().RandInt(2, 5); + if (a_World.GrowPlantAt(a_BlockPos, numStages) <= 0) + { + return false; + } + a_World.BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockPos, 0); + return true; + } // case wheat, carrots, potatoes, melon stem, pumpkin stem + + case E_BLOCK_BEETROOTS: + { + // 75% chance of 1-stage growth: + if (GetRandomProvider().RandBool(0.75)) + { + if (a_World.GrowPlantAt(a_BlockPos, 1) > 0) + { + a_World.BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockPos, 0); + } + } + return true; + } // case beetroots + + case E_BLOCK_SAPLING: + { + // 45% chance of growing to the next stage / full tree: + if (GetRandomProvider().RandBool(0.45)) + { + if (a_World.GrowPlantAt(a_BlockPos, 1) > 0) + { + a_World.BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockPos, 0); + } + } + return true; + } + + case E_BLOCK_BIG_FLOWER: + { + // Drop the corresponding flower item without destroying the block: + cItems pickups; + switch (blockMeta) + { + case E_META_BIG_FLOWER_SUNFLOWER: pickups.Add(E_BLOCK_BIG_FLOWER, 1, E_META_BIG_FLOWER_SUNFLOWER); break; + case E_META_BIG_FLOWER_LILAC: pickups.Add(E_BLOCK_BIG_FLOWER, 1, E_META_BIG_FLOWER_LILAC); break; + case E_META_BIG_FLOWER_ROSE_BUSH: pickups.Add(E_BLOCK_BIG_FLOWER, 1, E_META_BIG_FLOWER_ROSE_BUSH); break; + case E_META_BIG_FLOWER_PEONY: pickups.Add(E_BLOCK_BIG_FLOWER, 1, E_META_BIG_FLOWER_PEONY); break; + } + // TODO: Should we call any hook for this? + a_World.SpawnItemPickups(pickups, a_BlockPos); + return true; + } // big flower + + case E_BLOCK_TALL_GRASS: + case E_BLOCK_COCOA_POD: + case E_BLOCK_SUGARCANE: + case E_BLOCK_CACTUS: + { + // Always try to grow 1 stage: + if (a_World.GrowPlantAt(a_BlockPos, 1) <= 0) + { + return false; + } + a_World.BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockPos, 0); + return true; + } // case tall grass + + case E_BLOCK_RED_MUSHROOM: + case E_BLOCK_BROWN_MUSHROOM: + { + // 40% chance of growing into a large mushroom: + if (GetRandomProvider().RandBool(0.6)) + { + return false; + } + if (a_World.GrowPlantAt(a_BlockPos, 1) <= 0) + { + return false; + } + a_World.BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockPos, 0); + return true; + } // case red or brown mushroom + + case E_BLOCK_GRASS: + { + growPlantsAround(a_World, a_BlockPos); + return true; + } + + // TODO: case E_BLOCK_SWEET_BERRY_BUSH: + // TODO: case E_BLOCK_SEA_PICKLE: + // TODO: case E_BLOCK_KELP: + // TODO: case E_BLOCK_BAMBOO: + } // switch (blockType) + return false; + } + + + + + + /** Grows new plants around the specified block. + Places up to 40 new plants, with the following probability: + - 20 % big grass (2-block tall grass) + - 60 % tall grass (1-block tall grass) + - 20 % flowers (biome dependent variants) + The new plants are spawned within 7 taxicab distance of a_BlockPos, on a grass block. + Broadcasts a particle for each new spawned plant. */ + void growPlantsAround(cWorld & a_World, Vector3i a_BlockPos) + { + auto & r1 = GetRandomProvider(); + for (int i = 0; i < 40; ++i) + { + int ofsY = r1.RandInt(3) + r1.RandInt(3) - 3; + if (!cChunkDef::IsValidHeight(a_BlockPos.y + ofsY)) + { + continue; + } + int ofsX = (r1.RandInt(3) + r1.RandInt(3) + r1.RandInt(3) + r1.RandInt(3)) / 2 - 3; + int ofsZ = (r1.RandInt(3) + r1.RandInt(3) + r1.RandInt(3) + r1.RandInt(3)) / 2 - 3; + Vector3i ofs(ofsX, ofsY, ofsZ); + auto typeGround = a_World.GetBlock(a_BlockPos + ofs); + if (typeGround != E_BLOCK_GRASS) + { + continue; + } + auto pos = a_BlockPos + ofs.addedY(1); + auto typeAbove = a_World.GetBlock(pos); + if (typeAbove != E_BLOCK_AIR) + { + continue; + } + BLOCKTYPE spawnType; + NIBBLETYPE spawnMeta = 0; + switch (r1.RandInt(10)) + { + case 0: spawnType = E_BLOCK_YELLOW_FLOWER; break; + case 1: spawnType = E_BLOCK_RED_ROSE; break; + default: + { + spawnType = E_BLOCK_TALL_GRASS; + spawnMeta = E_META_TALL_GRASS_GRASS; + break; + } + } // switch (random spawn block type) + a_World.SetBlock(pos, spawnType, spawnMeta); + a_World.BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, pos, 0); + } // for i - attempts + } } ; diff --git a/src/MobSpawner.cpp b/src/MobSpawner.cpp index 0e41e72ec..17725e4d9 100644 --- a/src/MobSpawner.cpp +++ b/src/MobSpawner.cpp @@ -71,25 +71,29 @@ eMonsterType cMobSpawner::ChooseMobType(EMCSBiome a_Biome) -bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, eMonsterType a_MobType, EMCSBiome a_Biome) +bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, Vector3i a_RelPos, eMonsterType a_MobType, EMCSBiome a_Biome) { if (a_Chunk == nullptr) { return false; } + if ((a_RelPos.y >= cChunkDef::Height - 1) || (a_RelPos.y <= 0)) + { + return false; + } - if (cChunkDef::IsValidHeight(a_RelY - 1) && (a_Chunk->GetBlock(a_RelX, a_RelY - 1, a_RelZ) == E_BLOCK_BEDROCK)) + if (cChunkDef::IsValidHeight(a_RelPos.y - 1) && (a_Chunk->GetBlock(a_RelPos.addedY(-1)) == E_BLOCK_BEDROCK)) { return false; // Make sure mobs do not spawn on bedrock. } - auto & Random = GetRandomProvider(); - BLOCKTYPE TargetBlock = a_Chunk->GetBlock(a_RelX, a_RelY, a_RelZ); + auto & random = GetRandomProvider(); + auto targetBlock = a_Chunk->GetBlock(a_RelPos); // If too close to any player, don't spawn anything - auto WorldPos = a_Chunk->PositionToWorldPosition(a_RelX, a_RelY, a_RelZ); - static const double RangeLimit = 24; - if (a_Chunk->GetWorld()->DoWithNearestPlayer(WorldPos, RangeLimit, [](cPlayer & a_Player) -> bool + auto absPos = a_Chunk->RelativeToAbsolute(a_RelPos); + static const double rangeLimit = 24; + if (a_Chunk->GetWorld()->DoWithNearestPlayer(absPos, rangeLimit, [](cPlayer & a_Player) -> bool { return true; }) @@ -98,33 +102,28 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R return false; } - if ((a_RelY >= cChunkDef::Height - 1) || (a_RelY <= 0)) - { - return false; - } - - NIBBLETYPE BlockLight = a_Chunk->GetBlockLight(a_RelX, a_RelY, a_RelZ); - NIBBLETYPE SkyLight = a_Chunk->GetSkyLight(a_RelX, a_RelY, a_RelZ); - BLOCKTYPE BlockAbove = a_Chunk->GetBlock(a_RelX, a_RelY + 1, a_RelZ); - BLOCKTYPE BlockBelow = a_Chunk->GetBlock(a_RelX, a_RelY - 1, a_RelZ); + auto blockLight = a_Chunk->GetBlockLight(a_RelPos); + auto skyLight = a_Chunk->GetSkyLight(a_RelPos); + auto blockAbove = a_Chunk->GetBlock(a_RelPos.addedY(1)); + auto blockBelow = a_Chunk->GetBlock(a_RelPos.addedY(-1)); - SkyLight = a_Chunk->GetTimeAlteredLight(SkyLight); + skyLight = a_Chunk->GetTimeAlteredLight(skyLight); switch (a_MobType) { case mtGuardian: { - return IsBlockWater(TargetBlock) && IsBlockWater(BlockBelow) && (a_RelY >= 45) && (a_RelY <= 62); + return IsBlockWater(targetBlock) && IsBlockWater(blockBelow) && (a_RelPos.y >= 45) && (a_RelPos.y <= 62); } case mtSquid: { - return IsBlockWater(TargetBlock) && (a_RelY >= 45) && (a_RelY <= 62); + return IsBlockWater(targetBlock) && (a_RelPos.y >= 45) && (a_RelPos.y <= 62); } case mtBat: { - return (a_RelY <= 63) && (BlockLight <= 4) && (SkyLight <= 4) && (TargetBlock == E_BLOCK_AIR) && !cBlockInfo::IsTransparent(BlockAbove); + return (a_RelPos.y <= 63) && (blockLight <= 4) && (skyLight <= 4) && (targetBlock == E_BLOCK_AIR) && !cBlockInfo::IsTransparent(blockAbove); } case mtChicken: @@ -135,42 +134,42 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R case mtSheep: { return ( - (TargetBlock == E_BLOCK_AIR) && - (BlockAbove == E_BLOCK_AIR) && - (!cBlockInfo::IsTransparent(BlockBelow)) && - (BlockBelow == E_BLOCK_GRASS) && - (SkyLight >= 9) + (targetBlock == E_BLOCK_AIR) && + (blockAbove == E_BLOCK_AIR) && + (!cBlockInfo::IsTransparent(blockBelow)) && + (blockBelow == E_BLOCK_GRASS) && + (skyLight >= 9) ); } case mtOcelot: { return ( - (TargetBlock == E_BLOCK_AIR) && - (BlockAbove == E_BLOCK_AIR) && + (targetBlock == E_BLOCK_AIR) && + (blockAbove == E_BLOCK_AIR) && ( - (BlockBelow == E_BLOCK_GRASS) || (BlockBelow == E_BLOCK_LEAVES) || (BlockBelow == E_BLOCK_NEW_LEAVES) + (blockBelow == E_BLOCK_GRASS) || (blockBelow == E_BLOCK_LEAVES) || (blockBelow == E_BLOCK_NEW_LEAVES) ) && - (a_RelY >= 62) && - (Random.RandBool(2.0 / 3.0)) + (a_RelPos.y >= 62) && + (random.RandBool(2.0 / 3.0)) ); } case mtEnderman: { - if (a_RelY < 250) + if (a_RelPos.y < 250) { - BLOCKTYPE BlockTop = a_Chunk->GetBlock(a_RelX, a_RelY + 2, a_RelZ); - if (BlockTop == E_BLOCK_AIR) + auto blockTop = a_Chunk->GetBlock(a_RelPos.addedY(2)); + if (blockTop == E_BLOCK_AIR) { - BlockTop = a_Chunk->GetBlock(a_RelX, a_RelY + 3, a_RelZ); + blockTop = a_Chunk->GetBlock(a_RelPos.addedY(3)); return ( - (TargetBlock == E_BLOCK_AIR) && - (BlockAbove == E_BLOCK_AIR) && - (BlockTop == E_BLOCK_AIR) && - (!cBlockInfo::IsTransparent(BlockBelow)) && - (SkyLight <= 7) && - (BlockLight <= 7) + (targetBlock == E_BLOCK_AIR) && + (blockAbove == E_BLOCK_AIR) && + (blockTop == E_BLOCK_AIR) && + (!cBlockInfo::IsTransparent(blockBelow)) && + (skyLight <= 7) && + (blockLight <= 7) ); } } @@ -179,38 +178,38 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R case mtSpider: { - bool CanSpawn = true; - bool HasFloor = false; + bool canSpawn = true; + bool hasFloor = false; for (int x = 0; x < 2; ++x) { for (int z = 0; z < 2; ++z) { - CanSpawn = a_Chunk->UnboundedRelGetBlockType(a_RelX + x, a_RelY, a_RelZ + z, TargetBlock); - CanSpawn = CanSpawn && (TargetBlock == E_BLOCK_AIR); - if (!CanSpawn) + canSpawn = a_Chunk->UnboundedRelGetBlockType(a_RelPos.addedXZ(x, z), targetBlock); + canSpawn = canSpawn && (targetBlock == E_BLOCK_AIR); + if (!canSpawn) { return false; } - HasFloor = ( - HasFloor || + hasFloor = ( + hasFloor || ( - a_Chunk->UnboundedRelGetBlockType(a_RelX + x, a_RelY - 1, a_RelZ + z, TargetBlock) && - !cBlockInfo::IsTransparent(TargetBlock) + a_Chunk->UnboundedRelGetBlockType(a_RelPos + Vector3i(x, -1, z), targetBlock) && + !cBlockInfo::IsTransparent(targetBlock) ) ); } } - return CanSpawn && HasFloor && (SkyLight <= 7) && (BlockLight <= 7); + return canSpawn && hasFloor && (skyLight <= 7) && (blockLight <= 7); } case mtCaveSpider: { return ( - (TargetBlock == E_BLOCK_AIR) && - (!cBlockInfo::IsTransparent(BlockBelow)) && - (SkyLight <= 7) && - (BlockLight <= 7) && - (Random.RandBool()) + (targetBlock == E_BLOCK_AIR) && + (!cBlockInfo::IsTransparent(blockBelow)) && + (skyLight <= 7) && + (blockLight <= 7) && + (random.RandBool()) ); } @@ -219,12 +218,12 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R case mtZombie: { return ( - (TargetBlock == E_BLOCK_AIR) && - (BlockAbove == E_BLOCK_AIR) && - (!cBlockInfo::IsTransparent(BlockBelow)) && - (SkyLight <= 7) && - (BlockLight <= 7) && - (Random.RandBool()) + (targetBlock == E_BLOCK_AIR) && + (blockAbove == E_BLOCK_AIR) && + (!cBlockInfo::IsTransparent(blockBelow)) && + (skyLight <= 7) && + (blockLight <= 7) && + (random.RandBool()) ); } @@ -232,11 +231,11 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R case mtSlime: { return ( - (TargetBlock == E_BLOCK_AIR) && - (BlockAbove == E_BLOCK_AIR) && - (!cBlockInfo::IsTransparent(BlockBelow)) && + (targetBlock == E_BLOCK_AIR) && + (blockAbove == E_BLOCK_AIR) && + (!cBlockInfo::IsTransparent(blockBelow)) && ( - (a_RelY <= 40) || (a_Biome == biSwampland) + (a_RelPos.y <= 40) || (a_Biome == biSwampland) ) ); } @@ -245,18 +244,18 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R case mtZombiePigman: { return ( - (TargetBlock == E_BLOCK_AIR) && - (BlockAbove == E_BLOCK_AIR) && - (!cBlockInfo::IsTransparent(BlockBelow)) && - (Random.RandBool(0.05)) + (targetBlock == E_BLOCK_AIR) && + (blockAbove == E_BLOCK_AIR) && + (!cBlockInfo::IsTransparent(blockBelow)) && + (random.RandBool(0.05)) ); } case mtWolf: { return ( - (TargetBlock == E_BLOCK_GRASS) && - (BlockAbove == E_BLOCK_AIR) && + (targetBlock == E_BLOCK_GRASS) && + (blockAbove == E_BLOCK_AIR) && ( (a_Biome == biColdTaiga) || (a_Biome == biColdTaigaHills) || @@ -274,9 +273,9 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R case mtMooshroom: { return ( - (TargetBlock == E_BLOCK_AIR) && - (BlockAbove == E_BLOCK_AIR) && - (BlockBelow == E_BLOCK_MYCELIUM) && + (targetBlock == E_BLOCK_AIR) && + (blockAbove == E_BLOCK_AIR) && + (blockBelow == E_BLOCK_MYCELIUM) && ( (a_Biome == biMushroomShore) || (a_Biome == biMushroomIsland) @@ -431,7 +430,7 @@ std::set<eMonsterType> cMobSpawner::GetAllowedMobTypes(EMCSBiome a_Biome) -cMonster * cMobSpawner::TryToSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, EMCSBiome a_Biome, int & a_MaxPackSize) +cMonster * cMobSpawner::TryToSpawnHere(cChunk * a_Chunk, Vector3i a_RelPos, EMCSBiome a_Biome, int & a_MaxPackSize) { if (m_NewPack) { @@ -452,9 +451,9 @@ cMonster * cMobSpawner::TryToSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, } // Make sure we are looking at the right chunk to spawn in - a_Chunk = a_Chunk->GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); + a_Chunk = a_Chunk->GetRelNeighborChunkAdjustCoords(a_RelPos); - if ((m_AllowedTypes.find(m_MobType) != m_AllowedTypes.end()) && CanSpawnHere(a_Chunk, a_RelX, a_RelY, a_RelZ, m_MobType, a_Biome)) + if ((m_AllowedTypes.find(m_MobType) != m_AllowedTypes.end()) && CanSpawnHere(a_Chunk, a_RelPos, m_MobType, a_Biome)) { auto newMob = cMonster::NewMonsterFromType(m_MobType); auto NewMobPtr = newMob.get(); diff --git a/src/MobSpawner.h b/src/MobSpawner.h index 695467692..7d5ce5274 100644 --- a/src/MobSpawner.h +++ b/src/MobSpawner.h @@ -27,8 +27,8 @@ public : /** Try to create a monster here If this is the first of a Pack, determine the type of monster a_Biome, BlockType & BlockMeta are used to decide what kind of Mob can Spawn here - a_MaxPackSize is set to the maximal size for a pack this type of mob */ - cMonster * TryToSpawnHere(cChunk * a_Chunk, int A_RelX, int a_RelY, int a_RelZ, EMCSBiome a_Biome, int & a_MaxPackSize); + a_MaxPackSize is set to the maximum size for a pack this type of mob */ + cMonster * TryToSpawnHere(cChunk * a_Chunk, Vector3i a_RelPos, EMCSBiome a_Biome, int & a_MaxPackSize); /** Mark the beginning of a new Pack. All mobs of the same Pack are the same type */ @@ -43,12 +43,14 @@ public : } /** Returns true if specified type of mob can spawn on specified block */ - static bool CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, eMonsterType a_MobType, EMCSBiome a_Biome); + static bool CanSpawnHere(cChunk * a_Chunk, Vector3i a_RelPos, eMonsterType a_MobType, EMCSBiome a_Biome); /** Returns all mob types that can spawn that biome */ static std::set<eMonsterType> GetAllowedMobTypes(EMCSBiome a_Biome); + protected : + /** Returns a random type that can spawn in the specified biome. Returns mtInvalidType if none is possible. */ eMonsterType ChooseMobType(EMCSBiome a_Biome); diff --git a/src/Simulator/FireSimulator.cpp b/src/Simulator/FireSimulator.cpp index 7862ed335..2b603315e 100644 --- a/src/Simulator/FireSimulator.cpp +++ b/src/Simulator/FireSimulator.cpp @@ -102,40 +102,38 @@ void cFireSimulator::SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int NumMSecs = static_cast<int>(a_Dt.count()); for (cCoordWithIntList::iterator itr = Data.begin(); itr != Data.end();) { - int x = itr->x; - int y = itr->y; - int z = itr->z; - auto AbsPos = cChunkDef::RelativeToAbsolute({x, y, z}, a_Chunk->GetPosX(), a_Chunk->GetPosZ()); - BLOCKTYPE BlockType = a_Chunk->GetBlock(x, y, z); + Vector3i relPos(itr->x, itr->y, itr->z); + auto absPos = a_Chunk->RelativeToAbsolute(relPos); + auto blockType = a_Chunk->GetBlock(relPos); - if (!IsAllowedBlock(BlockType)) + if (!IsAllowedBlock(blockType)) { // The block is no longer eligible (not a fire block anymore; a player probably placed a block over the fire) - FIRE_FLOG("FS: Removing block {0}", AbsPos); + FIRE_FLOG("FS: Removing block {0}", absPos); itr = Data.erase(itr); continue; } - auto BurnsForever = ((y > 0) && DoesBurnForever(a_Chunk->GetBlock(x, (y - 1), z))); - auto BlockMeta = a_Chunk->GetMeta(x, y, z); + auto BurnsForever = ((relPos.y > 0) && DoesBurnForever(a_Chunk->GetBlock(relPos.addedY(-1)))); + auto BlockMeta = a_Chunk->GetMeta(relPos); auto Raining = std::any_of(std::begin(gCrossCoords), std::end(gCrossCoords), - [this, AbsPos](Vector3i cc) + [this, absPos](Vector3i cc) { - return (m_World.IsWeatherWetAtXYZ(AbsPos + cc)); + return (m_World.IsWeatherWetAtXYZ(absPos + cc)); } ); // Randomly burn out the fire if it is raining: if (!BurnsForever && Raining && GetRandomProvider().RandBool(CHANCE_BASE_RAIN_EXTINGUISH + (BlockMeta * CHANCE_AGE_M_RAIN_EXTINGUISH))) { - a_Chunk->SetBlock({x, y, z}, E_BLOCK_AIR, 0); + a_Chunk->SetBlock(relPos, E_BLOCK_AIR, 0); itr = Data.erase(itr); continue; } // Try to spread the fire: - TrySpreadFire(a_Chunk, x, y, z); + TrySpreadFire(a_Chunk, relPos); itr->Data -= NumMSecs; if (itr->Data >= 0) @@ -145,20 +143,15 @@ void cFireSimulator::SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, continue; } - /* - FIRE_FLOG("FS: Fire at {0} is stepping", - a_Chunk->PositionToWorldPosition(itr->x, itr->y, itr->z) - ); - */ + // FIRE_FLOG("FS: Fire at {0} is stepping", absPos); + // Has the fire burnt out? if (BlockMeta == 0x0f) { // The fire burnt out completely - FIRE_FLOG("FS: Fire at {0} burnt out, removing the fire block", - a_Chunk->PositionToWorldPosition({itr->x, itr->y, itr->z}) - ); - a_Chunk->SetBlock({x, y, z}, E_BLOCK_AIR, 0); - RemoveFuelNeighbors(a_Chunk, x, y, z); + FIRE_FLOG("FS: Fire at {0} burnt out, removing the fire block", absPos); + a_Chunk->SetBlock(relPos, E_BLOCK_AIR, 0); + RemoveFuelNeighbors(a_Chunk, relPos); itr = Data.erase(itr); continue; } @@ -166,10 +159,10 @@ void cFireSimulator::SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, // Burn out the fire one step by increasing the meta: if (!BurnsForever) { - a_Chunk->SetMeta(x, y, z, BlockMeta + 1); + a_Chunk->SetMeta(relPos, BlockMeta + 1); } - itr->Data = GetBurnStepTime(a_Chunk, x, y, z); // TODO: Add some randomness into this + itr->Data = GetBurnStepTime(a_Chunk, relPos); // TODO: Add some randomness into this ++itr; } // for itr - Data[] } @@ -278,12 +271,12 @@ void cFireSimulator::AddBlock(Vector3i a_Block, cChunk * a_Chunk) -int cFireSimulator::GetBurnStepTime(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ) +int cFireSimulator::GetBurnStepTime(cChunk * a_Chunk, Vector3i a_RelPos) { bool IsBlockBelowSolid = false; - if (a_RelY > 0) + if (a_RelPos.y > 0) { - BLOCKTYPE BlockBelow = a_Chunk->GetBlock(a_RelX, a_RelY - 1, a_RelZ); + BLOCKTYPE BlockBelow = a_Chunk->GetBlock(a_RelPos.addedY(-1)); if (DoesBurnForever(BlockBelow)) { // Is burning atop of netherrack, burn forever (re-check in 10 sec) @@ -296,11 +289,11 @@ int cFireSimulator::GetBurnStepTime(cChunk * a_Chunk, int a_RelX, int a_RelY, in IsBlockBelowSolid = cBlockInfo::IsSolid(BlockBelow); } - for (size_t i = 0; i < ARRAYCOUNT(gCrossCoords); i++) + for (const auto & cross: gCrossCoords) { BLOCKTYPE BlockType; NIBBLETYPE BlockMeta; - if (a_Chunk->UnboundedRelGetBlock(a_RelX + gCrossCoords[i].x, a_RelY, a_RelZ + gCrossCoords[i].z, BlockType, BlockMeta)) + if (a_Chunk->UnboundedRelGetBlock(a_RelPos + cross, BlockType, BlockMeta)) { if (IsFuel(BlockType)) { @@ -314,7 +307,7 @@ int cFireSimulator::GetBurnStepTime(cChunk * a_Chunk, int a_RelX, int a_RelY, in // Checked through everything, nothing was flammable // If block below isn't solid, we can't have fire, it would be a non-fueled fire // SetBlock just to make sure fire doesn't spawn - a_Chunk->SetBlock({a_RelX, a_RelY, a_RelZ}, E_BLOCK_AIR, 0); + a_Chunk->SetBlock(a_RelPos, E_BLOCK_AIR, 0); return 0; } return static_cast<int>(m_BurnStepTimeNonfuel); @@ -324,7 +317,7 @@ int cFireSimulator::GetBurnStepTime(cChunk * a_Chunk, int a_RelX, int a_RelY, in -void cFireSimulator::TrySpreadFire(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ) +void cFireSimulator::TrySpreadFire(cChunk * a_Chunk, Vector3i a_RelPos) { /* if (GetRandomProvider().RandBool(0.99)) @@ -334,11 +327,11 @@ void cFireSimulator::TrySpreadFire(cChunk * a_Chunk, int a_RelX, int a_RelY, int } */ - for (int x = a_RelX - 1; x <= a_RelX + 1; x++) + for (int x = -1; x <= 1; x++) { - for (int z = a_RelZ - 1; z <= a_RelZ + 1; z++) + for (int z = -1; z <= 1; z++) { - for (int y = a_RelY - 1; y <= a_RelY + 2; y++) // flames spread up one more block than around + for (int y = 1; y <= 2; y++) // flames spread up one more block than around { // No need to check the coords for equality with the parent block, // it cannot catch fire anyway (because it's not an air block) @@ -348,24 +341,18 @@ void cFireSimulator::TrySpreadFire(cChunk * a_Chunk, int a_RelX, int a_RelY, int continue; } - // Start the fire in the neighbor {x, y, z} - /* - FIRE_LOG("FS: Trying to start fire at {0}.", - a_Chunk->PositionToWorldPosition(x, y, z) - ); - */ - if (CanStartFireInBlock(a_Chunk, x, y, z)) + // Start the fire in the neighbor a_RelPos + {x, y, z} + auto dstRelPos = a_RelPos + Vector3i{x, y, z}; + if (CanStartFireInBlock(a_Chunk, dstRelPos)) { - int a_PosX = x + a_Chunk->GetPosX() * cChunkDef::Width; - int a_PosZ = z + a_Chunk->GetPosZ() * cChunkDef::Width; - - if (cRoot::Get()->GetPluginManager()->CallHookBlockSpread(m_World, a_PosX, y, a_PosZ, ssFireSpread)) + auto dstAbsPos = a_Chunk->RelativeToAbsolute(dstRelPos); + if (cRoot::Get()->GetPluginManager()->CallHookBlockSpread(m_World, dstAbsPos.x, dstAbsPos.y, dstAbsPos.z, ssFireSpread)) { return; } - FIRE_FLOG("FS: Starting new fire at {0}.", Vector3i{a_PosX, y, a_PosZ}); - a_Chunk->UnboundedRelSetBlock(x, y, z, E_BLOCK_FIRE, 0); + FIRE_FLOG("FS: Starting new fire at {0}.", dstAbsPos); + a_Chunk->UnboundedRelSetBlock(dstRelPos, E_BLOCK_FIRE, 0); } } // for y } // for z @@ -376,45 +363,40 @@ void cFireSimulator::TrySpreadFire(cChunk * a_Chunk, int a_RelX, int a_RelY, int -void cFireSimulator::RemoveFuelNeighbors(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ) +void cFireSimulator::RemoveFuelNeighbors(cChunk * a_Chunk, Vector3i a_RelPos) { - for (auto & Coord : gNeighborCoords) + for (auto & coord : gNeighborCoords) { BLOCKTYPE BlockType; - int X = a_RelX + Coord.x; - int Z = a_RelZ + Coord.z; - - cChunkPtr Neighbour = a_Chunk->GetRelNeighborChunkAdjustCoords(X, Z); - if (Neighbour == nullptr) + auto relPos = a_RelPos + coord; + auto neighbor = a_Chunk->GetRelNeighborChunkAdjustCoords(relPos); + if (neighbor == nullptr) { continue; } - BlockType = Neighbour->GetBlock(X, a_RelY + Coord.y, Z); + BlockType = neighbor->GetBlock(relPos); if (!IsFuel(BlockType)) { continue; } - int AbsX = (Neighbour->GetPosX() * cChunkDef::Width) + X; - int Y = a_RelY + Coord.y; - int AbsZ = (Neighbour->GetPosZ() * cChunkDef::Width) + Z; - + auto absPos = neighbor->RelativeToAbsolute(relPos); if (BlockType == E_BLOCK_TNT) { - m_World.SpawnPrimedTNT({static_cast<double>(AbsX), static_cast<double>(Y), static_cast<double>(AbsZ)}, 0); - Neighbour->SetBlock({X, Y, Z}, E_BLOCK_AIR, 0); + neighbor->SetBlock(relPos, E_BLOCK_AIR, 0); + m_World.SpawnPrimedTNT(absPos, 0); return; } bool ShouldReplaceFuel = (GetRandomProvider().RandBool(m_ReplaceFuelChance * (1.0 / MAX_CHANCE_REPLACE_FUEL))); - if (ShouldReplaceFuel && !cRoot::Get()->GetPluginManager()->CallHookBlockSpread(m_World, AbsX, Y, AbsZ, ssFireSpread)) + if (ShouldReplaceFuel && !cRoot::Get()->GetPluginManager()->CallHookBlockSpread(m_World, absPos.x, absPos.y, absPos.z, ssFireSpread)) { - Neighbour->SetBlock({X, Y, Z}, E_BLOCK_FIRE, 0); + neighbor->SetBlock(relPos, E_BLOCK_FIRE, 0); } else { - Neighbour->SetBlock({X, Y, Z}, E_BLOCK_AIR, 0); + neighbor->SetBlock(relPos, E_BLOCK_AIR, 0); } } // for i - Coords[] } @@ -423,11 +405,11 @@ void cFireSimulator::RemoveFuelNeighbors(cChunk * a_Chunk, int a_RelX, int a_Rel -bool cFireSimulator::CanStartFireInBlock(cChunk * a_NearChunk, int a_RelX, int a_RelY, int a_RelZ) +bool cFireSimulator::CanStartFireInBlock(cChunk * a_NearChunk, Vector3i a_RelPos) { BLOCKTYPE BlockType; NIBBLETYPE BlockMeta; - if (!a_NearChunk->UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ, BlockType, BlockMeta)) + if (!a_NearChunk->UnboundedRelGetBlock(a_RelPos, BlockType, BlockMeta)) { // The chunk is not accessible return false; @@ -439,9 +421,9 @@ bool cFireSimulator::CanStartFireInBlock(cChunk * a_NearChunk, int a_RelX, int a return false; } - for (size_t i = 0; i < ARRAYCOUNT(gNeighborCoords); i++) + for (const auto & neighbor: gNeighborCoords) { - if (!a_NearChunk->UnboundedRelGetBlock(a_RelX + gNeighborCoords[i].x, a_RelY + gNeighborCoords[i].y, a_RelZ + gNeighborCoords[i].z, BlockType, BlockMeta)) + if (!a_NearChunk->UnboundedRelGetBlock(a_RelPos + neighbor, BlockType, BlockMeta)) { // Neighbor inaccessible, skip it while evaluating continue; diff --git a/src/Simulator/FireSimulator.h b/src/Simulator/FireSimulator.h index bbfeb045e..236be5686 100644 --- a/src/Simulator/FireSimulator.h +++ b/src/Simulator/FireSimulator.h @@ -46,19 +46,19 @@ protected: virtual void AddBlock(Vector3i a_Block, cChunk * a_Chunk) override; /** Returns the time [msec] after which the specified fire block is stepped again; based on surrounding fuels */ - int GetBurnStepTime(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ); + int GetBurnStepTime(cChunk * a_Chunk, Vector3i a_RelPos); /** Tries to spread fire to a neighborhood of the specified block */ - void TrySpreadFire(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ); + void TrySpreadFire(cChunk * a_Chunk, Vector3i a_RelPos); /** Removes all burnable blocks neighboring the specified block */ - void RemoveFuelNeighbors(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ); + void RemoveFuelNeighbors(cChunk * a_Chunk, Vector3i a_RelPos); /** Returns true if a fire can be started in the specified block, that is, it is an air block and has fuel next to it. Note that a_NearChunk may be a chunk neighbor to the block specified! The coords are relative to a_NearChunk but not necessarily in it. */ - bool CanStartFireInBlock(cChunk * a_NearChunk, int a_RelX, int a_RelY, int a_RelZ); + bool CanStartFireInBlock(cChunk * a_NearChunk, Vector3i a_RelPos); } ; diff --git a/src/Simulator/FloodyFluidSimulator.cpp b/src/Simulator/FloodyFluidSimulator.cpp index f00dd3bfd..b620dddce 100644 --- a/src/Simulator/FloodyFluidSimulator.cpp +++ b/src/Simulator/FloodyFluidSimulator.cpp @@ -66,7 +66,7 @@ void cFloodyFluidSimulator::SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_Re } // When in contact with water, lava should harden - if (HardenBlock(a_Chunk, a_RelX, a_RelY, a_RelZ, MyBlock, MyMeta)) + if (HardenBlock(a_Chunk, {a_RelX, a_RelY, a_RelZ}, MyBlock, MyMeta)) { // Block was changed, bail out return; @@ -221,19 +221,18 @@ void cFloodyFluidSimulator::SpreadToNeighbor(cChunk * a_NearChunk, int a_RelX, i ASSERT(a_NewMeta <= 8); // Invalid meta values ASSERT(a_NewMeta > 0); // Source blocks aren't spread - a_NearChunk = a_NearChunk->GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); + Vector3i relPos(a_RelX, a_RelY, a_RelZ); + a_NearChunk = a_NearChunk->GetRelNeighborChunkAdjustCoords(relPos); if ((a_NearChunk == nullptr) || (!a_NearChunk->IsValid())) { // Chunk not available return; } - const int BlockX = a_NearChunk->GetPosX() * cChunkDef::Width + a_RelX; - const int BlockZ = a_NearChunk->GetPosZ() * cChunkDef::Width + a_RelZ; - + const auto absPos = a_NearChunk->RelativeToAbsolute(relPos); BLOCKTYPE BlockType; NIBBLETYPE BlockMeta; - a_NearChunk->GetBlockTypeMeta(a_RelX, a_RelY, a_RelZ, BlockType, BlockMeta); + a_NearChunk->GetBlockTypeMeta(relPos, BlockType, BlockMeta); if (IsAllowedBlock(BlockType)) { @@ -252,13 +251,13 @@ void cFloodyFluidSimulator::SpreadToNeighbor(cChunk * a_NearChunk, int a_RelX, i // Lava flowing into water, change to stone / cobblestone based on direction: BLOCKTYPE NewBlock = (a_NewMeta == 8) ? E_BLOCK_STONE : E_BLOCK_COBBLESTONE; FLUID_FLOG(" Lava flowing into water, turning water at rel {0} into {1}", - Vector3i{a_RelX, a_RelY, a_RelZ}, ItemTypeToString(NewBlock) + relPos, ItemTypeToString(NewBlock) ); - a_NearChunk->SetBlock({a_RelX, a_RelY, a_RelZ}, NewBlock, 0); + a_NearChunk->SetBlock(relPos, NewBlock, 0); m_World.BroadcastSoundEffect( "block.lava.extinguish", - Vector3d(BlockX, a_RelY, BlockZ), + absPos, 0.5f, 1.5f ); @@ -272,13 +271,13 @@ void cFloodyFluidSimulator::SpreadToNeighbor(cChunk * a_NearChunk, int a_RelX, i // Water flowing into lava, change to cobblestone / obsidian based on dest block: BLOCKTYPE NewBlock = (BlockMeta == 0) ? E_BLOCK_OBSIDIAN : E_BLOCK_COBBLESTONE; FLUID_FLOG(" Water flowing into lava, turning lava at rel {0} into {1}", - Vector3i{a_RelX, a_RelY, a_RelZ}, ItemTypeToString(NewBlock) + relPos, ItemTypeToString(NewBlock) ); - a_NearChunk->SetBlock({a_RelX, a_RelY, a_RelZ}, NewBlock, 0); + a_NearChunk->SetBlock(relPos, NewBlock, 0); m_World.BroadcastSoundEffect( "block.lava.extinguish", - Vector3d(BlockX, a_RelY, BlockZ), + absPos, 0.5f, 1.5f ); @@ -302,16 +301,16 @@ void cFloodyFluidSimulator::SpreadToNeighbor(cChunk * a_NearChunk, int a_RelX, i cBlockHandler * Handler = BlockHandler(BlockType); if (Handler->DoesDropOnUnsuitable()) { - m_World.DropBlockAsPickups({BlockX, a_RelY, BlockZ}, nullptr, nullptr); + m_World.DropBlockAsPickups(absPos, nullptr, nullptr); } } // if (CanWashAway) // Spread: - FLUID_FLOG(" Spreading to {0} with meta {1}", Vector3i{BlockX, a_RelY, BlockZ}, a_NewMeta); - a_NearChunk->SetBlock({a_RelX, a_RelY, a_RelZ}, m_FluidBlock, a_NewMeta); - m_World.GetSimulatorManager()->WakeUp({BlockX, a_RelY, BlockZ}, a_NearChunk); + FLUID_FLOG(" Spreading to {0} with meta {1}", absPos, a_NewMeta); + a_NearChunk->SetBlock(relPos, m_FluidBlock, a_NewMeta); + m_World.GetSimulatorManager()->WakeUp(absPos, a_NearChunk); - HardenBlock(a_NearChunk, a_RelX, a_RelY, a_RelZ, m_FluidBlock, a_NewMeta); + HardenBlock(a_NearChunk, relPos, m_FluidBlock, a_NewMeta); } @@ -365,8 +364,10 @@ bool cFloodyFluidSimulator::CheckNeighborsForSource(cChunk * a_Chunk, int a_RelX -bool cFloodyFluidSimulator::HardenBlock(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta) +bool cFloodyFluidSimulator::HardenBlock(cChunk * a_Chunk, Vector3i a_RelPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta) { + ASSERT(cChunkDef::IsValidRelPos(a_RelPos)); + // Only lava blocks can harden if (!IsBlockLava(a_BlockType)) { @@ -377,16 +378,16 @@ bool cFloodyFluidSimulator::HardenBlock(cChunk * a_Chunk, int a_RelX, int a_RelY BLOCKTYPE BlockType; NIBBLETYPE BlockMeta; - static const Vector3i Coords[] = + static const Vector3i neighborOffsets[] = { Vector3i( 1, 0, 0), Vector3i(-1, 0, 0), Vector3i( 0, 0, 1), Vector3i( 0, 0, -1), }; - for (size_t i = 0; i < ARRAYCOUNT(Coords); i++) + for (const auto & ofs: neighborOffsets) { - if (!a_Chunk->UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta)) + if (!a_Chunk->UnboundedRelGetBlock(a_RelPos + ofs, BlockType, BlockMeta)) { continue; } @@ -401,13 +402,13 @@ bool cFloodyFluidSimulator::HardenBlock(cChunk * a_Chunk, int a_RelX, int a_RelY if (a_Meta == 0) { // Source lava block - a_Chunk->SetBlock({a_RelX, a_RelY, a_RelZ}, E_BLOCK_OBSIDIAN, 0); + a_Chunk->SetBlock(a_RelPos, E_BLOCK_OBSIDIAN, 0); return true; } // Ignore last lava level else if (a_Meta <= 4) { - a_Chunk->SetBlock({a_RelX, a_RelY, a_RelZ}, E_BLOCK_COBBLESTONE, 0); + a_Chunk->SetBlock(a_RelPos, E_BLOCK_COBBLESTONE, 0); return true; } } diff --git a/src/Simulator/FloodyFluidSimulator.h b/src/Simulator/FloodyFluidSimulator.h index d193b0f90..1139da1fc 100644 --- a/src/Simulator/FloodyFluidSimulator.h +++ b/src/Simulator/FloodyFluidSimulator.h @@ -42,7 +42,7 @@ protected: /** Checks if the specified block should harden (Water / Lava interaction) and if so, converts it to a suitable block. Returns whether the block was changed or not. */ - bool HardenBlock(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta); + bool HardenBlock(cChunk * a_Chunk, Vector3i a_RelPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta); /** Spread fluid to XZ neighbors. The coords are of the block currently being processed; a_NewMeta is the new meta for the new fluid block. diff --git a/src/World.cpp b/src/World.cpp index 995ba12a4..e62be6761 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -12,6 +12,7 @@ #include "DeadlockDetect.h" #include "LineBlockTracer.h" #include "UUID.h" +#include "BlockInServerPluginInterface.h" // Serializers #include "WorldStorage/ScoreboardSerializer.h" @@ -1572,17 +1573,17 @@ bool cWorld::DoWithChunkAt(Vector3i a_BlockPos, cChunkCallback a_Callback) -void cWorld::GrowTree(int a_X, int a_Y, int a_Z) +bool cWorld::GrowTree(int a_X, int a_Y, int a_Z) { if (GetBlock(a_X, a_Y, a_Z) == E_BLOCK_SAPLING) { // There is a sapling here, grow a tree according to its type: - GrowTreeFromSapling(a_X, a_Y, a_Z, GetBlockMeta(a_X, a_Y, a_Z)); + return GrowTreeFromSapling(a_X, a_Y, a_Z, GetBlockMeta(a_X, a_Y, a_Z)); } else { // There is nothing here, grow a tree based on the current biome here: - GrowTreeByBiome(a_X, a_Y, a_Z); + return GrowTreeByBiome(a_X, a_Y, a_Z); } } @@ -1590,7 +1591,7 @@ void cWorld::GrowTree(int a_X, int a_Y, int a_Z) -void cWorld::GrowTreeFromSapling(int a_X, int a_Y, int a_Z, NIBBLETYPE a_SaplingMeta) +bool cWorld::GrowTreeFromSapling(int a_X, int a_Y, int a_Z, NIBBLETYPE a_SaplingMeta) { cNoise Noise(m_Generator.GetSeed()); sSetBlockVector Logs, Other; @@ -1611,7 +1612,7 @@ void cWorld::GrowTreeFromSapling(int a_X, int a_Y, int a_Z, NIBBLETYPE a_Sapling { if (!GetLargeTreeAdjustment(a_X, a_Y, a_Z, a_SaplingMeta)) { - return; + return false; } GetDarkoakTreeImage(a_X, a_Y, a_Z, Noise, WorldAge, Logs, Other); @@ -1620,7 +1621,7 @@ void cWorld::GrowTreeFromSapling(int a_X, int a_Y, int a_Z, NIBBLETYPE a_Sapling } Other.insert(Other.begin(), Logs.begin(), Logs.end()); Logs.clear(); - GrowTreeImage(Other); + return GrowTreeImage(Other); } @@ -1713,21 +1714,21 @@ bool cWorld::GetLargeTreeAdjustment(int & a_X, int & a_Y, int & a_Z, NIBBLETYPE -void cWorld::GrowTreeByBiome(int a_X, int a_Y, int a_Z) +bool cWorld::GrowTreeByBiome(int a_X, int a_Y, int a_Z) { cNoise Noise(m_Generator.GetSeed()); sSetBlockVector Logs, Other; GetTreeImageByBiome(a_X, a_Y, a_Z, Noise, static_cast<int>(std::chrono::duration_cast<cTickTimeLong>(m_WorldAge).count() & 0xffffffff), GetBiomeAt(a_X, a_Z), Logs, Other); Other.insert(Other.begin(), Logs.begin(), Logs.end()); Logs.clear(); - GrowTreeImage(Other); + return GrowTreeImage(Other); } -void cWorld::GrowTreeImage(const sSetBlockVector & a_Blocks) +bool cWorld::GrowTreeImage(const sSetBlockVector & a_Blocks) { // Check that the tree has place to grow @@ -1744,7 +1745,7 @@ void cWorld::GrowTreeImage(const sSetBlockVector & a_Blocks) // Query blocktypes and metas at those log blocks: if (!GetBlocks(b2, false)) { - return; + return false; } // Check that at each log's coord there's an block allowed to be overwritten: @@ -1758,339 +1759,32 @@ void cWorld::GrowTreeImage(const sSetBlockVector & a_Blocks) } default: { - return; + return false; } } } // for itr - b2[] // All ok, replace blocks with the tree image: m_ChunkMap->ReplaceTreeBlocks(a_Blocks); + return true; } -bool cWorld::GrowRipePlant(const int a_BlockX, const int a_BlockY, const int a_BlockZ, bool a_IsByBonemeal) +int cWorld::GrowPlantAt(Vector3i a_BlockPos, int a_NumStages) { - auto & random = GetRandomProvider(); - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); - const Vector3i BlockPos{a_BlockX, a_BlockY, a_BlockZ}; - switch (BlockType) - { - case E_BLOCK_BEETROOTS: - { - if ((a_IsByBonemeal && !m_IsBeetrootsBonemealable) || (BlockMeta >= 3)) - { - return false; - } - if (!a_IsByBonemeal) - { - ++BlockMeta; - } - else - { - BlockMeta += 1; - BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(3)); - } - FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); - BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, BlockPos, 0); - return true; - } - - case E_BLOCK_CARROTS: - { - if ((a_IsByBonemeal && !m_IsCarrotsBonemealable) || (BlockMeta >= 7)) - { - return false; - } - if (!a_IsByBonemeal) - { - ++BlockMeta; - } - else - { - BlockMeta += random.RandInt<NIBBLETYPE>(2, 5); - BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7)); - } - FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); - BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, BlockPos, 0); - return true; - } - - case E_BLOCK_COCOA_POD: - { - NIBBLETYPE TypeMeta = BlockMeta & 0x03; - int GrowState = BlockMeta >> 2; - - if (GrowState >= 2) - { - return false; - } - ++GrowState; - FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, static_cast<NIBBLETYPE>(GrowState << 2 | TypeMeta)); - BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, BlockPos, 0); - return true; - } - - case E_BLOCK_CROPS: - { - if ((a_IsByBonemeal && !m_IsCropsBonemealable) || (BlockMeta >= 7)) - { - return false; - } - if (!a_IsByBonemeal) - { - ++BlockMeta; - } - else - { - BlockMeta += random.RandInt<NIBBLETYPE>(2, 5); - BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7)); - } - FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); - BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, BlockPos, 0); - return true; - } - - case E_BLOCK_MELON_STEM: - { - if (BlockMeta < 7) - { - if (a_IsByBonemeal && !m_IsMelonStemBonemealable) - { - return false; - } - - if (!a_IsByBonemeal) - { - ++BlockMeta; - } - else - { - BlockMeta += random.RandInt<NIBBLETYPE>(2, 5); - BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7)); - } - FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); - } - else - { - if (a_IsByBonemeal && !m_IsMelonBonemealable) - { - return false; - } - if (!GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, BlockType)) - { - return false; - } - } - BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, BlockPos, 0); - return true; - } - - case E_BLOCK_POTATOES: - { - if ((a_IsByBonemeal && !m_IsPotatoesBonemealable) || (BlockMeta >= 7)) - { - return false; - } - if (!a_IsByBonemeal) - { - ++BlockMeta; - } - else - { - BlockMeta += random.RandInt<NIBBLETYPE>(2, 5); - BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7)); - } - FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); - BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, BlockPos, 0); - return true; - } - - case E_BLOCK_PUMPKIN_STEM: - { - if (BlockMeta < 7) - { - if (a_IsByBonemeal && !m_IsPumpkinStemBonemealable) - { - return false; - } - - if (!a_IsByBonemeal) - { - ++BlockMeta; - } - else - { - BlockMeta += random.RandInt<NIBBLETYPE>(2, 5); - BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7)); - } - FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); - } - else - { - if (a_IsByBonemeal && !m_IsPumpkinBonemealable) - { - return false; - } - if (!GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, BlockType)) - { - return false; - } - } - BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, BlockPos, 0); - return true; - } - - case E_BLOCK_SAPLING: - { - if (a_IsByBonemeal && !m_IsSaplingBonemealable) - { - return false; - } - NIBBLETYPE TypeMeta = BlockMeta & 0x07; - int GrowState = BlockMeta >> 3; - - if (GrowState < 1) - { - // Non-bonemeal forces a growth, while bonemeal only has a chance of growing it - if (!a_IsByBonemeal) - { - ++GrowState; - } - else if (random.RandBool(0.45)) - { - ++GrowState; - } - - FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, static_cast<NIBBLETYPE>(GrowState << 3 | TypeMeta)); - } - else if (random.RandBool(0.45)) - { - GrowTreeFromSapling(a_BlockX, a_BlockY, a_BlockZ, BlockMeta); - } - BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, BlockPos, 0); - return true; - } - - case E_BLOCK_GRASS: - { - if (a_IsByBonemeal && !m_IsGrassBonemealable) - { - return false; - } - auto & r1 = GetRandomProvider(); - for (int i = 0; i < 60; i++) - { - int OfsX = (r1.RandInt(3) + r1.RandInt(3) + r1.RandInt(3) + r1.RandInt(3)) / 2 - 3; - int OfsY = r1.RandInt(3) + r1.RandInt(3) - 3; - int OfsZ = (r1.RandInt(3) + r1.RandInt(3) + r1.RandInt(3) + r1.RandInt(3)) / 2 - 3; - BLOCKTYPE Ground = GetBlock(a_BlockX + OfsX, a_BlockY + OfsY, a_BlockZ + OfsZ); - if (Ground != E_BLOCK_GRASS) - { - continue; - } - BLOCKTYPE Above = GetBlock(a_BlockX + OfsX, a_BlockY + OfsY + 1, a_BlockZ + OfsZ); - if (Above != E_BLOCK_AIR) - { - continue; - } - BLOCKTYPE SpawnType; - NIBBLETYPE SpawnMeta = 0; - switch (r1.RandInt(10)) - { - case 0: SpawnType = E_BLOCK_YELLOW_FLOWER; break; - case 1: SpawnType = E_BLOCK_RED_ROSE; break; - default: - { - SpawnType = E_BLOCK_TALL_GRASS; - SpawnMeta = E_META_TALL_GRASS_GRASS; - break; - } - } // switch (random spawn block type) - FastSetBlock(a_BlockX + OfsX, a_BlockY + OfsY + 1, a_BlockZ + OfsZ, SpawnType, SpawnMeta); - BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, {a_BlockX + OfsX, a_BlockY + OfsY, a_BlockZ + OfsZ}, 0); - } // for i - 50 times - return true; - } + return m_ChunkMap->GrowPlantAt(a_BlockPos, a_NumStages); +} - case E_BLOCK_SUGARCANE: - { - if (a_IsByBonemeal && !m_IsSugarcaneBonemealable) - { - return false; - } - if (m_ChunkMap->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, 1) == 0) - { - return false; - } - BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, BlockPos, 0); - return true; - } - case E_BLOCK_CACTUS: - { - if (a_IsByBonemeal && !m_IsCactusBonemealable) - { - return false; - } - if (m_ChunkMap->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, 1) == 0) - { - return false; - } - BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, BlockPos, 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, BlockPos, 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, BlockPos, 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; +bool cWorld::GrowRipePlant(Vector3i a_BlockPos) +{ + return (GrowPlantAt(a_BlockPos, 16) > 0); } @@ -2099,7 +1793,8 @@ bool cWorld::GrowRipePlant(const int a_BlockX, const int a_BlockY, const int a_B int cWorld::GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow) { - return m_ChunkMap->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow); + LOGWARNING("cWorld::GrowCactus is obsolete, use cWorld::GrowPlantAt instead"); + return m_ChunkMap->GrowPlantAt({a_BlockX, a_BlockY, a_BlockZ}); } @@ -2108,7 +1803,8 @@ int cWorld::GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocks bool cWorld::GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType) { - return m_ChunkMap->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, a_BlockType); + LOGWARNING("cWorld::GrowMelonPumpkin is obsolete, use cWorld::GrowPlantAt instead"); + return (m_ChunkMap->GrowPlantAt({a_BlockX, a_BlockY, a_BlockZ}, 16) > 0); // 8 stages for the stem, 8 attempts for the produce } @@ -2117,7 +1813,8 @@ bool cWorld::GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYP int cWorld::GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow) { - return m_ChunkMap->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow); + LOGWARNING("cWorld::GrowSugarcane is obsolete, use cWorld::GrowPlantAt instead"); + return m_ChunkMap->GrowPlantAt({a_BlockX, a_BlockY, a_BlockZ}, a_NumBlocksToGrow); } @@ -2173,36 +1870,36 @@ void cWorld::SetMaxViewDistance(int a_MaxViewDistance) -void cWorld::SetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +void cWorld::SetBlock(Vector3i a_BlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) { - m_ChunkMap->SetBlock({a_BlockX, a_BlockY, a_BlockZ}, a_BlockType, a_BlockMeta); + m_ChunkMap->SetBlock(a_BlockPos, a_BlockType, a_BlockMeta); } -void cWorld::SetBlockMeta(int a_X, int a_Y, int a_Z, NIBBLETYPE a_MetaData, bool a_ShouldMarkDirty, bool a_ShouldInformClients) +void cWorld::SetBlockMeta(Vector3i a_BlockPos, NIBBLETYPE a_MetaData, bool a_ShouldMarkDirty, bool a_ShouldInformClients) { - m_ChunkMap->SetBlockMeta(a_X, a_Y, a_Z, a_MetaData, a_ShouldMarkDirty, a_ShouldInformClients); + m_ChunkMap->SetBlockMeta(a_BlockPos, a_MetaData, a_ShouldMarkDirty, a_ShouldInformClients); } -NIBBLETYPE cWorld::GetBlockSkyLight(int a_X, int a_Y, int a_Z) +NIBBLETYPE cWorld::GetBlockSkyLight(Vector3i a_BlockPos) { - return m_ChunkMap->GetBlockSkyLight(a_X, a_Y, a_Z); + return m_ChunkMap->GetBlockSkyLight(a_BlockPos); } -NIBBLETYPE cWorld::GetBlockBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ) +NIBBLETYPE cWorld::GetBlockBlockLight(Vector3i a_BlockPos) { - return m_ChunkMap->GetBlockBlockLight(a_BlockX, a_BlockY, a_BlockZ); + return m_ChunkMap->GetBlockBlockLight(a_BlockPos); } @@ -2218,9 +1915,9 @@ bool cWorld::GetBlockTypeMeta(Vector3i a_BlockPos, BLOCKTYPE & a_BlockType, NIBB -bool cWorld::GetBlockInfo(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight) +bool cWorld::GetBlockInfo(Vector3i a_BlockPos, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight) { - return m_ChunkMap->GetBlockInfo(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_Meta, a_SkyLight, a_BlockLight); + return m_ChunkMap->GetBlockInfo(a_BlockPos, a_BlockType, a_Meta, a_SkyLight, a_BlockLight); } diff --git a/src/World.h b/src/World.h index 742c1795e..159fa7d93 100644 --- a/src/World.h +++ b/src/World.h @@ -384,59 +384,137 @@ public: /** Sets the block at the specified coords to the specified value. Full processing, incl. updating neighbors, is performed. */ - void SetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + void SetBlock(Vector3i a_BlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + /** OBSOLETE, use the Vector3-based overload instead. + Sets the block at the specified coords to the specified value. + Full processing, incl. updating neighbors, is performed. */ + void SetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) + { + return SetBlock({a_BlockX, a_BlockY, a_BlockZ}, a_BlockType, a_BlockMeta); + } /** 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). */ + The replacement doesn't trigger block updates, nor wake up simulators. + 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) + { + m_ChunkMap->FastSetBlock(a_BlockPos, a_BlockType, a_BlockMeta); + } + + /** OBSOLETE, use the Vector3-based overload instead. + Sets the block at the specified coords to the specified value. + The replacement doesn't trigger block updates, nor wake up simulators. + 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) { - m_ChunkMap->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta); + return FastSetBlock({a_BlockX, a_BlockY, a_BlockZ}, a_BlockType, a_BlockMeta); + } + + /** Returns the block type at the specified position. + Returns 0 if the chunk is not valid. */ + BLOCKTYPE GetBlock(Vector3i a_BlockPos) + { + return m_ChunkMap->GetBlock(a_BlockPos); } - BLOCKTYPE GetBlock (int a_BlockX, int a_BlockY, int a_BlockZ) + /** OBSOLETE, use the Vector3-based overload instead. + Returns the block type at the specified position. + Returns 0 if the chunk is not valid. */ + BLOCKTYPE GetBlock(int a_BlockX, int a_BlockY, int a_BlockZ) + { + return m_ChunkMap->GetBlock({a_BlockX, a_BlockY, a_BlockZ}); + } + + /** Returns the block meta at the specified position. + Returns 0 if the chunk is not valid. */ + NIBBLETYPE GetBlockMeta(Vector3i a_BlockPos) + { + return m_ChunkMap->GetBlockMeta(a_BlockPos); + } + + /** OBSOLETE, use the Vector3-based overload instead. + Returns the block meta at the specified position. + Returns 0 if the chunk is not valid. */ + NIBBLETYPE GetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ) { - return m_ChunkMap->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + return m_ChunkMap->GetBlockMeta({a_BlockX, a_BlockY, a_BlockZ}); } - NIBBLETYPE GetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ) + /** 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_ShouldInformClients = 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_ShouldInformClients = true) { - return m_ChunkMap->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + return SetBlockMeta({a_BlockX, a_BlockY, a_BlockZ}, a_MetaData, a_ShouldMarkDirty, a_ShouldInformClients); } - void SetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_MetaData, bool a_ShouldMarkDirty = true, bool a_ShouldInformClients = true); - NIBBLETYPE GetBlockSkyLight (int a_BlockX, int a_BlockY, int a_BlockZ); - NIBBLETYPE GetBlockBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ); + /** Returns the sky light value at the specified block position. + The sky light is "raw" - not affected by time-of-day. + Returns 0 if chunk not valid. */ + NIBBLETYPE GetBlockSkyLight(Vector3i a_BlockPos); + + /** OBSOLETE, use the Vector3-based overload instead. + Returns the sky light value at the specified block position. + The sky light is "raw" - not affected by time-of-day. + Returns 0 if chunk not valid. */ + NIBBLETYPE GetBlockSkyLight(int a_BlockX, int a_BlockY, int a_BlockZ) + { + return GetBlockSkyLight({a_BlockX, a_BlockY, a_BlockZ}); + } + + /** Returns the block-light value at the specified block position. + Returns 0 if chunk not valid. */ + NIBBLETYPE GetBlockBlockLight(Vector3i a_BlockPos); + + /** OBSOLETE, use the Vector3-based overload instead. + Returns the block-light value at the specified block position. + Returns 0 if chunk not valid. */ + NIBBLETYPE GetBlockBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ) + { + return GetBlockBlockLight({a_BlockX, a_BlockY, a_BlockZ}); + } // tolua_end /** Retrieves the block type and meta at the specified coords. Stores the result into a_BlockType and a_BlockMeta. - Returns true if successful, false if chunk not present. */ - bool GetBlockTypeMeta(Vector3i a_BlockPos, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); // TODO: Export in ManualBindings_World.cpp + Returns true if successful, false if chunk not present. + TODO: Export in ManualBindings_World.cpp. */ + bool GetBlockTypeMeta(Vector3i a_BlockPos, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); /** OBSOLETE, use the Vector3i-based overload instead. Retrieves the block type and meta at the specified coords. Stores the result into a_BlockType and a_BlockMeta. - Returns true if successful, false if chunk not present. */ - bool GetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) // Exported in ManualBindings_World.cpp + Returns true if successful, false if chunk not present. + Exported in ManualBindings_World.cpp. */ + bool GetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) { return GetBlockTypeMeta({a_BlockX, a_BlockY, a_BlockZ}, a_BlockType, a_BlockMeta); } - bool GetBlockInfo(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight); // Exported in ManualBindings.cpp - - // TODO: NIBBLETYPE GetBlockActualLight(int a_BlockX, int a_BlockY, int a_BlockZ); + /** Queries the whole block specification from the world. + Returns true if all block info was retrieved successfully, false if not (invalid chunk / bad position). + Exported in ManualBindings_World.cpp. */ + bool GetBlockInfo(Vector3i a_BlockPos, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight); - // tolua_begin + /** Queries the whole block specification from the world. + Returns true if all block info was retrieved successfully, false if not (invalid chunk / bad position). + Exported in ManualBindings_World.cpp. */ + bool GetBlockInfo(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight) + { + return GetBlockInfo({a_BlockX, a_BlockY, a_BlockZ}, a_BlockType, a_Meta, a_SkyLight, a_BlockLight); + } - // Vector3i variants: - void FastSetBlock(Vector3i a_Pos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) { FastSetBlock( a_Pos.x, a_Pos.y, a_Pos.z, a_BlockType, a_BlockMeta); } - BLOCKTYPE GetBlock (Vector3i a_Pos) { return GetBlock( a_Pos.x, a_Pos.y, a_Pos.z); } - NIBBLETYPE GetBlockMeta(Vector3i a_Pos) { return GetBlockMeta( a_Pos.x, a_Pos.y, a_Pos.z); } - void SetBlockMeta(Vector3i a_Pos, NIBBLETYPE a_MetaData) { SetBlockMeta( a_Pos.x, a_Pos.y, a_Pos.z, a_MetaData); } - NIBBLETYPE GetBlockBlockLight(Vector3i a_Pos) { return GetBlockBlockLight( a_Pos.x, a_Pos.y, a_Pos.z); } - // tolua_end + // TODO: NIBBLETYPE GetBlockActualLight(int a_BlockX, int a_BlockY, int a_BlockZ); /** Writes the block area into the specified coords. Returns true if all chunks have been processed. @@ -718,21 +796,54 @@ public: Returns false if the chunk isn't loaded, otherwise returns the same value as the callback */ bool DoWithChunkAt(Vector3i a_BlockPos, cChunkCallback a_Callback); - void GrowTreeImage(const sSetBlockVector & a_Blocks); + /** Imprints the specified blocks into the world, as long as each log block replaces only allowed blocks. + a_Blocks specifies the logs, leaves, vines and possibly other blocks that comprise a single tree. + Returns true if the tree is imprinted successfully, false otherwise. */ + bool GrowTreeImage(const sSetBlockVector & a_Blocks); // tolua_begin - /** Grows a tree at the specified coords, either from a sapling there, or based on the biome */ - void GrowTree (int a_BlockX, int a_BlockY, int a_BlockZ); + /** Grows a tree at the specified coords. + If the specified block is a sapling, the tree is grown from that sapling. + Otherwise a tree is grown based on the biome. + Returns true if the tree was grown, false if not (invalid chunk, insufficient space). */ + bool GrowTree(int a_BlockX, int a_BlockY, int a_BlockZ); + + /** Grows a tree at the specified coords, based on the sapling meta provided. + Returns true if the tree was grown, false if not (invalid chunk, insufficient space). */ + bool GrowTreeFromSapling(Vector3i a_BlockPos, NIBBLETYPE a_SaplingMeta) + { + // TODO: Change the implementation to use Vector3i, once cTree uses Vector3i-based functions + return GrowTreeFromSapling(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, a_SaplingMeta); + } - /** Grows a tree at the specified coords, based on the sapling meta provided */ - void GrowTreeFromSapling(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_SaplingMeta); + /** OBSOLETE, use the Vector3-based overload instead. + Grows a tree at the specified coords, based on the sapling meta provided. + Returns true if the tree was grown, false if not (invalid chunk, insufficient space). */ + bool GrowTreeFromSapling(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_SaplingMeta); - /** Grows a tree at the specified coords, based on the biome in the place */ - void GrowTreeByBiome (int a_BlockX, int a_BlockY, int a_BlockZ); + /** Grows a tree at the specified coords, based on the biome in the place. + Returns true if the tree was grown, false if not (invalid chunk, insufficient space). */ + bool GrowTreeByBiome(int a_BlockX, int a_BlockY, int a_BlockZ); - /** 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 the plant at the specified position by at most a_NumStages. + The block's Grow handler is invoked. + Returns the number of stages the plant has grown, 0 if not a plant. */ + int GrowPlantAt(Vector3i a_BlockPos, int a_NumStages = 1); + + /** Grows the plant at the specified block to its ripe stage. + Returns true if grown, false if not (invalid chunk, non-growable block, already ripe). */ + bool GrowRipePlant(Vector3i a_BlockPos); + + /** OBSOLETE, use the Vector3-based overload instead. + Grows the plant at the specified block to its ripe stage. + a_IsByBonemeal is obsolete, do not use. + Returns true if grown, false if not (invalid chunk, non-growable block, already ripe). */ + bool GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsByBonemeal = false) + { + UNUSED(a_IsByBonemeal); + return GrowRipePlant({a_BlockX, a_BlockY, a_BlockZ}); + } /** 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); |