diff options
author | Mattes D <github@xoft.cz> | 2016-06-04 13:08:15 +0200 |
---|---|---|
committer | Mattes D <github@xoft.cz> | 2016-06-04 13:08:15 +0200 |
commit | fc874ed57d6f16dc5d4b616711e3bfbd6e963ccf (patch) | |
tree | d299b6e8cd24f087f8bf5047cde858d4bcedbcf5 | |
parent | Debuggers: Added the forgotten Inject.lua file. (diff) | |
parent | Makes tall grass and large flowers bonemealable (diff) | |
download | cuberite-fc874ed57d6f16dc5d4b616711e3bfbd6e963ccf.tar cuberite-fc874ed57d6f16dc5d4b616711e3bfbd6e963ccf.tar.gz cuberite-fc874ed57d6f16dc5d4b616711e3bfbd6e963ccf.tar.bz2 cuberite-fc874ed57d6f16dc5d4b616711e3bfbd6e963ccf.tar.lz cuberite-fc874ed57d6f16dc5d4b616711e3bfbd6e963ccf.tar.xz cuberite-fc874ed57d6f16dc5d4b616711e3bfbd6e963ccf.tar.zst cuberite-fc874ed57d6f16dc5d4b616711e3bfbd6e963ccf.zip |
-rw-r--r-- | Server/Plugins/APIDump/APIDesc.lua | 8 | ||||
-rw-r--r-- | src/BlockEntities/DispenserEntity.cpp | 89 | ||||
-rw-r--r-- | src/BlockEntities/DispenserEntity.h | 2 | ||||
-rw-r--r-- | src/Chunk.cpp | 50 | ||||
-rw-r--r-- | src/Chunk.h | 15 | ||||
-rw-r--r-- | src/ChunkMap.cpp | 33 | ||||
-rw-r--r-- | src/ChunkMap.h | 15 | ||||
-rw-r--r-- | src/Defines.h | 13 | ||||
-rw-r--r-- | src/World.cpp | 202 | ||||
-rw-r--r-- | src/World.h | 18 |
10 files changed, 335 insertions, 110 deletions
diff --git a/Server/Plugins/APIDump/APIDesc.lua b/Server/Plugins/APIDump/APIDesc.lua index 0f399bd07..16c39824b 100644 --- a/Server/Plugins/APIDump/APIDesc.lua +++ b/Server/Plugins/APIDump/APIDesc.lua @@ -2519,10 +2519,10 @@ end GetTNTShrapnelLevel = { Params = "", Return = "{{Globals#ShrapnelLevel|ShrapnelLevel}}", Notes = "Returns the shrapnel level, representing the block types that are propelled outwards following an explosion. Based on this value and a random picker, blocks are selectively converted to physics entities (FallingSand) and flung outwards." }, GetWeather = { Params = "", Return = "eWeather", Notes = "Returns the current weather in the world (wSunny, wRain, wStorm). To check for weather, use IsWeatherXXX() functions instead." }, GetWorldAge = { Params = "", Return = "number", Notes = "Returns the total age of the world, in ticks. The age always grows, cannot be set by plugins and is unrelated to TimeOfDay." }, - GrowCactus = { Params = "BlockX, BlockY, BlockZ, NumBlocksToGrow", Return = "", Notes = "Grows a cactus block at the specified coords, by up to the specified number of blocks. Adheres to the world's maximum cactus growth (GetMaxCactusHeight())." }, - GrowMelonPumpkin = { Params = "BlockX, BlockY, BlockZ, StemType", Return = "", Notes = "Grows a melon or pumpkin, based on the stem type specified (assumed to be in the coords provided). Checks for normal melon / pumpkin growth conditions - stem not having another produce next to it and suitable ground below." }, + GrowCactus = { Params = "BlockX, BlockY, BlockZ, NumBlocksToGrow", Return = "number", Notes = "Grows a cactus block at the specified coords, by up to the specified number of blocks. Adheres to the world's maximum cactus growth (GetMaxCactusHeight()). Returns the amount of blocks the cactus grew inside this call." }, + GrowMelonPumpkin = { Params = "BlockX, BlockY, BlockZ, StemType", Return = "bool", Notes = "Grows a melon or pumpkin, based on the stem type specified (assumed to be in the coords provided). Checks for normal melon / pumpkin growth conditions - stem not having another produce next to it and suitable ground below. Returns true if the melon or pumpkin grew successfully." }, GrowRipePlant = { Params = "BlockX, BlockY, BlockZ, IsByBonemeal", Return = "bool", Notes = "Grows the plant at the specified coords. If IsByBonemeal is true, checks first if the specified plant type is bonemealable in the settings. Returns true if the plant was grown, false if not." }, - GrowSugarcane = { Params = "BlockX, BlockY, BlockZ, NumBlocksToGrow", Return = "", Notes = "Grows a sugarcane block at the specified coords, by up to the specified number of blocks. Adheres to the world's maximum sugarcane growth (GetMaxSugarcaneHeight())." }, + GrowSugarcane = { Params = "BlockX, BlockY, BlockZ, NumBlocksToGrow", Return = "number", Notes = "Grows a sugarcane block at the specified coords, by up to the specified number of blocks. Adheres to the world's maximum sugarcane growth (GetMaxSugarcaneHeight()). Returns the amount of blocks the sugarcane grew inside this call." }, GrowTree = { Params = "BlockX, BlockY, BlockZ", Return = "", Notes = "Grows a tree based at the specified coords. If there is a sapling there, grows the tree based on that sapling, otherwise chooses a tree image based on the biome." }, GrowTreeByBiome = { Params = "BlockX, BlockY, BlockZ", Return = "", Notes = "Grows a tree based at the specified coords. The tree type is picked from types available for the biome at those coords." }, GrowTreeFromSapling = { Params = "BlockX, BlockY, BlockZ, SaplingMeta", Return = "", Notes = "Grows a tree based at the specified coords. The tree type is determined from the sapling meta (the sapling itself needn't be present)." }, @@ -2593,6 +2593,7 @@ end { Params = "{{cItems|Pickups}}, X, Y, Z, SpeedX, SpeedY, SpeedZ", Return = "", Notes = "Spawns the specified pickups at the position specified. All the pickups fly away from the spawn position using the specified speed." }, }, SpawnMinecart = { Params = "X, Y, Z, MinecartType, Item, BlockHeight", Return = "number", Notes = "Spawns a minecart at the specific coordinates. MinecartType is the item type of the minecart. If the minecart is an empty minecart then the given item is the block inside the minecart, and blockheight is the distance of the block and the minecart." }, + SpawnBoat = { Params = "X, Y, Z", Return = "number", Notes = "Spawns a boat at the specific coordinates." }, SpawnMob = { Params = "X, Y, Z, {{cMonster|MonsterType}}, [Baby]", Return = "EntityID", Notes = "Spawns the specified type of mob at the specified coords. If the Baby parameter is true, the mob will be a baby. Returns the EntityID of the creates entity, or -1 on failure. " }, SpawnFallingBlock = { Params = "X, Y, Z, BlockType, BlockMeta", Return = "EntityID", Notes = "Spawns an {{cFallingBlock|Falling Block}} entity at the specified coords with the given block type/meta" }, SpawnExperienceOrb = { Params = "X, Y, Z, Reward", Return = "EntityID", Notes = "Spawns an {{cExpOrb|experience orb}} at the specified coords, with the given reward" }, @@ -2679,6 +2680,7 @@ World:ForEachEntity( IsArmor = { Params = "ItemType", Return = "bool", Notes = "(STATIC) Returns true if the specified item type is any kind of an armor." }, IsAxe = { Params = "ItemType", Return = "bool", Notes = "(STATIC) Returns true if the specified item type is any kind of an axe." }, IsBoots = { Params = "ItemType", Return = "bool", Notes = "(STATIC) Returns true if the specified item type is any kind of boots." }, + IsMinecart = { Params = "ItemType", Return = "bool", Notes = "(STATIC) Returns true if the specified item type is any kind of a minecart." }, IsChestPlate = { Params = "ItemType", Return = "bool", Notes = "(STATIC) Returns true if the specified item type is any kind of a chestplate." }, IsHelmet = { Params = "ItemType", Return = "bool", Notes = "(STATIC) Returns true if the specified item type is any kind of a helmet." }, IsHoe = { Params = "ItemType", Return = "bool", Notes = "(STATIC) Returns true if the specified item type is any kind of a hoe." }, diff --git a/src/BlockEntities/DispenserEntity.cpp b/src/BlockEntities/DispenserEntity.cpp index 297d5273e..e03644a0f 100644 --- a/src/BlockEntities/DispenserEntity.cpp +++ b/src/BlockEntities/DispenserEntity.cpp @@ -3,8 +3,10 @@ #include "DispenserEntity.h" #include "../Simulator/FluidSimulator.h" +#include "../Entities/Boat.h" #include "../Chunk.h" +#include "../Defines.h" #include "../World.h" #include "../Entities/ProjectileEntity.h" @@ -39,7 +41,16 @@ void cDispenserEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) int BlockZ = (DispZ + DispChunk->GetPosZ() * cChunkDef::Width); // Dispense the item: - switch (m_Contents.GetSlot(a_SlotNum).m_ItemType) + const cItem & SlotItem = m_Contents.GetSlot(a_SlotNum); + if (ItemCategory::IsMinecart(SlotItem.m_ItemType) && IsBlockRail(DispBlock)) // only actually place the minecart if there are rails! + { + if (m_World->SpawnMinecart(BlockX + 0.5, DispY + 0.5, BlockZ + 0.5, SlotItem.m_ItemType) != cEntity::INVALID_ID) + { + m_Contents.ChangeSlotCount(a_SlotNum, -1); + } + return; + } + switch (SlotItem.m_ItemType) { case E_ITEM_BUCKET: { @@ -115,7 +126,7 @@ void cDispenserEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) case E_BLOCK_TNT: { // Spawn a primed TNT entity, if space allows: - if (DispChunk->GetBlock(DispX, DispY, DispZ) == E_BLOCK_AIR) + if (!cBlockInfo::IsSolid(DispBlock)) { double TNTX = 0.5 + (DispX + DispChunk->GetPosX() * cChunkDef::Width); double TNTZ = 0.5 + (DispZ + DispChunk->GetPosZ() * cChunkDef::Width); @@ -128,7 +139,7 @@ void cDispenserEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) case E_ITEM_FLINT_AND_STEEL: { // Spawn fire if the block in front is air. - if (DispChunk->GetBlock(DispX, DispY, DispZ) == E_BLOCK_AIR) + if (DispBlock == E_BLOCK_AIR) { DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_FIRE, 0); @@ -153,7 +164,7 @@ void cDispenserEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) case E_ITEM_ARROW: { - if (SpawnProjectileFromDispenser(BlockX, DispY, BlockZ, cProjectileEntity::pkArrow, GetShootVector(Meta) * 20 + Vector3d(0, 1, 0)) != cEntity::INVALID_ID) + if (SpawnProjectileFromDispenser(BlockX, DispY, BlockZ, cProjectileEntity::pkArrow, GetShootVector(Meta) * 30 + Vector3d(0, 1, 0)) != cEntity::INVALID_ID) { m_Contents.ChangeSlotCount(a_SlotNum, -1); } @@ -178,6 +189,68 @@ void cDispenserEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) break; } + case E_ITEM_BOTTLE_O_ENCHANTING: + { + if (SpawnProjectileFromDispenser(BlockX, DispY, BlockZ, cProjectileEntity::pkExpBottle, GetShootVector(Meta) * 20 + Vector3d(0, 1, 0)) != cEntity::INVALID_ID) + { + m_Contents.ChangeSlotCount(a_SlotNum, -1); + } + break; + } + + case E_ITEM_POTION: + { + if (SpawnProjectileFromDispenser(BlockX, DispY, BlockZ, cProjectileEntity::pkSplashPotion, GetShootVector(Meta) * 20 + Vector3d(0, 1, 0), &SlotItem) != cEntity::INVALID_ID) + { + m_Contents.ChangeSlotCount(a_SlotNum, -1); + } + break; + } + + case E_ITEM_DYE: + { + if (SlotItem.m_ItemDamage != E_META_DYE_WHITE) + { + DropFromSlot(a_Chunk, a_SlotNum); + break; + } + if (m_World->GrowRipePlant(BlockX, DispY, BlockZ, true)) + { + m_Contents.ChangeSlotCount(a_SlotNum, -1); + } + break; + } + + case E_ITEM_BOAT: + { + Vector3d SpawnPos; + if (IsBlockWater(DispBlock)) + { + // Water next to the dispenser, spawn a boat above the water block + SpawnPos.Set(BlockX, DispY + 1, BlockZ); + } + else if (IsBlockWater(DispChunk->GetBlock(DispX, DispY - 1, DispZ))) + { + // Water one block below the dispenser, spawn a boat at the dispenser's Y level + SpawnPos.Set(BlockX, DispY, BlockZ); + } + else + { + // There's no eligible water block, drop the boat as a pickup + DropFromSlot(a_Chunk, a_SlotNum); + break; + } + + SpawnPos += GetShootVector(Meta) * 0.8; // A boat is bigger than one block. Add the shoot vector to put it outside the dispenser. + SpawnPos += Vector3d(0.5, 0.5, 0.5); + + if (m_World->SpawnBoat(SpawnPos.x, SpawnPos.y, SpawnPos.z)) + { + m_Contents.ChangeSlotCount(a_SlotNum, -1); + } + break; + } + case E_ITEM_FIREWORK_ROCKET: { // TODO: Add the fireworks entity @@ -189,27 +262,25 @@ void cDispenserEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) DropFromSlot(a_Chunk, a_SlotNum); break; } - } // switch (ItemType) + } // switch (SlotItem.m_ItemType) } -UInt32 cDispenserEntity::SpawnProjectileFromDispenser(int a_BlockX, int a_BlockY, int a_BlockZ, cProjectileEntity::eKind a_Kind, const Vector3d & a_ShootVector) +UInt32 cDispenserEntity::SpawnProjectileFromDispenser(int a_BlockX, int a_BlockY, int a_BlockZ, cProjectileEntity::eKind a_Kind, const Vector3d & a_ShootVector, const cItem * a_Item) { return m_World->CreateProjectile( static_cast<double>(a_BlockX + 0.5), static_cast<double>(a_BlockY + 0.5), static_cast<double>(a_BlockZ + 0.5), - a_Kind, nullptr, nullptr, &a_ShootVector + a_Kind, nullptr, a_Item, &a_ShootVector ); } - - Vector3d cDispenserEntity::GetShootVector(NIBBLETYPE a_Meta) { switch (a_Meta & 0x7) diff --git a/src/BlockEntities/DispenserEntity.h b/src/BlockEntities/DispenserEntity.h index c9b553017..03ce3ad69 100644 --- a/src/BlockEntities/DispenserEntity.h +++ b/src/BlockEntities/DispenserEntity.h @@ -26,7 +26,7 @@ public: /** Spawns a projectile of the given kind in front of the dispenser with the specified speed. Returns the UniqueID of the spawned projectile, or 0 on failure. */ - UInt32 SpawnProjectileFromDispenser(int a_BlockX, int a_BlockY, int a_BlockZ, cProjectileEntity::eKind a_Kind, const Vector3d & a_Speed); + UInt32 SpawnProjectileFromDispenser(int a_BlockX, int a_BlockY, int a_BlockZ, cProjectileEntity::eKind a_Kind, const Vector3d & a_Speed, const cItem * a_Item = nullptr); /** Returns a unit vector in the cardinal direction of where the dispenser is facing. */ Vector3d GetShootVector(NIBBLETYPE a_Meta); diff --git a/src/Chunk.cpp b/src/Chunk.cpp index 363de9933..06d5eb319 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -929,7 +929,7 @@ void cChunk::ApplyWeatherToTop() -void cChunk::GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, MTRand & a_TickRandom) +bool cChunk::GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, MTRand & a_TickRandom) { // Convert the stem BlockType into produce BlockType BLOCKTYPE ProduceType; @@ -940,7 +940,7 @@ void cChunk::GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_Bl default: { ASSERT(!"Unhandled blocktype in TickMelonPumpkin()"); - return; + return false; } } @@ -961,7 +961,7 @@ void cChunk::GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_Bl ) { // Neighbors not valid or already taken by the same produce - return; + return false; } // Pick a direction in which to place the produce: @@ -985,7 +985,7 @@ void cChunk::GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_Bl { break; } - default: return; + default: return false; } // Check if there's soil under the neighbor. We already know the neighbors are valid. Place produce if ok @@ -1013,13 +1013,14 @@ void cChunk::GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_Bl break; } } + return true; } -void cChunk::GrowSugarcane(int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks) +int cChunk::GrowSugarcane(int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks) { // Check the total height of the sugarcane blocks here: int Top = a_RelY + 1; @@ -1051,16 +1052,17 @@ void cChunk::GrowSugarcane(int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks) } else { - break; + return i; } } // for i + return ToGrow; } -void cChunk::GrowCactus(int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks) +int cChunk::GrowCactus(int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks) { // Check the total height of the sugarcane blocks here: int Top = a_RelY + 1; @@ -1093,9 +1095,41 @@ void cChunk::GrowCactus(int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks) } else { - break; + return i; } } // for i + return ToGrow; +} + + + + + +bool cChunk::GrowTallGrass(int a_RelX, int a_RelY, int a_RelZ) +{ + if (a_RelY > (cChunkDef::Height - 2)) + { + return false; + } + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + if (!UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ, BlockType, BlockMeta)) + { + return false; + } + if (BlockType != E_BLOCK_TALL_GRASS) + { + return false; + } + NIBBLETYPE LargeFlowerMeta; + switch (BlockMeta) + { + case E_META_TALL_GRASS_GRASS: LargeFlowerMeta = E_META_BIG_FLOWER_DOUBLE_TALL_GRASS; break; + case E_META_TALL_GRASS_FERN: LargeFlowerMeta = E_META_BIG_FLOWER_LARGE_FERN; break; + default: return false; + } + return UnboundedRelFastSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_BIG_FLOWER, LargeFlowerMeta) && + UnboundedRelFastSetBlock(a_RelX, a_RelY + 1, a_RelZ, E_BLOCK_BIG_FLOWER, 8); } diff --git a/src/Chunk.h b/src/Chunk.h index c7987723b..162c8de96 100644 --- a/src/Chunk.h +++ b/src/Chunk.h @@ -580,14 +580,17 @@ private: /** Adds snow to the top of snowy biomes and hydrates farmland / fills cauldrons in rainy biomes */ void ApplyWeatherToTop(void); - /** Grows sugarcane by the specified number of blocks, but no more than 3 blocks high (used by both bonemeal and ticking) */ - void GrowSugarcane (int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks); + /** Grows sugarcane by the specified number of blocks, but no more than 3 blocks high (used by both bonemeal and ticking); returns the amount of blocks the sugarcane grew inside this call */ + int GrowSugarcane (int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks); - /** Grows cactus by the specified number of blocks, but no more than 3 blocks high (used by both bonemeal and ticking) */ - void GrowCactus (int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks); + /** Grows cactus by the specified number of blocks, but no more than 3 blocks high (used by both bonemeal and ticking); returns the amount of blocks the cactus grew inside this call */ + int GrowCactus (int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks); - /** Grows a melon or a pumpkin next to the block specified (assumed to be the stem) */ - void GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, MTRand & a_Random); + /** Grows a tall grass present at the block specified to a two tall grass; returns true if the grass grew */ + bool GrowTallGrass (int a_RelX, int a_RelY, int a_RelZ); + + /** Grows a melon or a pumpkin next to the block specified (assumed to be the stem); returns true if the pumpkin or melon sucessfully grew */ + bool GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, MTRand & a_Random); /** Called by Tick() when an entity moves out of this chunk into a neighbor; moves the entity and sends spawn / despawn packet to clients */ void MoveEntityToNewChunk(cEntity * a_Entity); diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp index 83c1c12dd..55cabaa67 100644 --- a/src/ChunkMap.cpp +++ b/src/ChunkMap.cpp @@ -2597,7 +2597,7 @@ void cChunkMap::GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty) -void cChunkMap::GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, MTRand & a_Rand) +bool cChunkMap::GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, MTRand & a_Rand) { int ChunkX, ChunkZ; cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); @@ -2606,15 +2606,16 @@ void cChunkMap::GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCK cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ); if (Chunk != nullptr) { - Chunk->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_Rand); + return Chunk->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_Rand); } + return false; } -void cChunkMap::GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow) +int cChunkMap::GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow) { int ChunkX, ChunkZ; cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); @@ -2623,15 +2624,16 @@ void cChunkMap::GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_Nu cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ); if (Chunk != nullptr) { - Chunk->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow); + return Chunk->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow); } + return 0; } -void cChunkMap::GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow) +int cChunkMap::GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow) { int ChunkX, ChunkZ; cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); @@ -2640,8 +2642,27 @@ void cChunkMap::GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBl cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ); if (Chunk != nullptr) { - Chunk->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow); + return Chunk->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow); } + return 0; +} + + + + + +bool cChunkMap::GrowTallGrass(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ); + if (Chunk != nullptr) + { + return Chunk->GrowTallGrass(a_BlockX, a_BlockY, a_BlockZ); + } + return 0; } diff --git a/src/ChunkMap.h b/src/ChunkMap.h index bbde06dcf..8a604ac09 100644 --- a/src/ChunkMap.h +++ b/src/ChunkMap.h @@ -360,14 +360,17 @@ public: /** Returns the number of valid chunks and the number of dirty chunks */ void GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty); - /** Grows a melon or a pumpkin next to the block specified (assumed to be the stem) */ - void GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, MTRand & a_Rand); + /** Grows a melon or a pumpkin next to the block specified (assumed to be the stem); returns true if the pumpkin or melon sucessfully grew */ + bool GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, MTRand & a_Rand); - /** Grows a sugarcane present at the block specified by the amount of blocks specified, up to the max height specified in the config */ - void GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow); + /** Grows a sugarcane present at the block specified by the amount of blocks specified, up to the max height specified in the config; returns the amount of blocks the sugarcane grew inside this call */ + int GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow); - /** Grows a cactus present at the block specified by the amount of blocks specified, up to the max height specified in the config */ - void GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow); + /** Grows a cactus present at the block specified by the amount of blocks specified, up to the max height specified in the config; returns the amount of blocks the cactus grew inside this call */ + int GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow); + + /** Grows a tall grass present at the block specified to a two tall grass; returns true if the grass grew */ + bool GrowTallGrass(int a_BlockX, int a_BlockY, int a_BlockZ); /** Sets the blockticking to start at the specified block. Only one blocktick per chunk may be set, second call overwrites the first call */ void SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ); diff --git a/src/Defines.h b/src/Defines.h index 37b80cca3..13049ce4f 100644 --- a/src/Defines.h +++ b/src/Defines.h @@ -788,6 +788,19 @@ namespace ItemCategory + inline bool IsMinecart(short a_ItemType) + { + return ( + (a_ItemType == E_ITEM_MINECART) || + (a_ItemType == E_ITEM_CHEST_MINECART) || + (a_ItemType == E_ITEM_FURNACE_MINECART) || + (a_ItemType == E_ITEM_MINECART_WITH_TNT) || + (a_ItemType == E_ITEM_MINECART_WITH_HOPPER) + ); + } + + + inline bool IsArmor(short a_ItemType) { return ( diff --git a/src/World.cpp b/src/World.cpp index d05f90365..a8225693f 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -17,6 +17,7 @@ #include "WorldStorage/ScoreboardSerializer.h" // Entities (except mobs): +#include "Entities/Boat.h" #include "Entities/ExpOrb.h" #include "Entities/FallingBlock.h" #include "Entities/Minecart.h" @@ -186,6 +187,8 @@ cWorld::cWorld(const AString & a_WorldName, eDimension a_Dimension, const AStrin m_IsPumpkinBonemealable(true), m_IsSaplingBonemealable(true), m_IsSugarcaneBonemealable(false), + m_IsBigFlowerBonemealable(true), + m_IsTallGrassBonemealable(true), m_bCommandBlocksEnabled(true), m_bUseChatPrefixes(false), m_TNTShrapnelLevel(slNone), @@ -470,6 +473,8 @@ void cWorld::Start(void) m_IsPumpkinBonemealable = IniFile.GetValueSetB("Plants", "IsPumpkinBonemealable", false); m_IsSaplingBonemealable = IniFile.GetValueSetB("Plants", "IsSaplingBonemealable", true); m_IsSugarcaneBonemealable = IniFile.GetValueSetB("Plants", "IsSugarcaneBonemealable", false); + m_IsBigFlowerBonemealable = IniFile.GetValueSetB("Plants", "IsBigFlowerBonemealable", true); + m_IsTallGrassBonemealable = IniFile.GetValueSetB("Plants", "IsTallGrassBonemealable", true); m_IsDeepSnowEnabled = IniFile.GetValueSetB("Physics", "DeepSnow", true); m_ShouldLavaSpawnFire = IniFile.GetValueSetB("Physics", "ShouldLavaSpawnFire", true); int TNTShrapnelLevel = IniFile.GetValueSetI("Physics", "TNTShrapnelLevel", static_cast<int>(slAll)); @@ -1705,25 +1710,22 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy { case E_BLOCK_CARROTS: { - if (a_IsByBonemeal && !m_IsCarrotsBonemealable) + if ((a_IsByBonemeal && !m_IsCarrotsBonemealable) || (BlockMeta >= 7)) { return false; } - if (BlockMeta < 7) + if (!a_IsByBonemeal) { - if (!a_IsByBonemeal) - { - ++BlockMeta; - } - else - { - BlockMeta += random.NextInt(4) + 2; - BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7)); - } - FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); - BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0); + ++BlockMeta; + } + else + { + BlockMeta += random.NextInt(4) + 2; + BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7)); } - return BlockMeta == 7; + FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); + BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0); + return true; } case E_BLOCK_COCOA_POD: @@ -1731,36 +1733,34 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy NIBBLETYPE TypeMeta = BlockMeta & 0x03; int GrowState = BlockMeta >> 2; - if (GrowState < 2) + if (GrowState >= 2) { - GrowState++; - FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, static_cast<NIBBLETYPE>(GrowState << 2 | TypeMeta)); - BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0); + return false; } - return GrowState == 2; + ++GrowState; + FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, static_cast<NIBBLETYPE>(GrowState << 2 | TypeMeta)); + BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0); + return true; } case E_BLOCK_CROPS: { - if (a_IsByBonemeal && !m_IsCropsBonemealable) + if ((a_IsByBonemeal && !m_IsCropsBonemealable) || (BlockMeta >= 7)) { return false; } - if (BlockMeta < 7) + if (!a_IsByBonemeal) { - if (!a_IsByBonemeal) - { - ++BlockMeta; - } - else - { - BlockMeta += random.NextInt(4) + 2; - BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7)); - } - FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); - BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0); + ++BlockMeta; + } + else + { + BlockMeta += random.NextInt(4) + 2; + BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7)); } - return BlockMeta == 7; + FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); + BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0); + return true; } case E_BLOCK_MELON_STEM: @@ -1782,7 +1782,6 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7)); } FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); - BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0); } else { @@ -1790,32 +1789,33 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy { return false; } - GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, BlockType); + if (!GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, BlockType)) + { + return false; + } } - return BlockMeta == 7; + BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0); + return true; } case E_BLOCK_POTATOES: { - if (a_IsByBonemeal && !m_IsPotatoesBonemealable) + if ((a_IsByBonemeal && !m_IsPotatoesBonemealable) || (BlockMeta >= 7)) { return false; } - if (BlockMeta < 7) + if (!a_IsByBonemeal) { - if (!a_IsByBonemeal) - { - ++BlockMeta; - } - else - { - BlockMeta += random.NextInt(4) + 2; - BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7)); - } - FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); - BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0); + ++BlockMeta; } - return BlockMeta == 7; + else + { + BlockMeta += random.NextInt(4) + 2; + BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7)); + } + FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); + BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0); + return true; } case E_BLOCK_PUMPKIN_STEM: @@ -1837,7 +1837,6 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7)); } FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); - BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0); } else { @@ -1845,9 +1844,13 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy { return false; } - GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, BlockType); + if (!GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, BlockType)) + { + return false; + } } - return BlockMeta == 7; + BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0); + return true; } case E_BLOCK_SAPLING: @@ -1872,15 +1875,13 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy } FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, static_cast<NIBBLETYPE>(GrowState << 3 | TypeMeta)); - BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0); } else if (random.NextInt(99) < 45) { - GrowTreeFromSapling(a_BlockX, a_BlockY, a_BlockZ, BlockMeta); - return true; } - return false; + BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0); + return true; } case E_BLOCK_GRASS: @@ -1930,7 +1931,11 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy { return false; } - m_ChunkMap->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, m_MaxSugarcaneHeight); + if (m_ChunkMap->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, 1) == 0) + { + return false; + } + BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0); return true; } @@ -1940,9 +1945,58 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy { return false; } - m_ChunkMap->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, m_MaxCactusHeight); + if (m_ChunkMap->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, 1) == 0) + { + return false; + } + BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0); + return true; + } + + case E_BLOCK_TALL_GRASS: + { + if (a_IsByBonemeal && !m_IsTallGrassBonemealable) + { + return false; + } + if (!m_ChunkMap->GrowTallGrass(a_BlockX, a_BlockY, a_BlockZ)) + { + return false; + } + BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0); + return true; + } + + case E_BLOCK_BIG_FLOWER: + { + if (a_IsByBonemeal && !m_IsBigFlowerBonemealable) + { + return false; + } + if (BlockMeta & 8) // the upper flower block does not save the type of the flower + { + GetBlockTypeMeta(a_BlockX, a_BlockY - 1, a_BlockZ, BlockType, BlockMeta); + if (BlockType != E_BLOCK_BIG_FLOWER) + { + return false; + } + } + if ( + (BlockMeta == E_META_BIG_FLOWER_DOUBLE_TALL_GRASS) || + (BlockMeta == E_META_BIG_FLOWER_LARGE_FERN) + ) // tall grass and fern do not work + { + return false; + } + + // spawn flower item + BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_BlockX, a_BlockY, a_BlockZ, 0); + cItems FlowerItem; + FlowerItem.Add(E_BLOCK_BIG_FLOWER, 1, BlockMeta); + SpawnItemPickups(FlowerItem, a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5); return true; } + } // switch (BlockType) return false; } @@ -1951,28 +2005,28 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy -void cWorld::GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow) +int cWorld::GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow) { - m_ChunkMap->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow); + return m_ChunkMap->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow); } -void cWorld::GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType) +bool cWorld::GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType) { MTRand Rand; - m_ChunkMap->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, Rand); + return m_ChunkMap->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, Rand); } -void cWorld::GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow) +int cWorld::GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow) { - m_ChunkMap->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow); + return m_ChunkMap->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow); } @@ -2181,6 +2235,24 @@ UInt32 cWorld::SpawnMinecart(double a_X, double a_Y, double a_Z, int a_MinecartT +UInt32 cWorld::SpawnBoat(double a_X, double a_Y, double a_Z) +{ + cBoat * Boat = new cBoat(a_X, a_Y, a_Z); + if (Boat == nullptr) + { + return cEntity::INVALID_ID; + } + if (!Boat->Initialize(*this)) + { + delete Boat; + return cEntity::INVALID_ID; + } + return Boat->GetUniqueID(); +} + + + + UInt32 cWorld::SpawnPrimedTNT(double a_X, double a_Y, double a_Z, int a_FuseTicks, double a_InitialVelocityCoeff) { cTNTEntity * TNT = new cTNTEntity(a_X, a_Y, a_Z, a_FuseTicks); diff --git a/src/World.h b/src/World.h index eafe7b9b6..faddc6f59 100644 --- a/src/World.h +++ b/src/World.h @@ -445,6 +445,10 @@ public: Returns the UniqueID of the spawned minecart, or cEntity::INVALID_ID on failure. */ UInt32 SpawnMinecart(double a_X, double a_Y, double a_Z, int a_MinecartType, const cItem & a_Content = cItem(), int a_BlockHeight = 1); + /** Spawns a boat at the given coordinates. + Returns the UniqueID of the spawned boat, or cEntity::INVALID_ID on failure. */ + UInt32 SpawnBoat(double a_X, double a_Y, double a_Z); + /** Spawns an experience orb at the given location with the given reward. Returns the UniqueID of the spawned experience orb, or cEntity::INVALID_ID on failure. */ virtual UInt32 SpawnExperienceOrb(double a_X, double a_Y, double a_Z, int a_Reward) override; @@ -582,14 +586,14 @@ public: /** Grows the plant at the specified block to its ripe stage (bonemeal used); returns false if the block is not growable. If a_IsBonemeal is true, block is not grown if not allowed in world.ini */ bool GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsByBonemeal = false); - /** Grows a cactus present at the block specified by the amount of blocks specified, up to the max height specified in the config */ - void GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow); + /** Grows a cactus present at the block specified by the amount of blocks specified, up to the max height specified in the config; returns the amount of blocks the cactus grew inside this call */ + int GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow); - /** Grows a melon or a pumpkin next to the block specified (assumed to be the stem) */ - void GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType); + /** Grows a melon or a pumpkin next to the block specified (assumed to be the stem); returns true if the pumpkin or melon sucessfully grew. */ + bool GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType); - /** Grows a sugarcane present at the block specified by the amount of blocks specified, up to the max height specified in the config */ - void GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow); + /** Grows a sugarcane present at the block specified by the amount of blocks specified, up to the max height specified in the config; returns the amount of blocks the sugarcane grew inside this call */ + int GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow); /** Returns the biome at the specified coords. Reads the biome from the chunk, if loaded, otherwise uses the world generator to provide the biome value */ EMCSBiome GetBiomeAt(int a_BlockX, int a_BlockZ); @@ -933,6 +937,8 @@ private: bool m_IsPumpkinBonemealable; bool m_IsSaplingBonemealable; bool m_IsSugarcaneBonemealable; + bool m_IsBigFlowerBonemealable; + bool m_IsTallGrassBonemealable; /** Whether command blocks are enabled or not */ bool m_bCommandBlocksEnabled; |