summaryrefslogtreecommitdiffstats
path: root/src/Mobs
diff options
context:
space:
mode:
authorAlexander Harkness <bearbin@gmail.com>2015-05-19 19:43:19 +0200
committerAlexander Harkness <bearbin@gmail.com>2015-05-19 19:43:19 +0200
commitcbb425f027a7b51c4aed5d3399b26cf325c4c8ce (patch)
tree6a35f2c9c44b7d3d5142635178bf1ec9ca5e428c /src/Mobs
parentUpdated Core. (diff)
parentMerge pull request #2057 from Seadragon91/master (diff)
downloadcuberite-cbb425f027a7b51c4aed5d3399b26cf325c4c8ce.tar
cuberite-cbb425f027a7b51c4aed5d3399b26cf325c4c8ce.tar.gz
cuberite-cbb425f027a7b51c4aed5d3399b26cf325c4c8ce.tar.bz2
cuberite-cbb425f027a7b51c4aed5d3399b26cf325c4c8ce.tar.lz
cuberite-cbb425f027a7b51c4aed5d3399b26cf325c4c8ce.tar.xz
cuberite-cbb425f027a7b51c4aed5d3399b26cf325c4c8ce.tar.zst
cuberite-cbb425f027a7b51c4aed5d3399b26cf325c4c8ce.zip
Diffstat (limited to 'src/Mobs')
-rw-r--r--src/Mobs/AggressiveMonster.cpp1
-rw-r--r--src/Mobs/MagmaCube.cpp14
-rw-r--r--src/Mobs/MagmaCube.h6
-rw-r--r--src/Mobs/Monster.cpp212
-rw-r--r--src/Mobs/Monster.h19
-rw-r--r--src/Mobs/Path.cpp131
-rw-r--r--src/Mobs/Path.h26
-rw-r--r--src/Mobs/Slime.cpp2
-rw-r--r--src/Mobs/Slime.h2
-rw-r--r--src/Mobs/Wolf.cpp5
10 files changed, 315 insertions, 103 deletions
diff --git a/src/Mobs/AggressiveMonster.cpp b/src/Mobs/AggressiveMonster.cpp
index d0fb79f6d..055ff47d2 100644
--- a/src/Mobs/AggressiveMonster.cpp
+++ b/src/Mobs/AggressiveMonster.cpp
@@ -36,7 +36,6 @@ void cAggressiveMonster::InStateChasing(std::chrono::milliseconds a_Dt)
return;
}
}
-
MoveToPosition(m_Target->GetPosition());
}
}
diff --git a/src/Mobs/MagmaCube.cpp b/src/Mobs/MagmaCube.cpp
index 3e9abc108..c5dd0def0 100644
--- a/src/Mobs/MagmaCube.cpp
+++ b/src/Mobs/MagmaCube.cpp
@@ -7,7 +7,7 @@
cMagmaCube::cMagmaCube(int a_Size) :
- super("MagmaCube", mtMagmaCube, "mob.MagmaCube.big", "mob.MagmaCube.big", 0.6 * a_Size, 0.6 * a_Size),
+ super("MagmaCube", mtMagmaCube, Printf("mob.magmacube.%s", GetSizeName(a_Size).c_str()), Printf("mob.magmacube.%s", GetSizeName(a_Size).c_str()), 0.6 * a_Size, 0.6 * a_Size),
m_Size(a_Size)
{
}
@@ -27,4 +27,14 @@ void cMagmaCube::GetDrops(cItems & a_Drops, cEntity * a_Killer)
-
+AString cMagmaCube::GetSizeName(int a_Size)
+{
+ if (a_Size > 1)
+ {
+ return "big";
+ }
+ else
+ {
+ return "small";
+ }
+}
diff --git a/src/Mobs/MagmaCube.h b/src/Mobs/MagmaCube.h
index d66ea423a..b914dc867 100644
--- a/src/Mobs/MagmaCube.h
+++ b/src/Mobs/MagmaCube.h
@@ -19,10 +19,14 @@ public:
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
int GetSize(void) const { return m_Size; }
+
+ /** Returns the text describing the slime's size, as used by the client's resource subsystem for sounds.
+ Returns either "big" or "small". */
+ static AString GetSizeName(int a_Size);
protected:
- /// Size of the MagmaCube, 1 .. 3, with 1 being the smallest
+ /// Size of the MagmaCube, 1, 2 and 4, with 1 being the smallest
int m_Size;
} ;
diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp
index 37774b08f..2b00f6959 100644
--- a/src/Mobs/Monster.cpp
+++ b/src/Mobs/Monster.cpp
@@ -89,7 +89,7 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A
, m_SoundDeath(a_SoundDeath)
, m_AttackRate(3)
, m_AttackDamage(1)
- , m_AttackRange(2)
+ , m_AttackRange(1)
, m_AttackInterval(0)
, m_SightDistance(25)
, m_DropChanceWeapon(0.085f)
@@ -147,21 +147,40 @@ bool cMonster::TickPathFinding(cChunk & a_Chunk)
(Recalculate lots when close, calculate rarely when far) */
if (
((GetPosition() - m_PathFinderDestination).Length() < 0.25) ||
- ((m_TicksSinceLastPathReset > 10) && (m_TicksSinceLastPathReset > (0.15 * (m_FinalDestination - GetPosition()).SqrLength())))
+ ((m_TicksSinceLastPathReset > 10) && (m_TicksSinceLastPathReset > (0.4 * (m_FinalDestination - GetPosition()).SqrLength())))
)
{
- ResetPathFinding();
+ /* Re-calculating is expensive when there's no path to target, and it results in mobs freezing very often as a result of always recalculating.
+ This is a workaround till we get better path recalculation. */
+ if (!m_NoPathToTarget)
+ {
+ ResetPathFinding();
+ }
}
}
if (m_Path == nullptr)
{
+ if (!EnsureProperDestination(a_Chunk))
+ {
+ StopMovingToPosition(); // Invalid chunks, probably world is loading or something, cancel movement.
+ return false;
+ }
+ m_NoPathToTarget = false;
+ m_NoMoreWayPoints = false;
m_PathFinderDestination = m_FinalDestination;
m_Path = new cPath(a_Chunk, GetPosition().Floor(), m_PathFinderDestination.Floor(), 20);
}
switch (m_Path->Step(a_Chunk))
{
+ case ePathFinderStatus::NEARBY_FOUND:
+ {
+ m_NoPathToTarget = true;
+ m_Path->AcceptNearbyPath();
+ break;
+ }
+
case ePathFinderStatus::PATH_NOT_FOUND:
{
StopMovingToPosition(); // Give up pathfinding to that destination.
@@ -174,15 +193,22 @@ bool cMonster::TickPathFinding(cChunk & a_Chunk)
}
case ePathFinderStatus::PATH_FOUND:
{
- if (--m_GiveUpCounter == 0)
+ if (m_NoMoreWayPoints || (--m_GiveUpCounter == 0))
{
ResetPathFinding(); // Try to calculate a path again.
return false;
}
- else if (!m_Path->IsLastPoint() && (m_Path->IsFirstPoint() || ReachedNextWaypoint())) // Have we arrived at the next cell, as denoted by m_NextWayPointPosition?
+ else if (!m_Path->IsLastPoint()) // Have we arrived at the next cell, as denoted by m_NextWayPointPosition?
+ {
+ if ((m_Path->IsFirstPoint() || ReachedNextWaypoint()))
+ {
+ m_NextWayPointPosition = Vector3d(0.5, 0, 0.5) + m_Path->GetNextPoint();
+ m_GiveUpCounter = 40; // Give up after 40 ticks (2 seconds) if failed to reach m_NextWayPointPosition.
+ }
+ }
+ else
{
- m_NextWayPointPosition = Vector3d(0.5, 0, 0.5) + m_Path->GetNextPoint();
- m_GiveUpCounter = 40; // Give up after 40 ticks (2 seconds) if failed to reach m_NextWayPointPosition.
+ m_NoMoreWayPoints = true;
}
return true;
}
@@ -199,10 +225,12 @@ void cMonster::MoveToWayPoint(cChunk & a_Chunk)
{
if (m_JumpCoolDown == 0)
{
- // We're not moving (or barely moving), and waypoint is above us, it means we are hitting something and we should jump.
- if ((GetSpeedX() < 0.1) && (GetSpeedZ() < 0.1) && DoesPosYRequireJump(FloorC(m_NextWayPointPosition.y)))
+ if (DoesPosYRequireJump(FloorC(m_NextWayPointPosition.y)))
{
- if (IsOnGround() || IsSwimming())
+ if (
+ (IsOnGround() && (GetSpeedX() == 0) && (GetSpeedY() == 0)) ||
+ (IsSwimming() && (m_GiveUpCounter < 15))
+ )
{
m_bOnGround = false;
m_JumpCoolDown = 20;
@@ -252,6 +280,103 @@ void cMonster::MoveToWayPoint(cChunk & a_Chunk)
+bool cMonster::EnsureProperDestination(cChunk & a_Chunk)
+{
+ cChunk * Chunk = a_Chunk.GetNeighborChunk(FloorC(m_FinalDestination.x), FloorC(m_FinalDestination.z));
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+
+ if ((Chunk == nullptr) || !Chunk->IsValid())
+ {
+ return false;
+ }
+
+ int RelX = FloorC(m_FinalDestination.x) - Chunk->GetPosX() * cChunkDef::Width;
+ int RelZ = FloorC(m_FinalDestination.z) - Chunk->GetPosZ() * cChunkDef::Width;
+
+ // If destination in the air, first try to go 1 block north, or east, or west.
+ // This fixes the player leaning issue.
+ // If that failed, we instead go down to the lowest air block.
+ Chunk->GetBlockTypeMeta(RelX, FloorC(m_FinalDestination.y) - 1, RelZ, BlockType, BlockMeta);
+ if (!cBlockInfo::IsSolid(BlockType))
+ {
+ bool InTheAir = true;
+ int x, z;
+ for (z = -1; z <= 1; ++z)
+ {
+ for (x = -1; x <= 1; ++x)
+ {
+ if ((x==0) && (z==0))
+ {
+ continue;
+ }
+ Chunk = a_Chunk.GetNeighborChunk(FloorC(m_FinalDestination.x+x), FloorC(m_FinalDestination.z+z));
+ if ((Chunk == nullptr) || !Chunk->IsValid())
+ {
+ return false;
+ }
+ RelX = FloorC(m_FinalDestination.x+x) - Chunk->GetPosX() * cChunkDef::Width;
+ RelZ = FloorC(m_FinalDestination.z+z) - Chunk->GetPosZ() * cChunkDef::Width;
+ Chunk->GetBlockTypeMeta(RelX, FloorC(m_FinalDestination.y) - 1, RelZ, BlockType, BlockMeta);
+ if (cBlockInfo::IsSolid(BlockType))
+ {
+ m_FinalDestination.x += x;
+ m_FinalDestination.z += z;
+ InTheAir = false;
+ goto breakBothLoops;
+ }
+ }
+ }
+ breakBothLoops:
+
+ // Go down to the lowest air block.
+ if (InTheAir)
+ {
+ while (m_FinalDestination.y > 0)
+ {
+ Chunk->GetBlockTypeMeta(RelX, FloorC(m_FinalDestination.y) - 1, RelZ, BlockType, BlockMeta);
+ if (cBlockInfo::IsSolid(BlockType))
+ {
+ break;
+ }
+ m_FinalDestination.y -= 1;
+ }
+ }
+ }
+
+ // If destination in water, go up to the highest water block.
+ // If destination in solid, go up to first air block.
+ bool InWater = false;
+ while (m_FinalDestination.y < cChunkDef::Height)
+ {
+ Chunk->GetBlockTypeMeta(RelX, FloorC(m_FinalDestination.y), RelZ, BlockType, BlockMeta);
+ if (BlockType == E_BLOCK_STATIONARY_WATER)
+ {
+ InWater = true;
+ }
+ else if (cBlockInfo::IsSolid(BlockType))
+ {
+ InWater = false;
+ }
+ else
+ {
+ break;
+ }
+ m_FinalDestination.y += 1;
+ }
+ if (InWater)
+ {
+ m_FinalDestination.y -= 1;
+ }
+
+
+ return true;
+}
+
+
+
+
+
void cMonster::MoveToPosition(const Vector3d & a_Position)
{
m_FinalDestination = a_Position;
@@ -292,7 +417,7 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
if (m_Health <= 0)
{
- // The mob is dead, but we're still animating the "puff" they leave when they die
+ // The mob is dead, but we're still animating the "puff" they leave when they die.
m_DestroyTimer += a_Dt;
if (m_DestroyTimer > std::chrono::seconds(1))
{
@@ -310,11 +435,19 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
m_Target = nullptr;
}
- // Process the undead burning in daylight
+ // Process the undead burning in daylight.
HandleDaylightBurning(*Chunk, WouldBurnAt(GetPosition(), *Chunk));
if (TickPathFinding(*Chunk))
{
- if (m_BurnsInDaylight && WouldBurnAt(m_NextWayPointPosition, *Chunk->GetNeighborChunk(FloorC(m_NextWayPointPosition.x), FloorC(m_NextWayPointPosition.z))) && !IsOnFire() && (m_TicksSinceLastDamaged == 100))
+ /* If I burn in daylight, and I won't burn where I'm standing, and I'll burn in my next position, and at least one of those is true:
+ 1. I am idle
+ 2. I was not hurt by a player recently.
+ Then STOP. */
+ if (
+ m_BurnsInDaylight && ((m_TicksSinceLastDamaged >= 100) || (m_EMState == IDLE)) &&
+ WouldBurnAt(m_NextWayPointPosition, *Chunk) &&
+ !WouldBurnAt(GetPosition(), *Chunk)
+ )
{
// If we burn in daylight, and we would burn at the next step, and we won't burn where we are right now, and we weren't provoked recently:
StopMovingToPosition();
@@ -373,21 +506,29 @@ void cMonster::SetPitchAndYawFromDestination()
}
}
+
+
+ Vector3d BodyDistance = m_NextWayPointPosition - GetPosition();
+ double BodyRotation, BodyPitch;
+ BodyDistance.Normalize();
+ VectorToEuler(BodyDistance.x, BodyDistance.y, BodyDistance.z, BodyRotation, BodyPitch);
+ SetYaw(BodyRotation);
+
Vector3d Distance = FinalDestination - GetPosition();
{
- double Rotation, Pitch;
+ double HeadRotation, HeadPitch;
Distance.Normalize();
- VectorToEuler(Distance.x, Distance.y, Distance.z, Rotation, Pitch);
- SetHeadYaw(Rotation);
- SetPitch(-Pitch);
- }
-
- {
- Vector3d BodyDistance = m_NextWayPointPosition - GetPosition();
- double Rotation, Pitch;
- BodyDistance.Normalize();
- VectorToEuler(BodyDistance.x, BodyDistance.y, BodyDistance.z, Rotation, Pitch);
- SetYaw(Rotation);
+ VectorToEuler(Distance.x, Distance.y, Distance.z, HeadRotation, HeadPitch);
+ if (std::abs(BodyRotation - HeadRotation) < 120)
+ {
+ SetHeadYaw(HeadRotation);
+ SetPitch(-HeadPitch);
+ }
+ else // We're not an owl. If it's more than 120, don't look behind and instead look at where you're walking.
+ {
+ SetHeadYaw(BodyRotation);
+ SetPitch(-BodyPitch);
+ }
}
}
@@ -886,7 +1027,7 @@ cMonster * cMonster::NewMonsterFromType(eMonsterType a_MobType)
{
case mtMagmaCube:
{
- toReturn = new cMagmaCube(Random.NextInt(2) + 1);
+ toReturn = new cMagmaCube(1 << Random.NextInt(3)); // Size 1, 2 or 4
break;
}
case mtSlime:
@@ -1098,12 +1239,19 @@ void cMonster::HandleDaylightBurning(cChunk & a_Chunk, bool WouldBurn)
bool cMonster::WouldBurnAt(Vector3d a_Location, cChunk & a_Chunk)
{
- int RelX = FloorC(a_Location.x) - a_Chunk.GetPosX() * cChunkDef::Width;
+ cChunk * Chunk = a_Chunk.GetNeighborChunk(FloorC(a_Location.x), FloorC(a_Location.z));
+ if ((Chunk == nullptr) || (!Chunk->IsValid()))
+ {
+ return false;
+ }
+
+ int RelX = FloorC(a_Location.x) - Chunk->GetPosX() * cChunkDef::Width;
int RelY = FloorC(a_Location.y);
- int RelZ = FloorC(a_Location.z) - a_Chunk.GetPosZ() * cChunkDef::Width;
+ int RelZ = FloorC(a_Location.z) - Chunk->GetPosZ() * cChunkDef::Width;
+
if (
- (a_Chunk.GetSkyLight(RelX, RelY, RelZ) == 15) && // In the daylight
- (a_Chunk.GetBlock(RelX, RelY, RelZ) != E_BLOCK_SOULSAND) && // Not on soulsand
+ (Chunk->GetSkyLight(RelX, RelY, RelZ) == 15) && // In the daylight
+ (Chunk->GetBlock(RelX, RelY, RelZ) != E_BLOCK_SOULSAND) && // Not on soulsand
(GetWorld()->GetTimeOfDay() < (12000 + 1000)) && // It is nighttime
GetWorld()->IsWeatherSunnyAt(POSX_TOINT, POSZ_TOINT) // Not raining
)
@@ -1121,7 +1269,3 @@ cMonster::eFamily cMonster::GetMobFamily(void) const
{
return FamilyFromType(m_MobType);
}
-
-
-
-
diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h
index c7f38c9f7..c4043b0e5 100644
--- a/src/Mobs/Monster.h
+++ b/src/Mobs/Monster.h
@@ -180,6 +180,13 @@ protected:
/** Coordinates for the ultimate, final destination last given to the pathfinder. */
Vector3d m_PathFinderDestination;
+ /** True if there's no path to target and we're walking to an approximated location. */
+ bool m_NoPathToTarget;
+
+ /** Whether The mob has finished their path, note that this does not imply reaching the destination,
+ the destination may sometimes differ from the current path. */
+ bool m_NoMoreWayPoints;
+
/** Finds the lowest non-air block position (not the highest, as cWorld::GetHeight does)
If current Y is nonsolid, goes down to try to find a solid block, then returns that + 1
If current Y is solid, goes up to find first nonsolid block, and returns that.
@@ -203,8 +210,18 @@ protected:
Returns if a path is ready, and therefore if the mob should move to m_NextWayPointPosition
*/
bool TickPathFinding(cChunk & a_Chunk);
+
+ /** Move in a straight line to the next waypoint in the path, will jump if needed. */
void MoveToWayPoint(cChunk & a_Chunk);
+ /** Ensures the destination is not buried underground or under water. Also ensures the destination is not in the air.
+ Only the Y coordinate of m_FinalDestination might be changed.
+ 1. If m_FinalDestination is the position of a water block, m_FinalDestination's Y will be modified to point to the heighest water block in the pool in the current column.
+ 2. If m_FinalDestination is the position of a solid, m_FinalDestination's Y will be modified to point to the first airblock above the solid in the current column.
+ 3. If m_FinalDestination is the position of an air block, Y will keep decreasing until hitting either a solid or water.
+ Now either 1 or 2 is performed. */
+ bool EnsureProperDestination(cChunk & a_Chunk);
+
/** Resets a pathfinding task, be it due to failure or something else
Resets the pathfinder. If m_IsFollowingPath is true, TickPathFinding starts a brand new path.
Should only be called by the pathfinder, cMonster::Tick or StopMovingToPosition. */
@@ -214,7 +231,7 @@ protected:
Calls ResetPathFinding and sets m_IsFollowingPath to false */
void StopMovingToPosition();
- /** Sets the body yaw and head yaw/pitch based on next/ultimate destinations */
+ /** Sets the body yaw and head yaw / pitch based on next / ultimate destinations */
void SetPitchAndYawFromDestination(void);
virtual void HandleFalling(void);
diff --git a/src/Mobs/Path.cpp b/src/Mobs/Path.cpp
index 84d888bf2..ba8046a2b 100644
--- a/src/Mobs/Path.cpp
+++ b/src/Mobs/Path.cpp
@@ -1,3 +1,4 @@
+
#include "Globals.h"
#include <cmath>
@@ -7,7 +8,7 @@
#define DISTANCE_MANHATTAN 0 // 1: More speed, a bit less accuracy 0: Max accuracy, less speed.
#define HEURISTICS_ONLY 0 // 1: Much more speed, much less accurate.
-#define CALCULATIONS_PER_STEP 60 // Higher means more CPU load but faster path calculations.
+#define CALCULATIONS_PER_STEP 10 // Higher means more CPU load but faster path calculations.
// The only version which guarantees the shortest path is 0, 0.
enum class eCellStatus {OPENLIST, CLOSEDLIST, NOLIST};
@@ -43,7 +44,8 @@ cPath::cPath(
m_Destination(a_EndingPoint.Floor()),
m_Source(a_StartingPoint.Floor()),
m_CurrentPoint(0), // GetNextPoint increments this to 1, but that's fine, since the first cell is always a_StartingPoint
- m_Chunk(&a_Chunk)
+ m_Chunk(&a_Chunk),
+ m_BadChunkFound(false)
{
// TODO: if src not walkable OR dest not walkable, then abort.
// Borrow a new "isWalkable" from ProcessIfWalkable, make ProcessIfWalkable also call isWalkable
@@ -54,31 +56,7 @@ cPath::cPath(
return;
}
- // If destination in water, set water surface as destination.
- cChunk * Chunk = m_Chunk->GetNeighborChunk(m_Destination.x, m_Destination.z);
- if ((Chunk != nullptr) && Chunk->IsValid())
- {
- BLOCKTYPE BlockType;
- NIBBLETYPE BlockMeta;
- int RelX = m_Destination.x - Chunk->GetPosX() * cChunkDef::Width;
- int RelZ = m_Destination.z - Chunk->GetPosZ() * cChunkDef::Width;
- bool inwater = false;
- for (;;)
- {
- Chunk->GetBlockTypeMeta(RelX, m_Destination.y, RelZ, BlockType, BlockMeta);
- if (BlockType != E_BLOCK_STATIONARY_WATER)
- {
- break;
- }
- inwater = true;
- m_Destination+=Vector3d(0, 1, 0);
- }
- if (inwater)
- {
- m_Destination+=Vector3d(0, -1, 0);
- }
- }
-
+ m_NearestPointToTarget = GetCell(m_Source);
m_Status = ePathFinderStatus::CALCULATING;
m_StepsLeft = a_MaxSteps;
@@ -105,15 +83,20 @@ cPath::~cPath()
ePathFinderStatus cPath::Step(cChunk & a_Chunk)
{
m_Chunk = &a_Chunk;
-
if (m_Status != ePathFinderStatus::CALCULATING)
{
return m_Status;
}
- if (m_StepsLeft == 0)
+ if (m_BadChunkFound)
{
FinishCalculation(ePathFinderStatus::PATH_NOT_FOUND);
+ return m_Status;
+ }
+
+ if (m_StepsLeft == 0)
+ {
+ AttemptToFindAlternative();
}
else
{
@@ -126,9 +109,9 @@ ePathFinderStatus cPath::Step(cChunk & a_Chunk)
break; // if we're here, m_Status must have changed either to PATH_FOUND or PATH_NOT_FOUND.
}
}
- }
- m_Chunk = nullptr;
+ m_Chunk = nullptr;
+ }
return m_Status;
}
@@ -136,6 +119,17 @@ ePathFinderStatus cPath::Step(cChunk & a_Chunk)
+Vector3i cPath::AcceptNearbyPath()
+{
+ ASSERT(m_Status == ePathFinderStatus::NEARBY_FOUND);
+ m_Status = ePathFinderStatus::PATH_FOUND;
+ return m_Destination;
+}
+
+
+
+
+
bool cPath::IsSolid(const Vector3i & a_Location)
{
ASSERT(m_Chunk != nullptr);
@@ -143,6 +137,7 @@ bool cPath::IsSolid(const Vector3i & a_Location)
auto Chunk = m_Chunk->GetNeighborChunk(a_Location.x, a_Location.z);
if ((Chunk == nullptr) || !Chunk->IsValid())
{
+ m_BadChunkFound = true;
return true;
}
m_Chunk = Chunk;
@@ -173,34 +168,29 @@ bool cPath::Step_Internal()
{
cPathCell * CurrentCell = OpenListPop();
- // Path not reachable, open list exauhsted.
+ // Path not reachable.
if (CurrentCell == nullptr)
{
- FinishCalculation(ePathFinderStatus::PATH_NOT_FOUND);
- ASSERT(m_Status == ePathFinderStatus::PATH_NOT_FOUND);
+ AttemptToFindAlternative();
return true;
}
// Path found.
- if (
- (CurrentCell->m_Location == m_Destination + Vector3i(0, 0, 1)) ||
- (CurrentCell->m_Location == m_Destination + Vector3i(1, 0, 0)) ||
- (CurrentCell->m_Location == m_Destination + Vector3i(-1, 0, 0)) ||
- (CurrentCell->m_Location == m_Destination + Vector3i(0, 0, -1)) ||
- (CurrentCell->m_Location == m_Destination + Vector3i(0, -1, 0))
- )
+ if (CurrentCell->m_Location == m_Destination)
{
- do
- {
- m_PathPoints.push_back(CurrentCell->m_Location); // Populate the cPath with points.
- CurrentCell = CurrentCell->m_Parent;
- } while (CurrentCell != nullptr);
-
+ BuildPath();
FinishCalculation(ePathFinderStatus::PATH_FOUND);
return true;
}
- // Calculation not finished yet, process a currentCell by inspecting all neighbors.
+ // Calculation not finished yet.
+ // Check if we have a new NearestPoint.
+ if (CurrentCell->m_H < m_NearestPointToTarget->m_H)
+ {
+ m_NearestPointToTarget = CurrentCell;
+ }
+
+ // process a currentCell by inspecting all neighbors.
// Check North, South, East, West on all 3 different heights.
int i;
@@ -237,15 +227,42 @@ bool cPath::Step_Internal()
-void cPath::FinishCalculation()
+void cPath::AttemptToFindAlternative()
{
- for (auto && pair : m_Map)
+ if (m_NearestPointToTarget == GetCell(m_Source))
+ {
+ FinishCalculation(ePathFinderStatus::PATH_NOT_FOUND);
+ }
+ else
{
- delete pair.second;
+ m_Destination = m_NearestPointToTarget->m_Location;
+ BuildPath();
+ FinishCalculation(ePathFinderStatus::NEARBY_FOUND);
}
+}
+
+
+
+
+void cPath::BuildPath()
+{
+ cPathCell * CurrentCell = GetCell(m_Destination);
+ do
+ {
+ m_PathPoints.push_back(CurrentCell->m_Location); // Populate the cPath with points.
+ CurrentCell = CurrentCell->m_Parent;
+ } while (CurrentCell != nullptr);
+}
+
+
+
+
+
+void cPath::FinishCalculation()
+{
m_Map.clear();
- m_OpenList.empty();
+ m_OpenList = std::priority_queue<cPathCell *, std::vector<cPathCell *>, compareHeuristics>{};
}
@@ -254,6 +271,10 @@ void cPath::FinishCalculation()
void cPath::FinishCalculation(ePathFinderStatus a_NewStatus)
{
+ if (m_BadChunkFound)
+ {
+ a_NewStatus = ePathFinderStatus::PATH_NOT_FOUND;
+ }
m_Status = a_NewStatus;
FinishCalculation();
}
@@ -279,7 +300,7 @@ cPathCell * cPath::OpenListPop() // Popping from the open list also means addin
{
if (m_OpenList.size() == 0)
{
- return nullptr; // We've exhausted the search space and nothing was found, this will trigger a PATH_NOT_FOUND status.
+ return nullptr; // We've exhausted the search space and nothing was found, this will trigger a PATH_NOT_FOUND or NEARBY_FOUND status.
}
cPathCell * Ret = m_OpenList.top();
@@ -372,7 +393,7 @@ cPathCell * cPath::GetCell(const Vector3i & a_Location)
{
Cell = new cPathCell();
Cell->m_Location = a_Location;
- m_Map[a_Location] = Cell;
+ m_Map[a_Location] = UniquePtr<cPathCell>(Cell);
Cell->m_IsSolid = IsSolid(a_Location);
Cell->m_Status = eCellStatus::NOLIST;
#ifdef COMPILING_PATHFIND_DEBUGGER
@@ -384,6 +405,6 @@ cPathCell * cPath::GetCell(const Vector3i & a_Location)
}
else
{
- return m_Map[a_Location];
+ return m_Map[a_Location].get();
}
}
diff --git a/src/Mobs/Path.h b/src/Mobs/Path.h
index 9e893f1d7..7a4182f17 100644
--- a/src/Mobs/Path.h
+++ b/src/Mobs/Path.h
@@ -1,3 +1,4 @@
+
#pragma once
/* Wanna use the pathfinder? Put this in your header file:
@@ -11,7 +12,7 @@ Put this in your .cpp:
*/
#ifdef COMPILING_PATHFIND_DEBUGGER
- /* Note: the COMPILING_PATHFIND_DEBUGGER flag is used by Native/WiseOldMan95 to debug
+ /* Note: the COMPILING_PATHFIND_DEBUGGER flag is used by Native / WiseOldMan95 to debug
this class outside of MCServer. This preprocessor flag is never set when compiling MCServer. */
#include "PathFinderIrrlicht_Head.h"
#endif
@@ -22,7 +23,7 @@ Put this in your .cpp:
class cChunk;
/* Various little structs and classes */
-enum class ePathFinderStatus {CALCULATING, PATH_FOUND, PATH_NOT_FOUND};
+enum class ePathFinderStatus {CALCULATING, PATH_FOUND, PATH_NOT_FOUND, NEARBY_FOUND};
struct cPathCell; // Defined inside Path.cpp
class compareHeuristics
{
@@ -61,9 +62,17 @@ public:
/** Destroys the path and frees its memory. */
~cPath();
- /** Performs part of the path calculation and returns true if the path computation has finished. */
+ /** Performs part of the path calculation and returns the appropriate status.
+ If NEARBY_FOUND is returned, it means that the destination is not reachable, but a nearby destination
+ is reachable. If the user likes the alternative destination, they can call AcceptNearbyPath to treat the path as found,
+ and to make consequent calls to step return PATH_FOUND*/
ePathFinderStatus Step(cChunk & a_Chunk);
+ /** Called after the PathFinder's step returns NEARBY_FOUND.
+ Changes the PathFinder status from NEARBY_FOUND to PATH_FOUND, returns the nearby destination that
+ the PathFinder found a path to. */
+ Vector3i AcceptNearbyPath();
+
/* Point retrieval functions, inlined for performance. */
/** Returns the next point in the path. */
inline Vector3i GetNextPoint()
@@ -92,7 +101,10 @@ public:
/** Returns the total number of points this path has. */
inline int GetPointCount()
{
- ASSERT(m_Status == ePathFinderStatus::PATH_FOUND);
+ if (m_Status != ePathFinderStatus::PATH_FOUND)
+ {
+ return 0;
+ }
return m_PathPoints.size();
}
@@ -118,6 +130,8 @@ private:
bool Step_Internal(); // The public version just calls this version * CALCULATIONS_PER_CALL times.
void FinishCalculation(); // Clears the memory used for calculating the path.
void FinishCalculation(ePathFinderStatus a_NewStatus); // Clears the memory used for calculating the path and changes the status.
+ void AttemptToFindAlternative();
+ void BuildPath();
/* Openlist and closedlist management */
void OpenListAdd(cPathCell * a_Cell);
@@ -130,10 +144,11 @@ private:
/* Pathfinding fields */
std::priority_queue<cPathCell *, std::vector<cPathCell *>, compareHeuristics> m_OpenList;
- std::unordered_map<Vector3i, cPathCell *, VectorHasher> m_Map;
+ std::unordered_map<Vector3i, UniquePtr<cPathCell>, VectorHasher> m_Map;
Vector3i m_Destination;
Vector3i m_Source;
int m_StepsLeft;
+ cPathCell * m_NearestPointToTarget;
/* Control fields */
ePathFinderStatus m_Status;
@@ -144,6 +159,7 @@ private:
/* Interfacing with the world */
cChunk * m_Chunk; // Only valid inside Step()!
+ bool m_BadChunkFound;
#ifdef COMPILING_PATHFIND_DEBUGGER
#include "../path_irrlicht.cpp"
#endif
diff --git a/src/Mobs/Slime.cpp b/src/Mobs/Slime.cpp
index e42501e47..7fc4821d8 100644
--- a/src/Mobs/Slime.cpp
+++ b/src/Mobs/Slime.cpp
@@ -89,7 +89,7 @@ void cSlime::KilledBy(TakeDamageInfo & a_TDI)
-const AString cSlime::GetSizeName(int a_Size) const
+AString cSlime::GetSizeName(int a_Size)
{
if (a_Size > 1)
{
diff --git a/src/Mobs/Slime.h b/src/Mobs/Slime.h
index 29605992d..40131b101 100644
--- a/src/Mobs/Slime.h
+++ b/src/Mobs/Slime.h
@@ -27,7 +27,7 @@ public:
/** Returns the text describing the slime's size, as used by the client's resource subsystem for sounds.
Returns either "big" or "small". */
- const AString GetSizeName(int a_Size) const;
+ static AString GetSizeName(int a_Size);
protected:
diff --git a/src/Mobs/Wolf.cpp b/src/Mobs/Wolf.cpp
index c66763f17..3c2ec1520 100644
--- a/src/Mobs/Wolf.cpp
+++ b/src/Mobs/Wolf.cpp
@@ -5,6 +5,7 @@
#include "../World.h"
#include "../Entities/Player.h"
#include "../Items/ItemHandler.h"
+#include "Broadcaster.h"
@@ -83,13 +84,13 @@ void cWolf::OnRightClicked(cPlayer & a_Player)
SetIsTame(true);
SetOwner(a_Player.GetName(), a_Player.GetUUID());
m_World->BroadcastEntityStatus(*this, esWolfTamed);
- m_World->BroadcastParticleEffect("heart", (float) GetPosX(), (float) GetPosY(), (float) GetPosZ(), 0, 0, 0, 0, 5);
+ m_World->GetBroadcaster().BroadcastParticleEffect("heart", static_cast<Vector3f>(GetPosition()), Vector3f{}, 0, 5);
}
else
{
// Taming failed
m_World->BroadcastEntityStatus(*this, esWolfTaming);
- m_World->BroadcastParticleEffect("smoke", (float) GetPosX(), (float) GetPosY(), (float) GetPosZ(), 0, 0, 0, 0, 5);
+ m_World->GetBroadcaster().BroadcastParticleEffect("smoke", static_cast<Vector3f>(GetPosition()), Vector3f{}, 0, 5);
}
}
}