Implement farmland trampling (#5401)
* Add DarkoGNU to CONTRIBUTORS * HandleFarmlandTrampling function & its docs * Fix decimal separators (, -> .) * Fix style. Adjust thresholds. Make function non-virtual * Adjust thresholds again. Prepare for fixing #5402 * Trying to fix falling through farmlands * Another style fix * Add FarmlandTramplingEnabled to world.ini * Docs for IsFarmlandTramplingEnabled * Style * Farmland trampling - handling the random chance * Trampling kinda works, very buggy * Trying to fix clang-tidy * Fix trampling * Trying to fix the 'undocumented API symbol' * Implement bearbin's suggestions * Calculate volume properly * Don't use std::pow for squaring * Improved comments * Really, should comments' style be checked?
This commit is contained in:
parent
844a339330
commit
4f554e91ab
@ -17,6 +17,7 @@ birkett (Anthony Birkett)
|
|||||||
Bond_009
|
Bond_009
|
||||||
changyongGuo
|
changyongGuo
|
||||||
Cocosushi6
|
Cocosushi6
|
||||||
|
DarkoGNU
|
||||||
derouinw
|
derouinw
|
||||||
dImrich (Damian Imrich)
|
dImrich (Damian Imrich)
|
||||||
Diusrex
|
Diusrex
|
||||||
|
@ -2245,6 +2245,16 @@ function OnAllChunksAvailable()</pre> All return values from the callbacks are i
|
|||||||
},
|
},
|
||||||
Notes = "Returns whether the configuration has DeepSnow enabled.",
|
Notes = "Returns whether the configuration has DeepSnow enabled.",
|
||||||
},
|
},
|
||||||
|
IsFarmlandTramplingEnabled =
|
||||||
|
{
|
||||||
|
Returns =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
Type = "boolean",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Notes = "Returns true if farmland trampling is enabled.",
|
||||||
|
},
|
||||||
IsGameModeAdventure =
|
IsGameModeAdventure =
|
||||||
{
|
{
|
||||||
Returns =
|
Returns =
|
||||||
|
@ -1164,6 +1164,7 @@ float cBlockInfo::GetBlockHeight(const BLOCKTYPE Block)
|
|||||||
case E_BLOCK_DARK_OAK_FENCE: return 1.5;
|
case E_BLOCK_DARK_OAK_FENCE: return 1.5;
|
||||||
case E_BLOCK_DARK_OAK_FENCE_GATE: return 1.5;
|
case E_BLOCK_DARK_OAK_FENCE_GATE: return 1.5;
|
||||||
case E_BLOCK_ENCHANTMENT_TABLE: return 0.75; // 12 pixels
|
case E_BLOCK_ENCHANTMENT_TABLE: return 0.75; // 12 pixels
|
||||||
|
case E_BLOCK_FARMLAND: return 0.9375; // 15 pixels
|
||||||
case E_BLOCK_FENCE: return 1.5;
|
case E_BLOCK_FENCE: return 1.5;
|
||||||
case E_BLOCK_JUNGLE_FENCE: return 1.5;
|
case E_BLOCK_JUNGLE_FENCE: return 1.5;
|
||||||
case E_BLOCK_JUNGLE_FENCE_GATE: return 1.5;
|
case E_BLOCK_JUNGLE_FENCE_GATE: return 1.5;
|
||||||
|
@ -10,7 +10,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "BlockHandler.h"
|
#include "BlockHandler.h"
|
||||||
|
#include "ChunkInterface.h"
|
||||||
#include "../BlockArea.h"
|
#include "../BlockArea.h"
|
||||||
|
#include "../Chunk.h"
|
||||||
|
#include "../ClientHandle.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -25,6 +28,58 @@ public:
|
|||||||
|
|
||||||
using Super::Super;
|
using Super::Super;
|
||||||
|
|
||||||
|
/** Turns farmland into dirt.
|
||||||
|
Will first check for any colliding entities and teleport them to a higher position.
|
||||||
|
*/
|
||||||
|
static void TurnToDirt(cChunk & a_Chunk, Vector3i a_AbsPos)
|
||||||
|
{
|
||||||
|
auto RelPos = cChunkDef::AbsoluteToRelative(a_AbsPos);
|
||||||
|
TurnToDirt(a_Chunk, a_AbsPos, RelPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** Turns farmland into dirt.
|
||||||
|
Will first check for any colliding entities and teleport them to a higher position.
|
||||||
|
*/
|
||||||
|
static void TurnToDirt(cChunk & a_Chunk, Vector3i a_AbsPos, Vector3i a_RelPos)
|
||||||
|
{
|
||||||
|
static const auto FarmlandHeight = cBlockInfo::GetBlockHeight(E_BLOCK_FARMLAND);
|
||||||
|
static const auto FullHeightDelta = 1 - FarmlandHeight;
|
||||||
|
|
||||||
|
a_Chunk.ForEachEntityInBox(
|
||||||
|
cBoundingBox(Vector3d(0.5, FarmlandHeight, 0.5) + a_AbsPos, 0.5, FullHeightDelta),
|
||||||
|
[&](cEntity & Entity)
|
||||||
|
{
|
||||||
|
if (!Entity.IsOnGround())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Entity.AddPosY(FullHeightDelta);
|
||||||
|
|
||||||
|
// Players need a packet that will update their position
|
||||||
|
if (Entity.IsPlayer())
|
||||||
|
{
|
||||||
|
auto Player = static_cast<cPlayer *>(&Entity);
|
||||||
|
// This works, but it's much worse than Vanilla.
|
||||||
|
// This can be easily improved by implementing relative
|
||||||
|
// "Player Position And Look" packets! See
|
||||||
|
// https://wiki.vg/Protocol#Player_Position_And_Look_.28clientbound.29
|
||||||
|
Player->GetClientHandle()->SendPlayerMoveLook();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
a_Chunk.SetBlock(a_RelPos, E_BLOCK_DIRT, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
virtual cItems ConvertToPickups(const NIBBLETYPE a_BlockMeta, const cItem * const a_Tool) const override
|
virtual cItems ConvertToPickups(const NIBBLETYPE a_BlockMeta, const cItem * const a_Tool) const override
|
||||||
@ -76,7 +131,8 @@ private:
|
|||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
a_Chunk.SetBlock(a_RelPos, E_BLOCK_DIRT, 0);
|
auto AbsPos = a_Chunk.RelativeToAbsolute(a_RelPos);
|
||||||
|
TurnToDirt(a_Chunk, AbsPos, a_RelPos);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -101,10 +157,17 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check whether we should revert to dirt:
|
// Check whether we should revert to dirt:
|
||||||
|
// TODO: fix for signs and slabs (possibly more blocks) - they should destroy farmland
|
||||||
auto upperBlock = a_ChunkInterface.GetBlock(a_BlockPos.addedY(1));
|
auto upperBlock = a_ChunkInterface.GetBlock(a_BlockPos.addedY(1));
|
||||||
if (cBlockInfo::FullyOccupiesVoxel(upperBlock))
|
if (cBlockInfo::FullyOccupiesVoxel(upperBlock))
|
||||||
{
|
{
|
||||||
a_ChunkInterface.SetBlock(a_BlockPos, E_BLOCK_DIRT, 0);
|
// Until the fix above is done, this line should also suffice:
|
||||||
|
// a_ChunkInterface.SetBlock(a_BlockPos, E_BLOCK_DIRT, 0);
|
||||||
|
a_ChunkInterface.DoWithChunkAt(a_BlockPos, [&](cChunk & Chunk)
|
||||||
|
{
|
||||||
|
TurnToDirt(Chunk, a_BlockPos);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include "../Bindings/PluginManager.h"
|
#include "../Bindings/PluginManager.h"
|
||||||
#include "../BoundingBox.h"
|
#include "../BoundingBox.h"
|
||||||
#include "../Blocks/BlockHandler.h"
|
#include "../Blocks/BlockHandler.h"
|
||||||
|
#include "../Blocks/BlockFarmland.h"
|
||||||
#include "../EffectID.h"
|
#include "../EffectID.h"
|
||||||
#include "../Mobs/Monster.h"
|
#include "../Mobs/Monster.h"
|
||||||
|
|
||||||
@ -430,7 +431,9 @@ void cPawn::HandleFalling(void)
|
|||||||
|
|
||||||
if (OnGround)
|
if (OnGround)
|
||||||
{
|
{
|
||||||
auto Damage = static_cast<int>(m_LastGroundHeight - GetPosY() - 3.0);
|
auto FallHeight = m_LastGroundHeight - GetPosY();
|
||||||
|
auto Damage = static_cast<int>(FallHeight - 3.0);
|
||||||
|
|
||||||
if ((Damage > 0) && !FallDamageAbsorbed)
|
if ((Damage > 0) && !FallDamageAbsorbed)
|
||||||
{
|
{
|
||||||
if (IsElytraFlying())
|
if (IsElytraFlying())
|
||||||
@ -438,7 +441,6 @@ void cPawn::HandleFalling(void)
|
|||||||
Damage = static_cast<int>(static_cast<float>(Damage) * 0.33);
|
Damage = static_cast<int>(static_cast<float>(Damage) * 0.33);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fall particles:
|
|
||||||
if (const auto Below = POS_TOINT.addedY(-1); Below.y >= 0)
|
if (const auto Below = POS_TOINT.addedY(-1); Below.y >= 0)
|
||||||
{
|
{
|
||||||
const auto BlockBelow = GetWorld()->GetBlock(Below);
|
const auto BlockBelow = GetWorld()->GetBlock(Below);
|
||||||
@ -448,6 +450,7 @@ void cPawn::HandleFalling(void)
|
|||||||
Damage = std::clamp(static_cast<int>(static_cast<float>(Damage) * 0.2), 1, 20);
|
Damage = std::clamp(static_cast<int>(static_cast<float>(Damage) * 0.2), 1, 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fall particles
|
||||||
GetWorld()->BroadcastParticleEffect(
|
GetWorld()->BroadcastParticleEffect(
|
||||||
"blockdust",
|
"blockdust",
|
||||||
GetPosition(),
|
GetPosition(),
|
||||||
@ -463,6 +466,15 @@ void cPawn::HandleFalling(void)
|
|||||||
|
|
||||||
m_bTouchGround = true;
|
m_bTouchGround = true;
|
||||||
m_LastGroundHeight = GetPosY();
|
m_LastGroundHeight = GetPosY();
|
||||||
|
|
||||||
|
// Farmland trampling. Mobs smaller than 0.512 cubic blocks won't trample (Java Edition's behavior)
|
||||||
|
// We only have width and height, so we have to calculate Width^2
|
||||||
|
if (GetWorld()->IsFarmlandTramplingEnabled() &&
|
||||||
|
(BlockAtFoot == E_BLOCK_FARMLAND) &&
|
||||||
|
(GetWidth() * GetWidth() * GetHeight() >= 0.512))
|
||||||
|
{
|
||||||
|
HandleFarmlandTrampling(FallHeight);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -478,6 +490,42 @@ void cPawn::HandleFalling(void)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cPawn::HandleFarmlandTrampling(double a_FallHeight)
|
||||||
|
{
|
||||||
|
bool ShouldTrample = true;
|
||||||
|
auto & Random = GetRandomProvider();
|
||||||
|
|
||||||
|
// No trampling if FallHeight <= 0.6875
|
||||||
|
if (a_FallHeight <= 0.6875)
|
||||||
|
{
|
||||||
|
ShouldTrample = false;
|
||||||
|
}
|
||||||
|
// For FallHeight <= 1.5625 we need to get a random bool
|
||||||
|
else if (a_FallHeight <= 1.0625)
|
||||||
|
{
|
||||||
|
ShouldTrample = Random.RandBool(0.25);
|
||||||
|
}
|
||||||
|
else if (a_FallHeight <= 1.5625)
|
||||||
|
{
|
||||||
|
ShouldTrample = Random.RandBool(0.66);
|
||||||
|
}
|
||||||
|
// For FallHeight > 1.5625 we always trample - ShouldTrample remains true
|
||||||
|
|
||||||
|
if (ShouldTrample)
|
||||||
|
{
|
||||||
|
auto AbsPos = GetPosition().Floor();
|
||||||
|
GetWorld()->DoWithChunkAt(AbsPos, [&](cChunk & Chunk)
|
||||||
|
{
|
||||||
|
cBlockFarmlandHandler::TurnToDirt(Chunk, AbsPos);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cPawn::OnRemoveFromWorld(cWorld & a_World)
|
void cPawn::OnRemoveFromWorld(cWorld & a_World)
|
||||||
{
|
{
|
||||||
StopEveryoneFromTargetingMe();
|
StopEveryoneFromTargetingMe();
|
||||||
|
@ -33,6 +33,16 @@ public:
|
|||||||
virtual void HandleFalling(void);
|
virtual void HandleFalling(void);
|
||||||
virtual void OnRemoveFromWorld(cWorld & a_World) override;
|
virtual void OnRemoveFromWorld(cWorld & a_World) override;
|
||||||
|
|
||||||
|
/** Handles farmland trampling when hitting the ground.
|
||||||
|
Algorithm:
|
||||||
|
fall height <= 0.6875 blocks: no trampling
|
||||||
|
fall height > 0.6875 and <= 1.0625: 25% chance of trampling
|
||||||
|
fall height > 1.0625 and <= 1.5625: 66% chance of trampling
|
||||||
|
fall height > 1.5625: always trample
|
||||||
|
The values may differ from vanilla, they were determined experimentally.
|
||||||
|
*/
|
||||||
|
void HandleFarmlandTrampling(double a_FallHeight);
|
||||||
|
|
||||||
/** Tells all pawns which are targeting us to stop targeting us. */
|
/** Tells all pawns which are targeting us to stop targeting us. */
|
||||||
void StopEveryoneFromTargetingMe();
|
void StopEveryoneFromTargetingMe();
|
||||||
|
|
||||||
|
@ -166,6 +166,7 @@ cWorld::cWorld(
|
|||||||
m_SkyDarkness(0),
|
m_SkyDarkness(0),
|
||||||
m_GameMode(gmSurvival),
|
m_GameMode(gmSurvival),
|
||||||
m_bEnabledPVP(false),
|
m_bEnabledPVP(false),
|
||||||
|
m_bFarmlandTramplingEnabled(false),
|
||||||
m_IsDeepSnowEnabled(false),
|
m_IsDeepSnowEnabled(false),
|
||||||
m_ShouldLavaSpawnFire(true),
|
m_ShouldLavaSpawnFire(true),
|
||||||
m_VillagersShouldHarvestCrops(true),
|
m_VillagersShouldHarvestCrops(true),
|
||||||
@ -300,6 +301,7 @@ cWorld::cWorld(
|
|||||||
int TNTShrapnelLevel = IniFile.GetValueSetI("Physics", "TNTShrapnelLevel", static_cast<int>(slAll));
|
int TNTShrapnelLevel = IniFile.GetValueSetI("Physics", "TNTShrapnelLevel", static_cast<int>(slAll));
|
||||||
m_bCommandBlocksEnabled = IniFile.GetValueSetB("Mechanics", "CommandBlocksEnabled", false);
|
m_bCommandBlocksEnabled = IniFile.GetValueSetB("Mechanics", "CommandBlocksEnabled", false);
|
||||||
m_bEnabledPVP = IniFile.GetValueSetB("Mechanics", "PVPEnabled", true);
|
m_bEnabledPVP = IniFile.GetValueSetB("Mechanics", "PVPEnabled", true);
|
||||||
|
m_bFarmlandTramplingEnabled = IniFile.GetValueSetB("Mechanics", "FarmlandTramplingEnabled", true);
|
||||||
m_bUseChatPrefixes = IniFile.GetValueSetB("Mechanics", "UseChatPrefixes", true);
|
m_bUseChatPrefixes = IniFile.GetValueSetB("Mechanics", "UseChatPrefixes", true);
|
||||||
m_MinNetherPortalWidth = IniFile.GetValueSetI("Mechanics", "MinNetherPortalWidth", 2);
|
m_MinNetherPortalWidth = IniFile.GetValueSetI("Mechanics", "MinNetherPortalWidth", 2);
|
||||||
m_MaxNetherPortalWidth = IniFile.GetValueSetI("Mechanics", "MaxNetherPortalWidth", 21);
|
m_MaxNetherPortalWidth = IniFile.GetValueSetI("Mechanics", "MaxNetherPortalWidth", 21);
|
||||||
|
@ -121,6 +121,9 @@ public:
|
|||||||
|
|
||||||
bool IsPVPEnabled(void) const { return m_bEnabledPVP; }
|
bool IsPVPEnabled(void) const { return m_bEnabledPVP; }
|
||||||
|
|
||||||
|
/** Returns true if farmland trampling is enabled */
|
||||||
|
bool IsFarmlandTramplingEnabled(void) const { return m_bFarmlandTramplingEnabled; }
|
||||||
|
|
||||||
bool IsDeepSnowEnabled(void) const { return m_IsDeepSnowEnabled; }
|
bool IsDeepSnowEnabled(void) const { return m_IsDeepSnowEnabled; }
|
||||||
|
|
||||||
bool ShouldLavaSpawnFire(void) const { return m_ShouldLavaSpawnFire; }
|
bool ShouldLavaSpawnFire(void) const { return m_ShouldLavaSpawnFire; }
|
||||||
@ -997,6 +1000,7 @@ private:
|
|||||||
|
|
||||||
eGameMode m_GameMode;
|
eGameMode m_GameMode;
|
||||||
bool m_bEnabledPVP;
|
bool m_bEnabledPVP;
|
||||||
|
bool m_bFarmlandTramplingEnabled;
|
||||||
bool m_IsDeepSnowEnabled;
|
bool m_IsDeepSnowEnabled;
|
||||||
bool m_ShouldLavaSpawnFire;
|
bool m_ShouldLavaSpawnFire;
|
||||||
bool m_VillagersShouldHarvestCrops;
|
bool m_VillagersShouldHarvestCrops;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user