1
0
Fork 0

blockheight mechanism

This commit is contained in:
Gargaj 2015-11-10 14:02:07 +01:00
parent fec522aa03
commit 66e6589883
16 changed files with 294 additions and 128 deletions

View File

@ -399,6 +399,7 @@ g_APIDesc =
CanBeTerraformed = { Params = "Type", Return = "bool", Notes = "(STATIC) Returns true if the block is suitable to be changed by a generator" },
FullyOccupiesVoxel = { Params = "Type", Return = "bool", Notes = "(STATIC) Returns whether the specified block fully occupies its voxel." },
Get = { Params = "Type", Return = "{{cBlockInfo}}", Notes = "(STATIC) Returns the {{cBlockInfo}} structure for the specified type." },
GetBlockHeight = { Params = "Type", Return = "number", Notes = "(STATIC) Returns the block's hitbox height." },
GetLightValue = { Params = "Type", Return = "number", Notes = "(STATIC) Returns how much light the specified block emits on its own." },
GetPlaceSound = { Params = "Type", Return = "", Notes = "(STATIC) Returns the name of the sound that is played when placing the block." },
GetSpreadLightFalloff = { Params = "Type", Return = "number", Notes = "(STATIC) Returns how much light the specified block consumes." },

View File

@ -584,6 +584,27 @@ void cBlockInfo::Initialize(cBlockInfoArray & a_Info)
a_Info[E_BLOCK_STONE ].m_CanBeTerraformed = true;
// Block heights:
a_Info[E_BLOCK_BED ].m_BlockHeight = 0.5625; // 9 pixels
a_Info[E_BLOCK_CAKE ].m_BlockHeight = 0.5; // 8 pixels
a_Info[E_BLOCK_ENCHANTMENT_TABLE ].m_BlockHeight = 0.75; // 12 pixels
a_Info[E_BLOCK_STONE_SLAB ].m_BlockHeight = 0.5;
a_Info[E_BLOCK_WOODEN_SLAB ].m_BlockHeight = 0.5;
a_Info[E_BLOCK_SNOW ].m_BlockHeight = 0.125; // one layer is 1 / 8 (2 pixels) tall
a_Info[E_BLOCK_ACACIA_FENCE ].m_BlockHeight = 1.5;
a_Info[E_BLOCK_ACACIA_FENCE_GATE ].m_BlockHeight = 1.5;
a_Info[E_BLOCK_BIRCH_FENCE ].m_BlockHeight = 1.5;
a_Info[E_BLOCK_BIRCH_FENCE_GATE ].m_BlockHeight = 1.5;
a_Info[E_BLOCK_DARK_OAK_FENCE ].m_BlockHeight = 1.5;
a_Info[E_BLOCK_DARK_OAK_FENCE_GATE ].m_BlockHeight = 1.5;
a_Info[E_BLOCK_FENCE ].m_BlockHeight = 1.5;
a_Info[E_BLOCK_OAK_FENCE_GATE ].m_BlockHeight = 1.5;
a_Info[E_BLOCK_JUNGLE_FENCE ].m_BlockHeight = 1.5;
a_Info[E_BLOCK_JUNGLE_FENCE_GATE ].m_BlockHeight = 1.5;
a_Info[E_BLOCK_SPRUCE_FENCE ].m_BlockHeight = 1.5;
a_Info[E_BLOCK_SPRUCE_FENCE_GATE ].m_BlockHeight = 1.5;
// Block place sounds:
a_Info[E_BLOCK_STONE ].m_PlaceSound = "dig.stone";
a_Info[E_BLOCK_GRASS ].m_PlaceSound = "dig.grass";

View File

@ -61,6 +61,9 @@ public:
/** Can a finisher change it? */
bool m_CanBeTerraformed;
/** Block height */
float m_BlockHeight;
/** Sound when placing this block */
AString m_PlaceSound;
@ -80,6 +83,7 @@ public:
inline static bool IsSolid (BLOCKTYPE a_Type) { return Get(a_Type).m_IsSolid; }
inline static bool FullyOccupiesVoxel (BLOCKTYPE a_Type) { return Get(a_Type).m_FullyOccupiesVoxel; }
inline static bool CanBeTerraformed (BLOCKTYPE a_Type) { return Get(a_Type).m_CanBeTerraformed; }
inline static float GetBlockHeight (BLOCKTYPE a_Type) { return Get(a_Type).m_BlockHeight; }
inline static AString GetPlaceSound (BLOCKTYPE a_Type) { return Get(a_Type).m_PlaceSound; }
// tolua_end
@ -101,6 +105,7 @@ protected:
, m_IsSolid(true)
, m_FullyOccupiesVoxel(false)
, m_CanBeTerraformed(false)
, m_BlockHeight(1.0)
, m_PlaceSound("")
, m_Handler(nullptr)
{}

View File

@ -553,6 +553,16 @@ bool cBlockHandler::DoesDropOnUnsuitable(void)
/* default functionality: only test for height, since we assume full voxels with varying height */
bool cBlockHandler::IsInsideBlock(const Vector3d & a_Position, const BLOCKTYPE a_BlockType, const NIBBLETYPE a_BlockMeta)
{
return a_Position.y < cBlockInfo::GetBlockHeight(a_BlockType);
}
void cBlockHandler::Check(cChunkInterface & a_ChunkInterface, cBlockPluginInterface & a_PluginInterface, int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk)
{
if (!CanBeAt(a_ChunkInterface, a_RelX, a_RelY, a_RelZ, a_Chunk))

View File

@ -124,7 +124,11 @@ public:
/** Returns if this block drops if it gets destroyed by an unsuitable situation.
Default: true */
virtual bool DoesDropOnUnsuitable(void);
/** Tests if a_Position is inside the block where a_Position is relative to the origin of the block
Note that this is considered from a "top-down" perspective i.e. empty spaces on the bottom of a block don't matter */
virtual bool IsInsideBlock(const Vector3d & a_Position, const BLOCKTYPE a_BlockType, const NIBBLETYPE a_BlockMeta);
/** Called when one of the neighbors gets set; equivalent to MC block update.
By default drops if position no more suitable (CanBeAt(), DoesDropOnUnsuitable(), Drop()),
and wakes up all simulators on the block. */

View File

@ -174,6 +174,15 @@ public:
}
}
}
virtual bool IsInsideBlock(const Vector3d & a_Position, const BLOCKTYPE a_BlockType, const NIBBLETYPE a_BlockMeta) override
{
if (a_BlockMeta & 0x8) // top half
{
return true;
}
return cBlockHandler::IsInsideBlock(a_Position, a_BlockType, a_BlockMeta);
}
} ;

View File

@ -88,6 +88,11 @@ public:
UNUSED(a_Meta);
return 14;
}
virtual bool IsInsideBlock(const Vector3d & a_Position, const BLOCKTYPE a_BlockType, const NIBBLETYPE a_BlockMeta) override
{
return a_Position.y < (cBlockInfo::GetBlockHeight(a_BlockType) * (a_BlockMeta & 7));
}
} ;

View File

@ -115,6 +115,38 @@ public:
}
}
}
/** EXCEPTION a.k.a. why is this removed:
This collision-detection is actually more accurate than the client, but since the client itself
sends inaccurate / sparse data, it's easier to just err on the side of the client and keep the
two in sync by assuming that if a player hit ANY of the stair's bounding cube, it counts as the ground. */
#if 0
bool IsInsideBlock(const Vector3d & a_Position, const BLOCKTYPE a_BlockType, const NIBBLETYPE a_BlockMeta)
{
if (a_BlockMeta & 0x4) // upside down
{
return true;
}
else if ((a_BlockMeta & 0x3) == 0) // tall side is east (+X)
{
return a_Position.y < ((a_Position.x > 0.5) ? 1.0 : 0.5);
}
else if ((a_BlockMeta & 0x3) == 1) // tall side is west (-X)
{
return a_Position.y < ((a_Position.x < 0.5) ? 1.0 : 0.5);
}
else if ((a_BlockMeta & 0x3) == 2) // tall side is south (+Z)
{
return a_Position.y < ((a_Position.z > 0.5) ? 1.0 : 0.5);
}
else if ((a_BlockMeta & 0x3) == 3) // tall side is north (-Z)
{
return a_Position.y < ((a_Position.z < 0.5) ? 1.0 : 0.5);
}
return false;
}
#endif
} ;

View File

@ -1029,16 +1029,17 @@ void cEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
NextSpeed.z = 0.0f;
}
if (Tracer.HitNormal.y == 1.0f) // Hit BLOCK_FACE_YP, we are on the ground
{
m_bOnGround = true;
}
// Now, set our position to the hit block (i.e. move part way along our intended trajectory)
NextPos.Set(Tracer.RealHit.x, Tracer.RealHit.y, Tracer.RealHit.z);
NextPos.x += Tracer.HitNormal.x * 0.1;
NextPos.y += Tracer.HitNormal.y * 0.05;
NextPos.z += Tracer.HitNormal.z * 0.1;
if (Tracer.HitNormal.y == 1.0f) // Hit BLOCK_FACE_YP, we are on the ground
{
m_bOnGround = true;
NextPos.y = FloorC(NextPos.y); // we clamp the height to 0 cos otherwise we'll constantly be slightly above the block
}
}
else
{

View File

@ -2,17 +2,22 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "Pawn.h"
#include "Player.h"
#include "../World.h"
#include "../Bindings/PluginManager.h"
#include "BoundingBox.h"
#include "../Blocks/BlockHandler.h"
#include "EffectID.h"
cPawn::cPawn(eEntityType a_EntityType, double a_Width, double a_Height) :
super(a_EntityType, 0, 0, 0, a_Width, a_Height)
, m_EntityEffects(tEffectMap())
super(a_EntityType, 0, 0, 0, a_Width, a_Height),
m_EntityEffects(tEffectMap()),
m_LastGroundHeight(0),
m_bTouchGround(false)
{
SetGravity(-32.0f);
SetAirDrag(0.02f);
@ -79,8 +84,10 @@ void cPawn::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
} Callback(this);
m_World->ForEachEntityInBox(cBoundingBox(GetPosition(), GetWidth(), GetHeight()), Callback);
super::Tick(a_Dt, a_Chunk);
HandleFalling();
}
@ -185,3 +192,174 @@ void cPawn::ClearEntityEffects()
void cPawn::HandleFalling(void)
{
/* Not pretty looking, and is more suited to wherever server-sided collision detection is implemented.
The following condition sets on-ground-ness if
The player isn't swimming or flying (client hardcoded conditions) and
they're on a block (Y is exact) - ensure any they could be standing on (including on the edges) is solid or
they're on a slab (Y significand is 0.5) - ditto with slab check
they're on a snow layer (Y divisible by 0.125) - ditto with snow layer check
*/
static const auto HalfWidth = GetWidth() / 2;
static const auto EPS = 0.0001;
/* Since swimming is decided in a tick and is asynchronous to this, we have to check for dampeners ourselves.
The behaviour as of 1.8.9 is the following:
- Landing in water alleviates all fall damage
- Passing through any liquid (water + lava) and cobwebs "slows" the player down,
i.e. resets the fall distance to that block, but only after checking for fall damage
(this means that plummeting into lava will still kill the player via fall damage, although cobwebs
will slow players down enough to have multiple updates that keep them alive)
- Slime blocks reverse falling velocity, unless it's a crouching player, in which case they act as standard blocks.
They also reset the topmost point of the damage calculation with each bounce,
so if the block is removed while the player is bouncing or crouches after a bounce, the last bounce's zenith is considered as fall damage.
With this in mind, we first check the block at the player's feet, then the one below that (because fences),
and decide which behaviour we want to go with.
*/
BLOCKTYPE BlockAtFoot = (cChunkDef::IsValidHeight(POSY_TOINT)) ? GetWorld()->GetBlock(POS_TOINT) : E_BLOCK_AIR;
/* We initialize these with what the foot is really IN, because for sampling we will move down with the epsilon above */
bool IsFootInWater = IsBlockWater(BlockAtFoot);
bool IsFootInLiquid = IsFootInWater || IsBlockLava(BlockAtFoot) || (BlockAtFoot == E_BLOCK_COBWEB); // okay so cobweb is not _technically_ a liquid...
bool IsFootOnSlimeBlock = false;
/* The "cross" we sample around to account for the player width/girth */
static const struct
{
int x, z;
} CrossSampleCoords[] =
{
{ 0, 0 },
{ 1, 0 },
{ -1, 0 },
{ 0, 1 },
{ 0, -1 },
};
/* The blocks we're interested in relative to the player to account for larger than 1 blocks.
This can be extended to do additional checks in case there are blocks that are represented as one block
in memory but have a hitbox larger than 1 (like fences) */
static const struct
{
int x, y, z;
} BlockSampleOffsets[] =
{
{ 0, 0, 0 },
{ 0, -1, 0 },
};
/* Here's the rough outline of how this mechanism works:
We take the player's pointlike position (sole of feet), and expand it into a crosslike shape.
If any of the five points hit a block, we consider the player to be "on" (or "in") the ground. */
bool OnGround = false;
for (size_t i = 0; i < ARRAYCOUNT(CrossSampleCoords); i++)
{
/* We calculate from the player's position, one of the cross-offsets above, and we move it down slightly so it's beyond inaccuracy.
The added advantage of this method is that if the player is simply standing on the floor,
the point will move into the next block, and the floor() will retrieve that instead of air. */
Vector3d CrossTestPosition = GetPosition() + Vector3d(CrossSampleCoords[i].x * HalfWidth, -EPS, CrossSampleCoords[i].z * HalfWidth);
/* We go through the blocks that we consider "relevant" */
for (size_t j = 0; j < ARRAYCOUNT(BlockSampleOffsets); j++)
{
Vector3i BlockTestPosition = CrossTestPosition.Floor() + Vector3i(BlockSampleOffsets[j].x, BlockSampleOffsets[j].y, BlockSampleOffsets[j].z);
if (!cChunkDef::IsValidHeight(BlockTestPosition.y))
{
continue;
}
BLOCKTYPE Block = GetWorld()->GetBlock(BlockTestPosition);
NIBBLETYPE BlockMeta = GetWorld()->GetBlockMeta(BlockTestPosition);
/* we do the cross-shaped sampling to check for water / liquids, but only on our level because water blocks are never bigger than unit voxels */
if (j == 0)
{
IsFootInWater |= IsBlockWater(Block);
IsFootInLiquid |= IsFootInWater || IsBlockLava(Block) || (Block == E_BLOCK_COBWEB); // okay so cobweb is not _technically_ a liquid...
IsFootOnSlimeBlock |= (Block == E_BLOCK_SLIME_BLOCK);
}
/* If the block is solid, and the blockhandler confirms the block to be inside, we're officially on the ground. */
if ((cBlockInfo::IsSolid(Block)) && (cBlockInfo::GetHandler(Block)->IsInsideBlock(CrossTestPosition - BlockTestPosition, Block, BlockMeta)))
{
OnGround = true;
}
}
}
// Update the ground height to have the highest position of the player (i.e. jumping up adds to the eventual fall damage)
if (!OnGround)
{
m_LastGroundHeight = std::max(m_LastGroundHeight, GetPosY());
}
/* So here's the use of the rules above: */
/* 1. Falling in water absorbs all fall damage */
bool FallDamageAbsorbed = IsFootInWater;
/* 2. Falling in liquid (lava, water, cobweb) or hitting a slime block resets the "fall zenith".
Note: Even though the pawn bounces back with no damage after hitting the slime block,
the "fall zenith" will continue to increase back up when flying upwards - which is good */
bool FallDistanceReset = IsFootOnSlimeBlock || IsFootInLiquid;
bool IsFlying = false;
bool ShouldBounceOnSlime = true;
if (GetEntityType() == eEntityType::etPlayer)
{
cPlayer * Player = static_cast<cPlayer *>(this);
IsFlying = Player->IsFlying();
/* 3. If the player is flying or climbing, absorb fall damage */
FallDamageAbsorbed |= IsFlying || Player->IsClimbing();
/* 4. If the player is about to bounce on a slime block and is not crouching, absorb all fall damage */
ShouldBounceOnSlime = !Player->IsCrouched();
FallDamageAbsorbed |= (IsFootOnSlimeBlock && ShouldBounceOnSlime);
}
else
{
/* 5. Bouncing on a slime block absorbs all fall damage */
FallDamageAbsorbed |= IsFootOnSlimeBlock;
}
/* If the player is not crouching or is not a player, shoot them back up.
NOTE: this will only work in some cases; should be done in HandlePhysics() */
if (IsFootOnSlimeBlock && ShouldBounceOnSlime)
{
SetSpeedY(-GetSpeedY());
}
if (!IsFlying && OnGround)
{
auto Damage = static_cast<int>(m_LastGroundHeight - GetPosY() - 3.0);
if ((Damage > 0) && !FallDamageAbsorbed)
{
TakeDamage(dtFalling, nullptr, Damage, Damage, 0);
// Fall particles
int ParticleSize = static_cast<int>((std::min(15, Damage) - 1.f) * ((50.f - 20.f) / (15.f - 1.f)) + 20.f);
GetWorld()->BroadcastSoundParticleEffect(EffectID::PARTICLE_FALL_PARTICLES, POSX_TOINT, POSY_TOINT - 1, POSZ_TOINT, ParticleSize);
}
m_bTouchGround = true;
m_LastGroundHeight = GetPosY();
}
else
{
m_bTouchGround = false;
}
/* Note: it is currently possible to fall through lava and still die from fall damage
because of the client skipping an update about the lava block. This can only be resolved by
somehow integrating these above checks into the tracer in HandlePhysics. */
if (FallDistanceReset)
{
m_LastGroundHeight = GetPosY();
}
}

View File

@ -25,7 +25,8 @@ public:
virtual bool IsFireproof(void) const override;
virtual void HandleAir(void) override;
virtual void HandleFalling(void);
// tolua_begin
/** Applies an entity effect
@ -49,12 +50,15 @@ public:
/** Removes all currently applied entity effects (used when drinking milk) */
void ClearEntityEffects(void);
// tolua_end
protected:
typedef std::map<cEntityEffect::eType, cEntityEffect *> tEffectMap;
tEffectMap m_EntityEffects;
double m_LastGroundHeight;
bool m_bTouchGround;
} ; // tolua_export

View File

@ -55,8 +55,6 @@ cPlayer::cPlayer(cClientHandlePtr a_Client, const AString & a_PlayerName) :
m_FoodSaturationLevel(5.0),
m_FoodTickTimer(0),
m_FoodExhaustionLevel(0.0),
m_LastGroundHeight(0),
m_bTouchGround(false),
m_Stance(0.0),
m_Inventory(*this),
m_EnderChestContents(9, 3),
@ -451,101 +449,6 @@ void cPlayer::SetTouchGround(bool a_bTouchGround)
return;
}
/* Not pretty looking, and is more suited to wherever server-sided collision detection is implemented.
The following condition sets on-ground-ness if
The player isn't swimming or flying (client hardcoded conditions) and
they're on a block (Y is exact) - ensure any they could be standing on (including on the edges) is solid or
they're on a slab (Y significand is 0.5) - ditto with slab check
they're on a snow layer (Y divisible by 0.125) - ditto with snow layer check
*/
static const auto HalfWidth = GetWidth() / 2;
static const auto EPS = 0.0001;
/* Since swimming is decided in a tick and is asynchronous to this, we have to check for dampeners ourselves.
The behaviour as of 1.8.8 is the following:
- Landing in water alleviates all fall damage
- Passing through any liquid (water + lava) and cobwebs "slows" the player down,
i.e. resets the fall distance to that block, but only after checking for fall damage
(this means that plummeting into lava will still kill the player via fall damage, although cobwebs
will slow players down enough to have multiple updates that keep them alive)
With this in mind, we first check the block at the player's feet, and decide which behaviour we want to go with.
*/
BLOCKTYPE BlockAtFoot = (cChunkDef::IsValidHeight(GetPosY())) ? GetWorld()->GetBlock(POS_TOINT) : E_BLOCK_AIR;
bool IsFootInWater = IsBlockWater(BlockAtFoot);
bool IsFootInLiquid = IsFootInWater || IsBlockLava(BlockAtFoot) || (BlockAtFoot == E_BLOCK_COBWEB); // okay so cobweb is not _technically_ a liquid...
if (
!IsFlying() &&
(
(
(cChunkDef::IsValidHeight(GetPosY()) && ((GetPosY() - POSY_TOINT) <= EPS)) &&
(
cBlockInfo::IsSolid(GetWorld()->GetBlock((GetPosition() + Vector3d(0, -1, 0)).Floor())) ||
cBlockInfo::IsSolid(GetWorld()->GetBlock((GetPosition() + Vector3d(HalfWidth, -1, 0)).Floor())) ||
cBlockInfo::IsSolid(GetWorld()->GetBlock((GetPosition() + Vector3d(-HalfWidth, -1, 0)).Floor())) ||
cBlockInfo::IsSolid(GetWorld()->GetBlock((GetPosition() + Vector3d(0, -1, HalfWidth)).Floor())) ||
cBlockInfo::IsSolid(GetWorld()->GetBlock((GetPosition() + Vector3d(0, -1, -HalfWidth)).Floor()))
)
) ||
(
(cChunkDef::IsValidHeight(GetPosY()) && (GetPosY() >= POSY_TOINT) && ((GetPosY() - (POSY_TOINT + 0.5)) <= EPS)) &&
(
cBlockSlabHandler::IsAnySlabType(GetWorld()->GetBlock((GetPosition() + Vector3d(0, 0, 0)).Floor())) ||
cBlockSlabHandler::IsAnySlabType(GetWorld()->GetBlock((GetPosition() + Vector3d(HalfWidth, 0, 0)).Floor())) ||
cBlockSlabHandler::IsAnySlabType(GetWorld()->GetBlock((GetPosition() + Vector3d(-HalfWidth, 0, 0)).Floor())) ||
cBlockSlabHandler::IsAnySlabType(GetWorld()->GetBlock((GetPosition() + Vector3d(0, 0, HalfWidth)).Floor())) ||
cBlockSlabHandler::IsAnySlabType(GetWorld()->GetBlock((GetPosition() + Vector3d(0, 0, -HalfWidth)).Floor()))
)
) ||
(
(cChunkDef::IsValidHeight(GetPosY()) && (fmod(GetPosY(), 0.125) <= EPS)) &&
(
(GetWorld()->GetBlock((GetPosition() + Vector3d(0, 0, 0)).Floor()) == E_BLOCK_SNOW) ||
(GetWorld()->GetBlock((GetPosition() + Vector3d(HalfWidth, 0, 0)).Floor()) == E_BLOCK_SNOW) ||
(GetWorld()->GetBlock((GetPosition() + Vector3d(-HalfWidth, 0, 0)).Floor()) == E_BLOCK_SNOW) ||
(GetWorld()->GetBlock((GetPosition() + Vector3d(0, 0, HalfWidth)).Floor()) == E_BLOCK_SNOW) ||
(GetWorld()->GetBlock((GetPosition() + Vector3d(0, 0, -HalfWidth)).Floor()) == E_BLOCK_SNOW)
)
)
)
)
{
auto Damage = static_cast<int>(m_LastGroundHeight - GetPosY() - 3.0);
if ((Damage > 0) && !IsFootInWater)
{
// cPlayer makes sure damage isn't applied in creative, no need to check here
TakeDamage(dtFalling, nullptr, Damage, Damage, 0);
// Fall particles
Damage = std::min(15, Damage);
GetClientHandle()->SendParticleEffect(
"blockdust",
GetPosition(),
{ 0, 0, 0 },
(Damage - 1.f) * ((0.3f - 0.1f) / (15.f - 1.f)) + 0.1f, // Map damage (1 - 15) to particle speed (0.1 - 0.3)
static_cast<int>((Damage - 1.f) * ((50.f - 20.f) / (15.f - 1.f)) + 20.f), // Map damage (1 - 15) to particle quantity (20 - 50)
{ { GetWorld()->GetBlock(POS_TOINT - Vector3i(0, 1, 0)), 0 } }
);
}
m_bTouchGround = true;
m_LastGroundHeight = GetPosY();
}
else
{
m_bTouchGround = false;
}
/* Note: it is currently possible to fall through lava and still die from fall damage
because of the client skipping an update about the lava block. This can only be resolved by
interpolating between positions. */
if (IsFlying() || IsFootInLiquid || IsClimbing())
{
m_LastGroundHeight = GetPosY();
}
UNUSED(a_bTouchGround);
/* Unfortunately whatever the reason, there are still desyncs in on-ground status between the client and server. For example:
1. Walking off a ledge (whatever height)

View File

@ -550,8 +550,6 @@ protected:
/** A "buffer" which adds up hunger before it is substracted from m_FoodSaturationLevel or m_FoodLevel. Each action adds a little */
double m_FoodExhaustionLevel;
double m_LastGroundHeight;
bool m_bTouchGround;
double m_Stance;
/** Stores the player's inventory, consisting of crafting grid, hotbar, and main slots */

View File

@ -77,7 +77,6 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A
, m_Target(nullptr)
, m_PathFinder(a_Width, a_Height)
, m_PathfinderActivated(false)
, m_LastGroundHeight(POSY_TOINT)
, m_JumpCoolDown(0)
, m_IdleInterval(0)
, m_DestroyTimer(0)
@ -298,7 +297,6 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
}
SetPitchAndYawFromDestination(a_IsFollowingPath);
HandleFalling();
switch (m_EMState)
{
@ -397,20 +395,8 @@ void cMonster::SetPitchAndYawFromDestination(bool a_IsFollowingPath)
void cMonster::HandleFalling()
{
if (m_bOnGround)
{
int Damage = (m_LastGroundHeight - POSY_TOINT) - 3;
if (Damage > 0)
{
TakeDamage(dtFalling, nullptr, Damage, Damage, 0);
// Fall particles
GetWorld()->BroadcastSoundParticleEffect(EffectID::PARTICLE_FALL_PARTICLES, POSX_TOINT, POSY_TOINT - 1, POSZ_TOINT, Damage /* Used as particle effect speed modifier */);
}
m_LastGroundHeight = POSY_TOINT;
}
m_bTouchGround = IsOnGround();
super::HandleFalling();
}

View File

@ -56,6 +56,8 @@ public:
virtual void OnRightClicked(cPlayer & a_Player) override;
virtual void HandleFalling(void) override;
/** Engage pathfinder and tell it to calculate a path to a given position, and move the mobile accordingly
Currently, the mob will only start moving to a new position after the position it is currently going to is reached. */
virtual void MoveToPosition(const Vector3d & a_Position); // tolua_export
@ -203,8 +205,6 @@ protected:
/** Sets the body yaw and head yaw */
void SetPitchAndYawFromDestination(bool a_IsFollowingPath);
virtual void HandleFalling(void);
int m_LastGroundHeight;
int m_JumpCoolDown;
std::chrono::milliseconds m_IdleInterval;

View File

@ -246,6 +246,15 @@ ColourID cBlockHandler::GetMapBaseColourID(NIBBLETYPE a_Meta)
bool cBlockHandler::IsInsideBlock(const Vector3d & a_Position, const BLOCKTYPE a_BlockType, const NIBBLETYPE a_BlockMeta)
{
return true;
}
cBlockEntity * cBlockEntity::CreateByBlockType(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World)
{
return nullptr;