More Vector3 in cBlockHandler (#4644)
* cBlockHandler.OnUpdate uses Vector3 params. Also slightly changed how block ticking works.
This commit is contained in:
parent
96bd4637d7
commit
26ac146f41
@ -3032,7 +3032,18 @@ function OnAllChunksAvailable()</pre> All return values from the callbacks are i
|
||||
Type = "number",
|
||||
},
|
||||
},
|
||||
Notes = "Sets the blockticking to start at the specified block in the next tick.",
|
||||
Notes = "DEPRECATED, use SetNextBlockToTick() instead.",
|
||||
},
|
||||
SetNextBlockToTick =
|
||||
{
|
||||
Params =
|
||||
{
|
||||
{
|
||||
Name = "BlockPos",
|
||||
Type = "Vector3i",
|
||||
},
|
||||
},
|
||||
Notes = "Requests that the specified block be ticked at the start of the next world tick. Only one block per chunk can be queued this way; a second call to the same chunk overwrites the previous call.",
|
||||
},
|
||||
SetSavingEnabled =
|
||||
{
|
||||
|
@ -580,7 +580,7 @@ function OnPlayerUsingItem(Player, BlockX, BlockY, BlockZ, BlockFace, CursorX, C
|
||||
if (HeldItemType == E_ITEM_STICK) then
|
||||
-- Magic sTick of ticking: set the pointed block for ticking at the next tick
|
||||
Player:SendMessage(cChatColor.LightGray .. "Setting next block tick to {" .. BlockX .. ", " .. BlockY .. ", " .. BlockZ .. "}")
|
||||
Player:GetWorld():SetNextBlockTick(BlockX, BlockY, BlockZ);
|
||||
Player:GetWorld():SetNextBlockToTick(Vector3i(BlockX, BlockY, BlockZ));
|
||||
return true
|
||||
elseif (HeldItemType == E_ITEM_BLAZE_ROD) then
|
||||
return OnUsingBlazeRod(Player, BlockX, BlockY, BlockZ, BlockFace, CursorX, CursorY, CursorZ);
|
||||
|
@ -518,6 +518,43 @@ static int tolua_cWorld_SetSignLines(lua_State * tolua_S)
|
||||
|
||||
|
||||
|
||||
/** function: cWorld:SetNextBlockTick */
|
||||
static int tolua_cWorld_SetNextBlockTick(lua_State * tolua_S)
|
||||
{
|
||||
cLuaState LuaState(tolua_S);
|
||||
|
||||
if (
|
||||
!LuaState.CheckParamUserType(1, "cWorld") ||
|
||||
!LuaState.CheckParamNumber(2, 4) ||
|
||||
!LuaState.CheckParamEnd(5)
|
||||
)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
cWorld * Self = nullptr;
|
||||
int BlockX = 0;
|
||||
int BlockY = 0;
|
||||
int BlockZ = 0;
|
||||
|
||||
if (!LuaState.GetStackValues(1, Self, BlockX, BlockY, BlockZ))
|
||||
{
|
||||
tolua_error(LuaState, "Failed to read parameters", nullptr);
|
||||
}
|
||||
if (Self == nullptr)
|
||||
{
|
||||
tolua_error(LuaState, "invalid 'self' in function 'SetNextBlockTick'", nullptr);
|
||||
}
|
||||
Self->SetNextBlockToTick({BlockX, BlockY, BlockZ});
|
||||
LOGWARNING("Warning: 'cWorld:SetNextBlockTick' function is deprecated. Please use 'cWorld:SetNextBlockToTick' instead.");
|
||||
LuaState.LogStackTrace(0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void DeprecatedBindings::Bind(lua_State * tolua_S)
|
||||
{
|
||||
tolua_beginmodule(tolua_S, nullptr);
|
||||
@ -558,6 +595,7 @@ void DeprecatedBindings::Bind(lua_State * tolua_S)
|
||||
tolua_endmodule(tolua_S);
|
||||
|
||||
tolua_beginmodule(tolua_S, "cWorld");
|
||||
tolua_function(tolua_S, "SetNextBlockTick", tolua_cWorld_SetNextBlockTick);
|
||||
tolua_function(tolua_S, "UpdateSign", tolua_cWorld_SetSignLines);
|
||||
tolua_endmodule(tolua_S);
|
||||
|
||||
|
@ -19,11 +19,19 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual cItems ConvertToPickups(NIBBLETYPE a_BlockMeta, cBlockEntity * a_BlockEntity, const cEntity * a_Digger, const cItem * a_Tool) override
|
||||
{
|
||||
return cItem(E_ITEM_CAULDRON, 1, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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});
|
||||
@ -86,29 +94,45 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual bool IsUseable() override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
||||
|
||||
virtual void OnUpdate(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cWorldInterface & a_WorldInterface,
|
||||
cBlockPluginInterface & a_PluginInterface,
|
||||
cChunk & a_Chunk,
|
||||
const Vector3i a_RelPos
|
||||
) override
|
||||
{
|
||||
int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width;
|
||||
int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width;
|
||||
if (!a_WorldInterface.IsWeatherWetAt(BlockX, BlockZ) || (a_RelY != a_WorldInterface.GetHeight(BlockX, BlockZ)))
|
||||
auto WorldPos = a_Chunk.RelativeToAbsolute(a_RelPos);
|
||||
if (!a_WorldInterface.IsWeatherWetAtXYZ(WorldPos.addedY(1)))
|
||||
{
|
||||
// It's not raining at our current location or we do not have a direct view of the sky
|
||||
// We cannot eat the rain :(
|
||||
return;
|
||||
}
|
||||
|
||||
NIBBLETYPE Meta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ);
|
||||
auto Meta = a_Chunk.GetMeta(a_RelPos);
|
||||
if (Meta < 3)
|
||||
{
|
||||
a_Chunk.SetMeta(a_RelX, a_RelY, a_RelZ, Meta + 1);
|
||||
a_Chunk.SetMeta(a_RelPos, Meta + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
|
||||
{
|
||||
UNUSED(a_Meta);
|
||||
|
@ -38,11 +38,17 @@ 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 void OnUpdate(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cWorldInterface & a_WorldInterface,
|
||||
cBlockPluginInterface & a_PluginInterface,
|
||||
cChunk & a_Chunk,
|
||||
const Vector3i a_RelPos
|
||||
) override
|
||||
{
|
||||
if (GetRandomProvider().RandBool(0.20))
|
||||
{
|
||||
Grow(a_Chunk, {a_RelX, a_RelY, a_RelZ});
|
||||
Grow(a_Chunk, a_RelPos);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,26 +41,32 @@ 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 void OnUpdate(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cWorldInterface & a_WorldInterface,
|
||||
cBlockPluginInterface & a_PluginInterface,
|
||||
cChunk & a_Chunk,
|
||||
const Vector3i a_RelPos
|
||||
) override
|
||||
{
|
||||
NIBBLETYPE BlockMeta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ);
|
||||
auto BlockMeta = a_Chunk.GetMeta(a_RelPos);
|
||||
|
||||
if (IsWaterInNear(a_Chunk, a_RelX, a_RelY, a_RelZ))
|
||||
if (IsWaterInNear(a_Chunk, a_RelPos))
|
||||
{
|
||||
// Water was found, set block meta to 7
|
||||
a_Chunk.FastSetBlock(a_RelX, a_RelY, a_RelZ, m_BlockType, 7);
|
||||
a_Chunk.FastSetBlock(a_RelPos, m_BlockType, 7);
|
||||
return;
|
||||
}
|
||||
|
||||
// Water wasn't found, de-hydrate block:
|
||||
if (BlockMeta > 0)
|
||||
{
|
||||
a_Chunk.FastSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_FARMLAND, --BlockMeta);
|
||||
a_Chunk.FastSetBlock(a_RelPos, E_BLOCK_FARMLAND, --BlockMeta);
|
||||
return;
|
||||
}
|
||||
|
||||
// Farmland too dry. If nothing is growing on top, turn back to dirt:
|
||||
BLOCKTYPE UpperBlock = (a_RelY >= cChunkDef::Height - 1) ? static_cast<BLOCKTYPE>(E_BLOCK_AIR) : a_Chunk.GetBlock(a_RelX, a_RelY + 1, a_RelZ);
|
||||
auto UpperBlock = cChunkDef::IsValidHeight(a_RelPos.y + 1) ? a_Chunk.GetBlock(a_RelPos.addedY(1)) : E_BLOCK_AIR;
|
||||
switch (UpperBlock)
|
||||
{
|
||||
case E_BLOCK_BEETROOTS:
|
||||
@ -75,7 +81,7 @@ public:
|
||||
}
|
||||
default:
|
||||
{
|
||||
a_Chunk.SetBlock({a_RelX, a_RelY, a_RelZ}, E_BLOCK_DIRT, 0);
|
||||
a_Chunk.SetBlock(a_RelPos, E_BLOCK_DIRT, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -111,11 +117,12 @@ public:
|
||||
|
||||
|
||||
|
||||
bool IsWaterInNear(cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
|
||||
/** Returns true if there's either a water source block close enough to hydrate the specified position, or it's raining there. */
|
||||
bool IsWaterInNear(cChunk & a_Chunk, const Vector3i a_RelPos)
|
||||
{
|
||||
if (a_Chunk.GetWorld()->IsWeatherWetAt(a_RelX, a_RelZ))
|
||||
if (a_Chunk.GetWorld()->IsWeatherWetAtXYZ(a_RelPos))
|
||||
{
|
||||
// Rain hydrates farmland, too, except in Desert biomes.
|
||||
// Rain hydrates farmland, too
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -123,9 +130,8 @@ public:
|
||||
// Ref.: https://minecraft.gamepedia.com/Farmland#Hydration
|
||||
// TODO: Rewrite this to use the chunk and its neighbors directly
|
||||
cBlockArea Area;
|
||||
int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width;
|
||||
int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width;
|
||||
if (!Area.Read(*a_Chunk.GetWorld(), BlockX - 4, BlockX + 4, a_RelY, a_RelY + 1, BlockZ - 4, BlockZ + 4))
|
||||
auto WorldPos = a_Chunk.RelativeToAbsolute(a_RelPos);
|
||||
if (!Area.Read(*a_Chunk.GetWorld(), WorldPos - Vector3i(4, 0, 4), WorldPos + Vector3i(4, 1, 4)))
|
||||
{
|
||||
// Too close to the world edge, cannot check surroundings
|
||||
return false;
|
||||
@ -144,6 +150,10 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual bool CanSustainPlant(BLOCKTYPE a_Plant) override
|
||||
{
|
||||
return (
|
||||
|
@ -80,6 +80,10 @@ public:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual bool CanSustainPlant(BLOCKTYPE a_Plant) override
|
||||
{
|
||||
return (
|
||||
@ -109,33 +113,47 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
/** Called to tick the block */
|
||||
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 void OnUpdate(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cWorldInterface & a_WorldInterface,
|
||||
cBlockPluginInterface & a_PluginInterface,
|
||||
cChunk & a_Chunk,
|
||||
const Vector3i a_RelPos
|
||||
) override
|
||||
{
|
||||
if (a_Chunk.GetWorld()->ShouldLavaSpawnFire())
|
||||
{
|
||||
// Try to start up to 5 fires:
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
TryStartFireNear(a_RelX, a_RelY, a_RelZ, a_Chunk);
|
||||
TryStartFireNear(a_RelPos, a_Chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Tries to start a fire near the lava at given coords. Returns true if fire started. */
|
||||
static bool TryStartFireNear(int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk)
|
||||
static bool TryStartFireNear(const Vector3i a_RelPos, cChunk & a_Chunk)
|
||||
{
|
||||
// Pick a block next to this lava block:
|
||||
// Pick a random block next to this lava block:
|
||||
int rnd = a_Chunk.GetWorld()->GetTickRandomNumber(cChunkDef::NumBlocks * 8) / 7;
|
||||
int x = (rnd % 3) - 1; // -1 .. 1
|
||||
int y = ((rnd / 4) % 4) - 1; // -1 .. 2
|
||||
int z = ((rnd / 16) % 3) - 1; // -1 .. 1
|
||||
auto Pos = a_RelPos + Vector3i(x, y, z);
|
||||
|
||||
// Check if it's fuel:
|
||||
BLOCKTYPE BlockType;
|
||||
if (
|
||||
((a_RelY + y < 0) || (a_RelY + y >= cChunkDef::Height)) ||
|
||||
!a_Chunk.UnboundedRelGetBlockType(a_RelX + x, a_RelY + y, a_RelZ + z, BlockType) ||
|
||||
!cChunkDef::IsValidHeight(Pos.y) ||
|
||||
!a_Chunk.UnboundedRelGetBlockType(Pos, BlockType) ||
|
||||
!cFireSimulator::IsFuel(BlockType)
|
||||
)
|
||||
{
|
||||
@ -143,10 +161,7 @@ public:
|
||||
}
|
||||
|
||||
// Try to set it on fire:
|
||||
static struct
|
||||
{
|
||||
int x, y, z;
|
||||
} CrossCoords[] =
|
||||
static Vector3i CrossCoords[] =
|
||||
{
|
||||
{-1, 0, 0},
|
||||
{ 1, 0, 0},
|
||||
@ -155,31 +170,37 @@ public:
|
||||
{ 0, 0, -1},
|
||||
{ 0, 0, 1},
|
||||
} ;
|
||||
int RelX = a_RelX + x;
|
||||
int RelY = a_RelY + y;
|
||||
int RelZ = a_RelZ + z;
|
||||
for (size_t i = 0; i < ARRAYCOUNT(CrossCoords); i++)
|
||||
{
|
||||
auto NeighborPos = Pos + CrossCoords[i];
|
||||
if (
|
||||
((RelY + CrossCoords[i].y >= 0) && (RelY + CrossCoords[i].y < cChunkDef::Height)) &&
|
||||
a_Chunk.UnboundedRelGetBlockType(RelX + CrossCoords[i].x, RelY + CrossCoords[i].y, RelZ + CrossCoords[i].z, BlockType) &&
|
||||
cChunkDef::IsValidHeight(NeighborPos.y) &&
|
||||
a_Chunk.UnboundedRelGetBlockType(NeighborPos, BlockType) &&
|
||||
(BlockType == E_BLOCK_AIR)
|
||||
)
|
||||
{
|
||||
// This is an air block next to a fuel next to lava, light it up:
|
||||
a_Chunk.UnboundedRelSetBlock(RelX + CrossCoords[i].x, RelY + CrossCoords[i].y, RelZ + CrossCoords[i].z, E_BLOCK_FIRE, 0);
|
||||
// This is an air block next to a fuel next to lava, light the fuel block up:
|
||||
a_Chunk.UnboundedRelSetBlock(NeighborPos, E_BLOCK_FIRE, 0);
|
||||
return true;
|
||||
}
|
||||
} // for i - CrossCoords[]
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
|
||||
{
|
||||
UNUSED(a_Meta);
|
||||
return 4;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual bool CanSustainPlant(BLOCKTYPE a_Plant) override
|
||||
{
|
||||
return false;
|
||||
|
@ -39,34 +39,39 @@ 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 void OnUpdate(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cWorldInterface & a_WorldInterface,
|
||||
cBlockPluginInterface & a_PluginInterface,
|
||||
cChunk & a_Chunk,
|
||||
const Vector3i a_RelPos
|
||||
) override
|
||||
{
|
||||
// Make sure that there is enough light at the source block to spread
|
||||
if (!a_Chunk.GetWorld()->IsChunkLighted(a_Chunk.GetPosX(), a_Chunk.GetPosZ()))
|
||||
{
|
||||
a_Chunk.GetWorld()->QueueLightChunk(a_Chunk.GetPosX(), a_Chunk.GetPosZ());
|
||||
return;
|
||||
}
|
||||
else if ((a_RelY < cChunkDef::Height - 1))
|
||||
auto AbovePos = a_RelPos.addedY(1);
|
||||
if (cChunkDef::IsValidHeight(AbovePos.y))
|
||||
{
|
||||
BLOCKTYPE above = a_Chunk.GetBlock(a_RelX, a_RelY + 1, a_RelZ);
|
||||
|
||||
// Grass turns back to dirt when the block above it is not transparent or water.
|
||||
// It does not turn to dirt when a snow layer is above.
|
||||
if ((above != E_BLOCK_SNOW) &&
|
||||
(!cBlockInfo::IsTransparent(above) || IsBlockWater(above)))
|
||||
// Grass turns back to dirt when the block Above it is not transparent or water.
|
||||
// It does not turn to dirt when a snow layer is Above.
|
||||
auto Above = a_Chunk.GetBlock(AbovePos);
|
||||
if (
|
||||
(Above != E_BLOCK_SNOW) &&
|
||||
(!cBlockInfo::IsTransparent(Above) || IsBlockWater(Above)))
|
||||
{
|
||||
a_Chunk.FastSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_DIRT, E_META_DIRT_NORMAL);
|
||||
a_Chunk.FastSetBlock(a_RelPos, E_BLOCK_DIRT, E_META_DIRT_NORMAL);
|
||||
return;
|
||||
}
|
||||
|
||||
NIBBLETYPE light = std::max(a_Chunk.GetBlockLight(a_RelX, a_RelY + 1, a_RelZ), a_Chunk.GetTimeAlteredLight(a_Chunk.GetSkyLight(a_RelX, a_RelY + 1, a_RelZ)));
|
||||
// Source block is not bright enough to spread
|
||||
// Make sure that there is enough light at the source block to spread
|
||||
auto light = std::max(a_Chunk.GetBlockLight(AbovePos), a_Chunk.GetTimeAlteredLight(a_Chunk.GetSkyLight(AbovePos)));
|
||||
if (light < 9)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Grass spreads to adjacent dirt blocks:
|
||||
@ -79,37 +84,36 @@ public:
|
||||
|
||||
BLOCKTYPE DestBlock;
|
||||
NIBBLETYPE DestMeta;
|
||||
if (!cChunkDef::IsValidHeight(a_RelY + OfsY))
|
||||
auto Pos = a_RelPos + Vector3i(OfsX, OfsY, OfsZ);
|
||||
if (!cChunkDef::IsValidHeight(Pos.y))
|
||||
{
|
||||
// Y Coord out of range
|
||||
continue;
|
||||
}
|
||||
Vector3i pos(a_RelX + OfsX, a_RelY + OfsY, a_RelZ + OfsZ);
|
||||
auto chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(pos);
|
||||
auto chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(Pos);
|
||||
if (chunk == nullptr)
|
||||
{
|
||||
// Unloaded chunk
|
||||
continue;
|
||||
}
|
||||
chunk->GetBlockTypeMeta(pos, DestBlock, DestMeta);
|
||||
chunk->GetBlockTypeMeta(Pos, DestBlock, DestMeta);
|
||||
if ((DestBlock != E_BLOCK_DIRT) || (DestMeta != E_META_DIRT_NORMAL))
|
||||
{
|
||||
// Not a regular dirt block
|
||||
continue;
|
||||
}
|
||||
auto abovePos = pos.addedY(1);
|
||||
BLOCKTYPE above = chunk->GetBlock(abovePos);
|
||||
NIBBLETYPE light = std::max(chunk->GetBlockLight(abovePos), chunk->GetTimeAlteredLight(chunk->GetSkyLight(abovePos)));
|
||||
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))
|
||||
cBlockInfo::IsTransparent(Above) &&
|
||||
(!IsBlockLava(Above)) &&
|
||||
(!IsBlockWaterOrIce(Above))
|
||||
)
|
||||
{
|
||||
auto absPos = chunk->RelativeToAbsolute(pos);
|
||||
auto absPos = chunk->RelativeToAbsolute(Pos);
|
||||
if (!cRoot::Get()->GetPluginManager()->CallHookBlockSpread(*chunk->GetWorld(), absPos.x, absPos.y, absPos.z, ssGrassSpread))
|
||||
{
|
||||
chunk->FastSetBlock(pos, E_BLOCK_GRASS, 0);
|
||||
chunk->FastSetBlock(Pos, E_BLOCK_GRASS, 0);
|
||||
}
|
||||
}
|
||||
} // for i - repeat twice
|
||||
|
@ -421,7 +421,13 @@ bool cBlockHandler::GetPlacementBlockTypeMeta(
|
||||
|
||||
|
||||
|
||||
void cBlockHandler::OnUpdate(cChunkInterface & cChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_PluginInterface, cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ)
|
||||
void cBlockHandler::OnUpdate(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cWorldInterface & a_WorldInterface,
|
||||
cBlockPluginInterface & a_PluginInterface,
|
||||
cChunk & a_Chunk,
|
||||
const Vector3i a_RelPos
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -30,8 +30,14 @@ public:
|
||||
virtual ~cBlockHandler() {}
|
||||
|
||||
/** Called when the block gets ticked either by a random tick or by a queued tick.
|
||||
Note that the coords are chunk-relative! */
|
||||
virtual void OnUpdate(cChunkInterface & cChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_BlockPluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ);
|
||||
Note that the coords in a_RelPos are chunk-relative! */
|
||||
virtual void OnUpdate(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cWorldInterface & a_WorldInterface,
|
||||
cBlockPluginInterface & a_BlockPluginInterface,
|
||||
cChunk & a_Chunk,
|
||||
const Vector3i a_RelPos
|
||||
);
|
||||
|
||||
/** Returns the relative bounding box that must be entity-free in
|
||||
order for the block to be placed. a_XM, a_XP, etc. stand for the
|
||||
@ -108,7 +114,14 @@ public:
|
||||
static void NeighborChanged(cChunkInterface & a_ChunkInterface, Vector3i a_NeighborPos, eBlockFace a_WhichNeighbor);
|
||||
|
||||
/** Called when the player starts digging the block. */
|
||||
virtual void OnDigging(cChunkInterface & cChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) {}
|
||||
virtual void OnDigging(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cWorldInterface & a_WorldInterface,
|
||||
cPlayer & a_Player,
|
||||
int a_BlockX, int a_BlockY, int a_BlockZ
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
/** Called if the user right clicks the block and the block is useable
|
||||
returns true if the use was successful, return false to use the block as a "normal" block */
|
||||
|
@ -10,29 +10,88 @@
|
||||
// Leaves can be this many blocks that away (inclusive) from the log not to decay
|
||||
#define LEAVES_CHECK_DISTANCE 6
|
||||
|
||||
#define PROCESS_NEIGHBOR(x, y, z) \
|
||||
do { \
|
||||
switch (a_Area.GetBlockType(x, y, z)) \
|
||||
{ \
|
||||
case E_BLOCK_LEAVES: a_Area.SetBlockType(x, y, z, static_cast<BLOCKTYPE>(E_BLOCK_SPONGE + i + 1)); break; \
|
||||
case E_BLOCK_LOG: return true; \
|
||||
case E_BLOCK_NEW_LEAVES: a_Area.SetBlockType(x, y, z, static_cast<BLOCKTYPE>(E_BLOCK_SPONGE + i + 1)); break; \
|
||||
case E_BLOCK_NEW_LOG: return true; \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
bool HasNearLog(cBlockArea &a_Area, int a_BlockX, int a_BlockY, int a_BlockZ);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cBlockLeavesHandler :
|
||||
class cBlockLeavesHandler:
|
||||
public cBlockHandler
|
||||
{
|
||||
using Super = cBlockHandler;
|
||||
|
||||
/** Returns true if the area contains a continous path from the specified block to a log block entirely made out of leaves blocks. */
|
||||
static bool HasNearLog(cBlockArea & a_Area, const Vector3i a_BlockPos)
|
||||
{
|
||||
// Filter the blocks into a {leaves, log, other (air)} set:
|
||||
auto * Types = a_Area.GetBlockTypes();
|
||||
for (size_t i = a_Area.GetBlockCount() - 1; i > 0; i--)
|
||||
{
|
||||
switch (Types[i])
|
||||
{
|
||||
case E_BLOCK_LEAVES:
|
||||
case E_BLOCK_LOG:
|
||||
case E_BLOCK_NEW_LEAVES:
|
||||
case E_BLOCK_NEW_LOG:
|
||||
{
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
Types[i] = E_BLOCK_AIR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // for i - Types[]
|
||||
|
||||
// Perform a breadth-first search to see if there's a log connected within 4 blocks of the leaves block:
|
||||
// Simply replace all reachable leaves blocks with a sponge block plus iteration (in the Area) and see if we can reach a log
|
||||
a_Area.SetBlockType(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, E_BLOCK_SPONGE);
|
||||
for (int i = 0; i < LEAVES_CHECK_DISTANCE; i++)
|
||||
{
|
||||
auto ProcessNeighbor = [&a_Area, i](int cbx, int cby, int cbz) -> bool
|
||||
{
|
||||
switch (a_Area.GetBlockType(cbx, cby, cbz))
|
||||
{
|
||||
case E_BLOCK_LEAVES: a_Area.SetBlockType(cbx, cby, cbz, static_cast<BLOCKTYPE>(E_BLOCK_SPONGE + i + 1)); break;
|
||||
case E_BLOCK_LOG: return true;
|
||||
case E_BLOCK_NEW_LEAVES: a_Area.SetBlockType(cbx, cby, cbz, static_cast<BLOCKTYPE>(E_BLOCK_SPONGE + i + 1)); break;
|
||||
case E_BLOCK_NEW_LOG: return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
for (int y = std::max(a_BlockPos.y - i, 0); y <= std::min(a_BlockPos.y + i, cChunkDef::Height - 1); y++)
|
||||
{
|
||||
for (int z = a_BlockPos.z - i; z <= a_BlockPos.z + i; z++)
|
||||
{
|
||||
for (int x = a_BlockPos.x - i; x <= a_BlockPos.x + i; x++)
|
||||
{
|
||||
if (a_Area.GetBlockType(x, y, z) != E_BLOCK_SPONGE + i)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
ProcessNeighbor(x - 1, y, z) ||
|
||||
ProcessNeighbor(x + 1, y, z) ||
|
||||
ProcessNeighbor(x, y, z - 1) ||
|
||||
ProcessNeighbor(x, y, z + 1) ||
|
||||
ProcessNeighbor(x, y + 1, z) ||
|
||||
ProcessNeighbor(x, y - 1, z)
|
||||
)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
} // for x
|
||||
} // for z
|
||||
} // for y
|
||||
} // for i - BFS iterations
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
cBlockLeavesHandler(BLOCKTYPE a_BlockType)
|
||||
: cBlockHandler(a_BlockType)
|
||||
|
||||
cBlockLeavesHandler(BLOCKTYPE a_BlockType):
|
||||
Super(a_BlockType)
|
||||
{
|
||||
}
|
||||
|
||||
@ -104,30 +163,34 @@ 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
|
||||
virtual void OnUpdate(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cWorldInterface & a_WorldInterface,
|
||||
cBlockPluginInterface & a_PluginInterface,
|
||||
cChunk & a_Chunk,
|
||||
const Vector3i a_RelPos
|
||||
) override
|
||||
{
|
||||
NIBBLETYPE Meta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ);
|
||||
auto Meta = a_Chunk.GetMeta(a_RelPos);
|
||||
if ((Meta & 0x04) != 0)
|
||||
{
|
||||
// Player-placed leaves, don't decay
|
||||
return;
|
||||
}
|
||||
|
||||
if ((Meta & 0x8) == 0)
|
||||
if ((Meta & 0x08) == 0)
|
||||
{
|
||||
// These leaves have been checked for decay lately and nothing around them changed
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the data around the leaves:
|
||||
int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width;
|
||||
int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width;
|
||||
auto worldPos = a_Chunk.RelativeToAbsolute(a_RelPos);
|
||||
cBlockArea Area;
|
||||
if (!Area.Read(
|
||||
*a_Chunk.GetWorld(),
|
||||
BlockX - LEAVES_CHECK_DISTANCE, BlockX + LEAVES_CHECK_DISTANCE,
|
||||
a_RelY - LEAVES_CHECK_DISTANCE, a_RelY + LEAVES_CHECK_DISTANCE,
|
||||
BlockZ - LEAVES_CHECK_DISTANCE, BlockZ + LEAVES_CHECK_DISTANCE,
|
||||
worldPos - Vector3i(LEAVES_CHECK_DISTANCE, LEAVES_CHECK_DISTANCE, LEAVES_CHECK_DISTANCE),
|
||||
worldPos + Vector3i(LEAVES_CHECK_DISTANCE, LEAVES_CHECK_DISTANCE, LEAVES_CHECK_DISTANCE),
|
||||
cBlockArea::baTypes)
|
||||
)
|
||||
{
|
||||
@ -135,15 +198,15 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
if (HasNearLog(Area, BlockX, a_RelY, BlockZ))
|
||||
if (HasNearLog(Area, worldPos))
|
||||
{
|
||||
// Wood found, the leaves stay; unset the check bit
|
||||
a_Chunk.SetMeta(a_RelX, a_RelY, a_RelZ, Meta ^ 0x08, true, false);
|
||||
a_Chunk.SetMeta(a_RelPos, Meta ^ 0x08, true, false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Decay the leaves:
|
||||
a_ChunkInterface.DropBlockAsPickups({BlockX, a_RelY, BlockZ});
|
||||
a_ChunkInterface.DropBlockAsPickups(worldPos);
|
||||
}
|
||||
|
||||
|
||||
@ -156,62 +219,3 @@ public:
|
||||
return 7;
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool HasNearLog(cBlockArea & a_Area, int a_BlockX, int a_BlockY, int a_BlockZ)
|
||||
{
|
||||
// Filter the blocks into a {leaves, log, other (air)} set:
|
||||
BLOCKTYPE * Types = a_Area.GetBlockTypes();
|
||||
for (size_t i = a_Area.GetBlockCount() - 1; i > 0; i--)
|
||||
{
|
||||
switch (Types[i])
|
||||
{
|
||||
case E_BLOCK_LEAVES:
|
||||
case E_BLOCK_LOG:
|
||||
case E_BLOCK_NEW_LEAVES:
|
||||
case E_BLOCK_NEW_LOG:
|
||||
{
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
Types[i] = E_BLOCK_AIR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // for i - Types[]
|
||||
|
||||
// Perform a breadth-first search to see if there's a log connected within 4 blocks of the leaves block:
|
||||
// Simply replace all reachable leaves blocks with a sponge block plus iteration (in the Area) and see if we can reach a log in 4 iterations
|
||||
a_Area.SetBlockType(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_SPONGE);
|
||||
for (int i = 0; i < LEAVES_CHECK_DISTANCE; i++)
|
||||
{
|
||||
for (int y = std::max(a_BlockY - i, 0); y <= std::min(a_BlockY + i, cChunkDef::Height - 1); y++)
|
||||
{
|
||||
for (int z = a_BlockZ - i; z <= a_BlockZ + i; z++)
|
||||
{
|
||||
for (int x = a_BlockX - i; x <= a_BlockX + i; x++)
|
||||
{
|
||||
if (a_Area.GetBlockType(x, y, z) != E_BLOCK_SPONGE + i)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
PROCESS_NEIGHBOR(x - 1, y, z);
|
||||
PROCESS_NEIGHBOR(x + 1, y, z);
|
||||
PROCESS_NEIGHBOR(x, y, z - 1);
|
||||
PROCESS_NEIGHBOR(x, y, z + 1);
|
||||
PROCESS_NEIGHBOR(x, y + 1, z);
|
||||
PROCESS_NEIGHBOR(x, y - 1, z);
|
||||
} // for x
|
||||
} // for z
|
||||
} // for y
|
||||
} // for i - BFS iterations
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -26,20 +26,25 @@ 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
|
||||
virtual void OnUpdate(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cWorldInterface & a_WorldInterface,
|
||||
cBlockPluginInterface & a_PluginInterface,
|
||||
cChunk & a_Chunk,
|
||||
const Vector3i a_RelPos
|
||||
) override
|
||||
{
|
||||
Vector3i relPos(a_RelX, a_RelY, a_RelZ);
|
||||
auto action = CanGrow(a_Chunk, relPos);
|
||||
switch (action)
|
||||
auto Action = CanGrow(a_Chunk, a_RelPos);
|
||||
switch (Action)
|
||||
{
|
||||
case paGrowth:
|
||||
{
|
||||
Grow(a_Chunk, relPos);
|
||||
Grow(a_Chunk, a_RelPos);
|
||||
break;
|
||||
}
|
||||
case paDeath:
|
||||
{
|
||||
a_ChunkInterface.DigBlock(a_WorldInterface, a_Chunk.RelativeToAbsolute(relPos));
|
||||
a_ChunkInterface.DigBlock(a_WorldInterface, a_Chunk.RelativeToAbsolute(a_RelPos));
|
||||
break;
|
||||
}
|
||||
case paStay: break; // do nothing
|
||||
|
@ -46,19 +46,27 @@ 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 void OnUpdate(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cWorldInterface & a_WorldInterface,
|
||||
cBlockPluginInterface & a_PluginInterface,
|
||||
cChunk & a_Chunk,
|
||||
const Vector3i a_RelPos
|
||||
) override
|
||||
{
|
||||
// Spawn zombie pigmen with a 0.05% chance:
|
||||
if (GetRandomProvider().RandBool(0.9995))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int PosX = a_Chunk.GetPosX() * cChunkDef::Width + a_RelX;
|
||||
int PosZ = a_Chunk.GetPosZ() * cChunkDef::Width + a_RelZ;
|
||||
|
||||
a_WorldInterface.SpawnMob(PosX, a_RelY, PosZ, mtZombiePigman, false);
|
||||
auto WorldPos = a_Chunk.RelativeToAbsolute(a_RelPos);
|
||||
a_WorldInterface.SpawnMob(WorldPos.x, WorldPos.y, WorldPos.z, mtZombiePigman, false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
|
||||
{
|
||||
if ((a_RelY <= 0) || (a_RelY >= cChunkDef::Height - 1))
|
||||
|
@ -12,9 +12,10 @@ class cBlockRedstoneOreHandler :
|
||||
public cBlockOreHandler
|
||||
{
|
||||
using Super = cBlockOreHandler;
|
||||
|
||||
public:
|
||||
|
||||
using Super::Super;
|
||||
using Super::Super; // Inherit constructor from base
|
||||
|
||||
virtual bool OnUse(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
@ -55,19 +56,19 @@ class cBlockGlowingRedstoneOreHandler:
|
||||
public cBlockOreHandler
|
||||
{
|
||||
using Super = cBlockOreHandler;
|
||||
|
||||
public:
|
||||
|
||||
using Super::Super;
|
||||
using Super::Super; // Inherit constructor from base
|
||||
|
||||
virtual void OnUpdate(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cWorldInterface & a_WorldInterface,
|
||||
cBlockPluginInterface & a_BlockPluginInterface,
|
||||
cChunk & a_Chunk,
|
||||
int a_RelX, int a_RelY, int a_RelZ
|
||||
const Vector3i a_RelPos
|
||||
) override
|
||||
{
|
||||
const Vector3i a_RelPos{a_RelX, a_RelY, a_RelZ};
|
||||
auto BlockPos = a_Chunk.RelativeToAbsolute(a_RelPos);
|
||||
a_ChunkInterface.SetBlock(BlockPos, E_BLOCK_REDSTONE_ORE, 0);
|
||||
}
|
||||
|
@ -43,26 +43,31 @@ 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 void OnUpdate(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cWorldInterface & a_WorldInterface,
|
||||
cBlockPluginInterface & a_PluginInterface,
|
||||
cChunk & a_Chunk,
|
||||
const Vector3i a_RelPos
|
||||
) override
|
||||
{
|
||||
NIBBLETYPE Meta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ);
|
||||
NIBBLETYPE Light = std::max(a_Chunk.GetBlockLight(a_RelX, a_RelY, a_RelZ), a_Chunk.GetTimeAlteredLight(a_Chunk.GetSkyLight(a_RelX, a_RelY, a_RelZ)));
|
||||
auto Meta = a_Chunk.GetMeta(a_RelPos);
|
||||
auto Light = std::max(a_Chunk.GetBlockLight(a_RelPos), a_Chunk.GetTimeAlteredLight(a_Chunk.GetSkyLight(a_RelPos)));
|
||||
|
||||
// Only grow if we have the right amount of light
|
||||
if (Light > 8)
|
||||
{
|
||||
auto & random = GetRandomProvider();
|
||||
// Only grow if we are in the right growth stage and have the right amount of space around them.
|
||||
if (((Meta & 0x08) != 0) && random.RandBool(0.45) && CanGrowAt(a_Chunk, a_RelX, a_RelY, a_RelZ, Meta))
|
||||
if (((Meta & 0x08) != 0) && random.RandBool(0.45) && CanGrowAt(a_Chunk, a_RelPos.x, a_RelPos.y, a_RelPos.z, Meta))
|
||||
{
|
||||
int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width;
|
||||
int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width;
|
||||
a_Chunk.GetWorld()->GrowTree(BlockX, a_RelY, BlockZ);
|
||||
auto WorldPos = a_Chunk.RelativeToAbsolute(a_RelPos);
|
||||
a_Chunk.GetWorld()->GrowTree(WorldPos.x, WorldPos.y, WorldPos.z);
|
||||
}
|
||||
// Only move to the next growth stage if we haven't gone there yet
|
||||
else if (((Meta & 0x08) == 0) && random.RandBool(0.45))
|
||||
{
|
||||
a_Chunk.SetMeta(a_RelX, a_RelY, a_RelZ, Meta | 0x08);
|
||||
a_Chunk.SetMeta(a_RelPos, Meta | 0x08);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -221,25 +221,33 @@ public:
|
||||
|
||||
|
||||
|
||||
virtual void OnUpdate(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_BlockPluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override
|
||||
virtual void OnUpdate(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cWorldInterface & a_WorldInterface,
|
||||
cBlockPluginInterface & a_PluginInterface,
|
||||
cChunk & a_Chunk,
|
||||
const Vector3i a_RelPos
|
||||
) override
|
||||
{
|
||||
UNUSED(a_ChunkInterface);
|
||||
UNUSED(a_WorldInterface);
|
||||
|
||||
// Vine cannot grow down if at the bottom:
|
||||
if (a_RelY < 1)
|
||||
auto GrowPos = a_RelPos.addedY(-1);
|
||||
if (!cChunkDef::IsValidHeight(GrowPos.y))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Grow one block down, if possible:
|
||||
BLOCKTYPE Block;
|
||||
a_Chunk.UnboundedRelGetBlockType(a_RelX, a_RelY - 1, a_RelZ, Block);
|
||||
a_Chunk.UnboundedRelGetBlockType(GrowPos, Block);
|
||||
if (Block == E_BLOCK_AIR)
|
||||
{
|
||||
if (!a_BlockPluginInterface.CallHookBlockSpread(a_RelX + a_Chunk.GetPosX() * cChunkDef::Width, a_RelY - 1, a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width, ssVineSpread))
|
||||
auto WorldPos = a_Chunk.RelativeToAbsolute(GrowPos);
|
||||
if (!a_PluginInterface.CallHookBlockSpread(WorldPos.x, WorldPos.y, WorldPos.z, ssVineSpread))
|
||||
{
|
||||
a_Chunk.UnboundedRelSetBlock(a_RelX, a_RelY - 1, a_RelZ, E_BLOCK_VINES, a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ));
|
||||
a_Chunk.UnboundedRelSetBlock(GrowPos, E_BLOCK_VINES, a_Chunk.GetMeta(a_RelPos));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,6 +47,7 @@ SET (HDRS
|
||||
BlockFurnace.h
|
||||
BlockGlass.h
|
||||
BlockGlowstone.h
|
||||
BlockGrass.h
|
||||
BlockGravel.h
|
||||
BlockHandler.h
|
||||
BlockHopper.h
|
||||
|
@ -73,6 +73,10 @@ public:
|
||||
/** Returns true if it is raining or storming at the specified location. This takes into account biomes. */
|
||||
virtual bool IsWeatherWetAt(int a_BlockX, int a_BlockZ) = 0;
|
||||
|
||||
/** Returns true if it is raining or storming at the specified location,
|
||||
and the rain reaches the specified block position. */
|
||||
virtual bool IsWeatherWetAtXYZ(Vector3i a_Pos) = 0;
|
||||
|
||||
/** Returns or sets the minumim or maximum netherportal width */
|
||||
virtual int GetMinNetherPortalWidth(void) const = 0;
|
||||
virtual int GetMaxNetherPortalWidth(void) const = 0;
|
||||
|
@ -71,9 +71,6 @@ cChunk::cChunk(
|
||||
m_World(a_World),
|
||||
m_ChunkMap(a_ChunkMap),
|
||||
m_ChunkData(a_Pool),
|
||||
m_BlockTickX(0),
|
||||
m_BlockTickY(0),
|
||||
m_BlockTickZ(0),
|
||||
m_NeighborXM(a_NeighborXM),
|
||||
m_NeighborXP(a_NeighborXP),
|
||||
m_NeighborZM(a_NeighborZM),
|
||||
@ -724,13 +721,13 @@ void cChunk::Tick(std::chrono::milliseconds a_Dt)
|
||||
|
||||
|
||||
|
||||
void cChunk::TickBlock(int a_RelX, int a_RelY, int a_RelZ)
|
||||
void cChunk::TickBlock(const Vector3i a_RelPos)
|
||||
{
|
||||
cBlockHandler * Handler = BlockHandler(GetBlock(a_RelX, a_RelY, a_RelZ));
|
||||
cBlockHandler * Handler = BlockHandler(GetBlock(a_RelPos));
|
||||
ASSERT(Handler != nullptr); // Happenned on server restart, FS #243
|
||||
cChunkInterface ChunkInterface(this->GetWorld()->GetChunkMap());
|
||||
cBlockInServerPluginInterface PluginInterface(*this->GetWorld());
|
||||
Handler->OnUpdate(ChunkInterface, *this->GetWorld(), PluginInterface, *this, a_RelX, a_RelY, a_RelZ);
|
||||
Handler->OnUpdate(ChunkInterface, *this->GetWorld(), PluginInterface, *this, a_RelPos);
|
||||
}
|
||||
|
||||
|
||||
@ -839,43 +836,27 @@ void cChunk::CheckBlocks()
|
||||
|
||||
void cChunk::TickBlocks(void)
|
||||
{
|
||||
// Tick dem blocks
|
||||
// _X: We must limit the random number or else we get a nasty int overflow bug - https://forum.cuberite.org/thread-457.html
|
||||
int RandomX = m_World->GetTickRandomNumber(0x00ffffff);
|
||||
int RandomY = m_World->GetTickRandomNumber(0x00ffffff);
|
||||
int RandomZ = m_World->GetTickRandomNumber(0x00ffffff);
|
||||
int TickX = m_BlockTickX;
|
||||
int TickY = m_BlockTickY;
|
||||
int TickZ = m_BlockTickZ;
|
||||
|
||||
cChunkInterface ChunkInterface(this->GetWorld()->GetChunkMap());
|
||||
cBlockInServerPluginInterface PluginInterface(*this->GetWorld());
|
||||
|
||||
// This for loop looks disgusting, but it actually does a simple thing - first processes m_BlockTick, then adds random to it
|
||||
// This is so that SetNextBlockTick() works
|
||||
for (int i = 0; i < 50; i++,
|
||||
|
||||
// This weird construct (*2, then /2) is needed,
|
||||
// otherwise the blocktick distribution is too biased towards even coords!
|
||||
|
||||
TickX = (TickX + RandomX) % (Width * 2),
|
||||
TickY = (TickY + RandomY) % (Height * 2),
|
||||
TickZ = (TickZ + RandomZ) % (Width * 2),
|
||||
m_BlockTickX = TickX / 2,
|
||||
m_BlockTickY = TickY / 2,
|
||||
m_BlockTickZ = TickZ / 2
|
||||
)
|
||||
// Tick random blocks, but the first one should be m_BlockToTick (so that SetNextBlockToTick() works)
|
||||
auto Idx = cChunkDef::MakeIndexNoCheck(m_BlockToTick);
|
||||
for (int i = 0; i < 50; ++i)
|
||||
{
|
||||
|
||||
if (m_BlockTickY > cChunkDef::GetHeight(m_HeightMap, m_BlockTickX, m_BlockTickZ))
|
||||
auto Pos = cChunkDef::IndexToCoordinate(static_cast<size_t>(Idx));
|
||||
Idx = m_World->GetTickRandomNumber(cChunkDef::NumBlocks - 1);
|
||||
if (Pos.y > cChunkDef::GetHeight(m_HeightMap, Pos.x, Pos.z))
|
||||
{
|
||||
continue; // It's all air up here
|
||||
}
|
||||
|
||||
cBlockHandler * Handler = BlockHandler(GetBlock(m_BlockTickX, m_BlockTickY, m_BlockTickZ));
|
||||
cBlockHandler * Handler = BlockHandler(GetBlock(Pos));
|
||||
ASSERT(Handler != nullptr); // Happenned on server restart, FS #243
|
||||
Handler->OnUpdate(ChunkInterface, *this->GetWorld(), PluginInterface, *this, m_BlockTickX, m_BlockTickY, m_BlockTickZ);
|
||||
} // for i - tickblocks
|
||||
Handler->OnUpdate(ChunkInterface, *this->GetWorld(), PluginInterface, *this, Pos);
|
||||
} // for i
|
||||
|
||||
// Set a new random coord for the next tick:
|
||||
m_BlockToTick = cChunkDef::IndexToCoordinate(static_cast<size_t>(Idx));
|
||||
}
|
||||
|
||||
|
||||
|
16
src/Chunk.h
16
src/Chunk.h
@ -147,7 +147,7 @@ public:
|
||||
void Tick(std::chrono::milliseconds a_Dt);
|
||||
|
||||
/** Ticks a single block. Used by cWorld::TickQueuedBlocks() to tick the queued blocks */
|
||||
void TickBlock(int a_RelX, int a_RelY, int a_RelZ);
|
||||
void TickBlock(const Vector3i a_RelPos);
|
||||
|
||||
int GetPosX(void) const { return m_PosX; }
|
||||
int GetPosZ(void) const { return m_PosZ; }
|
||||
@ -375,12 +375,12 @@ public:
|
||||
m_IsSaving = false;
|
||||
}
|
||||
|
||||
/** Sets the blockticking to start at the specified block. Only one blocktick may be set, second call overwrites the first call */
|
||||
inline void SetNextBlockTick(int a_RelX, int a_RelY, int a_RelZ)
|
||||
/** Causes the specified block to be ticked on the next Tick() call.
|
||||
Plugins can use this via the cWorld:SetNextBlockToTick() API.
|
||||
Only one block coord per chunk may be set, a second call overwrites the first call */
|
||||
inline void SetNextBlockToTick(const Vector3i a_RelPos)
|
||||
{
|
||||
m_BlockTickX = a_RelX;
|
||||
m_BlockTickY = a_RelY;
|
||||
m_BlockTickZ = a_RelZ;
|
||||
m_BlockToTick = a_RelPos;
|
||||
}
|
||||
|
||||
inline NIBBLETYPE GetMeta(int a_RelX, int a_RelY, int a_RelZ) const
|
||||
@ -629,7 +629,9 @@ private:
|
||||
cChunkDef::HeightMap m_HeightMap;
|
||||
cChunkDef::BiomeMap m_BiomeMap;
|
||||
|
||||
int m_BlockTickX, m_BlockTickY, m_BlockTickZ;
|
||||
/** Relative coords of the block to tick first in the next Tick() call.
|
||||
Plugins can use this to force a tick in a specific block, using cWorld:SetNextBlockToTick() API. */
|
||||
Vector3i m_BlockToTick;
|
||||
|
||||
cChunk * m_NeighborXM; // Neighbor at [X - 1, Z]
|
||||
cChunk * m_NeighborXP; // Neighbor at [X + 1, Z]
|
||||
|
@ -2030,16 +2030,16 @@ int cChunkMap::GrowPlantAt(Vector3i a_BlockPos, int a_NumStages)
|
||||
|
||||
|
||||
|
||||
void cChunkMap::SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ)
|
||||
void cChunkMap::SetNextBlockToTick(const 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);
|
||||
|
||||
cCSLock Lock(m_CSChunks);
|
||||
cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ);
|
||||
auto Chunk = GetChunkNoLoad(ChunkPos);
|
||||
if (Chunk != nullptr)
|
||||
{
|
||||
Chunk->SetNextBlockTick(a_BlockX, a_BlockY, a_BlockZ);
|
||||
Chunk->SetNextBlockToTick(RelPos);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2100,17 +2100,17 @@ void cChunkMap::Tick(std::chrono::milliseconds a_Dt)
|
||||
|
||||
|
||||
|
||||
void cChunkMap::TickBlock(int a_BlockX, int a_BlockY, int a_BlockZ)
|
||||
void cChunkMap::TickBlock(const Vector3i a_BlockPos)
|
||||
{
|
||||
auto ChunkPos = cChunkDef::BlockToChunk(a_BlockPos);
|
||||
auto RelPos = cChunkDef::AbsoluteToRelative(a_BlockPos, ChunkPos);
|
||||
cCSLock Lock(m_CSChunks);
|
||||
int ChunkX, ChunkZ;
|
||||
cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
|
||||
cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ);
|
||||
auto Chunk = GetChunkNoLoad(ChunkPos);
|
||||
if ((Chunk == nullptr) || !Chunk->IsValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
Chunk->TickBlock(a_BlockX, a_BlockY, a_BlockZ);
|
||||
Chunk->TickBlock(RelPos);
|
||||
}
|
||||
|
||||
|
||||
|
@ -373,8 +373,10 @@ public:
|
||||
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);
|
||||
/** Causes the specified block to be ticked on the next Tick() call.
|
||||
Plugins can use this via the cWorld:SetNextBlockToTick() API.
|
||||
Only one block coord per chunk may be set, a second call overwrites the first call */
|
||||
void SetNextBlockToTick(const Vector3i a_BlockPos);
|
||||
|
||||
/** Make a Mob census, of all mobs, their family, their chunk and their distance to closest player */
|
||||
void CollectMobCensus(cMobCensus & a_ToFill);
|
||||
@ -385,7 +387,7 @@ public:
|
||||
void Tick(std::chrono::milliseconds a_Dt);
|
||||
|
||||
/** Ticks a single block. Used by cWorld::TickQueuedBlocks() to tick the queued blocks */
|
||||
void TickBlock(int a_BlockX, int a_BlockY, int a_BlockZ);
|
||||
void TickBlock(const Vector3i a_BlockPos);
|
||||
|
||||
void UnloadUnusedChunks(void);
|
||||
void SaveAllChunks(void);
|
||||
|
@ -590,9 +590,9 @@ bool cWorld::IsWeatherWetAtXYZ(Vector3i a_Pos)
|
||||
|
||||
|
||||
|
||||
void cWorld::SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ)
|
||||
void cWorld::SetNextBlockToTick(const Vector3i a_BlockPos)
|
||||
{
|
||||
return m_ChunkMap->SetNextBlockTick(a_BlockX, a_BlockY, a_BlockZ);
|
||||
return m_ChunkMap->SetNextBlockToTick(a_BlockPos);
|
||||
}
|
||||
|
||||
|
||||
@ -3195,7 +3195,7 @@ void cWorld::TickQueuedBlocks(void)
|
||||
if (Block->TicksToWait <= 0)
|
||||
{
|
||||
// TODO: Handle the case when the chunk is already unloaded
|
||||
m_ChunkMap->TickBlock(Block->X, Block->Y, Block->Z);
|
||||
m_ChunkMap->TickBlock({Block->X, Block->Y, Block->Z});
|
||||
delete Block; // We don't have to remove it from the vector, this will happen automatically on the next tick
|
||||
}
|
||||
else
|
||||
|
13
src/World.h
13
src/World.h
@ -1043,11 +1043,9 @@ public:
|
||||
return (IsWeatherWet() && !IsBiomeNoDownfall(Biome) && !IsBiomeCold(Biome));
|
||||
}
|
||||
|
||||
/** Returns true if the specified location has wet weather (rain or storm),
|
||||
using the same logic as IsWeatherWetAt, except that any rain-blocking blocks
|
||||
above the specified position will block the precipitation and this function
|
||||
will return false. */
|
||||
virtual bool IsWeatherWetAtXYZ(Vector3i a_Pos);
|
||||
/** Returns true if it is raining or storming at the specified location,
|
||||
and the rain reaches (the bottom of) the specified block position. */
|
||||
virtual bool IsWeatherWetAtXYZ(Vector3i a_Pos) override;
|
||||
|
||||
/** Returns the seed of the world. */
|
||||
int GetSeed(void) { return m_Generator.GetSeed(); }
|
||||
@ -1058,8 +1056,9 @@ public:
|
||||
cWorldStorage & GetStorage (void) { return m_Storage; }
|
||||
cChunkMap * GetChunkMap (void) { return m_ChunkMap.get(); }
|
||||
|
||||
/** 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); // tolua_export
|
||||
/** Causes the specified block to be ticked on the next Tick() call.
|
||||
Only one block coord per chunk may be set, a second call overwrites the first call */
|
||||
void SetNextBlockToTick(const Vector3i a_BlockPos); // tolua_export
|
||||
|
||||
int GetMaxSugarcaneHeight(void) const { return m_MaxSugarcaneHeight; } // tolua_export
|
||||
int GetMaxCactusHeight (void) const { return m_MaxCactusHeight; } // tolua_export
|
||||
|
@ -132,7 +132,7 @@ bool cBlockHandler::GetPlacementBlockTypeMeta(
|
||||
|
||||
|
||||
|
||||
void cBlockHandler::OnUpdate(cChunkInterface & cChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_PluginInterface, cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ)
|
||||
void cBlockHandler::OnUpdate(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_PluginInterface, cChunk & a_Chunk, const Vector3i a_RelPos)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -157,7 +157,7 @@ bool cBlockHandler::GetPlacementBlockTypeMeta(
|
||||
|
||||
|
||||
|
||||
void cBlockHandler::OnUpdate(cChunkInterface & cChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_PluginInterface, cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ)
|
||||
void cBlockHandler::OnUpdate(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_PluginInterface, cChunk & a_Chunk, const Vector3i a_RelPos)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -84,7 +84,7 @@ bool cBlockHandler::GetPlacementBlockTypeMeta(
|
||||
|
||||
|
||||
|
||||
void cBlockHandler::OnUpdate(cChunkInterface & cChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_PluginInterface, cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ)
|
||||
void cBlockHandler::OnUpdate(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_PluginInterface, cChunk & a_Chunk, const Vector3i a_RelPos)
|
||||
{
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user