From 9328afe65c72b29f5cedbf1897ea8559f6b2c42f Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Tue, 5 Jan 2021 02:13:02 +0000 Subject: [PATCH] Convert most calls to blocking GetHeight/GetBiomeAt to direct chunk accesses * Hopefully fixes #5094 --- src/Blocks/BlockFarmland.h | 5 +-- src/Chunk.cpp | 58 ++++++++++++++++++++++++++---- src/Chunk.h | 12 ++++++- src/ChunkMap.cpp | 62 ++++++++++++++++++++++++++++++--- src/ChunkMap.h | 19 +++++----- src/Entities/Entity.cpp | 4 +-- src/Entities/Floater.cpp | 24 ++++++++----- src/MobSpawner.cpp | 7 ---- src/Mobs/Enderman.cpp | 14 +++++--- src/Mobs/Monster.cpp | 8 ++--- src/Mobs/SnowGolem.cpp | 18 ++++++---- src/Simulator/FireSimulator.cpp | 13 ++++--- src/World.cpp | 35 +++++++++---------- src/World.h | 13 ++----- 14 files changed, 204 insertions(+), 88 deletions(-) diff --git a/src/Blocks/BlockFarmland.h b/src/Blocks/BlockFarmland.h index f7003a650..2622ac98c 100644 --- a/src/Blocks/BlockFarmland.h +++ b/src/Blocks/BlockFarmland.h @@ -115,13 +115,14 @@ private: /** Returns true if there's either a water source block close enough to hydrate the specified position, or it's raining there. */ static bool IsWaterInNear(const cChunk & a_Chunk, const Vector3i a_RelPos) { - const auto WorldPos = a_Chunk.RelativeToAbsolute(a_RelPos); - if (a_Chunk.GetWorld()->IsWeatherWetAtXYZ(WorldPos)) + if (a_Chunk.IsWeatherWetAt(a_RelPos.addedY(1))) { // Rain hydrates farmland, too return true; } + const auto WorldPos = a_Chunk.RelativeToAbsolute(a_RelPos); + // Search for water in a close proximity: // Ref.: https://minecraft.gamepedia.com/Farmland#Hydration // TODO: Rewrite this to use the chunk and its neighbors directly diff --git a/src/Chunk.cpp b/src/Chunk.cpp index 5a6cca3eb..4140d4f85 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -626,7 +626,6 @@ void cChunk::SpawnMobs(cMobSpawner & a_MobSpawner) int WorldX, WorldY, WorldZ; PositionToWorldPosition(TryX, TryY, TryZ, WorldX, WorldY, WorldZ); - EMCSBiome Biome = m_ChunkMap->GetBiomeAt(WorldX, WorldZ); // MG TODO : // Moon cycle (for slime) // check player and playerspawn presence < 24 blocks @@ -641,12 +640,16 @@ void cChunk::SpawnMobs(cMobSpawner & a_MobSpawner) */ NumberOfTries++; - if (!IsLightValid()) + + Vector3i Try(TryX, TryY, TryZ); + const auto Chunk = GetRelNeighborChunkAdjustCoords(Try); + + if ((Chunk == nullptr) || !Chunk->IsValid() || !Chunk->IsLightValid()) { continue; } - auto newMob = a_MobSpawner.TryToSpawnHere(this, {TryX, TryY, TryZ}, Biome, MaxNbOfSuccess); + auto newMob = a_MobSpawner.TryToSpawnHere(this, Try, GetBiomeAt(Try.x, Try.z), MaxNbOfSuccess); if (newMob == nullptr) { continue; @@ -1190,15 +1193,56 @@ bool cChunk::UnboundedRelFastSetBlock(Vector3i a_RelPos, BLOCKTYPE a_BlockType, -int cChunk::GetHeight(int a_X, int a_Z) +int cChunk::GetHeight(int a_X, int a_Z) const { ASSERT((a_X >= 0) && (a_X < Width) && (a_Z >= 0) && (a_Z < Width)); + return m_HeightMap[a_X + a_Z * Width]; +} - if ((a_X >= 0) && (a_X < Width) && (a_Z >= 0) && (a_Z < Width)) + + + + +bool cChunk::IsWeatherSunnyAt(int a_RelX, int a_RelZ) const +{ + return m_World->IsWeatherSunny() || IsBiomeNoDownfall(GetBiomeAt(a_RelX, a_RelZ)); +} + + + + + +bool cChunk::IsWeatherWetAt(const int a_RelX, const int a_RelZ) const +{ + const auto Biome = GetBiomeAt(a_RelX, a_RelZ); + return m_World->IsWeatherWet() && !IsBiomeNoDownfall(Biome) && !IsBiomeCold(Biome); +} + + + + + +bool cChunk::IsWeatherWetAt(const Vector3i a_Position) const +{ + if ((a_Position.y < 0) || !IsWeatherWetAt(a_Position.x, a_Position.z)) { - return m_HeightMap[a_X + a_Z * Width]; + return false; } - return 0; + + if (a_Position.y >= cChunkDef::Height) + { + return true; + } + + for (int y = GetHeight(a_Position.x, a_Position.z); y >= a_Position.y; y--) + { + if (cBlockInfo::IsRainBlocker(GetBlock({ a_Position.x, y, a_Position.z }))) + { + return false; + } + } + + return true; } diff --git a/src/Chunk.h b/src/Chunk.h index 28f7b0bec..d0283f3e4 100644 --- a/src/Chunk.h +++ b/src/Chunk.h @@ -221,7 +221,17 @@ public: /** Sets the sign text. Returns true if successful. Also sends update packets to all clients in the chunk */ bool SetSignLines(int a_RelX, int a_RelY, int a_RelZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4); - int GetHeight( int a_X, int a_Z); + int GetHeight( int a_X, int a_Z) const; + + /** Returns true if it is sunny at the specified location. This takes into account biomes. */ + bool IsWeatherSunnyAt(int a_RelX, int a_RelZ) const; + + /** Returns true if it is raining or storming at the specified location, taking into account biomes. */ + bool IsWeatherWetAt(int a_RelX, int a_RelZ) const; + + /** Returns true if it is raining or storming at the specified location, + and the rain reaches (the bottom of) the specified block position. */ + bool IsWeatherWetAt(Vector3i a_Position) const; void SendBlockTo(int a_RelX, int a_RelY, int a_RelZ, cClientHandle * a_Client); diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp index f06dd057f..c5798260b 100644 --- a/src/ChunkMap.cpp +++ b/src/ChunkMap.cpp @@ -337,6 +337,63 @@ bool cChunkMap::IsChunkQueued(int a_ChunkX, int a_ChunkZ) const +bool cChunkMap::IsWeatherSunnyAt(int a_BlockX, int a_BlockZ) const +{ + int ChunkX, ChunkZ, BlockY = 0; + cChunkDef::AbsoluteToRelative(a_BlockX, BlockY, a_BlockZ, ChunkX, ChunkZ); + + cCSLock Lock(m_CSChunks); + const auto Chunk = FindChunk(ChunkX, ChunkZ); + if ((Chunk == nullptr) || !Chunk->IsValid()) + { + return m_World->IsWeatherSunny(); + } + + return Chunk->IsWeatherSunnyAt(a_BlockX, a_BlockZ); +} + + + + + +bool cChunkMap::IsWeatherWetAt(int a_BlockX, int a_BlockZ) const +{ + int ChunkX, ChunkZ, BlockY = 0; + cChunkDef::AbsoluteToRelative(a_BlockX, BlockY, a_BlockZ, ChunkX, ChunkZ); + + cCSLock Lock(m_CSChunks); + const auto Chunk = FindChunk(ChunkX, ChunkZ); + if ((Chunk == nullptr) || !Chunk->IsValid()) + { + return m_World->IsWeatherWet(); + } + + return Chunk->IsWeatherWetAt(a_BlockX, a_BlockZ); +} + + + + + +bool cChunkMap::IsWeatherWetAt(const Vector3i a_Position) const +{ + const auto ChunkPosition = cChunkDef::BlockToChunk(a_Position); + const auto Position = cChunkDef::AbsoluteToRelative(a_Position, ChunkPosition); + + cCSLock Lock(m_CSChunks); + const auto Chunk = FindChunk(ChunkPosition.m_ChunkX, ChunkPosition.m_ChunkZ); + if ((Chunk == nullptr) || !Chunk->IsValid()) + { + return m_World->IsWeatherWet(); + } + + return Chunk->IsWeatherWetAt(Position); +} + + + + + bool cChunkMap::IsChunkValid(int a_ChunkX, int a_ChunkZ) const { cCSLock Lock(m_CSChunks); @@ -647,10 +704,7 @@ EMCSBiome cChunkMap::GetBiomeAt(int a_BlockX, int a_BlockZ) const { return Chunk->GetBiomeAt(X, Z); } - else - { - return m_World->GetGenerator().GetBiomeAt(a_BlockX, a_BlockZ); - } + return EMCSBiome::biInvalidBiome; } diff --git a/src/ChunkMap.h b/src/ChunkMap.h index e40a5d59d..de1f29754 100644 --- a/src/ChunkMap.h +++ b/src/ChunkMap.h @@ -5,9 +5,6 @@ #pragma once - -#include - #include "ChunkDataCallback.h" #include "EffectID.h" #include "FunctionRef.h" @@ -120,6 +117,10 @@ public: /** Returns true iff the chunk is in the loader / generator queue. */ bool IsChunkQueued(int a_ChunkX, int a_ChunkZ) const; + bool IsWeatherSunnyAt(int a_BlockX, int a_BlockZ) const; + bool IsWeatherWetAt(int a_BlockX, int a_BlockZ) const; + bool IsWeatherWetAt(Vector3i a_Position) const; + bool IsChunkValid (int a_ChunkX, int a_ChunkZ) const; bool HasChunkAnyClients (int a_ChunkX, int a_ChunkZ) const; int GetHeight (int a_BlockX, int a_BlockZ); // Waits for the chunk to get loaded / generated @@ -143,15 +144,15 @@ public: Ignored if the chunk is invalid. */ void SetBlockMeta(Vector3i a_BlockPos, NIBBLETYPE a_BlockMeta); - void SetBlock (Vector3i a_BlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - bool GetBlockTypeMeta (Vector3i a_BlockPos, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const; - bool GetBlockInfo (Vector3i, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight) const; + void SetBlock (Vector3i a_BlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + bool GetBlockTypeMeta (Vector3i a_BlockPos, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const; + bool GetBlockInfo (Vector3i, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight) const; /** Special function used for growing trees, replaces only blocks that tree may overwrite */ - void ReplaceTreeBlocks(const sSetBlockVector & a_Blocks); + void ReplaceTreeBlocks(const sSetBlockVector & a_Blocks); - /** 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) const; + /** Returns the biome at the specified coords. Reads the biome from the chunk, if loaded, invalid otherwise. */ + EMCSBiome GetBiomeAt(int a_BlockX, int a_BlockZ) const; /** Sets the biome at the specified coords. Returns true if successful, false if not (chunk not loaded). Doesn't resend the chunk to clients. */ diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index 438117650..f07eab415 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -1198,8 +1198,8 @@ void cEntity::TickBurning(cChunk & a_Chunk) m_TicksLeftBurning = 0; } - // Fire is extinguished by rain - if (GetWorld()->IsWeatherWetAtXYZ(GetPosition().Floor())) + // Fire is extinguished by rain: + if (a_Chunk.IsWeatherWetAt(cChunkDef::AbsoluteToRelative(GetPosition().Floor(), a_Chunk.GetPos()))) { m_TicksLeftBurning = 0; } diff --git a/src/Entities/Floater.cpp b/src/Entities/Floater.cpp index bb326f70b..2738c12d3 100644 --- a/src/Entities/Floater.cpp +++ b/src/Entities/Floater.cpp @@ -101,11 +101,16 @@ void cFloater::SpawnOn(cClientHandle & a_Client) void cFloater::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { - auto & Random = GetRandomProvider(); - HandlePhysics(a_Dt, a_Chunk); - if (IsBlockWater(m_World->GetBlock(POSX_TOINT, POSY_TOINT, POSZ_TOINT)) - && (m_World->GetBlockMeta(POSX_TOINT, POSY_TOINT, POSZ_TOINT) == 0)) + + PREPARE_REL_AND_CHUNK(GetPosition().Floor(), a_Chunk); + if (!RelSuccess) + { + return; + } + + auto & Random = GetRandomProvider(); + if (IsBlockWater(Chunk->GetBlock(Rel)) && (Chunk->GetMeta(Rel) == 0)) { if (!m_CanPickupItem && (m_AttachedMobID == cEntity::INVALID_ID)) // Check if you can't already pickup a fish and if the floater isn't attached to a mob. { @@ -113,7 +118,7 @@ void cFloater::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { m_BitePos = GetPosition(); m_World->BroadcastSoundEffect("entity.bobber.splash", GetPosition(), 1, 1); - SetPosY(GetPosY() - 1); + AddSpeedY(-10); m_CanPickupItem = true; m_PickupCountDown = 20; m_CountDownTime = Random.RandInt(100, 900); @@ -132,9 +137,9 @@ void cFloater::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) } m_CountDownTime--; - if (m_World->GetHeight(POSX_TOINT, POSZ_TOINT) == POSY_TOINT) + if (Chunk->IsWeatherWetAt(Rel)) { - if (m_World->IsWeatherWet() && Random.RandBool(0.25)) // 25% chance of an extra countdown when being rained on. + if (Random.RandBool(0.25)) // 25% chance of an extra countdown when being rained on. { m_CountDownTime--; } @@ -150,7 +155,10 @@ void cFloater::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) } // Check water at the top of floater otherwise it floats into the air above the water - if (IsBlockWater(m_World->GetBlock(POSX_TOINT, FloorC(GetPosY() + GetHeight()), POSZ_TOINT))) + if ( + const auto Above = Rel.addedY(FloorC(GetPosY() + GetHeight())); + (Above.y < cChunkDef::Height) && IsBlockWater(m_World->GetBlock(Above)) + ) { SetSpeedY(0.7); } diff --git a/src/MobSpawner.cpp b/src/MobSpawner.cpp index b83cd247d..c04467fd3 100644 --- a/src/MobSpawner.cpp +++ b/src/MobSpawner.cpp @@ -74,10 +74,6 @@ eMonsterType cMobSpawner::ChooseMobType(EMCSBiome a_Biome) bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, Vector3i a_RelPos, eMonsterType a_MobType, EMCSBiome a_Biome, bool a_DisableSolidBelowCheck) { - if (a_Chunk == nullptr) - { - return false; - } if ((a_RelPos.y >= cChunkDef::Height - 1) || (a_RelPos.y <= 0)) { return false; @@ -502,9 +498,6 @@ cMonster * cMobSpawner::TryToSpawnHere(cChunk * a_Chunk, Vector3i a_RelPos, EMCS m_NewPack = false; } - // Make sure we are looking at the right chunk to spawn in - a_Chunk = a_Chunk->GetRelNeighborChunkAdjustCoords(a_RelPos); - if ((m_AllowedTypes.find(m_MobType) != m_AllowedTypes.end()) && CanSpawnHere(a_Chunk, a_RelPos, m_MobType, a_Biome)) { auto NewMob = cMonster::NewMonsterFromType(m_MobType); diff --git a/src/Mobs/Enderman.cpp b/src/Mobs/Enderman.cpp index 02450487b..a107762c6 100644 --- a/src/Mobs/Enderman.cpp +++ b/src/Mobs/Enderman.cpp @@ -1,6 +1,7 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules +#include "Chunk.h" #include "Enderman.h" #include "../Entities/Player.h" #include "../LineBlockTracer.h" @@ -148,11 +149,14 @@ void cEnderman::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) return; } - // Take damage when wet - if ( - cChunkDef::IsValidHeight(POSY_TOINT) && - (GetWorld()->IsWeatherWetAtXYZ(GetPosition().Floor()) || IsInWater()) - ) + PREPARE_REL_AND_CHUNK(GetPosition().Floor(), a_Chunk); + if (!RelSuccess) + { + return; + } + + // Take damage when wet: + if (IsInWater() || Chunk->IsWeatherWetAt(Rel)) { EventLosePlayer(); TakeDamage(dtEnvironment, nullptr, 1, 0); diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index 7e8e7eba3..52b3f8454 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -1667,10 +1667,10 @@ bool cMonster::WouldBurnAt(Vector3d a_Location, cChunk & a_Chunk) } if ( - (Chunk->GetBlock(Rel.x, Rel.y, Rel.z) != E_BLOCK_SOULSAND) && // Not on soulsand - (GetWorld()->GetTimeOfDay() < 12000 + 1000) && // Daytime - GetWorld()->IsWeatherSunnyAt(POSX_TOINT, POSZ_TOINT) && // Not raining - !IsInWater() // Isn't swimming + (Chunk->GetBlock(Rel) != E_BLOCK_SOULSAND) && // Not on soulsand + (GetWorld()->GetTimeOfDay() < 12000 + 1000) && // Daytime + Chunk->IsWeatherSunnyAt(Rel.x, Rel.z) && // Not raining + !IsInWater() // Isn't swimming ) { int MobHeight = CeilC(a_Location.y + GetHeight()) - 1; // The block Y coord of the mob's head diff --git a/src/Mobs/SnowGolem.cpp b/src/Mobs/SnowGolem.cpp index 93c29cafb..c6db0cbd1 100644 --- a/src/Mobs/SnowGolem.cpp +++ b/src/Mobs/SnowGolem.cpp @@ -1,6 +1,7 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules +#include "Chunk.h" #include "SnowGolem.h" #include "../BlockInfo.h" #include "../World.h" @@ -36,17 +37,22 @@ void cSnowGolem::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) // The base class tick destroyed us return; } - if (IsBiomeNoDownfall(m_World->GetBiomeAt(POSX_TOINT, POSZ_TOINT))) + + PREPARE_REL_AND_CHUNK(GetPosition().Floor(), a_Chunk); + if (!RelSuccess) + { + return; + } + + if (IsBiomeNoDownfall(Chunk->GetBiomeAt(Rel.x, Rel.z))) { TakeDamage(dtEnvironment, nullptr, GetRawDamageAgainst(*this), GetKnockbackAmountAgainst(*this)); } - else + else if (const auto Below = Rel.addedY(-1); Below.y >= 0) { - BLOCKTYPE BlockBelow = m_World->GetBlock(POSX_TOINT, POSY_TOINT - 1, POSZ_TOINT); - BLOCKTYPE Block = m_World->GetBlock(POSX_TOINT, POSY_TOINT, POSZ_TOINT); - if ((Block == E_BLOCK_AIR) && cBlockInfo::IsSolid(BlockBelow)) + if ((Chunk->GetBlock(Rel) == E_BLOCK_AIR) && cBlockInfo::IsSolid(Chunk->GetBlock(Below))) { - m_World->SetBlock(POSX_TOINT, POSY_TOINT, POSZ_TOINT, E_BLOCK_SNOW, 0); + Chunk->SetBlock(Rel, E_BLOCK_SNOW, 0); } } } diff --git a/src/Simulator/FireSimulator.cpp b/src/Simulator/FireSimulator.cpp index 30fe88acd..67a00f241 100644 --- a/src/Simulator/FireSimulator.cpp +++ b/src/Simulator/FireSimulator.cpp @@ -87,7 +87,6 @@ void cFireSimulator::SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, for (cCoordWithIntList::iterator itr = Data.begin(); itr != Data.end();) { Vector3i relPos(itr->x, itr->y, itr->z); - auto absPos = a_Chunk->RelativeToAbsolute(relPos); auto blockType = a_Chunk->GetBlock(relPos); if (!IsAllowedBlock(blockType)) @@ -101,12 +100,16 @@ void cFireSimulator::SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, 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) + auto Raining = std::any_of(std::begin(gCrossCoords), std::end(gCrossCoords), [a_Chunk, relPos](Vector3i cc) + { + auto Adjusted = relPos + cc; + const auto Chunk = a_Chunk->GetRelNeighborChunkAdjustCoords(Adjusted); + if ((Chunk != nullptr) && Chunk->IsValid()) { - return (m_World.IsWeatherWetAtXYZ(absPos + cc)); + return Chunk->IsWeatherWetAt(Adjusted); } - ); + return false; + }); // Randomly burn out the fire if it is raining: if (!BurnsForever && Raining && GetRandomProvider().RandBool(CHANCE_BASE_RAIN_EXTINGUISH + (BlockMeta * CHANCE_AGE_M_RAIN_EXTINGUISH))) diff --git a/src/World.cpp b/src/World.cpp index 46d08ab44..6b224b634 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -548,28 +548,27 @@ void cWorld::ChangeWeather(void) -bool cWorld::IsWeatherWetAtXYZ(Vector3i a_Pos) +bool cWorld::IsWeatherSunnyAt(int a_BlockX, int a_BlockZ) const { - if ((a_Pos.y < 0) || !IsWeatherWetAt(a_Pos.x, a_Pos.z)) - { - return false; - } + return m_ChunkMap.IsWeatherSunnyAt(a_BlockX, a_BlockZ); +} - if (a_Pos.y >= cChunkDef::Height) - { - return true; - } - for (int y = GetHeight(a_Pos.x, a_Pos.z); y >= a_Pos.y; y--) - { - auto BlockType = GetBlock({a_Pos.x, y, a_Pos.z}); - if (cBlockInfo::IsRainBlocker(BlockType)) - { - return false; - } - } - return true; + + +bool cWorld::IsWeatherWetAt(int a_BlockX, int a_BlockZ) +{ + return m_ChunkMap.IsWeatherWetAt(a_BlockX, a_BlockZ); +} + + + + + +bool cWorld::IsWeatherWetAtXYZ(const Vector3i a_Position) +{ + return m_ChunkMap.IsWeatherWetAt(a_Position); } diff --git a/src/World.h b/src/World.h index b0ffc2b07..ba4e5ee58 100644 --- a/src/World.h +++ b/src/World.h @@ -999,10 +999,7 @@ public: bool IsWeatherSunny(void) const { return (m_Weather == wSunny); } /** Returns true if it is sunny at the specified location. This takes into account biomes. */ - bool IsWeatherSunnyAt(int a_BlockX, int a_BlockZ) - { - return (IsWeatherSunny() || IsBiomeNoDownfall(GetBiomeAt(a_BlockX, a_BlockZ))); - } + bool IsWeatherSunnyAt(int a_BlockX, int a_BlockZ) const; /** Returns true if the current weather is rainy. */ bool IsWeatherRain(void) const { return (m_Weather == wRain); } @@ -1027,15 +1024,11 @@ 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) override - { - auto Biome = GetBiomeAt(a_BlockX, a_BlockZ); - return (IsWeatherWet() && !IsBiomeNoDownfall(Biome) && !IsBiomeCold(Biome)); - } + virtual bool IsWeatherWetAt(int a_BlockX, int a_BlockZ) override; /** 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; + virtual bool IsWeatherWetAtXYZ(Vector3i a_Position) override; /** Returns the seed of the world. */ int GetSeed(void) { return m_Generator.GetSeed(); }