diff options
Diffstat (limited to 'src/Mobs/Monster.cpp')
-rw-r--r-- | src/Mobs/Monster.cpp | 513 |
1 files changed, 325 insertions, 188 deletions
diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index a86497753..1da4124ed 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -13,7 +13,7 @@ #include "../Chunk.h" #include "../FastRandom.h" - +#include "Path.h" @@ -38,6 +38,7 @@ static const struct {mtEnderman, "enderman", "Enderman"}, {mtEnderDragon, "enderdragon", "EnderDragon"}, {mtGhast, "ghast", "Ghast"}, + {mtGiant, "giant", "Giant"}, {mtGuardian, "guardian", "Guardian"}, {mtHorse, "horse", "EntityHorse"}, {mtIronGolem, "irongolem", "VillagerGolem"}, @@ -73,8 +74,12 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A , m_EMState(IDLE) , m_EMPersonality(AGGRESSIVE) , m_Target(nullptr) - , m_bMovingToDestination(false) + , m_Path(nullptr) + , m_IsFollowingPath(false) + , m_GiveUpCounter(0) + , m_TicksSinceLastPathReset(1000) , m_LastGroundHeight(POSY_TOINT) + , m_JumpCoolDown(0) , m_IdleInterval(0) , m_DestroyTimer(0) , m_MobType(a_MobType) @@ -84,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) @@ -93,8 +98,9 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A , m_DropChanceLeggings(0.085f) , m_DropChanceBoots(0.085f) , m_CanPickUpLoot(true) + , m_TicksSinceLastDamaged(100) , m_BurnsInDaylight(false) - , m_RelativeWalkSpeed(1.0) + , m_RelativeWalkSpeed(1) { if (!a_ConfigName.empty()) { @@ -115,89 +121,159 @@ void cMonster::SpawnOn(cClientHandle & a_Client) -void cMonster::TickPathFinding() +bool cMonster::TickPathFinding(cChunk & a_Chunk) { - const int PosX = POSX_TOINT; - const int PosY = POSY_TOINT; - const int PosZ = POSZ_TOINT; - - std::vector<Vector3d> m_PotentialCoordinates; - m_TraversedCoordinates.push_back(Vector3i(PosX, PosY, PosZ)); - - static const struct // Define which directions to try to move to + if (!m_IsFollowingPath) { - int x, z; - } gCrossCoords[] = + return false; + } + if (m_TicksSinceLastPathReset < 1000) { - { 1, 0}, - {-1, 0}, - { 0, 1}, - { 0, -1}, - } ; - - if ((PosY - 1 < 0) || (PosY + 2 >= cChunkDef::Height) /* PosY + 1 will never be true if PosY + 2 is not */) + // No need to count beyond 1000. 1000 is arbitary here. + ++m_TicksSinceLastPathReset; + } + + if (ReachedFinalDestination()) { - // Too low/high, can't really do anything - FinishPathFinding(); - return; + StopMovingToPosition(); + return false; } - for (size_t i = 0; i < ARRAYCOUNT(gCrossCoords); i++) + if ((m_FinalDestination - m_PathFinderDestination).Length() > 0.25) // if the distance between where we're going and where we should go is too big. { - if (IsCoordinateInTraversedList(Vector3i(gCrossCoords[i].x + PosX, PosY, gCrossCoords[i].z + PosZ))) + /* If we reached the last path waypoint, + Or if we haven't re-calculated for too long. + Interval is proportional to distance squared, and its minimum is 10. + (Recalculate lots when close, calculate rarely when far) */ + if ( + ((GetPosition() - m_PathFinderDestination).Length() < 0.25) || + ((m_TicksSinceLastPathReset > 10) && (m_TicksSinceLastPathReset > (0.4 * (m_FinalDestination - GetPosition()).SqrLength()))) + ) { - continue; + /* 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(); + } } + } - BLOCKTYPE BlockAtY = m_World->GetBlock(gCrossCoords[i].x + PosX, PosY, gCrossCoords[i].z + PosZ); - BLOCKTYPE BlockAtYP = m_World->GetBlock(gCrossCoords[i].x + PosX, PosY + 1, gCrossCoords[i].z + PosZ); - BLOCKTYPE BlockAtYPP = m_World->GetBlock(gCrossCoords[i].x + PosX, PosY + 2, gCrossCoords[i].z + PosZ); - int LowestY = FindFirstNonAirBlockPosition(gCrossCoords[i].x + PosX, gCrossCoords[i].z + PosZ); - BLOCKTYPE BlockAtLowestY = (LowestY >= cChunkDef::Height) ? E_BLOCK_AIR : m_World->GetBlock(gCrossCoords[i].x + PosX, LowestY, gCrossCoords[i].z + PosZ); + 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(), m_PathFinderDestination, 20, GetWidth(), GetHeight()); + } - if ( - (!cBlockInfo::IsSolid(BlockAtY)) && - (!cBlockInfo::IsSolid(BlockAtYP)) && - (!IsBlockLava(BlockAtLowestY)) && - (BlockAtLowestY != E_BLOCK_CACTUS) && - (PosY - LowestY < FALL_DAMAGE_HEIGHT) - ) + switch (m_Path->Step(a_Chunk)) + { + case ePathFinderStatus::NEARBY_FOUND: { - m_PotentialCoordinates.push_back(Vector3d((gCrossCoords[i].x + PosX), PosY, gCrossCoords[i].z + PosZ)); + m_NoPathToTarget = true; + m_PathFinderDestination = m_Path->AcceptNearbyPath(); + break; } - else if ( - (cBlockInfo::IsSolid(BlockAtY)) && - (BlockAtY != E_BLOCK_CACTUS) && - (!cBlockInfo::IsSolid(BlockAtYP)) && - (!cBlockInfo::IsSolid(BlockAtYPP)) && - (BlockAtY != E_BLOCK_FENCE) && - (BlockAtY != E_BLOCK_FENCE_GATE) - ) + + case ePathFinderStatus::PATH_NOT_FOUND: + { + StopMovingToPosition(); // Try to calculate a path again. + // Note that the next time may succeed, e.g. if a player breaks a barrier. + break; + } + case ePathFinderStatus::CALCULATING: { - m_PotentialCoordinates.push_back(Vector3d((gCrossCoords[i].x + PosX), PosY + 1, gCrossCoords[i].z + PosZ)); + // Pathfinder needs more time + break; + } + case ePathFinderStatus::PATH_FOUND: + { + if (m_NoMoreWayPoints || (--m_GiveUpCounter == 0)) + { + ResetPathFinding(); // Try to calculate a path again. + return false; + } + else if (!m_Path->IsLastPoint()) // Have we arrived at the next cell, as denoted by m_NextWayPointPosition? + { + if ((m_Path->IsFirstPoint() || ReachedNextWaypoint())) + { + m_NextWayPointPosition = m_Path->GetNextPoint(); + m_GiveUpCounter = 40; // Give up after 40 ticks (2 seconds) if failed to reach m_NextWayPointPosition. + } + } + else + { + m_NoMoreWayPoints = true; + } + return true; } } - if (!m_PotentialCoordinates.empty()) + return false; +} + + + + + +void cMonster::MoveToWayPoint(cChunk & a_Chunk) +{ + if (m_JumpCoolDown == 0) { - Vector3f ShortestCoords = m_PotentialCoordinates.front(); - for (std::vector<Vector3d>::const_iterator itr = m_PotentialCoordinates.begin(); itr != m_PotentialCoordinates.end(); ++itr) + if (DoesPosYRequireJump(FloorC(m_NextWayPointPosition.y))) { - Vector3f Distance = m_FinalDestination - ShortestCoords; - Vector3f Distance2 = m_FinalDestination - *itr; - if (Distance.SqrLength() > Distance2.SqrLength()) + if ( + (IsOnGround() && (GetSpeedX() == 0) && (GetSpeedY() == 0)) || + (IsSwimming() && (m_GiveUpCounter < 15)) + ) { - ShortestCoords = *itr; + m_bOnGround = false; + m_JumpCoolDown = 20; + // TODO: Change to AddSpeedY once collision detection is fixed - currently, mobs will go into blocks attempting to jump without a teleport + AddPosY(1.6); // Jump!! + SetSpeedX(3.2 * (m_NextWayPointPosition.x - GetPosition().x)); // Move forward in a preset speed. + SetSpeedZ(3.2 * (m_NextWayPointPosition.z - GetPosition().z)); // The numbers were picked based on trial and error and 1.6 and 3.2 are perfect. } } - - m_Destination = ShortestCoords; - m_Destination.z += 0.5f; - m_Destination.x += 0.5f; } else { - FinishPathFinding(); + --m_JumpCoolDown; + } + + Vector3d Distance = m_NextWayPointPosition - GetPosition(); + if ((Distance.x != 0) || (Distance.z != 0)) + { + Distance.y = 0; + Distance.Normalize(); + + if (m_bOnGround) + { + Distance *= 2.5f; + } + else if (IsSwimming()) + { + Distance *= 1.3f; + } + else + { + // Don't let the mob move too much if he's falling. + Distance *= 0.25f; + } + // Apply walk speed: + Distance *= m_RelativeWalkSpeed; + /* Reduced default speed. + Close to Vanilla, easier for mobs to follow m_NextWayPointPositions, hence + better pathfinding. */ + Distance *= 0.5; + AddSpeedX(Distance.x); + AddSpeedZ(Distance.z); } } @@ -205,47 +281,130 @@ void cMonster::TickPathFinding() -void cMonster::MoveToPosition(const Vector3d & a_Position) +bool cMonster::EnsureProperDestination(cChunk & a_Chunk) { - FinishPathFinding(); + 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; + } - m_FinalDestination = a_Position; - m_bMovingToDestination = true; - TickPathFinding(); + + return true; } -bool cMonster::IsCoordinateInTraversedList(Vector3i a_Coords) + + +void cMonster::MoveToPosition(const Vector3d & a_Position) { - return (std::find(m_TraversedCoordinates.begin(), m_TraversedCoordinates.end(), a_Coords) != m_TraversedCoordinates.end()); + m_FinalDestination = a_Position; + m_IsFollowingPath = true; } -bool cMonster::ReachedDestination() +void cMonster::StopMovingToPosition() { - if ((m_Destination - GetPosition()).Length() < 0.5f) - { - return true; - } - - return false; + m_IsFollowingPath = false; } -bool cMonster::ReachedFinalDestination() + +void cMonster::ResetPathFinding(void) { - if ((GetPosition() - m_FinalDestination).Length() <= m_AttackRange) + m_TicksSinceLastPathReset = 0; + if (m_Path != nullptr) { - return true; + delete m_Path; + m_Path = nullptr; } - - return false; } @@ -255,10 +414,11 @@ bool cMonster::ReachedFinalDestination() void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); + GET_AND_VERIFY_CURRENT_CHUNK(Chunk, POSX_TOINT, POSZ_TOINT); 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)) { @@ -267,73 +427,36 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) return; } + if (m_TicksSinceLastDamaged < 100) + { + ++m_TicksSinceLastDamaged; + } if ((m_Target != nullptr) && m_Target->IsDestroyed()) { m_Target = nullptr; } - // Burning in daylight - HandleDaylightBurning(a_Chunk); - - if (m_bMovingToDestination) + // Process the undead burning in daylight. + HandleDaylightBurning(*Chunk, WouldBurnAt(GetPosition(), *Chunk)); + if (TickPathFinding(*Chunk)) { - if (m_bOnGround) - { - if (DoesPosYRequireJump((int)floor(m_Destination.y))) - { - m_bOnGround = false; - - // TODO: Change to AddSpeedY once collision detection is fixed - currently, mobs will go into blocks attempting to jump without a teleport - AddPosY(1.2); // Jump!! - } - } - - Vector3d Distance = m_Destination - GetPosition(); - if (!ReachedDestination() && !ReachedFinalDestination()) // If we haven't reached any sort of destination, move + /* 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) + ) { - Distance.y = 0; - Distance.Normalize(); - - if (m_bOnGround) - { - Distance *= 2.5f; - } - else if (IsSwimming()) - { - Distance *= 1.3f; - } - else - { - // Don't let the mob move too much if he's falling. - Distance *= 0.25f; - } - - // Apply walk speed: - Distance *= m_RelativeWalkSpeed; - - AddSpeedX(Distance.x); - AddSpeedZ(Distance.z); - - // It's too buggy! - /* - if (m_EMState == ESCAPING) - { - // Runs Faster when escaping :D otherwise they just walk away - SetSpeedX (GetSpeedX() * 2.f); - SetSpeedZ (GetSpeedZ() * 2.f); - } - */ + // 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(); + m_GiveUpCounter = 40; // This doesn't count as giving up, keep the giveup timer as is. } else { - if (ReachedFinalDestination()) // If we have reached the ultimate, final destination, stop pathfinding and attack if appropriate - { - FinishPathFinding(); - } - else - { - TickPathFinding(); // We have reached the next point in our path, calculate another point - } + MoveToWayPoint(*Chunk); } } @@ -344,13 +467,13 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { case IDLE: { - // If enemy passive we ignore checks for player visibility + // If enemy passive we ignore checks for player visibility. InStateIdle(a_Dt); break; } case CHASING: { - // If we do not see a player anymore skip chasing action + // If we do not see a player anymore skip chasing action. InStateChasing(a_Dt); break; } @@ -359,7 +482,6 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) InStateEscaping(a_Dt); break; } - case ATTACKING: break; } // switch (m_EMState) @@ -369,6 +491,7 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) + void cMonster::SetPitchAndYawFromDestination() { Vector3d FinalDestination = m_FinalDestination; @@ -376,31 +499,36 @@ void cMonster::SetPitchAndYawFromDestination() { if (m_Target->IsPlayer()) { - FinalDestination.y = ((cPlayer *)m_Target)->GetStance(); + FinalDestination.y = static_cast<cPlayer *>(m_Target)->GetStance() - 1; } else { - FinalDestination.y = GetHeight(); + FinalDestination.y = m_Target->GetPosY() + GetHeight(); } } + + + 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(); - if (Distance.SqrLength() > 0.1f) { + double HeadRotation, HeadPitch; + Distance.Normalize(); + VectorToEuler(Distance.x, Distance.y, Distance.z, HeadRotation, HeadPitch); + if (std::abs(BodyRotation - HeadRotation) < 120) { - double Rotation, Pitch; - Distance.Normalize(); - VectorToEuler(Distance.x, Distance.y, Distance.z, Rotation, Pitch); - SetHeadYaw(Rotation); - SetPitch(-Pitch); + 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. { - Vector3d BodyDistance = m_Destination - GetPosition(); - double Rotation, Pitch; - Distance.Normalize(); - VectorToEuler(BodyDistance.x, BodyDistance.y, BodyDistance.z, Rotation, Pitch); - SetYaw(Rotation); + SetHeadYaw(BodyRotation); + SetPitch(-BodyPitch); } } } @@ -408,6 +536,7 @@ void cMonster::SetPitchAndYawFromDestination() + void cMonster::HandleFalling() { if (m_bOnGround) @@ -459,7 +588,6 @@ int cMonster::FindFirstNonAirBlockPosition(double a_PosX, double a_PosZ) - bool cMonster::DoTakeDamage(TakeDamageInfo & a_TDI) { if (!super::DoTakeDamage(a_TDI)) @@ -475,6 +603,7 @@ bool cMonster::DoTakeDamage(TakeDamageInfo & a_TDI) if (a_TDI.Attacker != nullptr) { m_Target = a_TDI.Attacker; + m_TicksSinceLastDamaged = 0; } return true; } @@ -640,7 +769,7 @@ void cMonster::EventLosePlayer(void) void cMonster::InStateIdle(std::chrono::milliseconds a_Dt) { - if (m_bMovingToDestination) + if (m_IsFollowingPath) { return; // Still getting there } @@ -660,14 +789,8 @@ void cMonster::InStateIdle(std::chrono::milliseconds a_Dt) if ((Dist.SqrLength() > 2) && (rem >= 3)) { Vector3d Destination(GetPosX() + Dist.x, 0, GetPosZ() + Dist.z); - - int NextHeight = FindFirstNonAirBlockPosition(Destination.x, Destination.z); - - if (IsNextYPosReachable(NextHeight)) - { - Destination.y = NextHeight; - MoveToPosition(Destination); - } + Destination.y = FindFirstNonAirBlockPosition(Destination.x, Destination.z); + MoveToPosition(Destination); } } } @@ -691,7 +814,7 @@ void cMonster::InStateChasing(std::chrono::milliseconds a_Dt) void cMonster::InStateEscaping(std::chrono::milliseconds a_Dt) { UNUSED(a_Dt); - + if (m_Target != nullptr) { Vector3d newloc = GetPosition(); @@ -770,7 +893,7 @@ AString cMonster::MobTypeToString(eMonsterType a_MobType) return g_MobTypeNames[i].m_lcName; } } - + // Not found: return ""; } @@ -865,7 +988,7 @@ cMonster::eFamily cMonster::FamilyFromType(eMonsterType a_Type) case mtWolf: return mfHostile; case mtZombie: return mfHostile; case mtZombiePigman: return mfHostile; - + case mtInvalidType: break; } ASSERT(!"Unhandled mob type"); @@ -905,7 +1028,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: @@ -1040,7 +1163,7 @@ void cMonster::AddRandomArmorDropItem(cItems & a_Drops, short a_LootingLevel) a_Drops.push_back(GetEquippedHelmet()); } } - + if (r1.randInt() % 200 < ((m_DropChanceChestplate * 200) + (a_LootingLevel * 2))) { if (!GetEquippedChestplate().IsEmpty()) @@ -1048,7 +1171,7 @@ void cMonster::AddRandomArmorDropItem(cItems & a_Drops, short a_LootingLevel) a_Drops.push_back(GetEquippedChestplate()); } } - + if (r1.randInt() % 200 < ((m_DropChanceLeggings * 200) + (a_LootingLevel * 2))) { if (!GetEquippedLeggings().IsEmpty()) @@ -1056,7 +1179,7 @@ void cMonster::AddRandomArmorDropItem(cItems & a_Drops, short a_LootingLevel) a_Drops.push_back(GetEquippedLeggings()); } } - + if (r1.randInt() % 200 < ((m_DropChanceBoots * 200) + (a_LootingLevel * 2))) { if (!GetEquippedBoots().IsEmpty()) @@ -1086,36 +1209,26 @@ void cMonster::AddRandomWeaponDropItem(cItems & a_Drops, short a_LootingLevel) -void cMonster::HandleDaylightBurning(cChunk & a_Chunk) +void cMonster::HandleDaylightBurning(cChunk & a_Chunk, bool WouldBurn) { if (!m_BurnsInDaylight) { return; } - + int RelY = POSY_TOINT; if ((RelY < 0) || (RelY >= cChunkDef::Height)) { // Outside the world return; } - - int RelX = POSX_TOINT - GetChunkX() * cChunkDef::Width; - int RelZ = POSZ_TOINT - GetChunkZ() * cChunkDef::Width; - if (!a_Chunk.IsLightValid()) { m_World->QueueLightChunk(GetChunkX(), GetChunkZ()); return; } - if ( - (a_Chunk.GetSkyLight(RelX, RelY, RelZ) == 15) && // In the daylight - (a_Chunk.GetBlock(RelX, RelY, RelZ) != E_BLOCK_SOULSAND) && // Not on soulsand - (GetWorld()->GetTimeOfDay() < (12000 + 1000)) && // It is nighttime - !IsOnFire() && // Not already burning - GetWorld()->IsWeatherSunnyAt(POSX_TOINT, POSZ_TOINT) // Not raining - ) + if (!IsOnFire() && WouldBurn) { // Burn for 100 ticks, then decide again StartBurning(100); @@ -1125,11 +1238,35 @@ void cMonster::HandleDaylightBurning(cChunk & a_Chunk) -cMonster::eFamily cMonster::GetMobFamily(void) const +bool cMonster::WouldBurnAt(Vector3d a_Location, cChunk & a_Chunk) { - return FamilyFromType(m_MobType); + 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) - Chunk->GetPosZ() * cChunkDef::Width; + + if ( + (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 + ) + { + return true; + } + return false; } + +cMonster::eFamily cMonster::GetMobFamily(void) const +{ + return FamilyFromType(m_MobType); +} |