1
0
Fork 0

improve rain simulation (#4017)

* Uses vanilla logic to decide which blocks rain falls through.
 * Rain falls infinitely above the world, and stops at y=0.
 * Entities will now be extinguished if they are under rain-blocking
blocks, and fire will now be extinguished by rain similarly.
 * Create IsWeatherWetAtXYZ to identify wetness at a particular location. 
 * Use new code for enderman rain detection.
 * Fixes issue #916
 * Disable warnings for global constructors in the fire simulator.
This commit is contained in:
Alexander Harkness 2017-12-26 21:25:57 +00:00 committed by GitHub
parent ab5ff6a6f8
commit 6309c6a97f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 180 additions and 113 deletions

View File

@ -238,7 +238,7 @@ return
Type = "boolean",
},
},
Notes = "Returns whether the specified block type will be destroyed after a single hit.",
Notes = "Returns true if the specified block type will be destroyed after a single hit.",
},
IsPistonBreakable =
{
@ -256,7 +256,43 @@ return
Type = "boolean",
},
},
Notes = "Returns whether a piston can break the specified block type.",
Notes = "Returns true if a piston can break the specified block type.",
},
IsRainBlocker =
{
IsStatic = true,
Params =
{
{
Name = "BlockType",
Type = "number",
},
},
Returns =
{
{
Type = "boolean",
},
},
Notes = "Returns true if the specified block type blocks rain from passing through.",
},
IsSkylightDispersant =
{
IsStatic = true,
Params =
{
{
Name = "BlockType",
Type = "number",
},
},
Returns =
{
{
Type = "boolean",
},
},
Notes = "Returns true if skylight is impeded by passage through a block of the specified type.",
},
IsSnowable =
{
@ -294,24 +330,6 @@ return
},
Notes = "Returns whether the specified block type is solid.",
},
IsSkylightDispersant =
{
IsStatic = true,
Params =
{
{
Name = "BlockType",
Type = "number",
},
},
Returns =
{
{
Type = "boolean",
},
},
Notes = "Returns true if skylight is impeded by passage through a block of the specified type.",
},
IsTransparent =
{
IsStatic = true,

View File

@ -2237,7 +2237,7 @@ function OnAllChunksAvailable()</pre> All return values from the callbacks are i
Type = "boolean",
},
},
Notes = "Returns true if the current world is raining (no thunderstorm).",
Notes = "Returns true if the current weather is rainy.",
},
IsWeatherRainAt =
{
@ -2258,7 +2258,7 @@ function OnAllChunksAvailable()</pre> All return values from the callbacks are i
Type = "boolean",
},
},
Notes = "Returns true if the specified location is raining (takes biomes into account - it never rains in a desert).",
Notes = "Returns true if it is rainy at the specified location. This takes into account biomes.",
},
IsWeatherStorm =
{
@ -2268,7 +2268,7 @@ function OnAllChunksAvailable()</pre> All return values from the callbacks are i
Type = "boolean",
},
},
Notes = "Returns true if the current world is stormy.",
Notes = "Returns true if the current weather is stormy.",
},
IsWeatherStormAt =
{
@ -2289,7 +2289,7 @@ function OnAllChunksAvailable()</pre> All return values from the callbacks are i
Type = "boolean",
},
},
Notes = "Returns true if the specified location is stormy (takes biomes into account - no storm in a desert).",
Notes = "Returns true if it is stormy at the specified location. This takes into account biomes.",
},
IsWeatherSunny =
{
@ -2320,7 +2320,7 @@ function OnAllChunksAvailable()</pre> All return values from the callbacks are i
Type = "boolean",
},
},
Notes = "Returns true if the current weather is sunny at the specified location (takes into account biomes).",
Notes = "Returns true if it is sunny at the specified location. This takes into account biomes.",
},
IsWeatherWet =
{
@ -2330,7 +2330,7 @@ function OnAllChunksAvailable()</pre> All return values from the callbacks are i
Type = "boolean",
},
},
Notes = "Returns true if the current world has any precipitation (rain or storm).",
Notes = "Returns true if the world currently has any precipitation - rain, storm or snow.",
},
IsWeatherWetAt =
{
@ -2351,7 +2351,24 @@ function OnAllChunksAvailable()</pre> All return values from the callbacks are i
Type = "boolean",
},
},
Notes = "Returns true if the specified location has any precipitation (rain or storm) (takes biomes into account, deserts are never wet).",
Notes = "Returns true if it is raining or storming at the specified location. This takes into account biomes.",
},
IsWeatherWetAtXYZ =
{
Params =
{
{
Name = "Pos",
Type = "Vector3i",
},
},
Returns =
{
{
Type = "boolean",
},
},
Notes = "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.",
},
PrepareChunk =
{
@ -3637,4 +3654,3 @@ World:ForEachEntity(
},
},
}

View File

@ -464,6 +464,16 @@ cBlockInfo::cBlockInfoArray::cBlockInfoArray()
Info[E_BLOCK_YELLOW_SHULKER_BOX ].m_PistonBreakable = true;
/* Blocks that block rain or snow's passage:
* All solid blocks are also rain blockers, and they are set automatically
* at the end of this function.
*/
Info[E_BLOCK_SIGN_POST ].m_IsRainBlocker = true;
Info[E_BLOCK_WALLSIGN ].m_IsRainBlocker = true;
Info[E_BLOCK_WALL_BANNER ].m_IsRainBlocker = true;
Info[E_BLOCK_STANDING_BANNER ].m_IsRainBlocker = true;
// Blocks that can be snowed over:
Info[E_BLOCK_BEDROCK ].m_IsSnowable = true;
Info[E_BLOCK_BLOCK_OF_COAL ].m_IsSnowable = true;
@ -554,8 +564,8 @@ cBlockInfo::cBlockInfoArray::cBlockInfoArray()
Info[E_BLOCK_BIG_FLOWER ].m_IsSolid = false;
Info[E_BLOCK_BROWN_MUSHROOM ].m_IsSolid = false;
Info[E_BLOCK_CARROTS ].m_IsSolid = false;
Info[E_BLOCK_CHORUS_PLANT ].m_IsSolid = false;
Info[E_BLOCK_CHORUS_FLOWER ].m_IsSolid = false;
Info[E_BLOCK_CHORUS_PLANT ].m_IsSolid = false;
Info[E_BLOCK_COBWEB ].m_IsSolid = false;
Info[E_BLOCK_CROPS ].m_IsSolid = false;
Info[E_BLOCK_DANDELION ].m_IsSolid = false;
@ -575,17 +585,17 @@ cBlockInfo::cBlockInfoArray::cBlockInfoArray()
Info[E_BLOCK_POTATOES ].m_IsSolid = false;
Info[E_BLOCK_POWERED_RAIL ].m_IsSolid = false;
Info[E_BLOCK_RAIL ].m_IsSolid = false;
Info[E_BLOCK_RED_MUSHROOM ].m_IsSolid = false;
Info[E_BLOCK_REDSTONE_TORCH_OFF ].m_IsSolid = false;
Info[E_BLOCK_REDSTONE_TORCH_ON ].m_IsSolid = false;
Info[E_BLOCK_REDSTONE_WIRE ].m_IsSolid = false;
Info[E_BLOCK_RED_MUSHROOM ].m_IsSolid = false;
Info[E_BLOCK_REEDS ].m_IsSolid = false;
Info[E_BLOCK_SAPLING ].m_IsSolid = false;
Info[E_BLOCK_SIGN_POST ].m_IsSolid = false;
Info[E_BLOCK_SNOW ].m_IsSolid = false;
Info[E_BLOCK_STANDING_BANNER ].m_IsSolid = false;
Info[E_BLOCK_STATIONARY_LAVA ].m_IsSolid = false;
Info[E_BLOCK_STATIONARY_WATER ].m_IsSolid = false;
Info[E_BLOCK_STANDING_BANNER ].m_IsSolid = false;
Info[E_BLOCK_STONE_BUTTON ].m_IsSolid = false;
Info[E_BLOCK_STONE_PRESSURE_PLATE ].m_IsSolid = false;
Info[E_BLOCK_TALL_GRASS ].m_IsSolid = false;
@ -974,8 +984,9 @@ cBlockInfo::cBlockInfoArray::cBlockInfoArray()
Info[E_BLOCK_RED_SHULKER_BOX ].m_Hardness = 0.2f;
Info[E_BLOCK_BLACK_SHULKER_BOX ].m_Hardness = 0.2f;
Info[E_BLOCK_STRUCTURE_BLOCK ].m_Hardness = -1.0f;
for (size_t i = 0; i < Info.size(); ++i)
{
Info[i].m_IsRainBlocker |= Info[i].m_IsSolid;
}
}

View File

@ -37,7 +37,10 @@ public:
/** Can a piston break this block? */
bool m_PistonBreakable;
/** Does a block disperse sky light? (only relevant for transparent blocks) */
/** Does this block block the passage of rain? */
bool m_IsRainBlocker;
/** Does this block disperse sky light? (only relevant for transparent blocks) */
bool m_IsSkylightDispersant;
/** Can this block hold snow atop? */
@ -79,6 +82,7 @@ public:
inline static bool IsTransparent (BLOCKTYPE a_Type) { return Get(a_Type).m_Transparent; }
inline static bool IsOneHitDig (BLOCKTYPE a_Type) { return Get(a_Type).m_OneHitDig; }
inline static bool IsPistonBreakable (BLOCKTYPE a_Type) { return Get(a_Type).m_PistonBreakable; }
inline static bool IsRainBlocker (BLOCKTYPE a_Type) { return Get(a_Type).m_IsRainBlocker; }
inline static bool IsSkylightDispersant (BLOCKTYPE a_Type)
{
return ((Get(a_Type).m_IsSkylightDispersant) || (Get(a_Type).m_SpreadLightFalloff > 1));
@ -102,6 +106,7 @@ public:
, m_Transparent(false)
, m_OneHitDig(false)
, m_PistonBreakable(false)
, m_IsRainBlocker(false)
, m_IsSkylightDispersant(false)
, m_IsSnowable(false)
, m_IsSolid(true)
@ -149,7 +154,3 @@ inline cBlockHandler * BlockHandler(BLOCKTYPE a_BlockType)
{
return cBlockInfo::Get(a_BlockType).m_Handler.get();
}

View File

@ -65,7 +65,7 @@ public:
virtual void SetTimeOfDay(int a_TimeOfDay) = 0;
/** Returns true if it is raining, stormy or snowing at the specified location. This takes into account biomes. */
/** 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 or sets the minumim or maximum netherportal width */

View File

@ -1172,12 +1172,9 @@ void cEntity::TickBurning(cChunk & a_Chunk)
}
// Fire is extinguished by rain
if (GetWorld()->IsWeatherWetAt(POSX_TOINT, POSZ_TOINT))
if (GetWorld()->IsWeatherWetAtXYZ(GetPosition().Floor()))
{
if (POSY_TOINT > m_World->GetHeight(POSX_TOINT, POSZ_TOINT))
{
m_TicksLeftBurning = 0;
}
m_TicksLeftBurning = 0;
}
// Do the burning damage:

View File

@ -194,37 +194,13 @@ void cEnderman::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
}
// Take damage when touching water, drowning damage seems to be most appropriate
if (CheckRain() || IsSwimming())
if (
cChunkDef::IsValidHeight(POSY_TOINT) &&
(GetWorld()->IsWeatherWetAtXYZ(GetPosition().Floor()) || IsSwimming())
)
{
EventLosePlayer();
TakeDamage(dtDrowning, nullptr, 1, 0);
// TODO teleport to a safe location
}
}
bool cEnderman::CheckRain(void)
{
if (!GetWorld()->IsWeatherRain())
{
return false;
}
Vector3d coords = GetPosition();
for (int Y = static_cast<int>(coords.y); Y < cChunkDef::Height; ++Y)
{
BLOCKTYPE Block = m_World->GetBlock(static_cast<int>(coords.x), Y, static_cast<int>(coords.z));
if (Block != E_BLOCK_AIR)
{
return false;
}
}
return true;
}

View File

@ -29,8 +29,6 @@ public:
/** Returns if the current sky light level is sufficient for the enderman to become aggravated */
bool CheckLight(void);
/** Returns if the enderman gets hit by the rain */
bool CheckRain(void);
private:
@ -39,7 +37,3 @@ private:
NIBBLETYPE CarriedMeta;
} ;

View File

@ -29,7 +29,6 @@ SET (HDRS
VaporizeFluidSimulator.h
)
if(NOT MSVC)
add_library(Simulator ${SRCS} ${HDRS})
endif()

View File

@ -25,15 +25,22 @@
#define MAX_CHANCE_REPLACE_FUEL 100000
#define MAX_CHANCE_FLAMMABILITY 100000
// The base chance that in a tick, rain will extinguish a fire block.
#define CHANCE_BASE_RAIN_EXTINGUISH 0.2
// The additional chance, multiplied by the meta of the fire block, that rain
// will extinguish a fire block in a tick.
#define CHANCE_AGE_M_RAIN_EXTINGUISH 0.03
static const struct
{
int x, y, z;
} gCrossCoords[] =
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wglobal-constructors"
#endif
static const Vector3i gCrossCoords[] =
{
{ 1, 0, 0},
{-1, 0, 0},
@ -45,10 +52,7 @@ static const struct
static const struct
{
int x, y, z;
} gNeighborCoords[] =
static const Vector3i gNeighborCoords[] =
{
{ 1, 0, 0},
{-1, 0, 0},
@ -58,6 +62,10 @@ static const struct
{ 0, 0, -1},
} ;
#ifdef __clang__
#pragma clang diagnostic pop
#endif
@ -97,20 +105,39 @@ void cFireSimulator::SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX,
int x = itr->x;
int y = itr->y;
int z = itr->z;
auto AbsPos = cChunkDef::RelativeToAbsolute({x, y, z}, a_Chunk->GetPosX(), a_Chunk->GetPosZ());
BLOCKTYPE BlockType = a_Chunk->GetBlock(x, y, z);
if (!IsAllowedBlock(BlockType))
{
// The block is no longer eligible (not a fire block anymore; a player probably placed a block over the fire)
FLOG("FS: Removing block {%d, %d, %d}",
itr->x + a_ChunkX * cChunkDef::Width, itr->y, itr->z + a_ChunkZ * cChunkDef::Width
AbsPos.x, AbsPos.y, AbsPos.z
);
itr = Data.erase(itr);
continue;
}
auto BurnsForever = ((y > 0) && DoesBurnForever(a_Chunk->GetBlock(x, (y - 1), z)));
auto BlockMeta = a_Chunk->GetMeta(x, y, z);
auto Raining = std::any_of(std::begin(gCrossCoords), std::end(gCrossCoords),
[this, AbsPos](Vector3i cc)
{
return (m_World.IsWeatherWetAtXYZ(AbsPos + cc));
}
);
// Randomly burn out the fire if it is raining:
if (!BurnsForever && Raining && GetRandomProvider().RandBool(CHANCE_BASE_RAIN_EXTINGUISH + (BlockMeta * CHANCE_AGE_M_RAIN_EXTINGUISH)))
{
a_Chunk->SetBlock(x, y, z, E_BLOCK_AIR, 0);
itr = Data.erase(itr);
continue;
}
// Try to spread the fire:
TrySpreadFire(a_Chunk, itr->x, itr->y, itr->z);
TrySpreadFire(a_Chunk, x, y, z);
itr->Data -= NumMSecs;
if (itr->Data >= 0)
@ -120,30 +147,30 @@ void cFireSimulator::SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX,
continue;
}
// Burn out the fire one step by increasing the meta:
/*
FLOG("FS: Fire at {%d, %d, %d} is stepping",
itr->x + a_ChunkX * cChunkDef::Width, itr->y, itr->z + a_ChunkZ * cChunkDef::Width
);
*/
NIBBLETYPE BlockMeta = a_Chunk->GetMeta(x, y, z);
// Has the fire burnt out?
if (BlockMeta == 0x0f)
{
// The fire burnt out completely
FLOG("FS: Fire at {%d, %d, %d} burnt out, removing the fire block",
itr->x + a_ChunkX * cChunkDef::Width, itr->y, itr->z + a_ChunkZ * cChunkDef::Width
);
a_Chunk->SetBlock(itr->x, itr->y, itr->z, E_BLOCK_AIR, 0);
RemoveFuelNeighbors(a_Chunk, itr->x, itr->y, itr->z);
a_Chunk->SetBlock(x, y, z, E_BLOCK_AIR, 0);
RemoveFuelNeighbors(a_Chunk, x, y, z);
itr = Data.erase(itr);
continue;
}
if ((itr->y > 0) && (!DoesBurnForever(a_Chunk->GetBlock(itr->x, itr->y - 1, itr->z))))
// Burn out the fire one step by increasing the meta:
if (!BurnsForever)
{
a_Chunk->SetMeta(x, y, z, BlockMeta + 1);
}
itr->Data = GetBurnStepTime(a_Chunk, itr->x, itr->y, itr->z); // TODO: Add some randomness into this
itr->Data = GetBurnStepTime(a_Chunk, x, y, z); // TODO: Add some randomness into this
++itr;
} // for itr - Data[]
}
@ -283,7 +310,7 @@ int cFireSimulator::GetBurnStepTime(cChunk * a_Chunk, int a_RelX, int a_RelY, in
}
} // for i - gCrossCoords[]
if (!IsBlockBelowSolid && (a_RelY >= 0))
if (!IsBlockBelowSolid)
{
// Checked through everything, nothing was flammable
// If block below isn't solid, we can't have fire, it would be a non-fueled fire
@ -427,7 +454,3 @@ bool cFireSimulator::CanStartFireInBlock(cChunk * a_NearChunk, int a_RelX, int a
} // for i - Coords[]
return false;
}

View File

@ -542,6 +542,34 @@ void cWorld::ChangeWeather(void)
bool cWorld::IsWeatherWetAtXYZ(Vector3i a_Pos)
{
if ((a_Pos.y < 0) || !IsWeatherWetAt(a_Pos.x, a_Pos.z))
{
return false;
}
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;
}
void cWorld::SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ)
{
return m_ChunkMap->SetNextBlockTick(a_BlockX, a_BlockY, a_BlockZ);

View File

@ -766,7 +766,7 @@ public:
/** Returns the current weather. Instead of comparing values directly to the weather constants, use IsWeatherXXX() functions, if possible */
eWeather GetWeather(void) const { return m_Weather; }
/** Returns true if the current weather is sun */
/** Returns true if the current weather is sunny. */
bool IsWeatherSunny(void) const { return (m_Weather == wSunny); }
/** Returns true if it is sunny at the specified location. This takes into account biomes. */
@ -775,7 +775,7 @@ public:
return (IsWeatherSunny() || IsBiomeNoDownfall(GetBiomeAt(a_BlockX, a_BlockZ)));
}
/** Returns true if the current weather is rain */
/** Returns true if the current weather is rainy. */
bool IsWeatherRain(void) const { return (m_Weather == wRain); }
/** Returns true if it is raining at the specified location. This takes into account biomes. */
@ -784,7 +784,7 @@ public:
return (IsWeatherRain() && !IsBiomeNoDownfall(GetBiomeAt(a_BlockX, a_BlockZ)));
}
/** Returns true if the current weather is stormy */
/** Returns true if the current weather is stormy. */
bool IsWeatherStorm(void) const { return (m_Weather == wStorm); }
/** Returns true if the weather is stormy at the specified location. This takes into account biomes. */
@ -793,15 +793,23 @@ public:
return (IsWeatherStorm() && !IsBiomeNoDownfall(GetBiomeAt(a_BlockX, a_BlockZ)));
}
/** Returns true if the current weather has any precipitation - rain, storm or snow */
/** Returns true if the world currently has any precipitation - rain, storm or snow. */
bool IsWeatherWet(void) const { return !IsWeatherSunny(); }
/** Returns true if it is raining, stormy or snowing at the specified location. This takes into account biomes. */
/** 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
{
return (IsWeatherWet() && !IsBiomeNoDownfall(GetBiomeAt(a_BlockX, a_BlockZ)));
auto Biome = GetBiomeAt(a_BlockX, a_BlockZ);
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 the seed of the world. */
int GetSeed(void) { return m_Generator.GetSeed(); }
@ -1129,7 +1137,3 @@ private:
void SetChunkData(cSetChunkData & a_SetChunkData);
}; // tolua_export